]> git.lyx.org Git - lyx.git/blobdiff - src/insets/InsetInfo.cpp
Fix bug #12772
[lyx.git] / src / insets / InsetInfo.cpp
index d6c68ed6b2eaf3b93af4ea8d3260a3fc6a0fb952..06851bb08059bdac277aedd00d500e0d92faaba0 100644 (file)
@@ -15,6 +15,8 @@
 #include "Buffer.h"
 #include "BufferParams.h"
 #include "BufferView.h"
+#include "Changes.h"
+#include "Cursor.h"
 #include "CutAndPaste.h"
 #include "Font.h"
 #include "FuncRequest.h"
@@ -25,7 +27,6 @@
 #include "LaTeXFeatures.h"
 #include "Language.h"
 #include "LayoutFile.h"
-#include "Length.h"
 #include "LyXAction.h"
 #include "LyXRC.h"
 #include "LyXVC.h"
@@ -37,6 +38,7 @@
 
 #include "frontends/Application.h"
 
+#include "support/Changer.h"
 #include "support/convert.h"
 #include "support/debug.h"
 #include "support/docstream.h"
@@ -45,6 +47,7 @@
 #include "support/FileName.h"
 #include "support/filetools.h"
 #include "support/gettext.h"
+#include "support/Length.h"
 #include "support/Messages.h"
 #include "support/lstrings.h"
 #include "support/qstring_helpers.h"
@@ -156,6 +159,8 @@ set<string> getTexFileList(string const & filename)
                getVectorFromString(file.fileContents("UTF-8"), from_ascii("\n"));
 
        // Normalise paths like /foo//bar ==> /foo/bar
+       // No "auto const &" because doc is modified later
+       // coverity[auto_causes_copy]
        for (auto doc : doclist) {
                doc = subst(doc, from_ascii("\r"), docstring());
                while (contains(doc, from_ascii("//")))
@@ -167,10 +172,16 @@ set<string> getTexFileList(string const & filename)
        // remove duplicates
        return list;
 }
+
+bool translateString(docstring const & in, docstring & out, string const & lcode)
+{
+       out = translateIfPossible(in, lcode);
+       return in != out;
+}
 } // namespace anon
 
 
-docstring InsetInfoParams::getDate(string const iname, QDate const date) const
+docstring InsetInfoParams::getDate(string const iname, QDate const date) const
 {
        QLocale loc;
        if (lang)
@@ -195,7 +206,7 @@ docstring InsetInfoParams::getDate(string const iname, QDate const date) const
 }
 
 
