]> git.lyx.org Git - lyx.git/blob - src/insets/InsetInfo.cpp
DocBook: in InsetInfo, ensure that no db:date is inserted within a db:date.
[lyx.git] / src / insets / InsetInfo.cpp
1 /**
2  * \file InsetInfo.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Bo Peng
7  * \author Jürgen Spitzmüller
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11 #include <config.h>
12
13 #include "InsetInfo.h"
14 #include "LyX.h"
15 #include "Buffer.h"
16 #include "BufferParams.h"
17 #include "BufferView.h"
18 #include "Changes.h"
19 #include "Cursor.h"
20 #include "CutAndPaste.h"
21 #include "Font.h"
22 #include "FuncRequest.h"
23 #include "FuncStatus.h"
24 #include "InsetGraphics.h"
25 #include "InsetSpecialChar.h"
26 #include "KeyMap.h"
27 #include "LaTeXFeatures.h"
28 #include "Language.h"
29 #include "LayoutFile.h"
30 #include "LyXAction.h"
31 #include "LyXRC.h"
32 #include "LyXVC.h"
33 #include "Lexer.h"
34 #include "output_docbook.h"
35 #include "Paragraph.h"
36 #include "ParIterator.h"
37 #include "ParagraphParameters.h"
38 #include "version.h"
39
40 #include "frontends/Application.h"
41
42 #include "support/Changer.h"
43 #include "support/convert.h"
44 #include "support/debug.h"
45 #include "support/docstream.h"
46 #include "support/docstring_list.h"
47 #include "support/ExceptionMessage.h"
48 #include "support/FileName.h"
49 #include "support/filetools.h"
50 #include "support/gettext.h"
51 #include "support/Length.h"
52 #include "support/Messages.h"
53 #include "support/lstrings.h"
54 #include "support/qstring_helpers.h"
55 #include "support/Translator.h"
56
57 #include <sstream>
58 #include <tuple>
59
60 #include <QtGui/QImage>
61 #include <QDate>
62 #include <QLocale>
63
64 using namespace std;
65 using namespace lyx::support;
66
67 namespace lyx {
68
69 namespace {
70
71 typedef Translator<InsetInfoParams::info_type, string> NameTranslator;
72
73 NameTranslator const initTranslator()
74 {
75         NameTranslator translator(InsetInfoParams::UNKNOWN_INFO, "unknown");
76
77         translator.addPair(InsetInfoParams::SHORTCUTS_INFO, "shortcuts");
78         translator.addPair(InsetInfoParams::SHORTCUT_INFO, "shortcut");
79         translator.addPair(InsetInfoParams::LYXRC_INFO, "lyxrc");
80         translator.addPair(InsetInfoParams::PACKAGE_INFO, "package");
81         translator.addPair(InsetInfoParams::TEXTCLASS_INFO, "textclass");
82         translator.addPair(InsetInfoParams::MENU_INFO, "menu");
83         translator.addPair(InsetInfoParams::L7N_INFO, "l7n");
84         translator.addPair(InsetInfoParams::ICON_INFO, "icon");
85         translator.addPair(InsetInfoParams::BUFFER_INFO, "buffer");
86         translator.addPair(InsetInfoParams::LYX_INFO, "lyxinfo");
87         translator.addPair(InsetInfoParams::VCS_INFO, "vcs");
88         translator.addPair(InsetInfoParams::DATE_INFO, "date");
89         translator.addPair(InsetInfoParams::MODDATE_INFO, "moddate");
90         translator.addPair(InsetInfoParams::FIXDATE_INFO, "fixdate");
91         translator.addPair(InsetInfoParams::TIME_INFO, "time");
92         translator.addPair(InsetInfoParams::MODTIME_INFO, "modtime");
93         translator.addPair(InsetInfoParams::FIXTIME_INFO, "fixtime");
94
95         return translator;
96 }
97
98 /// The translator between the information type enum and corresponding string.
99 NameTranslator const & nameTranslator()
100 {
101         static NameTranslator const translator = initTranslator();
102         return translator;
103 }
104
105
106 typedef Translator<InsetInfoParams::info_type, string> DefaultValueTranslator;
107
108 DefaultValueTranslator const initDVTranslator()
109 {
110         DefaultValueTranslator translator(InsetInfoParams::UNKNOWN_INFO, "");
111
112         translator.addPair(InsetInfoParams::SHORTCUTS_INFO, "info-insert");
113         translator.addPair(InsetInfoParams::SHORTCUT_INFO, "info-insert");
114         translator.addPair(InsetInfoParams::LYXRC_INFO, "user_name");
115         translator.addPair(InsetInfoParams::PACKAGE_INFO, "graphics");
116         translator.addPair(InsetInfoParams::TEXTCLASS_INFO, "article");
117         translator.addPair(InsetInfoParams::MENU_INFO, "info-insert");
118         translator.addPair(InsetInfoParams::L7N_INFO, "");
119         translator.addPair(InsetInfoParams::ICON_INFO, "info-insert");
120         translator.addPair(InsetInfoParams::BUFFER_INFO, "name-noext");
121         translator.addPair(InsetInfoParams::LYX_INFO, "version");
122         translator.addPair(InsetInfoParams::VCS_INFO, "revision");
123         translator.addPair(InsetInfoParams::DATE_INFO, "loclong");
124         translator.addPair(InsetInfoParams::MODDATE_INFO, "loclong");
125         translator.addPair(InsetInfoParams::FIXDATE_INFO, "loclong");
126         translator.addPair(InsetInfoParams::TIME_INFO, "long");
127         translator.addPair(InsetInfoParams::MODTIME_INFO, "long");
128         translator.addPair(InsetInfoParams::FIXTIME_INFO, "long");
129
130         return translator;
131 }
132
133 /// The translator between the information type enum and some sensible default value.
134 DefaultValueTranslator const & defaultValueTranslator()
135 {
136         static DefaultValueTranslator const translator = initDVTranslator();
137         return translator;
138 }
139
140 } // namespace
141
142
143 /////////////////////////////////////////////////////////////////////
144 //
145 // InsetInfoParams
146 //
147 ///////////////////////////////////////////////////////////////////////
148
149 InsetInfoParams infoparams;
150
151 namespace{
152 set<string> getTexFileList(string const & filename)
153 {
154         set<string> list;
155         FileName const file = libFileSearch(string(), filename);
156         if (file.empty())
157                 return list;
158
159         // FIXME Unicode.
160         vector<docstring> doclist =
161                 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
162
163         // Normalise paths like /foo//bar ==> /foo/bar
164         // No "auto const &" because doc is modified later
165         // coverity[auto_causes_copy]
166         for (auto doc : doclist) {
167                 doc = subst(doc, from_ascii("\r"), docstring());
168                 while (contains(doc, from_ascii("//")))
169                         doc = subst(doc, from_ascii("//"), from_ascii("/"));
170                 if (!doc.empty())
171                         list.insert(removeExtension(onlyFileName(to_utf8(doc))));
172         }
173
174         // remove duplicates
175         return list;
176 }
177
178 bool translateString(docstring const & in, docstring & out, string const & lcode)
179 {
180         out = translateIfPossible(in, lcode);
181         return in != out;
182 }
183 } // namespace anon
184
185
186 docstring InsetInfoParams::getDate(string const & iname, QDate const date) const
187 {
188         QLocale loc;
189         if (lang)
190                 loc = QLocale(toqstr(lang->code()));
191         if (iname == "long")
192                 return qstring_to_ucs4(loc.toString(date, QLocale::LongFormat));
193         else if (iname == "short")
194                 return qstring_to_ucs4(loc.toString(date, QLocale::ShortFormat));
195         else if (iname == "ISO")
196                 return qstring_to_ucs4(date.toString(Qt::ISODate));
197         else if (iname == "loclong")
198                 return lang ? qstring_to_ucs4(loc.toString(date, toqstr(lang->dateFormat(0))))
199                             : _("No long date format (language unknown)!");
200         else if (iname == "locmedium")
201                 return lang ? qstring_to_ucs4(loc.toString(date, toqstr(lang->dateFormat(1))))
202                             : _("No medium date format (language unknown)!");
203         else if (iname == "locshort")
204                 return lang ? qstring_to_ucs4(loc.toString(date, toqstr(lang->dateFormat(2))))
205                                 : _("No short date format (language unknown)!");
206         else
207                 return qstring_to_ucs4(loc.toString(date, toqstr(iname)));
208 }
209
210
211 docstring InsetInfoParams::getTime(string const & iname, QTime const time) const
212 {
213         QLocale loc;
214         if (lang)
215                 loc = QLocale(toqstr(lang->code()));
216         if (iname == "long")
217                 return qstring_to_ucs4(loc.toString(time, QLocale::LongFormat));
218         else if (iname == "short")
219                 return qstring_to_ucs4(loc.toString(time, QLocale::ShortFormat));
220         else if (iname == "ISO")
221                 return qstring_to_ucs4(time.toString(Qt::ISODate));
222         else
223                 return qstring_to_ucs4(loc.toString(time, toqstr(iname)));
224 }
225
226
227 vector<pair<string,docstring>> InsetInfoParams::getArguments(Buffer const * buf,
228                                                              string const & itype) const
229 {
230         vector<pair<string,docstring>> result;
231
232         switch (nameTranslator().find(itype)) {
233         case UNKNOWN_INFO:
234                 result.push_back(make_pair("invalid", _("Please select a valid type!")));
235                 break;
236
237         case SHORTCUT_INFO:
238         case SHORTCUTS_INFO:
239         case MENU_INFO:
240         case ICON_INFO: {
241                 result.push_back(make_pair("custom", _("Custom")));
242                 for (auto const & name_code : lyxaction) {
243                         string const lfun = name_code.first;
244                         if (!lfun.empty())
245                                 result.push_back(make_pair(lfun, from_ascii(lfun)));
246                 }
247                 break;
248         }
249
250         case L7N_INFO:
251                 result.push_back(make_pair("custom", _("Custom")));
252                 break;
253
254         case LYXRC_INFO: {
255                 result.push_back(make_pair("custom", _("Custom")));
256                 set<string> rcs = lyxrc.getRCs();
257                 for (auto const & rc : rcs)
258                         result.push_back(make_pair(rc, from_ascii(rc)));
259                 break;
260         }
261
262         case PACKAGE_INFO:
263         case TEXTCLASS_INFO: {
264                 result.push_back(make_pair("custom", _("Custom")));
265                 string const filename = (itype == "package") ? "styFiles.lst"
266                                                             : "clsFiles.lst";
267                 set<string> flist = getTexFileList(filename);
268                 for (auto const & f : flist)
269                         result.push_back(make_pair(f, from_utf8(f)));
270                 break;
271         }
272
273         case BUFFER_INFO:
274                 result.push_back(make_pair("name", _("File name (with extension)")));
275                 result.push_back(make_pair("name-noext", _("File name (without extension)")));
276                 result.push_back(make_pair("path", _("File path")));
277                 result.push_back(make_pair("class", _("Used text class")));
278                 break;
279
280         case VCS_INFO: {
281                 if (!buf->lyxvc().inUse()) {
282                         result.push_back(make_pair("invalid", _("No version control!")));
283                         break;
284                 }
285                 result.push_back(make_pair("revision", _("Revision[[Version Control]]")));
286                 result.push_back(make_pair("revision-abbrev", _("Abbreviated revision[[Version Control]]")));
287                 result.push_back(make_pair("tree-revision", _("Tree revision")));
288                 result.push_back(make_pair("author", _("Author")));
289                 result.push_back(make_pair("date", _("Date")));
290                 result.push_back(make_pair("time", _("Time[[of day]]")));
291                 break;
292         }
293
294         case LYX_INFO:
295                 result.push_back(make_pair("version", _("LyX version")));
296                 result.push_back(make_pair("layoutformat", _("LyX layout format")));
297                 break;
298
299         case FIXDATE_INFO:
300         case DATE_INFO:
301         case MODDATE_INFO: {
302                 // TODO: away from a release, use parseDate instead.
303                 string const dt = split(name, '@');
304                 QDate date;
305                 if (itype == "moddate")
306 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
307                         date = QDateTime::fromSecsSinceEpoch(buf->fileName().lastModified()).date();
308 #else
309                         date = QDateTime::fromTime_t(buf->fileName().lastModified()).date();
310 #endif
311                 else if (itype == "fixdate" && !dt.empty()) {
312                         QDate const gdate = QDate::fromString(toqstr(dt), Qt::ISODate);
313                         date = (gdate.isValid()) ? gdate : QDate::currentDate();
314                 } else
315                         date = QDate::currentDate();
316                 result.push_back(make_pair("long",getDate("long", date)));
317                 result.push_back(make_pair("short", getDate("short", date)));
318                 result.push_back(make_pair("loclong", getDate("loclong", date)));
319                 result.push_back(make_pair("locmedium", getDate("locmedium", date)));
320                 result.push_back(make_pair("locshort", getDate("locshort", date)));
321                 result.push_back(make_pair("ISO", getDate("ISO", date)));
322                 result.push_back(make_pair("yyyy", getDate("yyyy", date)));
323                 result.push_back(make_pair("MMMM", getDate("MMMM", date)));
324                 result.push_back(make_pair("MMM", getDate("MMM", date)));
325                 result.push_back(make_pair("dddd", getDate("dddd", date)));
326                 result.push_back(make_pair("ddd", getDate("ddd", date)));
327                 result.push_back(make_pair("custom", _("Custom")));
328                 break;
329         }
330         case FIXTIME_INFO:
331         case TIME_INFO:
332         case MODTIME_INFO: {
333                 // TODO: away from a release, use parseTime instead.
334                 string const tt = split(name, '@');
335                 QTime time;
336                 if (itype == "modtime")
337 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
338                         time = QDateTime::fromSecsSinceEpoch(buf->fileName().lastModified()).time();
339 #else
340                         time = QDateTime::fromTime_t(buf->fileName().lastModified()).time();
341 #endif
342                 else if (itype == "fixtime" && !tt.empty()) {
343                         QTime const gtime = QTime::fromString(toqstr(tt), Qt::ISODate);
344                         time = (gtime.isValid()) ? gtime : QTime::currentTime();
345                 } else
346                         time = QTime::currentTime();
347                 result.push_back(make_pair("long",getTime("long", time)));
348                 result.push_back(make_pair("short", getTime("short", time)));
349                 result.push_back(make_pair("ISO", getTime("ISO", time)));
350                 result.push_back(make_pair("custom", _("Custom")));
351                 break;
352         }
353         }
354
355         return result;
356 }
357
358
359 bool InsetInfoParams::validateArgument(Buffer const * buf, docstring const & arg,
360                                        bool const usedefaults) const
361 {
362         string type;
363         string name = trim(split(to_utf8(arg), type, ' '));
364         if (name.empty() && usedefaults)
365                 name = defaultValueTranslator().find(type);
366
367         switch (nameTranslator().find(type)) {
368         case UNKNOWN_INFO:
369                 return false;
370
371         case SHORTCUT_INFO:
372         case SHORTCUTS_INFO:
373         case MENU_INFO: {
374                 FuncRequest func = lyxaction.lookupFunc(name);
375                 return func.action() != LFUN_UNKNOWN_ACTION;
376         }
377
378         case L7N_INFO:
379                 return !name.empty();
380
381         case ICON_INFO: {
382                 FuncCode const action = lyxaction.lookupFunc(name).action();
383                 if (action == LFUN_UNKNOWN_ACTION) {
384                         string dir = "images";
385                         return !imageLibFileSearch(dir, name, "svgz,png").empty();
386                 }
387                 return true;
388         }
389
390         case LYXRC_INFO: {
391                 set<string> rcs = lyxrc.getRCs();
392                 return rcs.find(name) != rcs.end();
393         }
394
395         case PACKAGE_INFO:
396         case TEXTCLASS_INFO:
397                 return true;
398
399         case BUFFER_INFO:
400                 return (name == "name" || name == "name-noext"
401                         || name == "path" || name == "class");
402
403         case VCS_INFO:
404                 if (name == "revision" || name == "revision-abbrev" || name == "tree-revision"
405                     || name == "author" || name == "date" || name == "time")
406                         return buf->lyxvc().inUse();
407                 return false;
408
409         case LYX_INFO:
410                 return name == "version" || name == "layoutformat";
411
412         case FIXDATE_INFO: {
413                 string date;
414                 string piece;
415                 date = split(name, piece, '@');
416                 if (!date.empty() && !QDate::fromString(toqstr(date), Qt::ISODate).isValid())
417                         return false;
418                 if (!piece.empty())
419                         name = piece;
420         }
421         // fall through
422         case DATE_INFO:
423         case MODDATE_INFO: {
424                 if (name == "long" || name == "short" || name == "ISO")
425                         return true;
426                 else {
427                         QDate date = QDate::currentDate();
428                         return !date.toString(toqstr(name)).isEmpty();
429                 }
430         }
431         case FIXTIME_INFO: {
432                 string time;
433                 string piece;
434                 time = split(name, piece, '@');
435                 if (!time.empty() && !QTime::fromString(toqstr(time), Qt::ISODate).isValid())
436                         return false;
437                 if (!piece.empty())
438                         name = piece;
439         }
440         // fall through
441         case TIME_INFO:
442         case MODTIME_INFO: {
443                 if (name == "long" || name == "short" || name == "ISO")
444                         return true;
445                 else {
446                         QTime time = QTime::currentTime();
447                         return !time.toString(toqstr(name)).isEmpty();
448                 }
449         }
450         }
451
452         return false;
453 }
454
455
456
457
458 string InsetInfoParams::infoType() const
459 {
460         return nameTranslator().find(type);
461 }
462
463
464
465 /////////////////////////////////////////////////////////////////////////
466 //
467 // InsetInfo
468 //
469 /////////////////////////////////////////////////////////////////////////
470
471
472 namespace {
473
474 class InsetGraphicsTight : public InsetGraphics
475 {
476 public:
477         ///
478         explicit InsetGraphicsTight(Buffer * buf) : InsetGraphics(buf) {}
479
480         ///
481         int leftOffset(BufferView const *) const override { return 0; }
482         ///
483         int rightOffset(BufferView const *) const override { return 0; }
484 };
485
486 }
487
488
489 InsetInfo::InsetInfo(Buffer * buf, string const & info)
490         : InsetCollapsible(buf), initialized_(false)
491 {
492         params_.type = InsetInfoParams::UNKNOWN_INFO;
493         params_.force_ltr = false;
494         setInfo(info);
495         status_ = Collapsed;
496 }
497
498
499 Inset * InsetInfo::editXY(Cursor & cur, int x, int y)
500 {
501         // do not allow the cursor to be set in this Inset
502         return Inset::editXY(cur, x, y);
503 }
504
505
506 docstring InsetInfo::layoutName() const
507 {
508         return from_ascii("Info:" + params_.infoType());
509 }
510
511
512 docstring InsetInfo::toolTip(BufferView const &, int, int) const
513 {
514         docstring result;
515         switch (nameTranslator().find(params_.infoType())) {
516         case InsetInfoParams::UNKNOWN_INFO:
517                 result = _("Invalid information inset");
518                 break;
519         case InsetInfoParams::SHORTCUT_INFO:
520                 result = bformat(_("The keybard shortcut for the function '%1$s'"),
521                                 from_utf8(params_.name));
522                 break;
523         case InsetInfoParams::SHORTCUTS_INFO:
524                 result = bformat(_("The keybard shortcuts for the function '%1$s'"),
525                                 from_utf8(params_.name));
526                 break;
527         case InsetInfoParams::MENU_INFO: 
528                 result = bformat(_("The menu location for the function '%1$s'"),
529                                 from_utf8(params_.name));
530                 break;
531         case InsetInfoParams::L7N_INFO: 
532                 result = bformat(_("The localization for the string '%1$s'"),
533                                 from_utf8(params_.name));
534                 break;
535         case InsetInfoParams::ICON_INFO:
536                 result = bformat(_("The toolbar icon for the function '%1$s'"),
537                                 from_utf8(params_.name));
538                 break;
539         case InsetInfoParams::LYXRC_INFO:
540                 result = bformat(_("The preference setting for the preference key '%1$s'"),
541                                 from_utf8(params_.name));
542                 break;
543         case InsetInfoParams::PACKAGE_INFO:
544                 result = bformat(_("Availability of the LaTeX package '%1$s'"),
545                                 from_utf8(params_.name));
546                 break;
547         case InsetInfoParams::TEXTCLASS_INFO:
548                 result = bformat(_("Availability of the LaTeX class '%1$s'"),
549                                 from_utf8(params_.name));
550                 break;
551         case InsetInfoParams::BUFFER_INFO:
552                 if (params_.name == "name")
553                         result = _("The name of this file (incl. extension)");
554                 else if (params_.name == "name-noext")
555                         result = _("The name of this file (without extension)");
556                 else if (params_.name == "path")
557                         result = _("The path where this file is saved");
558                 else if (params_.name == "class")
559                         result = _("The class this document uses");
560                 break;
561         case InsetInfoParams::VCS_INFO:
562                 if (params_.name == "revision")
563                         result = _("Version control revision");
564                 else if (params_.name == "revision-abbrev")
565                         result = _("Version control abbreviated revision");
566                 else if (params_.name == "tree-revision")
567                         result = _("Version control tree revision");
568                 else if (params_.name == "author")
569                          result = _("Version control author");
570                 else if (params_.name == "date")
571                         result = _("Version control date");
572                 else if (params_.name == "time")
573                         result = _("Version control time");
574                 break;
575         case InsetInfoParams::LYX_INFO:
576                 if (params_.name == "version")
577                         result = _("The current LyX version");
578                 else if (params_.name == "layoutformat")
579                         result = _("The current LyX layout format");
580                 break;
581         case InsetInfoParams::DATE_INFO:
582                 result = _("The current date");
583                 break;
584         case InsetInfoParams::MODDATE_INFO:
585                 result = _("The date of last save");
586                 break;
587         case InsetInfoParams::FIXDATE_INFO:
588                 result = _("A static date");
589                 break;
590         case InsetInfoParams::TIME_INFO:
591                 result = _("The current time");
592                 break;
593         case InsetInfoParams::MODTIME_INFO:
594                 result = _("The time of last save");
595                 break;
596         case InsetInfoParams::FIXTIME_INFO:
597                 result = _("A static time");
598                 break;
599         }
600
601         return result;
602 }
603
604
605 void InsetInfo::read(Lexer & lex)
606 {
607         string token;
608         while (lex.isOK()) {
609                 lex.next();
610                 token = lex.getString();
611                 if (token == "type") {
612                         lex.next();
613                         token = lex.getString();
614                         params_.type = nameTranslator().find(token);
615                 } else if (token == "arg") {
616                         lex.next(true);
617                         params_.name = lex.getString();
618                 } else if (token == "\\end_inset")
619                         break;
620         }
621         if (token != "\\end_inset") {
622                 lex.printError("Missing \\end_inset at this point");
623                 throw ExceptionMessage(WarningException,
624                         _("Missing \\end_inset at this point."),
625                         from_utf8(token));
626         }
627 }
628
629
630 void InsetInfo::write(ostream & os) const
631 {
632         os << "Info\ntype  \"" << params_.infoType()
633            << "\"\narg   " << Lexer::quoteString(params_.name);
634 }
635
636
637 bool InsetInfo::showInsetDialog(BufferView * bv) const
638 {
639         bv->showDialog("info");
640         return true;
641 }
642
643
644 bool InsetInfo::getStatus(Cursor & cur, FuncRequest const & cmd,
645                 FuncStatus & flag) const
646 {
647         switch (cmd.action()) {
648         case LFUN_INSET_SETTINGS:
649                 return InsetCollapsible::getStatus(cur, cmd, flag);
650
651         case LFUN_INSET_DIALOG_UPDATE:
652         case LFUN_INSET_COPY_AS:
653         case LFUN_INSET_DISSOLVE:
654                 flag.setEnabled(true);
655                 return true;
656
657         case LFUN_INSET_MODIFY:
658                 if (nameTranslator().find(cmd.getArg(0)) == InsetInfoParams::UNKNOWN_INFO)
659                         return Inset::getStatus(cur, cmd, flag);
660                 if (params_.validateArgument(&buffer(), cmd.argument())) {
661                         flag.setEnabled(true);
662                         string typestr;
663                         string name = trim(split(to_utf8(cmd.argument()), typestr, ' '));
664                         InsetInfoParams::info_type type = nameTranslator().find(typestr);
665                         string origname = params_.name;
666                         if (type == InsetInfoParams::FIXDATE_INFO
667                             || type == InsetInfoParams::FIXTIME_INFO)
668                                 split(params_.name, origname, '@');
669                         flag.setOnOff(type == params_.type && name == origname);
670                         return true;
671                 }
672                 //fall through
673
674         default:
675                 return false;
676         }
677 }
678
679
680 void InsetInfo::doDispatch(Cursor & cur, FuncRequest & cmd)
681 {
682         switch (cmd.action()) {
683         case LFUN_INSET_MODIFY:
684                 if (nameTranslator().find(cmd.getArg(0)) == InsetInfoParams::UNKNOWN_INFO) {
685                         cur.undispatched();
686                         break;
687                 }
688                 cur.recordUndo();
689                 setInfo(to_utf8(cmd.argument()));
690                 cur.forceBufferUpdate();
691                 initialized_ = false;
692                 break;
693
694         case LFUN_INSET_COPY_AS: {
695                 cap::clearSelection();
696                 Cursor copy(cur);
697                 copy.pushBackward(*this);
698                 copy.pit() = 0;
699                 copy.pos() = 0;
700                 copy.resetAnchor();
701                 copy.pit() = copy.lastpit();
702                 copy.pos() = copy.lastpos();
703                 copy.setSelection();
704                 cap::copySelection(copy);
705                 break;
706         }
707
708         default:
709                 InsetCollapsible::doDispatch(cur, cmd);
710                 break;
711         }
712 }
713
714
715 void InsetInfo::setInfo(string const & info)
716 {
717         if (info.empty())
718                 return;
719
720         string saved_date_specifier;
721         // Store old date specifier for potential re-use
722         if (!params_.name.empty())
723                 saved_date_specifier = split(params_.name, '@');
724         // info_type name
725         string type;
726         params_.name = trim(split(info, type, ' '));
727         params_.type = nameTranslator().find(type);
728         if (params_.name.empty())
729                 params_.name = defaultValueTranslator().find(params_.type);
730         if (params_.type == InsetInfoParams::FIXDATE_INFO) {
731                 string const date_specifier = split(params_.name, '@');
732                 // If an explicit new fix date is specified, use that
733                 // Otherwise, use the old one or, if there is none,
734                 // the current date
735                 if (date_specifier.empty()) {
736                         if (saved_date_specifier.empty())
737                                 params_.name += "@" + fromqstr(QDate::currentDate().toString(Qt::ISODate));
738                         else
739                                 params_.name += "@" + saved_date_specifier;
740                 }
741         }
742         else if (params_.type == InsetInfoParams::FIXTIME_INFO) {
743                 string const time_specifier = split(params_.name, '@');
744                 // If an explicit new fix time is specified, use that
745                 // Otherwise, use the old one or, if there is none,
746                 // the current time
747                 if (time_specifier.empty()) {
748                         if (saved_date_specifier.empty())
749                                 params_.name += "@" + fromqstr(QTime::currentTime().toString(Qt::ISODate));
750                         else
751                                 params_.name += "@" + saved_date_specifier;
752                 }
753         }
754 }
755
756
757 void InsetInfo::error(docstring const & err, Language const * lang)
758 {
759         docstring const res = translateIfPossible(err, lang->code());
760         bool const translated = res != err;
761         // If the string is not translated, we use default lang (English)
762         Font const f = translated ? Font(inherit_font, lang) : Font(inherit_font);
763         setText(bformat(res, from_utf8(params_.name)), f, false);
764 }
765
766
767 void InsetInfo::info(docstring const & err, Language const * lang)
768 {
769         docstring const res = translateIfPossible(err, lang->code());
770         bool const translated = res != err;
771         // If the string is not translated, we use default lang (English)
772         Font const f = translated ? Font(inherit_font, lang) : Font(inherit_font);
773         setText(translateIfPossible(err, lang->code()), f, false);
774 }
775
776
777 void InsetInfo::setText(docstring const & str, Language const * lang)
778 {
779         setText(str, Font(inherit_font, lang), false);
780 }
781
782
783 bool InsetInfo::forceLTR(OutputParams const &) const
784 {
785         return params_.force_ltr;
786 }
787
788
789 bool InsetInfo::forceLocalFontSwitch() const
790 {
791         return params_.type == InsetInfoParams::MENU_INFO
792                 || params_.type == InsetInfoParams::SHORTCUTS_INFO
793                 || params_.type == InsetInfoParams::SHORTCUT_INFO
794                 || params_.type == InsetInfoParams::L7N_INFO;
795 }
796
797
798 void InsetInfo::metrics(MetricsInfo & mi, Dimension & dim) const
799 {
800         const_cast<InsetInfo *>(this)->build();
801         InsetCollapsible::metrics(mi, dim);
802 }
803
804
805 void InsetInfo::draw(PainterInfo & pi, int x, int y) const
806 {
807         Changer chg = changeVar(lyxrc.mark_foreign_language, false);
808         InsetCollapsible::draw(pi, x, y);
809 }
810
811 void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
812
813 {
814         // If the Buffer is a clone, then we neither need nor want to do any
815         // of what follows. We want, rather, just to inherit how things were
816         // in the original Buffer. This is especially important for VCS.
817         // Otherwise, we could in principle have different settings here
818         // than in the Buffer we were exporting.
819         // However, we need to check whether the inset is in an intitle
820         // context.
821         if (buffer().isClone()) {
822                 InsetText::checkIntitleContext(it);
823                 return;
824         }
825         BufferParams const & bp = buffer().params();
826         params_.lang = it.paragraph().getFontSettings(bp, it.pos()).language();
827         InsetCollapsible::updateBuffer(it, utype, deleted);
828 }
829
830
831 void InsetInfo::build()
832 {
833         // If the Buffer is a clone, then we neither need nor want to do any
834         // of what follows. We want, rather, just to inherit how things were
835         // in the original Buffer. This is especially important for VCS.
836         // Otherwise, we could in principle have different settings here
837         // than in the Buffer we were exporting.
838         if (buffer().isClone())
839                 return;
840
841         Language const * tryguilang = languages.getFromCode(Messages::guiLanguage());
842         // Some info insets use the language of the GUI (if available)
843         Language const * guilang = tryguilang ? tryguilang : params_.lang;
844
845         params_.force_ltr = !params_.lang->rightToLeft();
846         // This is just to get the string into the po files
847         docstring gui;
848         switch (params_.type) {
849         case InsetInfoParams::UNKNOWN_INFO:
850                 gui = _("Unknown Info!");
851                 info(from_ascii("Unknown Info!"), params_.lang);
852                 initialized_ = false;
853                 break;
854         case InsetInfoParams::SHORTCUT_INFO:
855         case InsetInfoParams::SHORTCUTS_INFO: {
856                 // shortcuts can change, so we need to re-do this each time
857                 FuncRequest const func = lyxaction.lookupFunc(params_.name);
858                 if (func.action() == LFUN_UNKNOWN_ACTION) {
859                         gui = _("Unknown action %1$s");
860                         error(from_ascii("Unknown action %1$s"), params_.lang);
861                         break;
862                 }
863                 KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(func);
864                 if (bindings.empty()) {
865                         gui = _("undefined");
866                         info(from_ascii("undefined"), params_.lang);
867                         break;
868                 }
869                 docstring sequence;
870                 docstring seq_untranslated;
871                 if (params_.type == InsetInfoParams::SHORTCUT_INFO) {
872                         sequence = bindings.begin()->print(KeySequence::ForGui);
873                         seq_untranslated = bindings.begin()->print(KeySequence::ForGui, true);
874                 } else {
875                         sequence = theTopLevelKeymap().printBindings(func, KeySequence::ForGui);
876                         seq_untranslated = theTopLevelKeymap().printBindings(func, KeySequence::ForGui, true);
877                 }
878                 // QKeySequence returns special characters for keys on the mac
879                 // Since these are not included in many fonts, we
880                 // re-translate them to textual names (see #10641)
881                 odocstringstream ods;
882                 string const lcode = params_.lang->code();
883                 docstring trans;
884                 bool is_translated = sequence != seq_untranslated;
885                 for (char_type const c : sequence) {
886                         switch(c) {
887                         case 0x21b5://Return
888                                 gui = _("Return[[Key]]");
889                                 is_translated = translateString(from_ascii("Return[[Key]]"), trans, lcode);
890                                 ods << trans;
891                                 break;
892                         case 0x21b9://Tab both directions (Win)
893                                 gui = _("Tab[[Key]]");
894                                 is_translated = translateString(from_ascii("Tab[[Key]]"), trans, lcode);
895                                 ods << trans;
896                                 break;
897                         case 0x21de://Qt::Key_PageUp
898                                 gui = _("PgUp");
899                                 is_translated = translateString(from_ascii("PgUp"), trans, lcode);
900                                 ods << trans;
901                                 break;
902                         case 0x21df://Qt::Key_PageDown
903                                 gui = _("PgDown");
904                                 is_translated = translateString(from_ascii("PgDown"), trans, lcode);
905                                 ods << trans;
906                                 break;
907                         case 0x21e4://Qt::Key_Backtab
908                                 gui = _("Backtab");
909                                 is_translated = translateString(from_ascii("Backtab"), trans, lcode);
910                                 ods << trans;
911                                 break;
912                         case 0x21e5://Qt::Key_Tab
913                                 gui = _("Tab");
914                                 is_translated = translateString(from_ascii("Tab"), trans, lcode);
915                                 ods << trans;
916                                 break;
917                         case 0x21e7://Shift
918                                 gui = _("Shift");
919                                 is_translated = translateString(from_ascii("Shift"), trans, lcode);
920                                 ods << trans;
921                                 break;
922                         case 0x21ea://Qt::Key_CapsLock
923                                 gui = _("CapsLock");
924                                 is_translated = translateString(from_ascii("CapsLock"), trans, lcode);
925                                 ods << trans;
926                                 break;
927                         case 0x2303://Control
928                                 gui = _("Control[[Key]]");
929                                 is_translated = translateString(from_ascii("Control[[Key]]"), trans, lcode);
930                                 ods << trans;
931                                 break;
932                         case 0x2318://CMD
933                                 gui = _("Command[[Key]]");
934                                 is_translated = translateString(from_ascii("Command[[Key]]"), trans, lcode);
935                                 ods << trans;
936                                 break;
937                         case 0x2324://Qt::Key_Enter
938                                 gui = _("Return[[Key]]");
939                                 is_translated = translateString(from_ascii("Return[[Key]]"), trans, lcode);
940                                 ods << trans;
941                                 break;
942                         case 0x2325://Option key
943                                 gui = _("Option[[Key]]");
944                                 is_translated = translateString(from_ascii("Option[[Key]]"), trans, lcode);
945                                 ods << trans;
946                                 break;
947                         case 0x2326://Qt::Key_Delete
948                                 gui = _("Delete[[Key]]");
949                                 is_translated = translateString(from_ascii("Delete[[Key]]"), trans, lcode);
950                                 ods << trans;
951                                 break;
952                         case 0x232b://Qt::Key_Backspace
953                                 gui = _("Fn+Del");
954                                 is_translated = translateString(from_ascii("Fn+Del"), trans, lcode);
955                                 ods << trans;
956                                 break;
957                         case 0x238b://Qt::Key_Escape
958                                 gui = _("Esc");
959                                 is_translated = translateString(from_ascii("Esc"), trans, lcode);
960                                 ods << trans;
961                                 break;
962                         default:
963                                 ods.put(c);
964                         }
965                 }
966                 setText(ods.str(), is_translated ? guilang : nullptr);
967                 params_.force_ltr = !is_translated || (!guilang->rightToLeft() && !params_.lang->rightToLeft());
968                 break;
969         }
970         case InsetInfoParams::LYXRC_INFO: {
971                 // this information could change, if the preferences are changed,
972                 // so we will recalculate each time through.
973                 ostringstream oss;
974                 if (params_.name.empty()) {
975                         gui = _("undefined");
976                         info(from_ascii("undefined"), params_.lang);
977                         break;
978                 }
979                 // FIXME this uses the serialization mechanism to get the info
980                 // we want, which i guess works but is a bit strange.
981                 lyxrc.write(oss, true, params_.name);
982                 string result = oss.str();
983                 if (result.size() < 2) {
984                         gui = _("undefined");
985                         info(from_ascii("undefined"), params_.lang);
986                         break;
987                 }
988                 string::size_type loc = result.rfind("\n", result.size() - 2);
989                 loc = loc == string::npos ? 0 : loc + 1;
990                 if (result.size() < loc + params_.name.size() + 1
991                           || result.substr(loc + 1, params_.name.size()) != params_.name) {
992                         gui = _("undefined");
993                         info(from_ascii("undefined"), params_.lang);
994                         break;
995                 }
996                 // remove leading comments and \\name and space
997                 result = result.substr(loc + params_.name.size() + 2);
998
999                 // remove \n and ""
1000                 result = rtrim(result, "\n");
1001                 result = trim(result, "\"");
1002                 gui = _("not set");
1003                 if (result.empty())
1004                         result = "not set";
1005                 setText(from_utf8(result), params_.lang);
1006                 break;
1007         }
1008         case InsetInfoParams::PACKAGE_INFO:
1009                 // TODO: away from a release, replace with getPackageInfo.
1010                 // only need to do this once.
1011                 if (initialized_)
1012                         break;
1013                 // check in packages.lst
1014                 bool available;
1015                 // we also allow version check with version separated by blank
1016                 if (contains(params_.name, ' ')) {
1017                         string name;
1018                         string const version = split(params_.name, name, ' ');
1019                         int const y = convert<int>(version.substr(0,4));
1020                         int const m = convert<int>(version.substr(4,2));
1021                         int const d = convert<int>(version.substr(6,2));
1022                         available = LaTeXFeatures::isAvailableAtLeastFrom(name, y, m, d);
1023                 } else
1024                         available = LaTeXFeatures::isAvailable(params_.name);
1025
1026                 if (available) {
1027                         gui = _("yes");
1028                         info(from_ascii("yes"), params_.lang);
1029                 } else {
1030                         gui = _("no");
1031                         info(from_ascii("no"), params_.lang);
1032                 }
1033                 initialized_ = true;
1034                 break;
1035
1036         case InsetInfoParams::TEXTCLASS_INFO: {
1037                 // TODO: when away from a release, replace with getTextClassInfo.
1038                 // the TextClass can change
1039                 LayoutFileList const & list = LayoutFileList::get();
1040                 bool available = false;
1041                 // params_.name is the class name
1042                 if (list.haveClass(params_.name))
1043                         available = list[params_.name].isTeXClassAvailable();
1044                 if (available) {
1045                         gui = _("yes");
1046                         info(from_ascii("yes"), params_.lang);
1047                 } else {
1048                         gui = _("no");
1049                         info(from_ascii("no"), params_.lang);
1050                 }
1051                 break;
1052         }
1053         case InsetInfoParams::MENU_INFO: {
1054                 // only need to do this once.
1055                 if (initialized_)
1056                         break;
1057                 docstring_list names;
1058                 FuncRequest func = lyxaction.lookupFunc(params_.name);
1059                 if (func.action() == LFUN_UNKNOWN_ACTION) {
1060                         gui = _("Unknown action %1$s");
1061                         error(from_ascii("Unknown action %1$s"), params_.lang);
1062                         break;
1063                 }
1064                 if (func.action() == LFUN_BUFFER_VIEW || func.action() == LFUN_BUFFER_UPDATE)
1065                         // The default output format is in the menu without argument,
1066                         // so strip it here.
1067                         if (func.argument() == from_ascii(buffer().params().getDefaultOutputFormat()))
1068                                 func = FuncRequest(func.action());
1069                 // iterate through the menubackend to find it
1070                 if (!theApp()) {
1071                         gui = _("Can't determine menu entry for action %1$s in batch mode");
1072                         error(from_ascii("Can't determine menu entry for action %1$s in batch mode"), params_.lang);
1073                         initialized_ = true;
1074                         break;
1075                 }
1076                 // and we will not keep trying if we fail
1077                 initialized_ = theApp()->hasBufferView();
1078                 if (!theApp()->searchMenu(func, names)) {
1079                         gui = _("No menu entry for action %1$s");
1080                         error(from_ascii("No menu entry for action %1$s"), params_.lang);
1081                         break;
1082                 }
1083                 // if found, return its path.
1084                 clear();
1085                 Paragraph & par = paragraphs().front();
1086                 Font const f(inherit_font, guilang);
1087                 params_.force_ltr = !guilang->rightToLeft();
1088                 //Font fu = f;
1089                 //fu.fontInfo().setUnderbar(FONT_ON);
1090                 for (docstring const & name : names) {
1091                         // do not insert > for the top level menu item
1092                         if (&name != &names.front())
1093                                 par.insertInset(par.size(), new InsetSpecialChar(InsetSpecialChar::MENU_SEPARATOR),
1094                                                 f, Change(Change::UNCHANGED));
1095                         //FIXME: add proper underlines here. This
1096                         // involves rewriting searchMenu used above to
1097                         // return a vector of menus. If we do not do
1098                         // that, we might as well use below
1099                         // Paragraph::insert on each string (JMarc)
1100                         for (char_type c : name)
1101                                 par.insertChar(par.size(), c, f, Change(Change::UNCHANGED));
1102                 }
1103                 break;
1104         }
1105         case InsetInfoParams::L7N_INFO: {
1106                 // TODO: away from a release, use getNormalizedL7N instead.
1107                 docstring locstring = _(params_.name);
1108                 // Remove trailing colons
1109                 locstring = rtrim(locstring, ":");
1110                 // Remove menu accelerators
1111                 if (contains(locstring, from_ascii("|"))) {
1112                         docstring nlocstring;
1113                         rsplit(locstring, nlocstring, '|');
1114                         locstring = nlocstring;
1115                 }
1116                 // Remove Qt accelerators, but keep literal ampersands
1117                 locstring = subst(locstring, from_ascii(" & "), from_ascii("</amp;>"));
1118                 locstring = subst(locstring, from_ascii("&"), docstring());
1119                 locstring = subst(locstring, from_ascii("</amp;>"), from_ascii(" & "));
1120                 setText(locstring, guilang);
1121                 params_.force_ltr = !guilang->rightToLeft() && !params_.lang->rightToLeft();
1122                 break;
1123         }
1124         case InsetInfoParams::ICON_INFO: {
1125                 // only need to do this once.
1126                 if (initialized_)
1127                         break;
1128                 // and we will not keep trying if we fail
1129                 initialized_ = true;
1130                 FuncRequest func = lyxaction.lookupFunc(params_.name);
1131                 docstring icon_name = frontend::Application::iconName(func, true);
1132                 FileName file(to_utf8(icon_name));
1133                 if (file.onlyFileNameWithoutExt() == "unknown") {
1134                         string dir = "images";
1135                         FileName file2(imageLibFileSearch(dir, params_.name, "svgz,png"));
1136                         if (!file2.empty())
1137                                 file = file2;
1138                 }
1139                 if (!file.exists())
1140                         break;
1141                 int percent_scale = 100;
1142                 if (use_gui) {
1143                         // Compute the scale factor for the icon such that its
1144                         // width on screen is equal to 1em in pixels.
1145                         // The scale factor is rounded to the integer nearest
1146                         // to the float value of the ratio 100*iconsize/imgsize.
1147                         int imgsize = QImage(toqstr(file.absFileName())).width();
1148                         if (imgsize > 0) {
1149                                 int iconsize = Length(1, Length::EM).inPixels(1);
1150                                 percent_scale = (100 * iconsize + imgsize / 2)/imgsize;
1151                         }
1152                 }
1153                 InsetGraphicsTight * inset = new InsetGraphicsTight(buffer_);
1154                 InsetGraphicsParams igp;
1155                 igp.filename = file;
1156                 igp.lyxscale = percent_scale;
1157                 igp.scale = string();
1158                 igp.width = Length(1, Length::EM);
1159                 if (contains(file.absoluteFilePath(), from_ascii("math"))
1160                     || contains(file.absoluteFilePath(), from_ascii("ert-insert"))
1161                     || suffixIs(file.onlyPath().absoluteFilePath(), from_ascii("ipa")))
1162                         igp.darkModeSensitive = true;
1163                 inset->setParams(igp);
1164                 clear();
1165                 Font const f(inherit_font, params_.lang);
1166                 paragraphs().front().insertInset(0, inset, f,
1167                                                  Change(Change::UNCHANGED));
1168                 break;
1169         }
1170         case InsetInfoParams::BUFFER_INFO: {
1171                 // TODO: away from a release, replace by getBufferInfo.
1172                 // this could all change, so we will recalculate each time
1173                 if (params_.name == "name")
1174                         setText(from_utf8(buffer().fileName().onlyFileName()), params_.lang);
1175                 else if (params_.name == "name-noext")
1176                         setText(from_utf8(buffer().fileName().onlyFileNameWithoutExt()), params_.lang);
1177                 else if (params_.name == "path")
1178                         setText(from_utf8(os::latex_path(buffer().filePath())), params_.lang);
1179                 else if (params_.name == "class")
1180                         setText(from_utf8(buffer().params().documentClass().name()), params_.lang);
1181                 break;
1182         }
1183         case InsetInfoParams::VCS_INFO: {
1184                 // TODO: away from a release, replace by getVCSInfo.
1185                 // this information could change, in principle, so we will 
1186                 // recalculate each time through
1187                 if (!buffer().lyxvc().inUse()) {
1188                         gui = _("No version control!");
1189                         info(from_ascii("No version control!"), params_.lang);
1190                         break;
1191                 }
1192                 LyXVC::RevisionInfo itype = LyXVC::Unknown;
1193                 if (params_.name == "revision")
1194                         itype = LyXVC::File;
1195                 else if (params_.name == "revision-abbrev")
1196                         itype = LyXVC::FileAbbrev;
1197                 else if (params_.name == "tree-revision")
1198                         itype = LyXVC::Tree;
1199                 else if (params_.name == "author")
1200                         itype = LyXVC::Author;
1201                 else if (params_.name == "time")
1202                         itype = LyXVC::Time;
1203                 else if (params_.name == "date")
1204                         itype = LyXVC::Date;
1205                 string binfo = buffer().lyxvc().revisionInfo(itype);
1206                 if (binfo.empty()) {
1207                         gui = _("%1$s[[vcs data]] unknown");
1208                         error(from_ascii("%1$s[[vcs data]] unknown"), params_.lang);
1209                 } else
1210                         setText(from_utf8(binfo), params_.lang);
1211                 break;
1212         }
1213         case InsetInfoParams::LYX_INFO:
1214                 // only need to do this once.
1215                 if (initialized_)
1216                         break;
1217                 if (params_.name == "version")
1218                         setText(from_ascii(lyx_version), params_.lang);
1219                 else if (params_.name == "layoutformat")
1220                         setText(convert<docstring>(LAYOUT_FORMAT), params_.lang);
1221                 initialized_ = true;
1222                 break;
1223         case InsetInfoParams::DATE_INFO:
1224         case InsetInfoParams::MODDATE_INFO:
1225         case InsetInfoParams::FIXDATE_INFO: {
1226                 // TODO: away from a release, use parseDate instead.
1227                 string date_format = params_.name;
1228                 string const date_specifier = (params_.type == InsetInfoParams::FIXDATE_INFO
1229                                                && contains(params_.name, '@'))
1230                                 ? split(params_.name, date_format, '@') : string();
1231                 QDate date;
1232                 if (params_.type == InsetInfoParams::MODDATE_INFO)
1233 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
1234                         date = QDateTime::fromSecsSinceEpoch(buffer().fileName().lastModified()).date();
1235 #else
1236                         date = QDateTime::fromTime_t(buffer().fileName().lastModified()).date();
1237 #endif
1238                 else if (params_.type == InsetInfoParams::FIXDATE_INFO && !date_specifier.empty())
1239                         date = QDate::fromString(toqstr(date_specifier), Qt::ISODate);
1240                 else
1241                         date = QDate::currentDate();
1242                 setText(params_.getDate(date_format, date), params_.lang);
1243                 break;
1244         }
1245         case InsetInfoParams::TIME_INFO:
1246         case InsetInfoParams::MODTIME_INFO:
1247         case InsetInfoParams::FIXTIME_INFO: {
1248                 // TODO: away from a release, use parseTime instead.
1249                 string time_format = params_.name;
1250                 string const time_specifier = (params_.type == InsetInfoParams::FIXTIME_INFO
1251                                                && contains(params_.name, '@'))
1252                                 ? split(params_.name, time_format, '@') : string();
1253                 QTime time;
1254                 if (params_.type == InsetInfoParams::MODTIME_INFO)
1255 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
1256                         time = QDateTime::fromSecsSinceEpoch(buffer().fileName().lastModified()).time();
1257 #else
1258                         time = QDateTime::fromTime_t(buffer().fileName().lastModified()).time();
1259 #endif
1260                 else if (params_.type == InsetInfoParams::FIXTIME_INFO && !time_specifier.empty())
1261                         time = QTime::fromString(toqstr(time_specifier), Qt::ISODate);
1262                 else
1263                         time = QTime::currentTime();
1264                 setText(params_.getTime(time_format, time), params_.lang);
1265                 break;
1266         }
1267         }
1268
1269         // Just to do something with that string
1270         LYXERR(Debug::INFO, "info inset text: " << gui);
1271 }
1272
1273
1274 void InsetInfo::validate(LaTeXFeatures & features) const
1275 {
1276         const_cast<InsetInfo *>(this)->build();
1277         InsetCollapsible::validate(features);
1278 }
1279
1280
1281 string InsetInfo::contextMenu(BufferView const &, int, int) const
1282 {
1283         //FIXME: We override the implementation of InsetCollapsible,
1284         //because this inset is not a collapsible inset.
1285         return contextMenuName();
1286 }
1287
1288
1289 string InsetInfo::contextMenuName() const
1290 {
1291         return "context-info";
1292 }
1293
1294 namespace {
1295
1296 // TODO: away from a release, use these functions in InsetInfo::build and InsetInfoParams::getArguments.
1297
1298 QDate parseDate(Buffer const & buffer, const InsetInfoParams & params) {
1299         std::string date_format = params.name;
1300         std::string const date_specifier = (params.type == InsetInfoParams::FIXDATE_INFO
1301                                             && contains(params.name, '@'))
1302                                            ? split(params.name, date_format, '@') : string();
1303
1304         if (params.type == InsetInfoParams::MODDATE_INFO)
1305 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
1306                 return QDateTime::fromSecsSinceEpoch(buffer.fileName().lastModified()).date();
1307 #else
1308                 return QDateTime::fromTime_t(buffer.fileName().lastModified()).date();
1309 #endif
1310         else if (params.type == InsetInfoParams::FIXDATE_INFO && !date_specifier.empty()) {
1311                 QDate date = QDate::fromString(toqstr(date_specifier), Qt::ISODate);
1312                 return (date.isValid()) ? date : QDate::currentDate();
1313         } else {
1314                 if (params.type != InsetInfoParams::DATE_INFO && params.type != InsetInfoParams::FIXDATE_INFO)
1315                         lyxerr << "Unexpected InsetInfoParams::info_type in parseDate: " << params.type;
1316                 return QDate::currentDate();
1317         }
1318 }
1319
1320 QTime parseTime(Buffer const & buffer, const InsetInfoParams & params) {
1321         std::string time_format = params.name;
1322         std::string const date_specifier = (params.type == InsetInfoParams::FIXTIME_INFO
1323                                             && contains(params.name, '@'))
1324                                            ? split(params.name, time_format, '@') : string();
1325
1326         if (params.type == InsetInfoParams::MODTIME_INFO)
1327 #if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
1328                 return QDateTime::fromSecsSinceEpoch(buffer.fileName().lastModified()).time();
1329 #else
1330                 return QDateTime::fromTime_t(buffer.fileName().lastModified()).time();
1331 #endif
1332         else if (params.type == InsetInfoParams::FIXTIME_INFO && !date_specifier.empty()) {
1333                 QTime time = QTime::fromString(toqstr(date_specifier), Qt::ISODate);
1334                 return (time.isValid()) ? time : QTime::currentTime();
1335         } else {
1336                 if (params.type != InsetInfoParams::TIME_INFO && params.type != InsetInfoParams::FIXTIME_INFO)
1337                         lyxerr << "Unexpected InsetInfoParams::info_type in parseTime: " << params.type;
1338                 return QTime::currentTime();
1339         }
1340 }
1341
1342 docstring getBufferInfo(Buffer const & buffer, const InsetInfoParams & params) {
1343         if (params.name == "name")
1344                 return from_utf8(buffer.fileName().onlyFileName());
1345         else if (params.name == "name-noext")
1346                 return from_utf8(buffer.fileName().onlyFileNameWithoutExt());
1347         else if (params.name == "path")
1348                 return from_utf8(os::latex_path(buffer.filePath()));
1349         else if (params.name == "class")
1350                 return from_utf8(buffer.params().documentClass().name());
1351         else {
1352                 lyxerr << "Unexpected name for InsetInfoParams::BUFFER_INFO: " << params.name;
1353                 return from_ascii("");
1354         }
1355 }
1356
1357 docstring getVCSInfo(Buffer const & buffer, const InsetInfoParams & params) {
1358         if (!buffer.lyxvc().inUse())
1359                 return _("No version control!");
1360
1361         LyXVC::RevisionInfo itype = LyXVC::Unknown;
1362         if (params.name == "revision")
1363                 itype = LyXVC::File;
1364         else if (params.name == "revision-abbrev")
1365                 itype = LyXVC::FileAbbrev;
1366         else if (params.name == "tree-revision")
1367                 itype = LyXVC::Tree;
1368         else if (params.name == "author")
1369                 itype = LyXVC::Author;
1370         else if (params.name == "time")
1371                 itype = LyXVC::Time;
1372         else if (params.name == "date")
1373                 itype = LyXVC::Date;
1374
1375         string binfo = buffer.lyxvc().revisionInfo(itype);
1376         if (binfo.empty())
1377                 return from_ascii("VCS info unknown!");
1378         else
1379                 return from_utf8(binfo);
1380 }
1381
1382 docstring getPackageInfo(const InsetInfoParams & params) {
1383         // check in packages.lst
1384         bool available;
1385         // we also allow version check with version separated by blank
1386         if (contains(params.name, ' ')) {
1387                 string name;
1388                 string const version = split(params.name, name, ' ');
1389                 int const y = convert<int>(version.substr(0,4));
1390                 int const m = convert<int>(version.substr(4,2));
1391                 int const d = convert<int>(version.substr(6,2));
1392                 available = LaTeXFeatures::isAvailableAtLeastFrom(name, y, m, d);
1393         } else
1394                 available = LaTeXFeatures::isAvailable(params.name);
1395
1396         return from_ascii(available ? "yes" : "no");
1397 }
1398
1399 docstring getTextClassInfo(const InsetInfoParams & params) {
1400         LayoutFileList const & list = LayoutFileList::get();
1401         // params_.name is the class name
1402         const bool available = list.haveClass(params.name) && list[params.name].isTeXClassAvailable();
1403         return from_ascii(available ? "yes" : "no");
1404 }
1405
1406 // With C++17, it would be better to have a std::string_view instead of const char *.
1407 const static std::map<char_type, const char *> keyToString {
1408                 {0x21b5, "Return[[Key]]"}, // Return
1409                 {0x21b9, "Tab[[Key]]"}, // Tab both directions (Win)
1410                 {0x21de, "PgUp"}, // Qt::Key_PageUp
1411                 {0x21df, "PgDown"}, // Qt::Key_PageDown
1412                 {0x21e4, "Backtab"}, // Qt::Key_Backtab
1413                 {0x21e5, "Tab"}, // Qt::Key_Tab
1414                 {0x21e7, "Shift"}, // Shift
1415                 {0x21ea, "CapsLock"}, // Qt::Key_CapsLock
1416                 {0x2303, "Control[[Key]]"}, // Control
1417                 {0x2318, "Command[[Key]]"}, // CMD
1418                 {0x2324, "Return[[Key]]"}, // Qt::Key_Enter
1419                 {0x2325, "Option[[Key]]"}, // Option key
1420                 {0x2326, "Delete[[Key]]"}, // Qt::Key_Delete
1421                 {0x232b, "Fn+Del"}, // Qt::Key_Backspace
1422                 {0x238b, "Esc"}, // Qt::Key_Escape
1423 };
1424
1425 bool canTranslateKeySequence(const InsetInfoParams & params, const docstring & sequence,
1426                                                          const docstring & seq_untranslated) {
1427         bool is_translated = sequence != seq_untranslated;
1428         std::string const lcode = params.lang->code();
1429         docstring trans;
1430
1431         for (char_type const c : sequence) {
1432                 const auto keyMapping = keyToString.find(c);
1433                 if (keyMapping != keyToString.end()) {
1434                         is_translated = translateString(from_ascii(keyMapping->second), trans, lcode);
1435                 }
1436         }
1437
1438         return is_translated;
1439 }
1440
1441 void docbookShortcutInfo(XMLStream & xs, const InsetInfoParams & params) {
1442         // Usually, a keyboard shortcut should be encoded as db:shortcut. However, this element doesn't accept text, hence
1443         // the use of db:accel for error cases (not the right semantics, though).
1444
1445         std::string attr;
1446         if (params.type == InsetInfoParams::SHORTCUTS_INFO)
1447                 attr = R"(role="shorcuts")";
1448         else if (params.type == InsetInfoParams::SHORTCUT_INFO)
1449                 attr = R"(role="shortcut")";
1450         else {
1451                 // Only check for this assertion that exits this function.
1452                 lyxerr << "Assertion failed! InsetInfoParams::info_type: " << params.type;
1453                 return;
1454         }
1455
1456         // shortcuts can change, so we need to re-do this each time
1457         FuncRequest const func = lyxaction.lookupFunc(params.name);
1458         if (func.action() == LFUN_UNKNOWN_ACTION) {
1459                 xml::openTag(xs, "accel", attr, "inline");
1460                 xs << _("Unknown action %1$s");
1461                 xml::closeTag(xs, "accel", "inline");
1462                 return;
1463         }
1464
1465         KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(func);
1466         if (bindings.empty()) {
1467                 xml::openTag(xs, "accel", attr, "inline");
1468                 xs << _("undefined");
1469                 xml::closeTag(xs, "accel", "inline");
1470                 return;
1471         }
1472
1473         docstring sequence;
1474         docstring seq_untranslated;
1475         if (params.type == InsetInfoParams::SHORTCUT_INFO) {
1476                 sequence = bindings.begin()->print(KeySequence::ForGui);
1477                 seq_untranslated = bindings.begin()->print(KeySequence::ForGui, true);
1478         } else if (params.type == InsetInfoParams::SHORTCUTS_INFO) {
1479                 sequence = theTopLevelKeymap().printBindings(func, KeySequence::ForGui);
1480                 seq_untranslated = theTopLevelKeymap().printBindings(func, KeySequence::ForGui, true);
1481         }
1482         // No other possible case.
1483
1484         Language const * tryguilang = languages.getFromCode(Messages::guiLanguage());
1485         // Some info insets use the language of the GUI (if available)
1486         Language const * guilang = tryguilang ? tryguilang : params.lang;
1487         const bool isTranslated = canTranslateKeySequence(params, sequence, seq_untranslated);
1488         const bool isLtr = !isTranslated || (!guilang->rightToLeft() && !params.lang->rightToLeft());
1489         attr += std::string(" dir=\"") + (isLtr ? "ltr" : "rtl") + "\"";
1490         attr += " action=\"simul\"";
1491         xml::openTag(xs, "shortcut", attr, "inline");
1492         xml::openTag(xs, "keycombo", "", "inline");
1493
1494         // QKeySequence returns special characters for keys on the mac
1495         // Since these are not included in many fonts, we
1496         // re-translate them to textual names (see #10641)
1497         odocstringstream ods;
1498         string const lcode = params.lang->code();
1499         docstring trans;
1500         for (char_type const c : sequence) {
1501                 const auto keyMapping = keyToString.find(c);
1502                 if (keyMapping != keyToString.end()) {
1503                         translateString(from_ascii(keyMapping->second), trans, lcode);
1504
1505                         // db:keysym: symbolic name (like Page Up), unlike what is printed on the key (like
1506                         // ⇞, ↑, ▲, PgUp, Page Up, etc.)
1507                         xml::openTag(xs, "keysym", "", "inline");
1508                         xs << trans;
1509                         xml::closeTag(xs, "keysym", "inline");
1510                 } else {
1511                         // db:keycap: this is not a special key, c is really what is printed on the key.
1512                         xml::openTag(xs, "keycap", "", "inline");
1513                         xs << c;
1514                         xml::closeTag(xs, "keycap", "inline");
1515                 }
1516         }
1517
1518         xml::closeTag(xs, "keycombo", "inline");
1519         xml::closeTag(xs, "shortcut", "inline");
1520 }
1521
1522 docstring getLyxRCInfo(const InsetInfoParams & params) {
1523         if (params.name.empty())
1524                 return _("undefined");
1525
1526         // this information could change, if the preferences are changed,
1527         // so we will recalculate each time through.
1528         // FIXME this uses the serialization mechanism to get the info
1529         // we want, which i guess works but is a bit strange.
1530         ostringstream oss;
1531         lyxrc.write(oss, true, params.name);
1532         string result = oss.str();
1533         if (result.size() < 2) {
1534                 return _("undefined");
1535         }
1536
1537         string::size_type loc = result.rfind('\n', result.size() - 2);
1538         loc = loc == string::npos ? 0 : loc + 1;
1539         if (result.size() < loc + params.name.size() + 1
1540             || result.substr(loc + 1, params.name.size()) != params.name) {
1541                 return _("undefined");
1542         }
1543
1544         // remove leading comments and \\name and space
1545         result = result.substr(loc + params.name.size() + 2);
1546
1547         // remove \n and ""
1548         result = rtrim(result, "\n");
1549         result = trim(result, "\"");
1550
1551         if (result.empty())
1552                 return from_ascii("not set");
1553         else
1554                 return from_utf8(result);
1555 }
1556
1557 void docbookMenuInfo(XMLStream & xs, Buffer const & buffer, const InsetInfoParams & params) {
1558         docstring_list names;
1559         FuncRequest func = lyxaction.lookupFunc(params.name);
1560         if (func.action() == LFUN_UNKNOWN_ACTION) {
1561                 xml::openTag(xs, "guimenuitem", "", "inline");
1562                 xs << _("Unknown action %1$s");
1563                 xml::closeTag(xs, "guimenuitem", "inline");
1564                 return;
1565         }
1566
1567         if (func.action() == LFUN_BUFFER_VIEW || func.action() == LFUN_BUFFER_UPDATE) {
1568                 // The default output format is in the menu without argument,
1569                 // so strip it here.
1570                 if (func.argument() == from_ascii(buffer.params().getDefaultOutputFormat()))
1571                         func = FuncRequest(func.action());
1572         }
1573
1574         // iterate through the menubackend to find it
1575         if (!theApp()) {
1576                 xml::openTag(xs, "guimenuitem", "", "inline");
1577                 xs << _("Can't determine menu entry for action %1$s in batch mode");
1578                 xml::closeTag(xs, "guimenuitem", "inline");
1579                 return;
1580         }
1581
1582         // and we will not keep trying if we fail
1583         if (!theApp()->searchMenu(func, names)) {
1584                 xml::openTag(xs, "guimenuitem", "", "inline");
1585                 xs << _("No menu entry for action %1$s");
1586                 xml::closeTag(xs, "guimenuitem", "inline");
1587                 return;
1588         }
1589
1590         // if found, return its path.
1591         Language const * tryguilang = languages.getFromCode(Messages::guiLanguage());
1592         // Some info insets use the language of the GUI (if available)
1593         Language const * guilang = tryguilang ? tryguilang : params.lang;
1594         const bool isLtr = !guilang->rightToLeft();
1595         const std::string attr = std::string("dir=\"") + (isLtr ? "ltr" : "rtl") + "\"";
1596
1597         xml::openTag(xs, "menuchoice", attr, "inline"); // More of an inline tag in this case, as there is no db:shortcut to
1598         // accompany the succession of menus.
1599
1600         for (int i = 0; i < names.size(); ++i) {
1601             docstring const & name = names[i];
1602
1603                 std::string tag;
1604                 if (i == 0) {
1605                         tag = "guimenu";
1606                 } else if (i == names.size() - 1) {
1607                         tag = "guimenuitem";
1608                 } else {
1609                         tag = "guisubmenu";
1610                 }
1611
1612                 xml::openTag(xs, tag, "", "inline");
1613
1614                 //FIXME: add proper underlines here. This
1615                 // involves rewriting searchMenu used above to
1616                 // return a vector of menus. If we do not do
1617                 // that, we might as well use below
1618                 // Paragraph::insert on each string (JMarc)
1619                 // TODO: for DocBook, underlining corresponds to adding db:accel around the letter to underline.
1620                 xs << name;
1621
1622                 xml::closeTag(xs, tag, "inline");
1623         }
1624
1625         xml::closeTag(xs, "menuchoice", "inline");
1626 }
1627
1628 void docbookIconInfo(XMLStream & xs, const OutputParams & rp, Buffer * buffer, const InsetInfoParams & params) {
1629         FuncRequest func = lyxaction.lookupFunc(params.name);
1630         docstring icon_name = frontend::Application::iconName(func, true);
1631         FileName file(to_utf8(icon_name));
1632         if (file.onlyFileNameWithoutExt() == "unknown") {
1633                 std::string dir = "images";
1634                 FileName file2(imageLibFileSearch(dir, params.name, "svgz,png"));
1635                 if (!file2.empty())
1636                         file = file2;
1637         }
1638
1639         if (!file.exists())
1640                 return;
1641
1642         int percent_scale = 100;
1643         if (use_gui) {
1644                 // Compute the scale factor for the icon such that its
1645                 // width on screen is equal to 1em in pixels.
1646                 // The scale factor is rounded to the integer nearest
1647                 // to the float value of the ratio 100*iconsize/imgsize.
1648                 int imgsize = QImage(toqstr(file.absFileName())).width();
1649                 if (imgsize > 0) {
1650                         int iconsize = Length(1, Length::EM).inPixels(1);
1651                         percent_scale = (100 * iconsize + imgsize / 2) / imgsize;
1652                 }
1653         }
1654
1655         InsetGraphicsTight * inset = new InsetGraphicsTight(buffer);
1656         InsetGraphicsParams igp;
1657         igp.filename = file;
1658         igp.lyxscale = percent_scale;
1659         igp.scale = string();
1660         igp.width = Length(1, Length::EM);
1661         if (contains(file.absoluteFilePath(), from_ascii("math"))
1662             || contains(file.absoluteFilePath(), from_ascii("ert-insert"))
1663             || suffixIs(file.onlyPath().absoluteFilePath(), from_ascii("ipa")))
1664                 igp.darkModeSensitive = true;
1665         inset->setParams(igp);
1666
1667         xml::openTag(xs, "guiicon", "", "inline");
1668         inset->docbook(xs, rp);
1669         xml::closeTag(xs, "guiicon", "inline");
1670 }
1671
1672 docstring getLyXInfo(const InsetInfoParams & params) {
1673         if (params.name == "version")
1674                 return from_ascii(lyx_version);
1675         else if (params.name == "layoutformat")
1676                 return convert<docstring>(LAYOUT_FORMAT);
1677         else {
1678                 lyxerr << "Unexpected name for InsetInfoParams::BUFFER_INFO: " << params.name;
1679                 return from_ascii("");
1680         }
1681 }
1682
1683 docstring getNormalizedL7N(const InsetInfoParams & params) {
1684         docstring locstring = _(params.name);
1685
1686         // Remove trailing colons
1687         locstring = rtrim(locstring, ":");
1688
1689         // Remove menu accelerators
1690         if (contains(locstring, from_ascii("|"))) {
1691                 docstring nlocstring;
1692                 rsplit(locstring, nlocstring, '|');
1693                 locstring = nlocstring;
1694         }
1695
1696         // Remove Qt accelerators, but keep literal ampersands
1697         locstring = subst(locstring, from_ascii(" & "), from_ascii("</amp;>"));
1698         locstring = subst(locstring, from_ascii("&"), docstring());
1699         locstring = subst(locstring, from_ascii("</amp;>"), from_ascii(" & "));
1700
1701         return locstring;
1702 }
1703
1704 } // namespace
1705
1706 void InsetInfo::docbook(XMLStream & xs, OutputParams const & rp) const
1707 {
1708         // TODO: away from a release, merge some of this code with InsetInfo::build and InsetInfoParams::getArguments.
1709         switch (params_.type) {
1710         case InsetInfoParams::DATE_INFO:
1711         case InsetInfoParams::MODDATE_INFO:
1712         case InsetInfoParams::FIXDATE_INFO: {
1713                 std::string role;
1714                 switch (params_.type) {
1715                 case InsetInfoParams::DATE_INFO:
1716                         role = "current-date";
1717                         break;
1718                 case InsetInfoParams::MODDATE_INFO:
1719                         role = "last-modification-date";
1720                         break;
1721                 case InsetInfoParams::FIXDATE_INFO:
1722                         role = "fix-date";
1723                         break;
1724                 default:
1725                         lyxerr << "Assertion failed! InsetInfoParams::info_type: " << params().type;
1726                         break;
1727                 }
1728
1729                 // A db:date cannot be nested within a db:date. This case typically happens when the document class defines a
1730                 // Date layout. In this case, avoid outputting a new db:date. This means that InsetInfo cannot add a role on top
1731                 // of the previous db:date, hence add it as a comment. (Another solution would be an XML processing instruction,
1732                 // but this case is not common enough.) Adding the role to the already output tag might have consequences for
1733                 // some document classes where the layout already has a role or uses the same role for another purpose.
1734                 const bool isWithinDate = buffer().getParFromID(rp.lastid).top().paragraph().layout().docbooktag() == "date";
1735
1736                 if (!isWithinDate)
1737                         xml::openTag(xs, "date", "role=\"" + role + "\"", "inline");
1738                 else
1739                         xs << XMLStream::ESCAPE_NONE << from_ascii(std::string("<!-- ") + role + " -->");
1740                 xs << qstring_to_ucs4(parseDate(buffer(), params_).toString(Qt::ISODate));
1741                 if (!isWithinDate)
1742                         xml::closeTag(xs, "date", "inline");
1743                 break;
1744         }
1745
1746         case InsetInfoParams::TIME_INFO:
1747         case InsetInfoParams::MODTIME_INFO:
1748         case InsetInfoParams::FIXTIME_INFO: {
1749                 std::string role;
1750                 switch (params_.type) {
1751                 case InsetInfoParams::TIME_INFO:
1752                         role = "current-time";
1753                         break;
1754                 case InsetInfoParams::MODTIME_INFO:
1755                         role = "last-modification-time";
1756                         break;
1757                 case InsetInfoParams::FIXTIME_INFO:
1758                         role = "fix-time";
1759                         break;
1760                 default:
1761                         lyxerr << "Assertion failed! InsetInfoParams::info_type: " << params().type;
1762                         break;
1763                 }
1764
1765                 // DocBook has no specific element for time, so use a date.
1766                 // See the discussion above (DATE_INFO, MODDATE_INFO, and FIXDATE_INFO) for a discussion about the choices that
1767                 // have been made.
1768                 const bool isWithinDate = buffer().getParFromID(rp.lastid).top().paragraph().layout().docbooktag() == "date";
1769
1770                 if (!isWithinDate)
1771                         xml::openTag(xs, "date", "role=\"" + role + "\"", "inline");
1772                 else
1773                         xs << XMLStream::ESCAPE_NONE << from_ascii(std::string("<!-- ") + role + " -->");
1774                 xs << qstring_to_ucs4(parseTime(buffer(), params_).toString(Qt::ISODate));
1775                 if (!isWithinDate)
1776                         xml::closeTag(xs, "date", "inline");
1777                 break;
1778         }
1779
1780         case InsetInfoParams::BUFFER_INFO:
1781                 xml::openTag(xs, "phrase", "role=\"buffer-info " + params_.name + "\"", "inline");
1782                 xs << getBufferInfo(buffer(), params_);
1783                 xml::closeTag(xs, "phrase", "inline");
1784                 break;
1785         case InsetInfoParams::VCS_INFO:
1786                 xml::openTag(xs, "phrase", "role=\"vcs-info " + params_.name + "\"", "inline");
1787                 xs << getVCSInfo(buffer(), params_);
1788                 xml::closeTag(xs, "phrase", "inline");
1789                 break;
1790         case InsetInfoParams::PACKAGE_INFO:
1791                 xml::openTag(xs, "phrase", "role=\"package-availability " + params_.name + "\"", "inline");
1792                 xs << getPackageInfo(params_);
1793                 xml::closeTag(xs, "phrase", "inline");
1794                 break;
1795         case InsetInfoParams::TEXTCLASS_INFO:
1796                 xml::openTag(xs, "phrase", "role=\"textclass-availability " + params_.name + "\"", "inline");
1797                 xs << getTextClassInfo(params_);
1798                 xml::closeTag(xs, "phrase", "inline");
1799                 break;
1800
1801         case InsetInfoParams::SHORTCUTS_INFO:
1802         case InsetInfoParams::SHORTCUT_INFO:
1803                 docbookShortcutInfo(xs, params_);
1804                 break;
1805                 
1806         case InsetInfoParams::LYXRC_INFO:
1807                 xml::openTag(xs, "phrase", "role=\"lyxrc-entry " + params_.name + "\"", "inline");
1808                 xs << getLyxRCInfo(params_);
1809                 xml::closeTag(xs, "phrase", "inline");
1810                 break;
1811
1812         case InsetInfoParams::MENU_INFO:
1813                 docbookMenuInfo(xs, buffer(), params_);
1814                 break;
1815         case InsetInfoParams::ICON_INFO:
1816                 docbookIconInfo(xs, rp, buffer_, params_);
1817                 break;
1818         case InsetInfoParams::LYX_INFO:
1819                 xml::openTag(xs, "phrase", "role=\"lyx-info " + params_.name + "\"", "inline");
1820                 xs << getLyXInfo(params_);
1821                 xml::closeTag(xs, "phrase", "inline");
1822                 break;
1823
1824         case InsetInfoParams::L7N_INFO:
1825                 // TODO: add "its:translate="no"" in the attributes if ITS is globally enabled for LyX (quite rare to have ITS
1826                 // for DocBook documents).
1827                 xml::openTag(xs, "phrase", R"(role="localized")", "inline");
1828                 xs << getNormalizedL7N(params_);
1829                 xml::closeTag(xs, "phrase", "inline");
1830                 break;
1831
1832         case InsetInfoParams::UNKNOWN_INFO:
1833                 xml::openTag(xs, "phrase", R"(role="unknown")", "inline");
1834                 xs << from_ascii("Unknown Info!");
1835                 xml::closeTag(xs, "phrase", "inline");
1836                 break;
1837         default:
1838                 lyxerr << "Unrecognised InsetInfoParams::info_type: " << params().type;
1839
1840                 xml::openTag(xs, "phrase", R"(role="unrecognized")", "inline");
1841                 xs << from_ascii("Unrecognized Info!");
1842                 xml::closeTag(xs, "phrase", "inline");
1843                 break;
1844         }
1845 }
1846
1847
1848 } // namespace lyx