]> git.lyx.org Git - lyx.git/blob - src/insets/InsetInfo.cpp
Introduce InsetInfoParams
[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 "CutAndPaste.h"
19 #include "Font.h"
20 #include "FuncRequest.h"
21 #include "FuncStatus.h"
22 #include "InsetGraphics.h"
23 #include "InsetSpecialChar.h"
24 #include "KeyMap.h"
25 #include "LaTeXFeatures.h"
26 #include "Language.h"
27 #include "LayoutFile.h"
28 #include "Length.h"
29 #include "LyXAction.h"
30 #include "LyXRC.h"
31 #include "LyXVC.h"
32 #include "Lexer.h"
33 #include "Paragraph.h"
34 #include "ParIterator.h"
35 #include "ParagraphParameters.h"
36 #include "version.h"
37
38 #include "frontends/Application.h"
39
40 #include "support/convert.h"
41 #include "support/debug.h"
42 #include "support/docstream.h"
43 #include "support/docstring_list.h"
44 #include "support/ExceptionMessage.h"
45 #include "support/FileName.h"
46 #include "support/filetools.h"
47 #include "support/gettext.h"
48 #include "support/Messages.h"
49 #include "support/lstrings.h"
50 #include "support/qstring_helpers.h"
51 #include "support/Translator.h"
52
53 #include <sstream>
54
55 #include <QtGui/QImage>
56 #include <QDate>
57 #include <QLocale>
58
59 using namespace std;
60 using namespace lyx::support;
61
62 namespace lyx {
63
64 namespace {
65
66 typedef Translator<InsetInfoParams::info_type, string> NameTranslator;
67
68 NameTranslator const initTranslator()
69 {
70         NameTranslator translator(InsetInfoParams::UNKNOWN_INFO, "unknown");
71
72         translator.addPair(InsetInfoParams::SHORTCUTS_INFO, "shortcuts");
73         translator.addPair(InsetInfoParams::SHORTCUT_INFO, "shortcut");
74         translator.addPair(InsetInfoParams::LYXRC_INFO, "lyxrc");
75         translator.addPair(InsetInfoParams::PACKAGE_INFO, "package");
76         translator.addPair(InsetInfoParams::TEXTCLASS_INFO, "textclass");
77         translator.addPair(InsetInfoParams::MENU_INFO, "menu");
78         translator.addPair(InsetInfoParams::ICON_INFO, "icon");
79         translator.addPair(InsetInfoParams::BUFFER_INFO, "buffer");
80         translator.addPair(InsetInfoParams::LYX_INFO, "lyxinfo");
81         translator.addPair(InsetInfoParams::VCS_INFO, "vcs");
82         translator.addPair(InsetInfoParams::DATE_INFO, "date");
83         translator.addPair(InsetInfoParams::MODDATE_INFO, "moddate");
84         translator.addPair(InsetInfoParams::FIXDATE_INFO, "fixdate");
85
86         return translator;
87 }
88
89 /// The translator between the information type enum and corresponding string.
90 NameTranslator const & nameTranslator()
91 {
92         static NameTranslator const translator = initTranslator();
93         return translator;
94 }
95
96
97 typedef Translator<InsetInfoParams::info_type, string> DefaultValueTranslator;
98
99 DefaultValueTranslator const initDVTranslator()
100 {
101         DefaultValueTranslator translator(InsetInfoParams::UNKNOWN_INFO, "");
102
103         translator.addPair(InsetInfoParams::SHORTCUTS_INFO, "info-insert");
104         translator.addPair(InsetInfoParams::SHORTCUT_INFO, "info-insert");
105         translator.addPair(InsetInfoParams::LYXRC_INFO, "user_name");
106         translator.addPair(InsetInfoParams::PACKAGE_INFO, "graphics");
107         translator.addPair(InsetInfoParams::TEXTCLASS_INFO, "article");
108         translator.addPair(InsetInfoParams::MENU_INFO, "info-insert");
109         translator.addPair(InsetInfoParams::ICON_INFO, "info-insert");
110         translator.addPair(InsetInfoParams::BUFFER_INFO, "name");
111         translator.addPair(InsetInfoParams::LYX_INFO, "version");
112         translator.addPair(InsetInfoParams::VCS_INFO, "revision");
113         translator.addPair(InsetInfoParams::DATE_INFO, "loclong");
114         translator.addPair(InsetInfoParams::MODDATE_INFO, "loclong");
115         translator.addPair(InsetInfoParams::FIXDATE_INFO, "loclong");
116
117         return translator;
118 }
119
120 /// The translator between the information type enum and some sensible default value.
121 DefaultValueTranslator const & defaultValueTranslator()
122 {
123         static DefaultValueTranslator const translator = initDVTranslator();
124         return translator;
125 }
126
127 } // namespace
128
129
130 /////////////////////////////////////////////////////////////////////
131 //
132 // InsetInfoParams
133 //
134 ///////////////////////////////////////////////////////////////////////
135
136 InsetInfoParams infoparams;
137
138 namespace{
139 set<string> getTexFileList(string const & filename)
140 {
141         set<string> list;
142         FileName const file = libFileSearch(string(), filename);
143         if (file.empty())
144                 return list;
145
146         // FIXME Unicode.
147         vector<docstring> doclist =
148                 getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
149
150         // Normalise paths like /foo//bar ==> /foo/bar
151         for (auto doc : doclist) {
152                 subst(doc, from_ascii("\r"), docstring());
153                 while (contains(doc, from_ascii("//")))
154                         subst(doc, from_ascii("//"), from_ascii("/"));
155                 if (!doc.empty())
156                         list.insert(removeExtension(onlyFileName(to_utf8(doc))));
157         }
158
159         // remove duplicates
160         return list;
161 }
162 } // namespace anon
163
164 docstring InsetInfoParams::getDate(string const iname, QDate const date) const
165 {
166         QLocale loc;
167         if (lang)
168                 loc = QLocale(toqstr(lang->code()));
169         if (iname == "long")
170                 return qstring_to_ucs4(loc.toString(date, QLocale::LongFormat));
171         else if (iname == "short")
172                 return qstring_to_ucs4(loc.toString(date, QLocale::ShortFormat));
173         else if (iname == "ISO")
174                 return qstring_to_ucs4(date.toString(Qt::ISODate));
175         else if (iname == "loclong")
176                 return qstring_to_ucs4(loc.toString(date, toqstr(lang->dateFormat(0))));
177         else if (iname == "locmedium")
178                 return qstring_to_ucs4(loc.toString(date, toqstr(lang->dateFormat(1))));
179         else if (iname == "locshort")
180                 return qstring_to_ucs4(loc.toString(date, toqstr(lang->dateFormat(2))));
181         else
182                 return qstring_to_ucs4(loc.toString(date, toqstr(iname)));
183 }
184
185
186 vector<pair<string,docstring>> InsetInfoParams::getArguments(Buffer const * buf,
187                                                              string const & itype) const
188 {
189         vector<pair<string,docstring>> result;
190
191         switch (nameTranslator().find(itype)) {
192         case UNKNOWN_INFO:
193                 result.push_back(make_pair("invalid", _("Please select a valid type!")));
194                 break;
195
196         case SHORTCUT_INFO:
197         case SHORTCUTS_INFO:
198         case MENU_INFO:
199         case ICON_INFO: {
200                 result.push_back(make_pair("custom", _("Custom")));
201                 LyXAction::const_iterator fit = lyxaction.func_begin();
202                 LyXAction::const_iterator const fen = lyxaction.func_end();
203                 for (; fit != fen; ++fit) {
204                         string const lfun = fit->first;
205                         if (!lfun.empty())
206                                 result.push_back(make_pair(lfun, from_ascii(lfun)));
207                 }
208                 break;
209         }
210
211         case LYXRC_INFO: {
212                 result.push_back(make_pair("custom", _("Custom")));
213                 set<string> rcs = lyxrc.getRCs();
214                 for (auto const & rc : rcs)
215                         result.push_back(make_pair(rc, from_ascii(rc)));
216                 break;
217         }
218
219         case PACKAGE_INFO:
220         case TEXTCLASS_INFO: {
221                 result.push_back(make_pair("custom", _("Custom")));
222                 string const filename = (itype == "package") ? "styFiles.lst"
223                                                             : "clsFiles.lst";
224                 set<string> flist = getTexFileList(filename);
225                 for (auto const & f : flist)
226                         result.push_back(make_pair(f, from_utf8(f)));
227                 break;
228         }
229
230         case BUFFER_INFO:
231                 result.push_back(make_pair("name", _("File name")));
232                 result.push_back(make_pair("path", _("File path")));
233                 result.push_back(make_pair("class", _("Used text class")));
234                 break;
235
236         case VCS_INFO: {
237                 if (!buf->lyxvc().inUse()) {
238                         result.push_back(make_pair("invalid", _("No version control!")));
239                         break;
240                 }
241                 result.push_back(make_pair("revision", _("Revision[[Version Control]]")));
242                 result.push_back(make_pair("tree-revision", _("Tree revision")));
243                 result.push_back(make_pair("author", _("Author")));
244                 result.push_back(make_pair("date", _("Date")));
245                 result.push_back(make_pair("time", _("Time")));
246                 break;
247         }
248
249         case LYX_INFO:
250                 result.push_back(make_pair("version", _("LyX version")));
251                 break;
252
253         case FIXDATE_INFO:
254         case DATE_INFO:
255         case MODDATE_INFO:
256                 string const dt = split(name, '@');
257                 QDate date;
258                 if (itype == "moddate")
259                         date = QDateTime::fromTime_t(buf->fileName().lastModified()).date();
260                 else if (itype == "fixdate" && !dt.empty())
261                         date = QDate::fromString(toqstr(dt), Qt::ISODate);
262                 else
263                         date = QDate::currentDate();
264                 result.push_back(make_pair("long",getDate("long", date)));
265                 result.push_back(make_pair("short", getDate("short", date)));
266                 result.push_back(make_pair("loclong", getDate("loclong", date)));
267                 result.push_back(make_pair("locmedium", getDate("locmedium", date)));
268                 result.push_back(make_pair("locshort", getDate("locshort", date)));
269                 result.push_back(make_pair("ISO", getDate("ISO", date)));
270                 result.push_back(make_pair("yyyy", getDate("yyyy", date)));
271                 result.push_back(make_pair("MMMM", getDate("MMMM", date)));
272                 result.push_back(make_pair("MMM", getDate("MMM", date)));
273                 result.push_back(make_pair("dddd", getDate("dddd", date)));
274                 result.push_back(make_pair("ddd", getDate("ddd", date)));
275                 result.push_back(make_pair("custom", _("Custom")));
276                 break;
277         }
278
279         return result;
280 }
281
282
283 string InsetInfoParams::infoType() const
284 {
285         return nameTranslator().find(type);
286 }
287
288
289
290 /////////////////////////////////////////////////////////////////////////
291 //
292 // InsetInfo
293 //
294 /////////////////////////////////////////////////////////////////////////
295
296
297
298 InsetInfo::InsetInfo(Buffer * buf, string const & name)
299         : InsetCollapsible(buf), initialized_(false)
300 {
301         params_.type = InsetInfoParams::UNKNOWN_INFO;
302         params_.force_ltr = false;
303         setInfo(name);
304         status_ = Collapsed;
305 }
306
307
308 Inset * InsetInfo::editXY(Cursor & cur, int x, int y)
309 {
310         // do not allow the cursor to be set in this Inset
311         return Inset::editXY(cur, x, y);
312 }
313
314
315 docstring InsetInfo::layoutName() const
316 {
317         return from_ascii("Info:" + params_.infoType());
318 }
319
320
321 docstring InsetInfo::toolTip(BufferView const &, int, int) const
322 {
323         docstring result;
324         switch (nameTranslator().find(params_.infoType())) {
325         case InsetInfoParams::UNKNOWN_INFO:
326                 result = _("Invalid information inset");
327                 break;
328         case InsetInfoParams::SHORTCUT_INFO:
329                 result = bformat(_("The keybard shortcut for the function '%1$s'"),
330                                 from_utf8(params_.name));
331                 break;
332         case InsetInfoParams::SHORTCUTS_INFO:
333                 result = bformat(_("The keybard shortcuts for the function '%1$s'"),
334                                 from_utf8(params_.name));
335                 break;
336         case InsetInfoParams::MENU_INFO: 
337                 result = bformat(_("The menu location for the function '%1$s'"),
338                                 from_utf8(params_.name));
339                 break;
340         case InsetInfoParams::ICON_INFO:
341                 result = bformat(_("The toolbar icon for the function '%1$s'"),
342                                 from_utf8(params_.name));
343                 break;
344         case InsetInfoParams::LYXRC_INFO:
345                 result = bformat(_("The preference setting for the preference key '%1$s'"),
346                                 from_utf8(params_.name));
347                 break;
348         case InsetInfoParams::PACKAGE_INFO:
349                 result = bformat(_("Availability of the LaTeX package '%1$s'"),
350                                 from_utf8(params_.name));
351                 break;
352         case InsetInfoParams::TEXTCLASS_INFO:
353                 result = bformat(_("Availability of the LaTeX class '%1$s'"),
354                                 from_utf8(params_.name));
355                 break;
356         case InsetInfoParams::BUFFER_INFO:
357                 if (params_.name == "name")
358                         result = _("The name of this file");
359                 else if (params_.name == "path")
360                         result = _("The path where this file is saved");
361                 else if (params_.name == "class")
362                         result = _("The class this document uses");
363                 break;
364         case InsetInfoParams::VCS_INFO:
365                 if (params_.name == "revision")
366                         result = _("Version control revision");
367                 else if (params_.name == "tree-revision")
368                         result = _("Version control tree revision");
369                 else if (params_.name == "author")
370                          result = _("Version control author");
371                 else if (params_.name == "date")
372                         result = _("Version control date");
373                 else if (params_.name == "time")
374                         result = _("Version control time");
375                 break;
376         case InsetInfoParams::LYX_INFO:
377                 result = _("The current LyX version");
378                 break;
379         case InsetInfoParams::DATE_INFO:
380                 result = _("The current date");
381                 break;
382         case InsetInfoParams::MODDATE_INFO:
383                 result = _("The date of last save");
384                 break;
385         case InsetInfoParams::FIXDATE_INFO:
386                 result = _("A static date");
387                 break;
388         }
389
390         return result;
391 }
392
393
394 void InsetInfo::read(Lexer & lex)
395 {
396         string token;
397         while (lex.isOK()) {
398                 lex.next();
399                 token = lex.getString();
400                 if (token == "type") {
401                         lex.next();
402                         token = lex.getString();
403                         params_.type = nameTranslator().find(token);
404                 } else if (token == "arg") {
405                         lex.next(true);
406                         params_.name = lex.getString();
407                 } else if (token == "\\end_inset")
408                         break;
409         }
410         if (token != "\\end_inset") {
411                 lex.printError("Missing \\end_inset at this point");
412                 throw ExceptionMessage(WarningException,
413                         _("Missing \\end_inset at this point."),
414                         from_utf8(token));
415         }
416 }
417
418
419 void InsetInfo::write(ostream & os) const
420 {
421         os << "Info\ntype  \"" << params_.infoType()
422            << "\"\narg   " << Lexer::quoteString(params_.name);
423 }
424
425
426 bool InsetInfo::validateModifyArgument(docstring const & arg) const
427 {
428         string type;
429         string name = trim(split(to_utf8(arg), type, ' '));
430
431         switch (nameTranslator().find(type)) {
432         case InsetInfoParams::UNKNOWN_INFO:
433                 return false;
434
435         case InsetInfoParams::SHORTCUT_INFO:
436         case InsetInfoParams::SHORTCUTS_INFO:
437         case InsetInfoParams::MENU_INFO: {
438                 FuncRequest func = lyxaction.lookupFunc(name);
439                 return func.action() != LFUN_UNKNOWN_ACTION;
440         }
441
442         case InsetInfoParams::ICON_INFO: {
443                 FuncCode const action = lyxaction.lookupFunc(name).action();
444                 if (action == LFUN_UNKNOWN_ACTION) {
445                         string dir = "images";
446                         return !imageLibFileSearch(dir, name, "svgz,png").empty();
447                 }
448                 return true;
449         }
450
451         case InsetInfoParams::LYXRC_INFO: {
452                 set<string> rcs = lyxrc.getRCs();
453                 return rcs.find(name) != rcs.end();
454         }
455
456         case InsetInfoParams::PACKAGE_INFO:
457         case InsetInfoParams::TEXTCLASS_INFO:
458                 return true;
459
460         case InsetInfoParams::BUFFER_INFO:
461                 return (name == "name" || name == "path" || name == "class");
462
463         case InsetInfoParams::VCS_INFO:
464                 if (name == "revision" || name == "tree-revision"
465                     || name == "author" || name == "date" || name == "time")
466                         return buffer().lyxvc().inUse();
467                 return false;
468
469         case InsetInfoParams::LYX_INFO:
470                 return name == "version";
471
472         case InsetInfoParams::FIXDATE_INFO: {
473                 string date;
474                 string piece;
475                 date = split(name, piece, '@');
476                 if (!date.empty() && !QDate::fromString(toqstr(date), Qt::ISODate).isValid())
477                         return false;
478                 if (!piece.empty())
479                         name = piece;
480         }
481         // fall through
482         case InsetInfoParams::DATE_INFO:
483         case InsetInfoParams::MODDATE_INFO: {
484                 if (name == "long" || name == "short" || name == "ISO")
485                         return true;
486                 else {
487                         QDate date = QDate::currentDate();
488                         return !date.toString(toqstr(name)).isEmpty();
489                 }
490         }
491         }
492
493         return false;
494 }
495
496
497 bool InsetInfo::showInsetDialog(BufferView * bv) const
498 {
499         bv->showDialog("info");
500         return true;
501 }
502
503
504 bool InsetInfo::getStatus(Cursor & cur, FuncRequest const & cmd,
505                 FuncStatus & flag) const
506 {
507         switch (cmd.action()) {
508         case LFUN_INSET_SETTINGS:
509                 return InsetCollapsible::getStatus(cur, cmd, flag);
510
511         case LFUN_INSET_DIALOG_UPDATE:
512         case LFUN_INSET_COPY_AS:
513                 flag.setEnabled(true);
514                 return true;
515
516         case LFUN_INSET_MODIFY:
517                 if (validateModifyArgument(cmd.argument())) {
518                         flag.setEnabled(true);
519                         string typestr;
520                         string name = trim(split(to_utf8(cmd.argument()), typestr, ' '));
521                         InsetInfoParams::info_type type = nameTranslator().find(typestr);
522                         string origname = params_.name;
523                         if (type == InsetInfoParams::FIXDATE_INFO)
524                                 split(params_.name, origname, '@');
525                         flag.setOnOff(type == params_.type && name == origname);
526                         return true;
527                 }
528                 //fall through
529
530         default:
531                 return false;
532         }
533 }
534
535
536 void InsetInfo::doDispatch(Cursor & cur, FuncRequest & cmd)
537 {
538         switch (cmd.action()) {
539         case LFUN_INSET_MODIFY:
540                 cur.recordUndo();
541                 setInfo(to_utf8(cmd.argument()));
542                 cur.forceBufferUpdate();
543                 initialized_ = false;
544                 break;
545
546         case LFUN_INSET_COPY_AS: {
547                 cap::clearSelection();
548                 Cursor copy(cur);
549                 copy.pushBackward(*this);
550                 copy.pit() = 0;
551                 copy.pos() = 0;
552                 copy.resetAnchor();
553                 copy.pit() = copy.lastpit();
554                 copy.pos() = copy.lastpos();
555                 copy.setSelection();
556                 cap::copySelection(copy);
557                 break;
558         }
559
560         default:
561                 InsetCollapsible::doDispatch(cur, cmd);
562                 break;
563         }
564 }
565
566
567 void InsetInfo::setInfo(string const & name)
568 {
569         if (name.empty())
570                 return;
571
572         string saved_date_specifier;
573         // Store old date specifier for potential re-use
574         if (!params_.name.empty())
575                 saved_date_specifier = split(params_.name, '@');
576         // info_type name
577         string type;
578         params_.name = trim(split(name, type, ' '));
579         params_.type = nameTranslator().find(type);
580         if (params_.name.empty())
581                 params_.name = defaultValueTranslator().find(params_.type);
582         if (params_.type == InsetInfoParams::FIXDATE_INFO) {
583                 string const date_specifier = split(params_.name, '@');
584                 // If an explicit new fix date is specified, use that
585                 // Otherwise, use the old one or, if there is none,
586                 // the current date
587                 if (date_specifier.empty()) {
588                         if (saved_date_specifier.empty())
589                                 params_.name += "@" + fromqstr(QDate::currentDate().toString(Qt::ISODate));
590                         else
591                                 params_.name += "@" + saved_date_specifier;
592                 }
593         }
594 }
595
596
597 void InsetInfo::error(docstring const & err, Language const * lang)
598 {
599         setText(bformat(translateIfPossible(err, lang->code()), from_utf8(params_.name)),
600                 Font(inherit_font, lang), false);
601 }
602
603
604 void InsetInfo::info(docstring const & err, Language const * lang)
605 {
606         setText(translateIfPossible(err, lang->code()),
607                         Font(inherit_font, lang), false);
608 }
609
610
611 void InsetInfo::setText(docstring const & str, Language const * lang)
612 {
613         setText(str, Font(inherit_font, lang), false);
614 }
615
616
617 bool InsetInfo::forceLTR() const
618 {
619         return params_.force_ltr;
620 }
621
622
623 void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
624         // If the Buffer is a clone, then we neither need nor want to do any
625         // of what follows. We want, rather, just to inherit how things were
626         // in the original Buffer. This is especially important for VCS.
627         // Otherwise, we could in principle have different settings here
628         // than in the Buffer we were exporting.
629         if (buffer().isClone())
630                 return;
631
632         BufferParams const & bp = buffer().params();
633         params_.lang = it.paragraph().getFontSettings(bp, it.pos()).language();
634         Language const * tryguilang = languages.getFromCode(Messages::guiLanguage());
635         // Some info insets use the language of the GUI (if available)
636         Language const * guilang = tryguilang ? tryguilang : params_.lang;
637
638         params_.force_ltr = !params_.lang->rightToLeft();
639         // This is just to get the string into the po files
640         docstring gui;
641         switch (params_.type) {
642         case InsetInfoParams::UNKNOWN_INFO:
643                 gui = _("Unknown Info!");
644                 info(from_ascii("Unknown Info!"), params_.lang);
645                 initialized_ = false;
646                 break;
647         case InsetInfoParams::SHORTCUT_INFO:
648         case InsetInfoParams::SHORTCUTS_INFO: {
649                 // shortcuts can change, so we need to re-do this each time
650                 FuncRequest const func = lyxaction.lookupFunc(params_.name);
651                 if (func.action() == LFUN_UNKNOWN_ACTION) {
652                         gui = _("Unknown action %1$s");
653                         error(from_ascii("Unknown action %1$s"), params_.lang);
654                         break;
655                 }
656                 KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(func);
657                 if (bindings.empty()) {
658                         gui = _("undefined");
659                         info(from_ascii("undefined"), params_.lang);
660                         break;
661                 }
662                 if (params_.type == InsetInfoParams::SHORTCUT_INFO)
663                         setText(bindings.begin()->print(KeySequence::ForGui), guilang);
664                 else
665                         setText(theTopLevelKeymap().printBindings(func, KeySequence::ForGui), guilang);
666                 params_.force_ltr = !guilang->rightToLeft() && !params_.lang->rightToLeft();
667                 break;
668         }
669         case InsetInfoParams::LYXRC_INFO: {
670                 // this information could change, if the preferences are changed,
671                 // so we will recalculate each time through.
672                 ostringstream oss;
673                 if (params_.name.empty()) {
674                         gui = _("undefined");
675                         info(from_ascii("undefined"), params_.lang);
676                         break;
677                 }
678                 // FIXME this uses the serialization mechanism to get the info
679                 // we want, which i guess works but is a bit strange.
680                 lyxrc.write(oss, true, params_.name);
681                 string result = oss.str();
682                 if (result.size() < 2) {
683                         gui = _("undefined");
684                         info(from_ascii("undefined"), params_.lang);
685                         break;
686                 }
687                 string::size_type loc = result.rfind("\n", result.size() - 2);
688                 loc = loc == string::npos ? 0 : loc + 1;
689                 if (result.size() < loc + params_.name.size() + 1
690                           || result.substr(loc + 1, params_.name.size()) != params_.name) {
691                         gui = _("undefined");
692                         info(from_ascii("undefined"), params_.lang);
693                         break;
694                 }
695                 // remove leading comments and \\name and space
696                 result = result.substr(loc + params_.name.size() + 2);
697
698                 // remove \n and ""
699                 result = rtrim(result, "\n");
700                 result = trim(result, "\"");
701                 setText(from_utf8(result), params_.lang);
702                 break;
703         }
704         case InsetInfoParams::PACKAGE_INFO:
705                 // only need to do this once.
706                 if (initialized_)
707                         break;
708                 // check in packages.lst
709                 if (LaTeXFeatures::isAvailable(params_.name)) {
710                         gui = _("yes");
711                         info(from_ascii("yes"), params_.lang);
712                 } else {
713                         gui = _("no");
714                         info(from_ascii("no"), params_.lang);
715                 }
716                 initialized_ = true;
717                 break;
718
719         case InsetInfoParams::TEXTCLASS_INFO: {
720                 // the TextClass can change
721                 LayoutFileList const & list = LayoutFileList::get();
722                 bool available = false;
723                 // params_.name is the class name
724                 if (list.haveClass(params_.name))
725                         available = list[params_.name].isTeXClassAvailable();
726                 if (available) {
727                         gui = _("yes");
728                         info(from_ascii("yes"), params_.lang);
729                 } else {
730                         gui = _("no");
731                         info(from_ascii("no"), params_.lang);
732                 }
733                 break;
734         }
735         case InsetInfoParams::MENU_INFO: {
736                 // only need to do this once.
737                 if (initialized_)
738                         break;
739                 // and we will not keep trying if we fail
740                 initialized_ = true;
741                 docstring_list names;
742                 FuncRequest const func = lyxaction.lookupFunc(params_.name);
743                 if (func.action() == LFUN_UNKNOWN_ACTION) {
744                         gui = _("Unknown action %1$s");
745                         error(from_ascii("Unknown action %1$s"), params_.lang);
746                         break;
747                 }
748                 // iterate through the menubackend to find it
749                 if (!theApp()) {
750                         gui = _("Can't determine menu entry for action %1$s in batch mode");
751                         error(from_ascii("Can't determine menu entry for action %1$s in batch mode"), params_.lang);
752                         break;
753                 }
754                 if (!theApp()->searchMenu(func, names)) {
755                         gui = _("No menu entry for action %1$s");
756                         error(from_ascii("No menu entry for action %1$s"), params_.lang);
757                         break;
758                 }
759                 // if found, return its path.
760                 clear();
761                 Paragraph & par = paragraphs().front();
762                 Font const f(inherit_font, guilang);
763                 params_.force_ltr = !guilang->rightToLeft();
764                 //Font fu = f;
765                 //fu.fontInfo().setUnderbar(FONT_ON);
766                 for (docstring const & name : names) {
767                         // do not insert > for the top level menu item
768                         if (&name != &names.front())
769                                 par.insertInset(par.size(), new InsetSpecialChar(InsetSpecialChar::MENU_SEPARATOR),
770                                                 f, Change(Change::UNCHANGED));
771                         //FIXME: add proper underlines here. This
772                         // involves rewriting searchMenu used above to
773                         // return a vector of menus. If we do not do
774                         // that, we might as well use below
775                         // Paragraph::insert on each string (JMarc)
776                         for (char_type c : name)
777                                 par.insertChar(par.size(), c, f, Change(Change::UNCHANGED));
778                 }
779                 break;
780         }
781         case InsetInfoParams::ICON_INFO: {
782                 // only need to do this once.
783                 if (initialized_)
784                         break;
785                 // and we will not keep trying if we fail
786                 initialized_ = true;
787                 FuncRequest func = lyxaction.lookupFunc(params_.name);
788                 docstring icon_name = frontend::Application::iconName(func, true);
789                 // FIXME: We should use the icon directly instead of
790                 // going through FileName. The code below won't work
791                 // if the icon is embedded in the executable through
792                 // the Qt resource system.
793                 // This is only a negligible performance problem:
794                 // If the installed icon differs from the resource icon the
795                 // installed one is preferred anyway, and all icons that are
796                 // embedded in the resources are installed as well.
797                 FileName file(to_utf8(icon_name));
798                 if (file.onlyFileNameWithoutExt() == "unknown") {
799                         string dir = "images";
800                         FileName file2(imageLibFileSearch(dir, params_.name, "svgz,png"));
801                         if (!file2.empty())
802                                 file = file2;
803                 }
804                 if (!file.exists())
805                         break;
806                 int percent_scale = 100;
807                 if (use_gui) {
808                         // Compute the scale factor for the icon such that its
809                         // width on screen is equal to 1em in pixels.
810                         // The scale factor is rounded to the integer nearest
811                         // to the float value of the ratio 100*iconsize/imgsize.
812                         int imgsize = QImage(toqstr(file.absFileName())).width();
813                         if (imgsize > 0) {
814                                 int iconsize = Length(1, Length::EM).inPixels(1);
815                                 percent_scale = (100 * iconsize + imgsize / 2)/imgsize;
816                         }
817                 }
818                 InsetGraphics * inset = new InsetGraphics(buffer_);
819                 InsetGraphicsParams igp;
820                 igp.filename = file;
821                 igp.lyxscale = percent_scale;
822                 igp.scale = string();
823                 igp.width = Length(1, Length::EM);
824                 inset->setParams(igp);
825                 clear();
826                 Font const f(inherit_font, params_.lang);
827                 paragraphs().front().insertInset(0, inset, f,
828                                                  Change(Change::UNCHANGED));
829                 break;
830         }
831         case InsetInfoParams::BUFFER_INFO: {
832                 // this could all change, so we will recalculate each time
833                 if (params_.name == "name")
834                         setText(from_utf8(buffer().fileName().onlyFileName()), params_.lang);
835                 else if (params_.name == "path")
836                         setText(from_utf8(os::latex_path(buffer().filePath())), params_.lang);
837                 else if (params_.name == "class")
838                         setText(from_utf8(bp.documentClass().name()), params_.lang);
839                 break;
840         }
841         case InsetInfoParams::VCS_INFO: {
842                 // this information could change, in principle, so we will 
843                 // recalculate each time through
844                 if (!buffer().lyxvc().inUse()) {
845                         gui = _("No version control!");
846                         info(from_ascii("No version control!"), params_.lang);
847                         break;
848                 }
849                 LyXVC::RevisionInfo itype = LyXVC::Unknown;
850                 if (params_.name == "revision")
851                         itype = LyXVC::File;
852                 else if (params_.name == "tree-revision")
853                         itype = LyXVC::Tree;
854                 else if (params_.name == "author")
855                         itype = LyXVC::Author;
856                 else if (params_.name == "time")
857                         itype = LyXVC::Time;
858                 else if (params_.name == "date")
859                         itype = LyXVC::Date;
860                 string binfo = buffer().lyxvc().revisionInfo(itype);
861                 if (binfo.empty()) {
862                         gui = _("%1$s[[vcs data]] unknown");
863                         error(from_ascii("%1$s[[vcs data]] unknown"), params_.lang);
864                 } else
865                         setText(from_utf8(binfo), params_.lang);
866                 break;
867         }
868         case InsetInfoParams::LYX_INFO:
869                 // only need to do this once.
870                 if (initialized_)
871                         break;
872                 if (params_.name == "version")
873                         setText(from_ascii(lyx_version), params_.lang);
874                 initialized_ = true;
875                 break;
876         case InsetInfoParams::DATE_INFO:
877         case InsetInfoParams::MODDATE_INFO:
878         case InsetInfoParams::FIXDATE_INFO: {
879                 string date_format = params_.name;
880                 string const date_specifier = (params_.type == InsetInfoParams::FIXDATE_INFO
881                                                && contains(params_.name, '@'))
882                                 ? split(params_.name, date_format, '@') : string();
883                 QDate date;
884                 if (params_.type == InsetInfoParams::MODDATE_INFO)
885                         date = QDateTime::fromTime_t(buffer().fileName().lastModified()).date();
886                 else if (params_.type == InsetInfoParams::FIXDATE_INFO && !date_specifier.empty())
887                         date = QDate::fromString(toqstr(date_specifier), Qt::ISODate);
888                 else
889                         date = QDate::currentDate();
890                 setText(params_.getDate(date_format, date), params_.lang);
891         }
892         }
893         // Just to do something with that string
894         LYXERR(Debug::INFO, "info inset text: " << gui);
895         InsetCollapsible::updateBuffer(it, utype);
896 }
897
898
899 string InsetInfo::contextMenu(BufferView const &, int, int) const
900 {
901         //FIXME: We override the implementation of InsetCollapsible,
902         //because this inset is not a collapsible inset.
903         return contextMenuName();
904 }
905
906
907 string InsetInfo::contextMenuName() const
908 {
909         return "context-info";
910 }
911
912
913 } // namespace lyx