3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
12 #include "InsetInfo.h"
15 #include "BufferParams.h"
16 #include "BufferView.h"
17 #include "CutAndPaste.h"
19 #include "FuncRequest.h"
20 #include "FuncStatus.h"
21 #include "InsetGraphics.h"
22 #include "InsetSpecialChar.h"
24 #include "LaTeXFeatures.h"
26 #include "LayoutFile.h"
28 #include "LyXAction.h"
32 #include "Paragraph.h"
33 #include "ParIterator.h"
34 #include "ParagraphParameters.h"
37 #include "frontends/Application.h"
39 #include "support/convert.h"
40 #include "support/debug.h"
41 #include "support/docstream.h"
42 #include "support/docstring_list.h"
43 #include "support/ExceptionMessage.h"
44 #include "support/FileName.h"
45 #include "support/filetools.h"
46 #include "support/gettext.h"
47 #include "support/Messages.h"
48 #include "support/lstrings.h"
49 #include "support/qstring_helpers.h"
50 #include "support/Translator.h"
54 #include <QtGui/QImage>
57 using namespace lyx::support;
63 typedef Translator<InsetInfo::info_type, string> NameTranslator;
65 NameTranslator const initTranslator()
67 NameTranslator translator(InsetInfo::UNKNOWN_INFO, "unknown");
69 translator.addPair(InsetInfo::SHORTCUTS_INFO, "shortcuts");
70 translator.addPair(InsetInfo::SHORTCUT_INFO, "shortcut");
71 translator.addPair(InsetInfo::LYXRC_INFO, "lyxrc");
72 translator.addPair(InsetInfo::PACKAGE_INFO, "package");
73 translator.addPair(InsetInfo::TEXTCLASS_INFO, "textclass");
74 translator.addPair(InsetInfo::MENU_INFO, "menu");
75 translator.addPair(InsetInfo::ICON_INFO, "icon");
76 translator.addPair(InsetInfo::BUFFER_INFO, "buffer");
77 translator.addPair(InsetInfo::LYX_INFO, "lyxinfo");
82 /// The translator between the information type enum and corresponding string.
83 NameTranslator const & nameTranslator()
85 static NameTranslator const translator = initTranslator();
91 /////////////////////////////////////////////////////////////////////////
95 /////////////////////////////////////////////////////////////////////////
99 InsetInfo::InsetInfo(Buffer * buf, string const & name)
100 : InsetCollapsible(buf), initialized_(false),
101 type_(UNKNOWN_INFO), name_(), force_ltr_(false)
108 Inset * InsetInfo::editXY(Cursor & cur, int x, int y)
110 // do not allow the cursor to be set in this Inset
111 return Inset::editXY(cur, x, y);
115 string InsetInfo::infoType() const
117 return nameTranslator().find(type_);
121 docstring InsetInfo::layoutName() const
123 return from_ascii("Info:" + infoType());
127 docstring InsetInfo::toolTip(BufferView const &, int, int) const
129 return bformat(_("Information regarding %1$s '%2$s'"),
130 _(infoType()), from_utf8(name_));
134 void InsetInfo::read(Lexer & lex)
139 token = lex.getString();
140 if (token == "type") {
142 token = lex.getString();
143 type_ = nameTranslator().find(token);
144 } else if (token == "arg") {
146 name_ = lex.getString();
147 } else if (token == "\\end_inset")
150 if (token != "\\end_inset") {
151 lex.printError("Missing \\end_inset at this point");
152 throw ExceptionMessage(WarningException,
153 _("Missing \\end_inset at this point."),
159 void InsetInfo::write(ostream & os) const
161 os << "Info\ntype \"" << infoType()
162 << "\"\narg " << Lexer::quoteString(name_);
166 bool InsetInfo::validateModifyArgument(docstring const & arg) const
169 string const name = trim(split(to_utf8(arg), type, ' '));
171 switch (nameTranslator().find(type)) {
178 FuncRequest func = lyxaction.lookupFunc(name);
179 return func.action() != LFUN_UNKNOWN_ACTION;
183 FuncCode const action = lyxaction.lookupFunc(name).action();
184 if (action == LFUN_UNKNOWN_ACTION) {
185 string dir = "images";
186 return !imageLibFileSearch(dir, name, "svgz,png").empty();
193 lyxrc.write(oss, true, name);
194 return !oss.str().empty();
202 if (name == "name" || name == "path" || name == "class")
204 if (name == "vcs-revision" || name == "vcs-tree-revision" ||
205 name == "vcs-author" || name == "vcs-date" || name == "vcs-time")
206 return buffer().lyxvc().inUse();
210 return name == "version";
217 bool InsetInfo::showInsetDialog(BufferView * bv) const
219 bv->showDialog("info");
224 bool InsetInfo::getStatus(Cursor & cur, FuncRequest const & cmd,
225 FuncStatus & flag) const
227 switch (cmd.action()) {
228 case LFUN_INSET_SETTINGS:
229 return InsetCollapsible::getStatus(cur, cmd, flag);
231 case LFUN_INSET_DIALOG_UPDATE:
232 case LFUN_INSET_COPY_AS:
233 flag.setEnabled(true);
236 case LFUN_INSET_MODIFY:
237 if (validateModifyArgument(cmd.argument())) {
238 flag.setEnabled(true);
249 void InsetInfo::doDispatch(Cursor & cur, FuncRequest & cmd)
251 switch (cmd.action()) {
252 case LFUN_INSET_MODIFY:
254 setInfo(to_utf8(cmd.argument()));
255 cur.forceBufferUpdate();
256 initialized_ = false;
259 case LFUN_INSET_COPY_AS: {
260 cap::clearSelection();
262 copy.pushBackward(*this);
266 copy.pit() = copy.lastpit();
267 copy.pos() = copy.lastpos();
269 cap::copySelection(copy);
274 InsetCollapsible::doDispatch(cur, cmd);
280 void InsetInfo::setInfo(string const & name)
286 name_ = trim(split(name, type, ' '));
287 type_ = nameTranslator().find(type);
291 void InsetInfo::error(docstring const & err, Language const * lang)
293 setText(bformat(translateIfPossible(err, lang->code()), from_utf8(name_)),
294 Font(inherit_font, lang), false);
298 void InsetInfo::info(docstring const & err, Language const * lang)
300 setText(translateIfPossible(err, lang->code()),
301 Font(inherit_font, lang), false);
305 void InsetInfo::setText(docstring const & str, Language const * lang)
307 setText(str, Font(inherit_font, lang), false);
311 bool InsetInfo::forceLTR() const
317 void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
318 // If the Buffer is a clone, then we neither need nor want to do any
319 // of what follows. We want, rather, just to inherit how things were
320 // in the original Buffer. This is especially important for VCS.
321 // Otherwise, we could in principle have different settings here
322 // than in the Buffer we were exporting.
323 if (buffer().isClone())
326 BufferParams const & bp = buffer().params();
327 Language const * lang = it.paragraph().getFontSettings(bp, it.pos()).language();
328 Language const * tryguilang = languages.getFromCode(Messages::guiLanguage());
329 // Some info insets use the language of the GUI (if available)
330 Language const * guilang = tryguilang ? tryguilang : lang;
332 force_ltr_ = !lang->rightToLeft();
333 // This is just to get the string into the po files
337 gui = _("Unknown Info!");
338 info(from_ascii("Unknown Info!"), lang);
339 initialized_ = false;
342 case SHORTCUTS_INFO: {
343 // shortcuts can change, so we need to re-do this each time
344 FuncRequest const func = lyxaction.lookupFunc(name_);
345 if (func.action() == LFUN_UNKNOWN_ACTION) {
346 gui = _("Unknown action %1$s");
347 error(from_ascii("Unknown action %1$s"), lang);
350 KeyMap::Bindings bindings = theTopLevelKeymap().findBindings(func);
351 if (bindings.empty()) {
352 gui = _("undefined");
353 info(from_ascii("undefined"), lang);
356 if (type_ == SHORTCUT_INFO)
357 setText(bindings.begin()->print(KeySequence::Portable), guilang);
359 setText(theTopLevelKeymap().printBindings(func, KeySequence::Portable), guilang);
360 force_ltr_ = !guilang->rightToLeft() && !lang->rightToLeft();
364 // this information could change, if the preferences are changed,
365 // so we will recalculate each time through.
368 gui = _("undefined");
369 info(from_ascii("undefined"), lang);
372 // FIXME this uses the serialization mechanism to get the info
373 // we want, which i guess works but is a bit strange.
374 lyxrc.write(oss, true, name_);
375 string result = oss.str();
376 if (result.size() < 2) {
377 gui = _("undefined");
378 info(from_ascii("undefined"), lang);
381 string::size_type loc = result.rfind("\n", result.size() - 2);
382 loc = loc == string::npos ? 0 : loc + 1;
383 if (result.size() < loc + name_.size() + 1
384 || result.substr(loc + 1, name_.size()) != name_) {
385 gui = _("undefined");
386 info(from_ascii("undefined"), lang);
389 // remove leading comments and \\name and space
390 result = result.substr(loc + name_.size() + 2);
393 result = rtrim(result, "\n");
394 result = trim(result, "\"");
395 setText(from_utf8(result), lang);
399 // only need to do this once.
402 // check in packages.lst
403 if (LaTeXFeatures::isAvailable(name_)) {
405 info(from_ascii("yes"), lang);
408 info(from_ascii("no"), lang);
413 case TEXTCLASS_INFO: {
414 // the TextClass can change
415 LayoutFileList const & list = LayoutFileList::get();
416 bool available = false;
417 // name_ is the class name
418 if (list.haveClass(name_))
419 available = list[name_].isTeXClassAvailable();
422 info(from_ascii("yes"), lang);
425 info(from_ascii("no"), lang);
430 // only need to do this once.
433 // and we will not keep trying if we fail
435 docstring_list names;
436 FuncRequest const func = lyxaction.lookupFunc(name_);
437 if (func.action() == LFUN_UNKNOWN_ACTION) {
438 gui = _("Unknown action %1$s");
439 error(from_ascii("Unknown action %1$s"), lang);
442 // iterate through the menubackend to find it
444 gui = _("Can't determine menu entry for action %1$s in batch mode");
445 error(from_ascii("Can't determine menu entry for action %1$s in batch mode"), lang);
448 if (!theApp()->searchMenu(func, names)) {
449 gui = _("No menu entry for action %1$s");
450 error(from_ascii("No menu entry for action %1$s"), lang);
453 // if found, return its path.
455 Paragraph & par = paragraphs().front();
456 Font const f(inherit_font, guilang);
457 force_ltr_ = !guilang->rightToLeft();
459 //fu.fontInfo().setUnderbar(FONT_ON);
460 for (docstring const & name : names) {
461 // do not insert > for the top level menu item
462 if (&name != &names.front())
463 par.insertInset(par.size(), new InsetSpecialChar(InsetSpecialChar::MENU_SEPARATOR),
464 f, Change(Change::UNCHANGED));
465 //FIXME: add proper underlines here. This
466 // involves rewriting searchMenu used above to
467 // return a vector of menus. If we do not do
468 // that, we might as well use below
469 // Paragraph::insert on each string (JMarc)
470 for (char_type c : name)
471 par.insertChar(par.size(), c, f, Change(Change::UNCHANGED));
476 // only need to do this once.
479 // and we will not keep trying if we fail
481 FuncRequest func = lyxaction.lookupFunc(name_);
482 docstring icon_name = frontend::Application::iconName(func, true);
483 // FIXME: We should use the icon directly instead of
484 // going through FileName. The code below won't work
485 // if the icon is embedded in the executable through
486 // the Qt resource system.
487 // This is only a negligible performance problem:
488 // If the installed icon differs from the resource icon the
489 // installed one is preferred anyway, and all icons that are
490 // embedded in the resources are installed as well.
491 FileName file(to_utf8(icon_name));
492 if (file.onlyFileNameWithoutExt() == "unknown") {
493 string dir = "images";
494 FileName file2(imageLibFileSearch(dir, name_, "svgz,png"));
500 int percent_scale = 100;
502 // Compute the scale factor for the icon such that its
503 // width on screen is equal to 1em in pixels.
504 // The scale factor is rounded to the integer nearest
505 // to the float value of the ratio 100*iconsize/imgsize.
506 int imgsize = QImage(toqstr(file.absFileName())).width();
508 int iconsize = Length(1, Length::EM).inPixels(1);
509 percent_scale = (100 * iconsize + imgsize / 2)/imgsize;
512 InsetGraphics * inset = new InsetGraphics(buffer_);
513 InsetGraphicsParams igp;
515 igp.lyxscale = percent_scale;
516 igp.scale = string();
517 igp.width = Length(1, Length::EM);
518 inset->setParams(igp);
520 Font const f(inherit_font, lang);
521 paragraphs().front().insertInset(0, inset, f,
522 Change(Change::UNCHANGED));
526 // this could all change, so we will recalculate each time
527 if (name_ == "name") {
528 setText(from_utf8(buffer().fileName().onlyFileName()), lang);
531 if (name_ == "path") {
532 setText(from_utf8(os::latex_path(buffer().filePath())), lang);
535 if (name_ == "class") {
536 setText(from_utf8(bp.documentClass().name()), lang);
540 ////////////////////////////////////////////////////////////////
541 // everything that follows is for version control.
542 // nothing that isn't version control should go below this line.
544 // this information could change, in principle, so we will
545 // recalculate each time through
546 if (!buffer().lyxvc().inUse()) {
547 gui = _("No version control");
548 info(from_ascii("No version control"), lang);
551 LyXVC::RevisionInfo itype = LyXVC::Unknown;
552 if (name_ == "vcs-revision")
554 else if (name_ == "vcs-tree-revision")
556 else if (name_ == "vcs-author")
557 itype = LyXVC::Author;
558 else if (name_ == "vcs-time")
560 else if (name_ == "vcs-date")
562 string binfo = buffer().lyxvc().revisionInfo(itype);
564 gui = _("%1$s[[vcs data]] unknown");
565 error(from_ascii("%1$s[[vcs data]] unknown"), lang);
567 setText(from_utf8(binfo), lang);
571 // only need to do this once.
574 if (name_ == "version")
575 setText(from_ascii(lyx_version), lang);
579 // Just to do something with that string
580 LYXERR(Debug::INFO, "info inset text: " << gui);
581 InsetCollapsible::updateBuffer(it, utype);
585 string InsetInfo::contextMenu(BufferView const &, int, int) const
587 //FIXME: We override the implementation of InsetCollapsible,
588 //because this inset is not a collapsible inset.
589 return contextMenuName();
593 string InsetInfo::contextMenuName() const
595 return "context-info";