Provide way to add (optional) user initials

This makes it easier to hook the changes package into LyX's ct markup.
This commit is contained in:
Juergen Spitzmueller 2019-12-24 14:31:08 +01:00
parent 0139bcf48f
commit a08cbf41cf
13 changed files with 169 additions and 84 deletions

View File

@ -19,12 +19,12 @@ Format 80
AddToPreamble AddToPreamble
\usepackage{changebar} \usepackage{changebar}
\providecommand{\lyxadded}[3]{} \providecommand{\lyxadded}[4][]{}
\providecommand{\lyxdeleted}{} \providecommand{\lyxdeleted}{}
\renewcommand{\lyxadded}[3]{ \renewcommand{\lyxadded}[4][]{
{\protect\cbstart\color{lyxadded}{}#3\protect\cbend} {\protect\cbstart\color{lyxadded}{}#4\protect\cbend}
} }
\renewcommand{\lyxdeleted}[3]{% \renewcommand{\lyxdeleted}[4][]{%
{\protect\cbstart\color{lyxdeleted}\sout{#3}\protect\cbend} {\protect\cbstart\color{lyxdeleted}\sout{#4}\protect\cbend}
} }
EndPreamble EndPreamble

View File

@ -37,14 +37,14 @@ static int computeHash(docstring const & name,
} }
Author::Author(docstring const & name, docstring const & email) Author::Author(docstring const & name, docstring const & email, docstring const & initials)
: name_(name), email_(email), used_(true), : name_(name), email_(email), initials_(initials), used_(true),
buffer_id_(computeHash(name, email)) buffer_id_(computeHash(name, email))
{} {}
Author::Author(int buffer_id) Author::Author(int buffer_id)
: name_(convert<docstring>(buffer_id)), email_(docstring()), used_(false), : name_(convert<docstring>(buffer_id)), email_(docstring()), initials_(docstring()), used_(false),
buffer_id_(buffer_id) buffer_id_(buffer_id)
{} {}
@ -67,7 +67,7 @@ bool Author::valid() const
bool operator==(Author const & l, Author const & r) bool operator==(Author const & l, Author const & r)
{ {
return l.name() == r.name() && l.email() == r.email(); return l.name() == r.name() && l.email() == r.email() && l.initials() == r.initials();
} }

View File

@ -24,7 +24,8 @@ public:
/// ///
Author() : used_(false), buffer_id_(0) {}; Author() : used_(false), buffer_id_(0) {};
/// ///
Author(docstring const & name, docstring const & email); Author(docstring const & name, docstring const & email,
docstring const & initials);
/// For when the \author line is missing (#9854) /// For when the \author line is missing (#9854)
Author(int buffer_id); Author(int buffer_id);
/// ///
@ -32,6 +33,8 @@ public:
/// ///
docstring email() const { return email_; } docstring email() const { return email_; }
/// ///
docstring initials() const { return initials_; }
///
docstring nameAndEmail() const; docstring nameAndEmail() const;
/// ///
int bufferId() const { return buffer_id_; } int bufferId() const { return buffer_id_; }
@ -53,6 +56,8 @@ private:
docstring name_; docstring name_;
/// The author's email address /// The author's email address
docstring email_; docstring email_;
/// The author's initials
docstring initials_;
/// ///
mutable bool used_; mutable bool used_;
/// The id of the author in the lyx-file /// The id of the author in the lyx-file

View File

@ -365,7 +365,9 @@ BufferParams::Impl::Impl()
{ {
// set initial author // set initial author
// FIXME UNICODE // FIXME UNICODE
authorlist.record(Author(from_utf8(lyxrc.user_name), from_utf8(lyxrc.user_email))); authorlist.record(Author(from_utf8(lyxrc.user_name),
from_utf8(lyxrc.user_email),
from_utf8(lyxrc.user_initials)));
} }

View File

@ -338,7 +338,7 @@ void Changes::merge()
namespace { namespace {
docstring getLaTeXMarkup(docstring const & macro, docstring const & author, docstring getLaTeXMarkup(docstring const & macro, Author const & author,
docstring const & chgTime, docstring const & chgTime,
OutputParams const & runparams) OutputParams const & runparams)
{ {
@ -348,18 +348,52 @@ docstring getLaTeXMarkup(docstring const & macro, docstring const & author,
docstring uncodable_author; docstring uncodable_author;
odocstringstream ods; odocstringstream ods;
docstring const author_name = author.name();
docstring const author_initials = author.initials();
ods << macro; ods << macro;
if (!author_initials.empty()) {
docstring uncodable_initials;
// convert utf8 author initials to something representable
// in the current encoding
pair<docstring, docstring> author_initials_latexed =
runparams.encoding->latexString(author_initials, runparams.dryrun);
if (!author_initials_latexed.second.empty()) {
LYXERR0("Omitting uncodable characters '"
<< author_initials_latexed.second
<< "' in change author initials!");
uncodable_initials = author_initials;
}
ods << "[" << author_initials_latexed.first << "]";
// warn user (once) if we found uncodable glyphs.
if (!uncodable_initials.empty()) {
static std::set<docstring> warned_author_initials;
static Mutex warned_mutex;
Mutex::Locker locker(&warned_mutex);
if (warned_author_initials.find(uncodable_initials) == warned_author_initials.end()) {
frontend::Alert::warning(_("Uncodable character in author initials"),
support::bformat(_("The author initials '%1$s',\n"
"used for change tracking, contain the following glyphs that\n"
"cannot be represented in the current encoding: %2$s.\n"
"These glyphs will be omitted in the exported LaTeX file.\n\n"
"Choose an appropriate document encoding (such as utf8)\n"
"or change the author initials."),
uncodable_initials, author_initials_latexed.second));
warned_author_initials.insert(uncodable_author);
}
}
}
// convert utf8 author name to something representable // convert utf8 author name to something representable
// in the current encoding // in the current encoding
pair<docstring, docstring> author_latexed = pair<docstring, docstring> author_latexed =
runparams.encoding->latexString(author, runparams.dryrun); runparams.encoding->latexString(author_name, runparams.dryrun);
if (!author_latexed.second.empty()) { if (!author_latexed.second.empty()) {
LYXERR0("Omitting uncodable characters '" LYXERR0("Omitting uncodable characters '"
<< author_latexed.second << author_latexed.second
<< "' in change author name!"); << "' in change author name!");
uncodable_author = author; uncodable_author = author_name;
} }
ods << author_latexed.first << "}{" << chgTime << "}{"; ods << "{" << author_latexed.first << "}{" << chgTime << "}{";
// warn user (once) if we found uncodable glyphs. // warn user (once) if we found uncodable glyphs.
if (!uncodable_author.empty()) { if (!uncodable_author.empty()) {
@ -414,15 +448,15 @@ int Changes::latexMarkChange(otexstream & os, BufferParams const & bparams,
docstring macro_beg; docstring macro_beg;
if (change.type == Change::DELETED) { if (change.type == Change::DELETED) {
macro_beg = from_ascii("\\lyxdeleted{"); macro_beg = from_ascii("\\lyxdeleted");
if (!runparams.inDisplayMath && !dvipost) if (!runparams.inDisplayMath && !dvipost)
++runparams.inulemcmd; ++runparams.inulemcmd;
} }
else if (change.type == Change::INSERTED) else if (change.type == Change::INSERTED)
macro_beg = from_ascii("\\lyxadded{"); macro_beg = from_ascii("\\lyxadded");
docstring str = getLaTeXMarkup(macro_beg, docstring str = getLaTeXMarkup(macro_beg,
bparams.authors().get(change.author).name(), bparams.authors().get(change.author),
chgTime, runparams); chgTime, runparams);
// signature needed by \lyxsout to correctly strike out display math // signature needed by \lyxsout to correctly strike out display math

View File

@ -242,20 +242,20 @@ static docstring const changetracking_dvipost_def = from_ascii(
"\\dvipost{osend color pop}\n" "\\dvipost{osend color pop}\n"
"\\dvipost{cbstart color push Blue}\n" "\\dvipost{cbstart color push Blue}\n"
"\\dvipost{cbend color pop}\n" "\\dvipost{cbend color pop}\n"
"\\DeclareRobustCommand{\\lyxadded}[3]{\\changestart#3\\changeend}\n" "\\DeclareRobustCommand{\\lyxadded}[4][]{\\changestart#4\\changeend}\n"
"\\DeclareRobustCommand{\\lyxdeleted}[3]{%\n" "\\DeclareRobustCommand{\\lyxdeleted}[4][]{%\n"
"\\changestart\\overstrikeon#3\\overstrikeoff\\changeend}\n"); "\\changestart\\overstrikeon#4\\overstrikeoff\\changeend}\n");
static docstring const changetracking_xcolor_ulem_def = from_ascii( static docstring const changetracking_xcolor_ulem_def = from_ascii(
"%% Change tracking with ulem\n" "%% Change tracking with ulem\n"
"\\DeclareRobustCommand{\\lyxadded}[3]{{\\color{lyxadded}{}#3}}\n" "\\DeclareRobustCommand{\\lyxadded}[4][]{{\\color{lyxadded}{}#4}}\n"
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\color{lyxdeleted}\\lyxsout{#3}}}\n" "\\DeclareRobustCommand{\\lyxdeleted}[4][]{{\\color{lyxdeleted}\\lyxsout{#4}}}\n"
"\\DeclareRobustCommand{\\lyxsout}[1]{\\ifx\\\\#1\\else\\sout{#1}\\fi}\n"); "\\DeclareRobustCommand{\\lyxsout}[1]{\\ifx\\\\#1\\else\\sout{#1}\\fi}\n");
static docstring const changetracking_xcolor_ulem_hyperref_def = from_ascii( static docstring const changetracking_xcolor_ulem_hyperref_def = from_ascii(
"%% Change tracking with ulem\n" "%% Change tracking with ulem\n"
"\\DeclareRobustCommand{\\lyxadded}[3]{{\\texorpdfstring{\\color{lyxadded}{}}{}#3}}\n" "\\DeclareRobustCommand{\\lyxadded}[4][]{{\\texorpdfstring{\\color{lyxadded}{}}{}#4}}\n"
"\\DeclareRobustCommand{\\lyxdeleted}[3]{{\\texorpdfstring{\\color{lyxdeleted}\\lyxsout{#3}}{}}}\n" "\\DeclareRobustCommand{\\lyxdeleted}[4][]{{\\texorpdfstring{\\color{lyxdeleted}\\lyxsout{#4}}{}}}\n"
"\\DeclareRobustCommand{\\lyxsout}[1]{\\ifx\\\\#1\\else\\sout{#1}\\fi}\n"); "\\DeclareRobustCommand{\\lyxsout}[1]{\\ifx\\\\#1\\else\\sout{#1}\\fi}\n");
static docstring const changetracking_tikz_math_sout_def = from_ascii( static docstring const changetracking_tikz_math_sout_def = from_ascii(

View File

@ -201,6 +201,7 @@ LexerKeyword lyxrcTags[] = {
{ "\\use_system_theme_icons", LyXRC::RC_USE_SYSTEM_THEME_ICONS }, { "\\use_system_theme_icons", LyXRC::RC_USE_SYSTEM_THEME_ICONS },
{ "\\use_tooltip", LyXRC::RC_USE_TOOLTIP }, { "\\use_tooltip", LyXRC::RC_USE_TOOLTIP },
{ "\\user_email", LyXRC::RC_USER_EMAIL }, { "\\user_email", LyXRC::RC_USER_EMAIL },
{ "\\user_initials", LyXRC::RC_USER_INITIALS },
{ "\\user_name", LyXRC::RC_USER_NAME }, { "\\user_name", LyXRC::RC_USER_NAME },
{ "\\view_dvi_paper_option", LyXRC::RC_VIEWDVI_PAPEROPTION }, { "\\view_dvi_paper_option", LyXRC::RC_VIEWDVI_PAPEROPTION },
// compatibility with versions older than 1.4.0 only // compatibility with versions older than 1.4.0 only
@ -971,6 +972,9 @@ LyXRC::ReturnValues LyXRC::read(Lexer & lexrc, bool check_format)
case RC_USER_EMAIL: case RC_USER_EMAIL:
lexrc >> user_email; lexrc >> user_email;
break; break;
case RC_USER_INITIALS:
lexrc >> user_initials;
break;
case RC_PATH_PREFIX: case RC_PATH_PREFIX:
lexrc >> path_prefix; lexrc >> path_prefix;
@ -1446,6 +1450,11 @@ void LyXRC::write(ostream & os, bool ignore_system_lyxrc, string const & name) c
if (tag != RC_LAST) if (tag != RC_LAST)
break; break;
// fall through // fall through
case RC_USER_INITIALS:
os << "\\user_initials \"" << user_initials << "\"\n";
if (tag != RC_LAST)
break;
// fall through
case RC_SHOW_BANNER: case RC_SHOW_BANNER:
if (ignore_system_lyxrc || if (ignore_system_lyxrc ||
show_banner != system_lyxrc.show_banner) { show_banner != system_lyxrc.show_banner) {
@ -2869,6 +2878,7 @@ void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
case LyXRC::RC_THESAURUSDIRPATH: case LyXRC::RC_THESAURUSDIRPATH:
case LyXRC::RC_UIFILE: case LyXRC::RC_UIFILE:
case LyXRC::RC_USER_EMAIL: case LyXRC::RC_USER_EMAIL:
case LyXRC::RC_USER_INITIALS:
case LyXRC::RC_USER_NAME: case LyXRC::RC_USER_NAME:
case LyXRC::RC_USE_CONVERTER_CACHE: case LyXRC::RC_USE_CONVERTER_CACHE:
case LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN: case LyXRC::RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN:

View File

@ -169,6 +169,7 @@ public:
RC_UIFILE, RC_UIFILE,
RC_USELASTFILEPOS, RC_USELASTFILEPOS,
RC_USER_EMAIL, RC_USER_EMAIL,
RC_USER_INITIALS,
RC_USER_NAME, RC_USER_NAME,
RC_USE_CONVERTER_CACHE, RC_USE_CONVERTER_CACHE,
RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN, RC_USE_CONVERTER_NEEDAUTH_FORBIDDEN,
@ -449,6 +450,8 @@ public:
std::string user_name; // set in constructor std::string user_name; // set in constructor
/// user email /// user email
std::string user_email; // set in constructor (empty for now) std::string user_email; // set in constructor (empty for now)
/// user initials
std::string user_initials;
/// icon set name /// icon set name
std::string icon_set; std::string icon_set;
/// whether to use the icons from the theme /// whether to use the icons from the theme

View File

@ -3409,9 +3409,12 @@ PrefIdentity::PrefIdentity(GuiPreferences * form)
this, SIGNAL(changed())); this, SIGNAL(changed()));
connect(emailED, SIGNAL(textChanged(QString)), connect(emailED, SIGNAL(textChanged(QString)),
this, SIGNAL(changed())); this, SIGNAL(changed()));
connect(initialsED, SIGNAL(textChanged(QString)),
this, SIGNAL(changed()));
nameED->setValidator(new NoNewLineValidator(nameED)); nameED->setValidator(new NoNewLineValidator(nameED));
emailED->setValidator(new NoNewLineValidator(emailED)); emailED->setValidator(new NoNewLineValidator(emailED));
initialsED->setValidator(new NoNewLineValidator(initialsED));
} }
@ -3419,6 +3422,7 @@ void PrefIdentity::applyRC(LyXRC & rc) const
{ {
rc.user_name = fromqstr(nameED->text()); rc.user_name = fromqstr(nameED->text());
rc.user_email = fromqstr(emailED->text()); rc.user_email = fromqstr(emailED->text());
rc.user_initials = fromqstr(initialsED->text());
} }
@ -3426,6 +3430,7 @@ void PrefIdentity::updateRC(LyXRC const & rc)
{ {
nameED->setText(toqstr(rc.user_name)); nameED->setText(toqstr(rc.user_name));
emailED->setText(toqstr(rc.user_email)); emailED->setText(toqstr(rc.user_email));
initialsED->setText(toqstr(rc.user_initials));
} }
@ -3565,7 +3570,8 @@ void GuiPreferences::dispatchParams()
// FIXME: these need lfuns // FIXME: these need lfuns
// FIXME UNICODE // FIXME UNICODE
Author const & author = Author const & author =
Author(from_utf8(rc_.user_name), from_utf8(rc_.user_email)); Author(from_utf8(rc_.user_name), from_utf8(rc_.user_email),
from_utf8(rc_.user_initials));
theBufferList().recordCurrentAuthor(author); theBufferList().recordCurrentAuthor(author);
theFormats() = formats_; theFormats() = formats_;

View File

@ -1,77 +1,99 @@
<ui version="4.0" > <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PrefIdentityUi</class> <class>PrefIdentityUi</class>
<widget class="QWidget" name="PrefIdentityUi" > <widget class="QWidget" name="PrefIdentityUi">
<property name="geometry" > <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>388</width> <width>433</width>
<height>209</height> <height>135</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle">
<string/> <string/>
</property> </property>
<layout class="QVBoxLayout" > <layout class="QGridLayout" name="gridLayout_2">
<property name="margin" > <item row="0" column="0">
<number>11</number> <layout class="QGridLayout" name="gridLayout">
</property> <item row="0" column="0">
<property name="spacing" > <widget class="QLabel" name="nameLA">
<number>6</number> <property name="text">
</property>
<item>
<layout class="QGridLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item row="1" column="0" >
<widget class="QLabel" name="emailLA" >
<property name="text" >
<string>&amp;E-mail:</string>
</property>
<property name="buddy" >
<cstring>emailED</cstring>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLineEdit" name="nameED" >
<property name="toolTip" >
<string>Your name</string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="nameLA" >
<property name="text" >
<string>&amp;Name:</string> <string>&amp;Name:</string>
</property> </property>
<property name="buddy" > <property name="buddy">
<cstring>nameED</cstring> <cstring>nameED</cstring>
</property> </property>
</widget> </widget>
</item> </item>
<item row="1" column="1" > <item row="0" column="1">
<widget class="QLineEdit" name="emailED" > <layout class="QHBoxLayout" name="horizontalLayout">
<property name="toolTip" > <item>
<widget class="QLineEdit" name="nameED">
<property name="toolTip">
<string>Your name</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="initialsLA">
<property name="text">
<string>&amp;Initials:</string>
</property>
<property name="buddy">
<cstring>initialsED</cstring>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="initialsED">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip">
<string>Initials of your name</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="emailLA">
<property name="text">
<string>&amp;E-mail:</string>
</property>
<property name="buddy">
<cstring>emailED</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="emailED">
<property name="toolTip">
<string>Your E-mail address</string> <string>Your E-mail address</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item> <item row="1" column="0">
<spacer> <spacer>
<property name="orientation" > <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
</property> </property>
<property name="sizeType" > <property name="sizeType">
<enum>QSizePolicy::Expanding</enum> <enum>QSizePolicy::Expanding</enum>
</property> </property>
<property name="sizeHint" > <property name="sizeHint" stdset="0">
<size> <size>
<width>20</width> <width>20</width>
<height>20</height> <height>20</height>
@ -81,14 +103,13 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<pixmapfunction></pixmapfunction>
<includes>
<include location="local" >qt_i18n.h</include>
</includes>
<tabstops> <tabstops>
<tabstop>nameED</tabstop> <tabstop>nameED</tabstop>
<tabstop>emailED</tabstop> <tabstop>emailED</tabstop>
</tabstops> </tabstops>
<includes>
<include location="local">qt_i18n.h</include>
</includes>
<resources/> <resources/>
<connections/> <connections/>
</ui> </ui>

View File

@ -411,9 +411,9 @@ void Preamble::suppressDate(bool suppress)
} }
void Preamble::registerAuthor(std::string const & name) void Preamble::registerAuthor(std::string const & name, string const & initials)
{ {
Author author(from_utf8(name), empty_docstring()); Author author(from_utf8(name), empty_docstring(), from_utf8(initials));
author.setUsed(true); author.setUsed(true);
authors_.record(author); authors_.record(author);
h_tracking_changes = "true"; h_tracking_changes = "true";
@ -423,7 +423,7 @@ void Preamble::registerAuthor(std::string const & name)
Author const & Preamble::getAuthor(std::string const & name) const Author const & Preamble::getAuthor(std::string const & name) const
{ {
Author author(from_utf8(name), empty_docstring()); Author author(from_utf8(name), empty_docstring(), empty_docstring());
for (AuthorList::Authors::const_iterator it = authors_.begin(); for (AuthorList::Authors::const_iterator it = authors_.begin();
it != authors_.end(); ++it) it != authors_.end(); ++it)
if (*it == author) if (*it == author)

View File

@ -92,7 +92,7 @@ public:
/// ///
void titleLayoutFound(bool found) { title_layout_found = found; } void titleLayoutFound(bool found) { title_layout_found = found; }
/// Register an author named \p name in the author list /// Register an author named \p name in the author list
void registerAuthor(std::string const & name); void registerAuthor(std::string const & name, std::string const & initials);
/// Get author named \p name (must be registered first) /// Get author named \p name (must be registered first)
Author const & getAuthor(std::string const & name) const; Author const & getAuthor(std::string const & name) const;
/// Get number of arguments of special table column type \c or -1 /// Get number of arguments of special table column type \c or -1

View File

@ -4053,9 +4053,13 @@ void parse_text(Parser & p, ostream & os, unsigned flags, bool outer,
if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") { if (t.cs() == "lyxadded" || t.cs() == "lyxdeleted") {
context.check_layout(os); context.check_layout(os);
string initials;
if (p.hasOpt()) {
initials = p.getArg('[', ']');
}
string name = p.getArg('{', '}'); string name = p.getArg('{', '}');
string localtime = p.getArg('{', '}'); string localtime = p.getArg('{', '}');
preamble.registerAuthor(name); preamble.registerAuthor(name, initials);
Author const & author = preamble.getAuthor(name); Author const & author = preamble.getAuthor(name);
// from_asctime_utc() will fail if LyX decides to output the // from_asctime_utc() will fail if LyX decides to output the
// time in the text language. // time in the text language.