-docstring InsetInfoParams::getTime(string const iname, QTime const time) const
+docstring InsetInfoParams::getTime(string const iname, QTime const time) const
 {
        QLocale loc;
        if (lang)
@@ -226,10 +237,8 @@ vector<pair<string,docstring>> InsetInfoParams::getArguments(Buffer const * buf,
        case MENU_INFO:
        case ICON_INFO: {
                result.push_back(make_pair("custom", _("Custom")));
-               LyXAction::const_iterator fit = lyxaction.func_begin();
-               LyXAction::const_iterator const fen = lyxaction.func_end();
-               for (; fit != fen; ++fit) {
-                       string const lfun = fit->first;
+               for (auto const & name_code : lyxaction) {
+                       string const lfun = name_code.first;
                        if (!lfun.empty())
                                result.push_back(make_pair(lfun, from_ascii(lfun)));
                }
@@ -272,6 +281,7 @@ vector<pair<string,docstring>> InsetInfoParams::getArguments(Buffer const * buf,
                        break;
                }
                result.push_back(make_pair("revision", _("Revision[[Version Control]]")));
+               result.push_back(make_pair("revision-abbrev", _("Abbreviated revision[[Version Control]]")));
                result.push_back(make_pair("tree-revision", _("Tree revision")));
                result.push_back(make_pair("author", _("Author")));
                result.push_back(make_pair("date", _("Date")));
@@ -290,7 +300,11 @@ vector<pair<string,docstring>> InsetInfoParams::getArguments(Buffer const * buf,
                string const dt = split(name, '@');
                QDate date;
                if (itype == "moddate")
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+                       date = QDateTime::fromSecsSinceEpoch(buf->fileName().lastModified()).date();
+#else
                        date = QDateTime::fromTime_t(buf->fileName().lastModified()).date();
+#endif
                else if (itype == "fixdate" && !dt.empty()) {
                        QDate const gdate = QDate::fromString(toqstr(dt), Qt::ISODate);
                        date = (gdate.isValid()) ? gdate : QDate::currentDate();
@@ -316,7 +330,11 @@ vector<pair<string,docstring>> InsetInfoParams::getArguments(Buffer const * buf,
                string const tt = split(name, '@');
                QTime time;
                if (itype == "modtime")
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+                       time = QDateTime::fromSecsSinceEpoch(buf->fileName().lastModified()).time();
+#else
                        time = QDateTime::fromTime_t(buf->fileName().lastModified()).time();
+#endif
                else if (itype == "fixtime" && !tt.empty()) {
                        QTime const gtime = QTime::fromString(toqstr(tt), Qt::ISODate);
                        time = (gtime.isValid()) ? gtime : QTime::currentTime();
@@ -379,7 +397,7 @@ bool InsetInfoParams::validateArgument(Buffer const * buf, docstring const & arg
                        || name == "path" || name == "class");
 
        case VCS_INFO:
-               if (name == "revision" || name == "tree-revision"
+               if (name == "revision" || name == "revision-abbrev" || name == "tree-revision"
                    || name == "author" || name == "date" || name == "time")
                        return buf->lyxvc().inUse();
                return false;
@@ -447,13 +465,29 @@ string InsetInfoParams::infoType() const
 /////////////////////////////////////////////////////////////////////////
 
 
+namespace {
+
+class InsetGraphicsTight : public InsetGraphics
+{
+public:
+       ///
+       explicit InsetGraphicsTight(Buffer * buf) : InsetGraphics(buf) {}
+
+       ///
+       int leftOffset(BufferView const *) const override { return 0; }
+       ///
+       int rightOffset(BufferView const *) const override { return 0; }
+};
 
-InsetInfo::InsetInfo(Buffer * buf, string const & name)
+}
+
+
+InsetInfo::InsetInfo(Buffer * buf, string const & info)
        : InsetCollapsible(buf), initialized_(false)
 {
        params_.type = InsetInfoParams::UNKNOWN_INFO;
        params_.force_ltr = false;
-       setInfo(name);
+       setInfo(info);
        status_ = Collapsed;
 }
 
@@ -523,6 +557,8 @@ docstring InsetInfo::toolTip(BufferView const &, int, int) const
        case InsetInfoParams::VCS_INFO:
                if (params_.name == "revision")
                        result = _("Version control revision");
+               else if (params_.name == "revision-abbrev")
+                       result = _("Version control abbreviated revision");
                else if (params_.name == "tree-revision")
                        result = _("Version control tree revision");
                else if (params_.name == "author")
@@ -615,6 +651,8 @@ bool InsetInfo::getStatus(Cursor & cur, FuncRequest const & cmd,
                return true;
 
        case LFUN_INSET_MODIFY:
+               if (nameTranslator().find(cmd.getArg(0)) == InsetInfoParams::UNKNOWN_INFO)
+                       return Inset::getStatus(cur, cmd, flag);
                if (params_.validateArgument(&buffer(), cmd.argument())) {
                        flag.setEnabled(true);
                        string typestr;
@@ -639,6 +677,10 @@ void InsetInfo::doDispatch(Cursor & cur, FuncRequest & cmd)
 {
        switch (cmd.action()) {
        case LFUN_INSET_MODIFY:
+               if (nameTranslator().find(cmd.getArg(0)) == InsetInfoParams::UNKNOWN_INFO) {
+                       cur.undispatched();
+                       break;
+               }
                cur.recordUndo();
                setInfo(to_utf8(cmd.argument()));
                cur.forceBufferUpdate();
@@ -666,9 +708,9 @@ void InsetInfo::doDispatch(Cursor & cur, FuncRequest & cmd)
 }
 
 
-void InsetInfo::setInfo(string const & name)
+void InsetInfo::setInfo(string const & info)
 {
-       if (name.empty())
+       if (info.empty())
                return;
 
        string saved_date_specifier;
@@ -677,7 +719,7 @@ void InsetInfo::setInfo(string const & name)
                saved_date_specifier = split(params_.name, '@');
        // info_type name
        string type;
-       params_.name = trim(split(name, type, ' '));
+       params_.name = trim(split(info, type, ' '));
        params_.type = nameTranslator().find(type);
        if (params_.name.empty())
                params_.name = defaultValueTranslator().find(params_.type);
@@ -710,15 +752,21 @@ void InsetInfo::setInfo(string const & name)
 
 void InsetInfo::error(docstring const & err, Language const * lang)
 {
-       setText(bformat(translateIfPossible(err, lang->code()), from_utf8(params_.name)),
-               Font(inherit_font, lang), false);
+       docstring const res = translateIfPossible(err, lang->code());
+       bool const translated = res != err;
+       // If the string is not translated, we use default lang (English)
+       Font const f = translated ? Font(inherit_font, lang) : Font(inherit_font);
+       setText(bformat(res, from_utf8(params_.name)), f, false);
 }
 
 
 void InsetInfo::info(docstring const & err, Language const * lang)
 {
-       setText(translateIfPossible(err, lang->code()),
-                       Font(inherit_font, lang), false);
+       docstring const res = translateIfPossible(err, lang->code());
+       bool const translated = res != err;
+       // If the string is not translated, we use default lang (English)
+       Font const f = translated ? Font(inherit_font, lang) : Font(inherit_font);
+       setText(translateIfPossible(err, lang->code()), f, false);
 }
 
 
@@ -728,23 +776,64 @@ void InsetInfo::setText(docstring const & str, Language const * lang)
 }
 
 
-bool InsetInfo::forceLTR() const
+bool InsetInfo::forceLTR(OutputParams const &) const
 {
        return params_.force_ltr;
 }
 
 
-void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
+bool InsetInfo::forceLocalFontSwitch() const
+{
+       return params_.type == InsetInfoParams::MENU_INFO
+               || params_.type == InsetInfoParams::SHORTCUTS_INFO
+               || params_.type == InsetInfoParams::SHORTCUT_INFO
+               || params_.type == InsetInfoParams::L7N_INFO;
+}
+
+
+void InsetInfo::metrics(MetricsInfo & mi, Dimension & dim) const
+{
+       const_cast<InsetInfo *>(this)->build();
+       InsetCollapsible::metrics(mi, dim);
+}
+
+
+void InsetInfo::draw(PainterInfo & pi, int x, int y) const
+{
+       Changer chg = changeVar(lyxrc.mark_foreign_language, false);
+       InsetCollapsible::draw(pi, x, y);
+}
+
+void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
+
+{
        // If the Buffer is a clone, then we neither need nor want to do any
        // of what follows. We want, rather, just to inherit how things were
        // in the original Buffer. This is especially important for VCS.
        // Otherwise, we could in principle have different settings here
        // than in the Buffer we were exporting.
-       if (buffer().isClone())
+       // However, we need to check whether the inset is in an intitle
+       // context.
+       if (buffer().isClone()) {
+               InsetText::checkIntitleContext(it);
                return;
-
+       }
        BufferParams const & bp = buffer().params();
        params_.lang = it.paragraph().getFontSettings(bp, it.pos()).language();
+       InsetCollapsible::updateBuffer(it, utype, deleted);
+}
+
+
+void InsetInfo::build()
+{
+       // If the Buffer is a clone, then we neither need nor want to do any
+       // of what follows. We want, rather, just to inherit how things were
+       // in the original Buffer. This is especially important for VCS.
+       // Otherwise, we could in principle have different settings here
+       // than in the Buffer we were exporting.
+       if (buffer().isClone())
+               return;
+
        Language const * tryguilang = languages.getFromCode(Messages::guiLanguage());
        // Some info insets use the language of the GUI (if available)
        Language const * guilang = tryguilang ? tryguilang : params_.lang;
@@ -774,84 +863,104 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
                        break;
                }
                docstring sequence;
-               if (params_.type == InsetInfoParams::SHORTCUT_INFO)
+               docstring seq_untranslated;
+               if (params_.type == InsetInfoParams::SHORTCUT_INFO) {
                        sequence = bindings.begin()->print(KeySequence::ForGui);
-               else
+                       seq_untranslated = bindings.begin()->print(KeySequence::ForGui, true);
+               } else {
                        sequence = theTopLevelKeymap().printBindings(func, KeySequence::ForGui);
+                       seq_untranslated = theTopLevelKeymap().printBindings(func, KeySequence::ForGui, true);
+               }
                // QKeySequence returns special characters for keys on the mac
                // Since these are not included in many fonts, we
                // re-translate them to textual names (see #10641)
                odocstringstream ods;
                string const lcode = params_.lang->code();
-               for (size_t n = 0; n < sequence.size(); ++n) {
-                       char_type const c = sequence[n];
+               docstring trans;
+               bool is_translated = sequence != seq_untranslated;
+               for (char_type const c : sequence) {
                        switch(c) {
                        case 0x21b5://Return
                                gui = _("Return[[Key]]");
-                               ods << translateIfPossible(from_ascii("Return[[Key]]"), lcode);
+                               is_translated = translateString(from_ascii("Return[[Key]]"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x21b9://Tab both directions (Win)
                                gui = _("Tab[[Key]]");
-                               ods << translateIfPossible(from_ascii("Tab[[Key]]"), lcode);
+                               is_translated = translateString(from_ascii("Tab[[Key]]"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x21de://Qt::Key_PageUp
                                gui = _("PgUp");
-                               ods << translateIfPossible(from_ascii("PgUp"), lcode);
+                               is_translated = translateString(from_ascii("PgUp"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x21df://Qt::Key_PageDown
                                gui = _("PgDown");
-                               ods << translateIfPossible(from_ascii("PgDown"), lcode);
+                               is_translated = translateString(from_ascii("PgDown"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x21e4://Qt::Key_Backtab
                                gui = _("Backtab");
-                               ods << translateIfPossible(from_ascii("Backtab"), lcode);
+                               is_translated = translateString(from_ascii("Backtab"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x21e5://Qt::Key_Tab
                                gui = _("Tab");
-                               ods << translateIfPossible(from_ascii("Tab"), lcode);
+                               is_translated = translateString(from_ascii("Tab"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x21e7://Shift
                                gui = _("Shift");
-                               ods << translateIfPossible(from_ascii("Shift"), lcode);
+                               is_translated = translateString(from_ascii("Shift"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x21ea://Qt::Key_CapsLock
                                gui = _("CapsLock");
-                               ods << translateIfPossible(from_ascii("CapsLock"), lcode);
+                               is_translated = translateString(from_ascii("CapsLock"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x2303://Control
                                gui = _("Control[[Key]]");
-                               ods << translateIfPossible(from_ascii("Control[[Key]]"), lcode);
+                               is_translated = translateString(from_ascii("Control[[Key]]"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x2318://CMD
                                gui = _("Command[[Key]]");
-                               ods << translateIfPossible(from_ascii("Command[[Key]]"), lcode);
+                               is_translated = translateString(from_ascii("Command[[Key]]"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x2324://Qt::Key_Enter
                                gui = _("Return[[Key]]");
-                               ods << translateIfPossible(from_ascii("Return[[Key]]"), lcode);
+                               is_translated = translateString(from_ascii("Return[[Key]]"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x2325://Option key
                                gui = _("Option[[Key]]");
-                               ods << translateIfPossible(from_ascii("Option[[Key]]"), lcode);
+                               is_translated = translateString(from_ascii("Option[[Key]]"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x2326://Qt::Key_Delete
                                gui = _("Delete[[Key]]");
-                               ods << translateIfPossible(from_ascii("Delete[[Key]]"), lcode);
+                               is_translated = translateString(from_ascii("Delete[[Key]]"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x232b://Qt::Key_Backspace
                                gui = _("Fn+Del");
-                               ods << translateIfPossible(from_ascii("Fn+Delete"), lcode);
+                               is_translated = translateString(from_ascii("Fn+Del"), trans, lcode);
+                               ods << trans;
                                break;
                        case 0x238b://Qt::Key_Escape
                                gui = _("Esc");
-                               ods << translateIfPossible(from_ascii("Esc"), lcode);
+                               is_translated = translateString(from_ascii("Esc"), trans, lcode);
+                               ods << trans;
                                break;
                        default:
                                ods.put(c);
                        }
                }
-               setText(ods.str(), guilang);
-               params_.force_ltr = !guilang->rightToLeft() && !params_.lang->rightToLeft();
+               setText(ods.str(), is_translated ? guilang : nullptr);
+               params_.force_ltr = !is_translated || (!guilang->rightToLeft() && !params_.lang->rightToLeft());
                break;
        }
        case InsetInfoParams::LYXRC_INFO: {
@@ -897,7 +1006,19 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
                if (initialized_)
                        break;
                // check in packages.lst
-               if (LaTeXFeatures::isAvailable(params_.name)) {
+               bool available;
+               // we also allow version check with version separated by blank
+               if (contains(params_.name, ' ')) {
+                       string name;
+                       string const version = split(params_.name, name, ' ');
+                       int const y = convert<int>(version.substr(0,4));
+                       int const m = convert<int>(version.substr(4,2));
+                       int const d = convert<int>(version.substr(6,2));
+                       available = LaTeXFeatures::isAvailableAtLeastFrom(name, y, m, d);
+               } else
+                       available = LaTeXFeatures::isAvailable(params_.name);
+
+               if (available) {
                        gui = _("yes");
                        info(from_ascii("yes"), params_.lang);
                } else {
@@ -1001,14 +1122,6 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
                initialized_ = true;
                FuncRequest func = lyxaction.lookupFunc(params_.name);
                docstring icon_name = frontend::Application::iconName(func, true);
-               // FIXME: We should use the icon directly instead of
-               // going through FileName. The code below won't work
-               // if the icon is embedded in the executable through
-               // the Qt resource system.
-               // This is only a negligible performance problem:
-               // If the installed icon differs from the resource icon the
-               // installed one is preferred anyway, and all icons that are
-               // embedded in the resources are installed as well.
                FileName file(to_utf8(icon_name));
                if (file.onlyFileNameWithoutExt() == "unknown") {
                        string dir = "images";
@@ -1030,12 +1143,16 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
                                percent_scale = (100 * iconsize + imgsize / 2)/imgsize;
                        }
                }
-               InsetGraphics * inset = new InsetGraphics(buffer_);
+               InsetGraphicsTight * inset = new InsetGraphicsTight(buffer_);
                InsetGraphicsParams igp;
                igp.filename = file;
                igp.lyxscale = percent_scale;
                igp.scale = string();
                igp.width = Length(1, Length::EM);
+               if (contains(file.absoluteFilePath(), from_ascii("math"))
+                   || contains(file.absoluteFilePath(), from_ascii("ert-insert"))
+                   || suffixIs(file.onlyPath().absoluteFilePath(), from_ascii("ipa")))
+                       igp.darkModeSensitive = true;
                inset->setParams(igp);
                clear();
                Font const f(inherit_font, params_.lang);
@@ -1052,7 +1169,7 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
                else if (params_.name == "path")
                        setText(from_utf8(os::latex_path(buffer().filePath())), params_.lang);
                else if (params_.name == "class")
-                       setText(from_utf8(bp.documentClass().name()), params_.lang);
+                       setText(from_utf8(buffer().params().documentClass().name()), params_.lang);
                break;
        }
        case InsetInfoParams::VCS_INFO: {
@@ -1066,6 +1183,8 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
                LyXVC::RevisionInfo itype = LyXVC::Unknown;
                if (params_.name == "revision")
                        itype = LyXVC::File;
+               else if (params_.name == "revision-abbrev")
+                       itype = LyXVC::FileAbbrev;
                else if (params_.name == "tree-revision")
                        itype = LyXVC::Tree;
                else if (params_.name == "author")
@@ -1101,7 +1220,11 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
                                ? split(params_.name, date_format, '@') : string();
                QDate date;
                if (params_.type == InsetInfoParams::MODDATE_INFO)
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+                       date = QDateTime::fromSecsSinceEpoch(buffer().fileName().lastModified()).date();
+#else
                        date = QDateTime::fromTime_t(buffer().fileName().lastModified()).date();
+#endif
                else if (params_.type == InsetInfoParams::FIXDATE_INFO && !date_specifier.empty())
                        date = QDate::fromString(toqstr(date_specifier), Qt::ISODate);
                else
@@ -1118,7 +1241,11 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
                                ? split(params_.name, time_format, '@') : string();
                QTime time;
                if (params_.type == InsetInfoParams::MODTIME_INFO)
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+                       time = QDateTime::fromSecsSinceEpoch(buffer().fileName().lastModified()).time();
+#else
                        time = QDateTime::fromTime_t(buffer().fileName().lastModified()).time();
+#endif
                else if (params_.type == InsetInfoParams::FIXTIME_INFO && !time_specifier.empty())
                        time = QTime::fromString(toqstr(time_specifier), Qt::ISODate);
                else
@@ -1130,7 +1257,13 @@ void InsetInfo::updateBuffer(ParIterator const & it, UpdateType utype) {
 
        // Just to do something with that string
        LYXERR(Debug::INFO, "info inset text: " << gui);
-       InsetCollapsible::updateBuffer(it, utype);
+}
+
+
+void InsetInfo::validate(LaTeXFeatures & features) const
+{
+       const_cast<InsetInfo *>(this)->build();
+       InsetCollapsible::validate(features);
 }