"Fossies" - the Fresh Open Source Software Archive

Member "texstudio-3.1.1/src/configmanager.cpp" (21 Feb 2021, 164159 Bytes) of package /linux/misc/texstudio-3.1.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "configmanager.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.1.0_vs_3.1.1.

    1 
    2 #include "configmanager.h"
    3 
    4 #include "configdialog.h"
    5 #include "filedialog.h"
    6 #include "latexeditorview.h"
    7 #include "latexpackage.h"
    8 #include "latexcompleter_config.h"
    9 #include "latexeditorview_config.h"
   10 #include "webpublishdialog_config.h"
   11 #include "insertgraphics_config.h"
   12 #include "grammarcheck_config.h"
   13 #include "PDFDocument_config.h"
   14 #include "terminal_config.h"
   15 #include "encoding.h"
   16 #include "codesnippet.h"
   17 #include "updatechecker.h"
   18 #include "utilsVersion.h"
   19 #include "utilsUI.h"
   20 
   21 #include <QDomElement>
   22 
   23 #include "qformatconfig.h"
   24 
   25 #include "manhattanstyle.h"
   26 
   27 #ifdef ADWAITA
   28 #include "adwaitastyle.h"
   29 #endif
   30 
   31 const QString TXS_AUTO_REPLACE_QUOTE_OPEN = "TMX:Replace Quote Open";
   32 const QString TXS_AUTO_REPLACE_QUOTE_CLOSE = "TMX:Replace Quote Close";
   33 
   34 const char *PROPERTY_COMMAND_ID = "cmdID";
   35 const char *PROPERTY_NAME_WIDGET = "nameWidget";
   36 const char *PROPERTY_WIDGET_TYPE = "widgetType";
   37 const char *PROPERTY_ASSOCIATED_INPUT = "associatedInput";
   38 const char *PROPERTY_ADD_BUTTON = "addButton";
   39 Q_DECLARE_METATYPE(QPushButton *)
   40 
   41 
   42 ManagedProperty::ManagedProperty(): storage(nullptr), type(PT_VOID), widgetOffset(0)
   43 {
   44 }
   45 
   46 #define CONSTRUCTOR(TYPE, ID) \
   47     ManagedProperty::ManagedProperty(TYPE* storage, QVariant def, ptrdiff_t widgetOffset)\
   48     : storage(storage), type(ID), def(def), widgetOffset(widgetOffset){} \
   49     ManagedProperty ManagedProperty::fromValue(TYPE value) { \
   50         ManagedProperty res;    \
   51             res.storage = new TYPE; \
   52         *(static_cast<TYPE*>(res.storage)) = value; \
   53         res.type = ID;                   \
   54         res.def = res.valueToQVariant(); \
   55         res.widgetOffset = 0;            \
   56         return res;                      \
   57     }
   58 PROPERTY_TYPE_FOREACH_MACRO(CONSTRUCTOR)
   59 #undef CONSTRUCTOR
   60 
   61 void ManagedProperty::deallocate()
   62 {
   63     switch (type) {
   64 #define CASE(TYPE, ID) case ID: delete (static_cast<TYPE*>(storage)); break;
   65         PROPERTY_TYPE_FOREACH_MACRO(CASE)
   66 #undef CASE
   67     default:
   68         Q_ASSERT(false);
   69     }
   70     storage = nullptr;
   71 }
   72 
   73 
   74 static ConfigManager *globalConfigManager = nullptr;
   75 
   76 ConfigManagerInterface *ConfigManagerInterface::getInstance()
   77 {
   78     Q_ASSERT(globalConfigManager);
   79     return globalConfigManager;
   80 }
   81 
   82 Q_DECLARE_METATYPE(ManagedProperty *)
   83 Q_DECLARE_METATYPE(StringStringMap)
   84 
   85 ManagedToolBar::ManagedToolBar(const QString &newName, const QStringList &defs): name(newName), defaults(defs), toolbar(nullptr) {}
   86 
   87 QVariant ManagedProperty::valueToQVariant() const
   88 {
   89     Q_ASSERT(storage);
   90     if (!storage) return QVariant();
   91     switch (type) {
   92 #define CONVERT(TYPE, ID) case ID: return *(static_cast<TYPE*>(storage));
   93         PROPERTY_TYPE_FOREACH_MACRO_SIMPLE(CONVERT)
   94 #undef CONVERT
   95 #define CONVERT(TYPE, ID) case ID: return QVariant::fromValue<TYPE>(*(static_cast<TYPE*>(storage)));
   96         PROPERTY_TYPE_FOREACH_MACRO_COMPLEX(CONVERT)
   97 #undef CONVERT
   98     default:
   99         Q_ASSERT(false);
  100         return QVariant();
  101     }
  102 }
  103 
  104 void ManagedProperty::valueFromQVariant(const QVariant v)
  105 {
  106     Q_ASSERT(storage);
  107     if (!storage) return;
  108     switch (type) {
  109     case PT_VARIANT:
  110         *(static_cast<QVariant *>(storage)) = v;
  111         break;
  112     case PT_INT:
  113         *(static_cast<int *>(storage)) = v.toInt();
  114         break;
  115     case PT_BOOL:
  116         *(static_cast<bool *>(storage)) = v.toBool();
  117         break;
  118     case PT_STRING:
  119         *(static_cast<QString *>(storage)) = v.toString();
  120         break;
  121     case PT_STRINGLIST:
  122         *(static_cast<QStringList *>(storage)) = v.toStringList();
  123         break;
  124     case PT_DATETIME:
  125         *(static_cast<QDateTime *>(storage)) = v.toDateTime();
  126         break;
  127     case PT_FLOAT:
  128         *(static_cast<float *>(storage)) = v.toFloat();
  129         break;
  130     case PT_DOUBLE:
  131         *(static_cast<double *>(storage)) = v.toDouble();
  132         break;
  133     case PT_BYTEARRAY:
  134         *(static_cast<QByteArray *>(storage)) = v.toByteArray();
  135         break;
  136     case PT_LIST:
  137         *(static_cast<QList<QVariant> *>(storage)) = v.toList();
  138         break;
  139     case PT_MAP_STRING_STRING:
  140         *(static_cast<StringStringMap *>(storage)) = v.value<StringStringMap>();
  141         break;
  142     default:
  143         Q_ASSERT(false);
  144     }
  145 }
  146 
  147 void ManagedProperty::writeToObject(QObject *w) const
  148 {
  149     Q_ASSERT(storage && w);
  150     if (!storage || !w) return;
  151 
  152     QCheckBox *checkBox = qobject_cast<QCheckBox *>(w);
  153     if (checkBox) {
  154         Q_ASSERT(type == PT_BOOL);
  155         checkBox->setChecked(*(static_cast<bool *>(storage)));
  156         return;
  157     }
  158     QToolButton *toolButton = qobject_cast<QToolButton *>(w);
  159     if (toolButton) {
  160         Q_ASSERT(type == PT_BOOL);
  161         toolButton->setChecked(*(static_cast<bool *>(storage)));
  162         return;
  163     }
  164     QLineEdit *edit = qobject_cast<QLineEdit *>(w);
  165     if (edit) {
  166         Q_ASSERT(type == PT_STRING);
  167         edit->setText(*(static_cast<QString *>(storage)));
  168         return;
  169     }
  170     /*QTextEdit* tedit = qobject_cast<QTextEdit*>(w);
  171     if (tedit){
  172     *((QString*)storage) = tedit->toPlainText();
  173     continue;
  174     }*/
  175     QSpinBox *spinBox = qobject_cast<QSpinBox *>(w);
  176     if (spinBox) {
  177         Q_ASSERT(type == PT_INT);
  178         spinBox->setValue(*(static_cast<int *>(storage)));
  179         return;
  180     }
  181     QComboBox *comboBox = qobject_cast<QComboBox *>(w);
  182     if (comboBox) {
  183         switch (type) {
  184         case PT_BOOL:
  185             comboBox->setCurrentIndex(*(static_cast<bool *>(storage)) ? 1 : 0);
  186             return;
  187         case PT_INT:
  188             comboBox->setCurrentIndex(*(static_cast<int *>(storage)));
  189             return;
  190         case PT_STRING: {
  191             int index = comboBox->findText(*static_cast<QString *>(storage));
  192             if (index > 0) comboBox->setCurrentIndex(index);
  193             if (comboBox->isEditable()) comboBox->setEditText(*static_cast<QString *>(storage));
  194             return;
  195         }
  196         case PT_STRINGLIST: {
  197             QStringList &sl = *static_cast<QStringList *>(storage);
  198 
  199             int cp = comboBox->lineEdit() ? comboBox->lineEdit()->cursorPosition() : -1000;
  200             while (comboBox->count() > sl.size())
  201                 comboBox->removeItem(comboBox->count() - 1);
  202             for (int i = 0; i < qMin(sl.size(), comboBox->count()); i++)
  203                 if (comboBox->itemText(i) != sl[i])
  204                     comboBox->setItemText(i, sl[i]);
  205             for (int i = comboBox->count(); i < sl.size(); i++)
  206                 comboBox->addItem(sl[i]);
  207             if (cp != -1000) {
  208                 //combobox visible (i.e. as used in search panel)
  209                 if (!sl.isEmpty() && comboBox->currentText() != sl.last() && comboBox->currentIndex() != sl.size() - 1)
  210                     comboBox->setCurrentIndex(sl.size() - 1);
  211                 comboBox->lineEdit()->setCursorPosition(cp);
  212             } // else:  combobox invisible (i.e. as used in universal input dialog)
  213             return;
  214         }
  215         default:
  216             Q_ASSERT(false);
  217         }
  218     }
  219     QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox *>(w);
  220     if (doubleSpinBox) {
  221         switch (type) {
  222         case PT_DOUBLE:
  223             doubleSpinBox->setValue(*(static_cast<double *>(storage)));
  224             break;
  225         case PT_FLOAT:
  226             doubleSpinBox->setValue(*(static_cast<float *>(storage)));
  227             break;
  228         default:
  229             Q_ASSERT(false);
  230         }
  231         return;
  232     }
  233     QAction *action = qobject_cast<QAction *>(w);
  234     if (action) {
  235         Q_ASSERT(type == PT_BOOL);
  236         action->setChecked(*(static_cast<bool *>(storage)));
  237         return;
  238     }
  239     QTextEdit *textEdit = qobject_cast<QTextEdit *>(w);
  240     if (textEdit) {
  241         switch (type) {
  242         case PT_STRING:
  243             textEdit->setPlainText(*(static_cast<QString *>(storage)));
  244             break;
  245         case PT_STRINGLIST:
  246             textEdit->setPlainText((static_cast<QStringList *>(storage))->join("\n"));
  247             break;
  248         default:
  249             Q_ASSERT(false);
  250         }
  251         return;
  252     }
  253     Q_ASSERT(false);
  254 }
  255 
  256 bool ManagedProperty::readFromObject(const QObject *w)
  257 {
  258 #define READ_FROM_OBJECT(TYPE, VALUE) {           \
  259     TYPE oldvalue = *(static_cast<TYPE*>(storage));         \
  260     *(static_cast<TYPE*>(storage)) = VALUE;                 \
  261     return oldvalue != *(static_cast<TYPE*>(storage));      \
  262 }
  263     Q_ASSERT(storage);
  264     if (!storage) return false;
  265     const QCheckBox *checkBox = qobject_cast<const QCheckBox *>(w);
  266     if (checkBox) {
  267         Q_ASSERT(type == PT_BOOL);
  268         READ_FROM_OBJECT(bool, checkBox->isChecked())
  269     }
  270     const QToolButton *toolButton = qobject_cast<const QToolButton *>(w);
  271     if (toolButton) {
  272         Q_ASSERT(type == PT_BOOL);
  273         READ_FROM_OBJECT(bool, toolButton->isChecked())
  274     }
  275     const QLineEdit *edit = qobject_cast<const QLineEdit *>(w);
  276     if (edit) {
  277         Q_ASSERT(type == PT_STRING);
  278         READ_FROM_OBJECT(QString, edit->text())
  279     }
  280     /*QTextEdit* tedit = qobject_cast<QTextEdit*>(w);
  281     if (tedit){
  282     *((QString*)storage) = tedit->toPlainText();
  283     continue;
  284     }*/
  285     const QSpinBox *spinBox = qobject_cast<const QSpinBox *>(w);
  286     if (spinBox) {
  287         Q_ASSERT(type == PT_INT);
  288         READ_FROM_OBJECT(int, spinBox->value())
  289     }
  290     const QComboBox *comboBox = qobject_cast<const QComboBox *>(w);
  291     if (comboBox) {
  292         switch (type) {
  293         case PT_BOOL:
  294             READ_FROM_OBJECT(bool, comboBox->currentIndex() != 0)
  295         case PT_INT:
  296             READ_FROM_OBJECT(int, comboBox->currentIndex())
  297         case PT_STRING:
  298             READ_FROM_OBJECT(QString, comboBox->currentText())
  299         case PT_STRINGLIST: {
  300             QString oldvalue;
  301             if (!(static_cast<QStringList *>(storage))->isEmpty())
  302                 oldvalue = (static_cast<QStringList *>(storage))->first();
  303             *(static_cast<QStringList *>(storage)) = QStringList(comboBox->currentText());
  304             return oldvalue != comboBox->currentText();
  305         }
  306         default:
  307             Q_ASSERT(false);
  308         }
  309     }
  310     const QDoubleSpinBox *doubleSpinBox = qobject_cast<const QDoubleSpinBox *>(w);
  311     if (doubleSpinBox) {
  312         switch (type) {
  313         case PT_DOUBLE:
  314             READ_FROM_OBJECT(double, doubleSpinBox->value())
  315         case PT_FLOAT:
  316             READ_FROM_OBJECT(float, doubleSpinBox->value())
  317         default:
  318             Q_ASSERT(false);
  319         }
  320     }
  321     const QAction *action = qobject_cast<const QAction *>(w);
  322     if (action) {
  323         Q_ASSERT(type == PT_BOOL);
  324         Q_ASSERT(action->isCheckable());
  325         READ_FROM_OBJECT(bool, action->isChecked())
  326     }
  327 
  328     const QTextEdit *textEdit = qobject_cast<const QTextEdit *>(w);
  329     if (textEdit) {
  330         switch (type) {
  331         case PT_STRING:
  332             READ_FROM_OBJECT(QString, textEdit->toPlainText())
  333         case PT_STRINGLIST:
  334             READ_FROM_OBJECT(QStringList, textEdit->toPlainText().split("\n"))
  335         default:
  336             Q_ASSERT(false);
  337         }
  338     }
  339     Q_ASSERT(false);
  340     return false;
  341 }
  342 #undef READ_FROM_OBJECT
  343 
  344 const int ConfigManager::MAX_NUM_MACROS;
  345 QTextCodec *ConfigManager::newFileEncoding = nullptr;
  346 QString ConfigManager::configDirOverride;
  347 bool ConfigManager::dontRestoreSession=false;
  348 int ConfigManager::RUNAWAYLIMIT=30;
  349 
  350 QString getText(QWidget *w)
  351 {
  352     if (qobject_cast<QLineEdit *>(w)) return qobject_cast<QLineEdit *>(w)->text();
  353     else if (qobject_cast<QComboBox *>(w)) return qobject_cast<QComboBox *>(w)->currentText();
  354     else REQUIRE_RET(false, "");
  355 }
  356 
  357 void setText(QWidget *w, const QString &t)
  358 {
  359     if (qobject_cast<QLineEdit *>(w)) qobject_cast<QLineEdit *>(w)->setText(t);
  360     else if (qobject_cast<QComboBox *>(w)) {
  361         QComboBox * cb=qobject_cast<QComboBox *>(w);
  362         if(!cb->isEditable())
  363             cb->setEditable(true);
  364         cb->setEditText(t);
  365     }
  366     else REQUIRE(false);
  367 }
  368 
  369 void assignNameWidget(QWidget *w, QWidget *nameWidget)
  370 {
  371     w->setProperty(PROPERTY_NAME_WIDGET, QVariant::fromValue<QWidget *>(nameWidget));
  372     QString cmdID = nameWidget->property(PROPERTY_COMMAND_ID).toString();
  373     if (!cmdID.isEmpty()) {
  374         // user commands don't store the ID in the property, because it's editable
  375         // In builtin commmands, the ID is fixed, so we can directly assign it to the widget.
  376         // this speeds up lookup in getCmdID
  377         w->setProperty(PROPERTY_COMMAND_ID, cmdID);
  378     }
  379 }
  380 
  381 QString getCmdID(QWidget *w)
  382 {
  383     QString cmdID = w->property(PROPERTY_COMMAND_ID).toString();
  384     if (!cmdID.isEmpty()) return cmdID;
  385 
  386     QWidget *nameWidget = w->property(PROPERTY_NAME_WIDGET).value<QWidget *>();
  387     if (!nameWidget) nameWidget = w;
  388     cmdID = nameWidget->property(PROPERTY_COMMAND_ID).toString();
  389     if (!cmdID.isEmpty()) return cmdID;
  390 
  391     // user commands don't store the ID in the property, because it's editable
  392     QLineEdit *le = qobject_cast<QLineEdit *>(nameWidget);
  393     REQUIRE_RET(le, "");
  394     QString combinedName = le->text();
  395     int pos = combinedName.indexOf(":");
  396     cmdID = (pos == -1) ? combinedName : combinedName.left(pos);
  397     return cmdID;
  398 }
  399 
  400 ConfigManager::ConfigManager(QObject *parent): QObject (parent),
  401   buildManager(nullptr), editorConfig(new LatexEditorViewConfig),
  402     completerConfig (new LatexCompleterConfig),
  403     webPublishDialogConfig (new WebPublishDialogConfig),
  404     pdfDocumentConfig(new PDFDocumentConfig),
  405     insertGraphicsConfig(new InsertGraphicsConfig),
  406     grammarCheckerConfig(new GrammarCheckerConfig),
  407   terminalConfig(new InternalTerminalConfig),
  408   menuParent(nullptr), menuParentsBar(nullptr), modifyMenuContentsFirstCall(true), pdflatexEdit(nullptr), persistentConfig(nullptr)
  409 {
  410 
  411     Q_ASSERT(!globalConfigManager);
  412     globalConfigManager = this;
  413 
  414     //interface - store these values once before they are overwritten by some customizaton
  415     systemPalette = QApplication::palette();
  416     defaultStyleName = QApplication::style()->objectName();
  417 
  418     qRegisterMetaTypeStreamOperators<StringStringMap>("StringStringMap");
  419 
  420     managedToolBars.append(ManagedToolBar("Custom", QStringList()));
  421     managedToolBars.append(ManagedToolBar("File", QStringList() << "main/file/new" << "main/file/open" << "main/file/save" << "main/file/close"));
  422     managedToolBars.append(ManagedToolBar("Edit", QStringList() << "main/edit/undo" << "main/edit/redo" << "main/edit/copy" << "main/edit/cut" << "main/edit/paste"));
  423     managedToolBars.append(ManagedToolBar("Tools", QStringList() << "main/tools/quickbuild" << "main/tools/compile" << "main/tools/stopcompile" << "main/tools/view" << "main/tools/viewlog"));
  424     managedToolBars.append(ManagedToolBar("Math", QStringList() << "tags/brackets/left" << "separator" << "tags/brackets/right"));
  425     managedToolBars.append(ManagedToolBar("Format", QStringList() << "main/latex/sectioning" << "separator" << "main/latex/references" << "separator" << "main/latex/fontsizes"));
  426     managedToolBars.append(ManagedToolBar("Table", QStringList() << "main/latex/tabularmanipulation/addRow" << "main/latex/tabularmanipulation/addColumn" << "main/latex/tabularmanipulation/pasteColumn" << "main/latex/tabularmanipulation/removeRow" << "main/latex/tabularmanipulation/removeColumn" << "main/latex/tabularmanipulation/cutColumn" << "main/latex/tabularmanipulation/alignColumns"));
  427     managedToolBars.append(ManagedToolBar("Diff", QStringList() << "main/file/svn/prevdiff" << "main/file/svn/nextdiff"  ));
  428     managedToolBars.append(ManagedToolBar("Central", QStringList() << "main/edit/goto/goback" << "main/edit/goto/goforward" << "separator" << "main/latex/fontstyles/textbf" << "main/latex/fontstyles/textit" << "main/latex/fontstyles/underline" << "main/latex/environment/flushleft" << "main/latex/environment/center" << "main/latex/environment/flushright" << "separator" <<
  429                                           "main/latex/spacing/newline" << "separator" <<
  430                                           "main/math/mathmode" << "main/math/subscript" << "main/math/superscript" << "main/math/frac" << "main/math/dfrac" << "main/math/sqrt"));
  431 
  432     Ui::ConfigDialog *pseudoDialog = static_cast<Ui::ConfigDialog *>(nullptr);
  433 
  434     registerOption("Startup/CheckLatexConfiguration", &checkLatexConfiguration, true, &pseudoDialog->checkBoxCheckLatexConfiguration);
  435 
  436     registerOption("ToolBar/CentralVisible", &centralVisible, true);
  437     registerOption("StructureView/ShowLinenumbers", &showLineNumbersInStructure, false);
  438     registerOption("StructureView/Indentation", &indentationInStructure, -1);
  439     registerOption("StructureView/IndentIncludes", &indentIncludesInStructure, false, &pseudoDialog->checkBoxIndentIncludesInStructureTree);
  440     registerOption("Structure/ShowElementsInComments", &showCommentedElementsInStructure, false, &pseudoDialog->checkBoxShowCommentedElementsInStructure);
  441     registerOption("Structure/MarkStructureElementsBeyondEnd", &markStructureElementsBeyondEnd, true, &pseudoDialog->checkBoxMarkStructureElementsBeyondEnd);
  442     registerOption("Structure/MarkStructureElementsInAppendix", &markStructureElementsInAppendix, true, &pseudoDialog->checkBoxMarkStructureElementsInAppendix);
  443     registerOption("StructureView/ReferenceCommandsInContextMenu", &referenceCommandsInContextMenu, "\\ref", &pseudoDialog->leReferenceCommandsInContextMenu);
  444 
  445     //beginRegisterGroup("texmaker");
  446     //files
  447     registerOption("Files/New File Encoding", &newFileEncodingName, "utf-8", &pseudoDialog->comboBoxEncoding); //check
  448     registerOption("Files/AutoDetectEncodingFromChars", &autoDetectEncodingFromChars, true, &pseudoDialog->checkBoxAutoDetectEncodingFromChars);
  449     registerOption("Files/AutoDetectEncodingFromLatex", &autoDetectEncodingFromLatex, true, &pseudoDialog->checkBoxAutoDetectEncodingFromLatex);
  450 
  451     registerOption("Common Encodings", &commonEncodings, QStringList() << "UTF-8" << "ISO-8859-1" << "windows-1252" << "Apple Roman");
  452     //recent files
  453     registerOption("Files/Max Recent Files", &maxRecentFiles, 5, &pseudoDialog->spinBoxMaxRecentFiles);
  454     registerOption("Files/Max Recent Projects", &maxRecentProjects, 3, &pseudoDialog->spinBoxMaxRecentProjects);
  455     registerOption("Files/Max Recent Sessions", &maxRecentSessions, 5);
  456     registerOption("Files/Recent Files", &recentFilesList);
  457     registerOption("Files/Recent Project Files", &recentProjectList);
  458     registerOption("Files/Recent Session Files", &recentSessionList);
  459     registerOption("Files/Remember File Filter", &rememberFileFilter, true, &pseudoDialog->checkBoxRememberFileFilter);
  460     registerOption("Files/Use Native File Dialog", &useNativeFileDialog, true, &pseudoDialog->checkBoxUseNativeFileDialog);
  461     registerOption("Files/Recent Files Highlighting", &recentFileHighlightLanguage);
  462     registerOption("Files/RestoreSession", &sessionRestore, true, &pseudoDialog->checkBoxRestoreSession);
  463 
  464     registerOption("Files/Last Document", &lastDocument);
  465     registerOption("Files/Parse BibTeX", &parseBibTeX, true, &pseudoDialog->checkBoxParseBibTeX);
  466     registerOption("Bibliography/BibFileEncoding", &bibFileEncoding, "UTF-8", &pseudoDialog->comboBoxBibFileEncoding);
  467     registerOption("Files/Parse Master", &parseMaster, true, &pseudoDialog->checkBoxParseMaster);
  468     registerOption("Files/Autosave", &autosaveEveryMinutes, 0);
  469     registerOption("Files/Autoload", &autoLoadChildren, true, &pseudoDialog->checkBoxAutoLoad);
  470     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
  471     registerOption("Files/Bib Paths", &additionalBibPaths, env.value("BIBINPUTS", ""), &pseudoDialog->lineEditPathBib);
  472     registerOption("Files/Image Paths", &additionalImagePaths, env.value("TEXINPUTS", ""), &pseudoDialog->lineEditPathImages);
  473 
  474     registerOption("Session/StoreRelativePaths", &sessionStoreRelativePaths, true, &pseudoDialog->checkBoxSessionStoreRelativePaths);
  475 
  476     registerOption("Editor/GoToErrorWhenDisplayingLog", &goToErrorWhenDisplayingLog , true, &pseudoDialog->checkBoxGoToErrorWhenDisplayingLog);
  477     registerOption("Editor/ShowLogMarkersWhenClickingLogEntry", &showLogMarkersWhenClickingLogEntry , true, &pseudoDialog->checkBoxShowLogMarkersWhenClickingLogEntry);
  478     registerOption("Editor/LogFileEncoding", &logFileEncoding, "Document", &pseudoDialog->comboBoxLogFileEncoding);
  479     registerOption("Editor/ScanInstalledLatexPackages", &scanInstalledLatexPackages, true, &pseudoDialog->checkBoxScanInstalledLatexPackages);
  480 
  481     registerOption("Tools/Insert Unicode From SymbolGrid", &insertSymbolsAsUnicode, false, &pseudoDialog->checkBoxInsertSymbolAsUCS);
  482 
  483     registerOption("Spell/DictionaryDir", &spellDictDir, "", &pseudoDialog->leDictDir); //don't translate it
  484     registerOption("Spell/Language", &spellLanguage, "<none>", &pseudoDialog->comboBoxSpellcheckLang);
  485     registerOption("Spell/Dic", &spell_dic, "<dic not found>", nullptr);
  486     registerOption("Thesaurus/Database", &thesaurus_database, "<dic not found>", &pseudoDialog->comboBoxThesaurusFileName);
  487 
  488     //macro repository
  489     registerOption("Macros/RepositoryURL", &URLmacroRepository, "https://api.github.com/repos/texstudio-org/texstudio-macro/contents/", nullptr);
  490 
  491     //updates
  492     registerOption("Update/AutoCheck", &autoUpdateCheck, true, &pseudoDialog->checkBoxAutoUpdateCheck);
  493     registerOption("Update/UpdateLevel", &updateLevel, 0, &pseudoDialog->comboBoxUpdateLevel);
  494     registerOption("Update/AutoCheckInvervalDays", &autoUpdateCheckIntervalDays, 7, &pseudoDialog->spinBoxAutoUpdateCheckIntervalDays);
  495     registerOption("Update/LastCheck", &lastUpdateCheck, QDateTime());
  496 
  497     //editor
  498     registerOption("Editor/WordWrapMode", &editorConfig->wordwrap, 1, &pseudoDialog->comboBoxLineWrap);
  499     registerOption("Editor/WrapLineWidth", &editorConfig->lineWidth, 80, &pseudoDialog->spinBoxWrapLineWidth);
  500     registerOption("Editor/Parentheses Matching", &editorConfig->parenmatch, true); //TODO: checkbox?
  501     registerOption("Editor/Parentheses Completion", &editorConfig->parenComplete, true, &pseudoDialog->checkBoxAutoCompleteParens);
  502     registerOption("Editor/Line Number Multiples", &editorConfig->showlinemultiples, 0);
  503     registerOption("Editor/Cursor Surrounding Lines", &editorConfig->cursorSurroundLines, 5);
  504     registerOption("Editor/BoldCursor", &editorConfig->boldCursor, true, &pseudoDialog->checkBoxBoldCursor);
  505     registerOption("Editor/CenterDocumentInEditor", &editorConfig->centerDocumentInEditor, false, &pseudoDialog->checkBoxCenterDocumentInEditor);
  506     registerOption("Editor/Auto Indent", &editorConfig->autoindent, true);
  507     registerOption("Editor/Weak Indent", &editorConfig->weakindent, false);
  508     registerOption("Editor/Indent with Spaces", &editorConfig->replaceIndentTabs, false, &pseudoDialog->checkBoxReplaceIndentTabByWhitespace);
  509     registerOption("Editor/ReplaceTextTabs", &editorConfig->replaceTextTabs, false, &pseudoDialog->checkBoxReplaceTextTabByWhitespace);
  510     registerOption("Editor/RemoveTrailingWsOnSave", &editorConfig->removeTrailingWsOnSave, false, &pseudoDialog->checkboxRemoveTrailingWsOnSave);
  511     registerOption("Editor/Folding", &editorConfig->folding, true, &pseudoDialog->checkBoxFolding);
  512     registerOption("Editor/Show Line State", &editorConfig->showlinestate, true, &pseudoDialog->checkBoxLineState);
  513     registerOption("Editor/Show Cursor State", &editorConfig->showcursorstate, true, &pseudoDialog->checkBoxState);
  514     registerOption("Editor/Real-Time Spellchecking", &editorConfig->realtimeChecking, true, &pseudoDialog->checkBoxRealTimeCheck); //named for compatibility reasons with older txs versions
  515     registerOption("Editor/Check Spelling", &editorConfig->inlineSpellChecking, true, &pseudoDialog->checkBoxInlineSpellCheck);
  516     registerOption("Editor/Check Citations", &editorConfig->inlineCitationChecking, true, &pseudoDialog->checkBoxInlineCitationCheck);
  517     registerOption("Editor/Check References", &editorConfig->inlineReferenceChecking, true, &pseudoDialog->checkBoxInlineReferenceCheck);
  518     registerOption("Editor/Check Syntax", &editorConfig->inlineSyntaxChecking, true, &pseudoDialog->checkBoxInlineSyntaxCheck);
  519     registerOption("Editor/Check Grammar", &editorConfig->inlineGrammarChecking, true, &pseudoDialog->checkBoxInlineGrammarCheck);
  520     registerOption("Editor/Check Package", &editorConfig->inlinePackageChecking, true, &pseudoDialog->checkBoxInlinePackageCheck);
  521     registerOption("Editor/Check In Non TeX Files", &editorConfig->inlineCheckNonTeXFiles, true, &pseudoDialog->checkBoxInlineCheckNonTeXFiles);
  522     registerOption("Editor/Hide Spelling Errors in Non Text", &editorConfig->hideNonTextSpellingErrors, true, &pseudoDialog->checkBoxHideSpellingErrorsInNonText);
  523     registerOption("Editor/Hide Grammar Errors in Non Text", &editorConfig->hideNonTextGrammarErrors, true, &pseudoDialog->checkBoxHideGrammarErrorsInNonText);
  524     registerOption("Editor/Show Whitespace", &editorConfig->showWhitespace, false, &pseudoDialog->checkBoxShowWhitespace);
  525     registerOption("Editor/TabStop", &editorConfig->tabStop, 4 , &pseudoDialog->sbTabSpace);
  526     registerOption("Editor/ToolTip Help", &editorConfig->toolTipHelp, true, &pseudoDialog->checkBoxToolTipHelp2);
  527     registerOption("Editor/ToolTip Preview", &editorConfig->toolTipPreview, true, &pseudoDialog->checkBoxToolTipPreview);
  528     registerOption("Editor/ImageToolTip", &editorConfig->imageToolTip, true, &pseudoDialog->checkBoxImageToolTip);
  529     registerOption("Editor/MaxImageTooltipWidth", &editorConfig->maxImageTooltipWidth, 400);
  530     registerOption("Editor/ContextMenuKeyboardModifiers", &editorConfig->contextMenuKeyboardModifiers, Qt::ShiftModifier);
  531     registerOption("Editor/ContextMenuSpellcheckingEntryLocation", &editorConfig->contextMenuSpellcheckingEntryLocation, 0, &pseudoDialog->comboBoxContextMenuSpellcheckingEntryLocation);
  532 
  533     registerOption("Editor/TexDoc Help Internal", &editorConfig->texdocHelpInInternalViewer, true , &pseudoDialog->checkBoxTexDocInternal);
  534     registerOption("Editor/MonitorFilesForExternalChanges", &editorConfig->monitorFilesForExternalChanges, true, &pseudoDialog->checkBoxMonitorFilesForExternalChanges);
  535     registerOption("Editor/SilentReload", &editorConfig->silentReload, false, &pseudoDialog->checkBoxSilentReload);
  536 #ifdef Q_OS_WIN
  537     // QSaveFile does not work with dropbox on windows: https://sourceforge.net/p/texstudio/bugs/1933/, https://bugreports.qt.io/browse/QTBUG-57299
  538     // We disable usage of QSaveFile and revert to our own file saving mechanism until the problem gets fixed.
  539     // Note: When deleting this, also delete ui.checkBoxUseQSaveWrite->setVisible(false);
  540     editorConfig->useQSaveFile = false;
  541 #else
  542     registerOption("Editor/UseQSaveFile", &editorConfig->useQSaveFile, true, &pseudoDialog->checkBoxUseQSaveWrite);
  543 #endif
  544 
  545     registerOption("Editor/Replace Quotes", &replaceQuotes, 0 , &pseudoDialog->comboBoxReplaceQuotes);
  546 
  547     registerOption("Editor/Close Search Replace Together", &editorConfig->closeSearchAndReplace, true, &pseudoDialog->checkBoxCloseSearchReplaceTogether);
  548     registerOption("Editor/Use Line For Search", &editorConfig->useLineForSearch, true, &pseudoDialog->checkBoxUseLineForSearch);
  549     registerOption("Editor/Search Only In Selection", &editorConfig->searchOnlyInSelection, true, &pseudoDialog->checkBoxSearchOnlyInSelection);
  550     registerOption("Editor/Auto Replace Commands", &CodeSnippet::autoReplaceCommands, true, &pseudoDialog->checkBoxAutoReplaceCommands);
  551 
  552 
  553     registerOption("Editor/Font Family", &editorConfig->fontFamily, "", &pseudoDialog->comboBoxFont);
  554     registerOption("Editor/Font Size", &editorConfig->fontSize, -1, &pseudoDialog->spinBoxSize);
  555     registerOption("Editor/Line Spacing Percent", &editorConfig->lineSpacingPercent, 100, &pseudoDialog->spinBoxLineSpacingPercent);
  556     registerOption("Editor/Esc for closing log", &useEscForClosingLog, false, &pseudoDialog->checkBoxCloseLogByEsc);
  557     registerOption("Editor/UseEscForClosingEmbeddedViewer", &useEscForClosingEmbeddedViewer, true, &pseudoDialog->checkBoxCloseEmbeddedViewerByEsc);
  558     registerOption("Editor/UseEscForClosingFullscreen", &useEscForClosingFullscreen, true, &pseudoDialog->checkBoxCloseFullscreenByEsc);
  559     registerOption("Editor/ShowShortcutsInTooltips", &showShortcutsInTooltips, true, &pseudoDialog->checkBoxShowShortcutsInTooltips);
  560 
  561     registerOption("Editor/AllowDragAndDrop", &editorConfig->allowDragAndDrop, true, &pseudoDialog->checkBoxAllowDragAndDrop);
  562     registerOption("Editor/Mouse Wheel Zoom", &editorConfig->mouseWheelZoom, true, &pseudoDialog->checkBoxMouseWheelZoom);
  563     registerOption("Editor/Smooth Scrolling", &editorConfig->smoothScrolling, true, &pseudoDialog->checkBoxSmoothScrolling);
  564     registerOption("Editor/Vertical Over Scroll", &editorConfig->verticalOverScroll, false, &pseudoDialog->checkBoxVerticalOverScroll);
  565 
  566     registerOption("Editor/Hack Auto Choose", &editorConfig->hackAutoChoose, true, &pseudoDialog->checkBoxHackAutoRendering);
  567     registerOption("Editor/Hack Disable Fixed Pitch", &editorConfig->hackDisableFixedPitch, false, &pseudoDialog->checkBoxHackDisableFixedPitch);
  568     registerOption("Editor/Hack Disable Width Cache", &editorConfig->hackDisableWidthCache, false, &pseudoDialog->checkBoxHackDisableWidthCache);
  569     registerOption("Editor/Hack Disable Line Cache", &editorConfig->hackDisableLineCache, false, &pseudoDialog->checkBoxHackDisableLineCache);
  570     registerOption("Editor/Hack Disable Accent Workaround", &editorConfig->hackDisableAccentWorkaround, false, &pseudoDialog->checkBoxHackDisableAccentWorkaround);
  571     registerOption("Editor/Hack Render Mode", &editorConfig->hackRenderingMode, 0, &pseudoDialog->comboBoxHackRenderMode);
  572     registerOption("Editor/Hack QImage Cache", &editorConfig->hackQImageCache, false, &pseudoDialog->checkBoxHackQImageCache);
  573 
  574     //completion
  575     registerOption("Editor/Completion", &completerConfig->enabled, true, &pseudoDialog->checkBoxCompletion);
  576     Q_ASSERT(sizeof(int) == sizeof(LatexCompleterConfig::CaseSensitive));
  577     registerOption("Editor/Completion Case Sensitive", reinterpret_cast<int *>(&completerConfig->caseSensitive), 2);
  578     registerOption("Editor/Completion Complete Common Prefix", &completerConfig->completeCommonPrefix, true, &pseudoDialog->checkBoxCompletePrefix);
  579     registerOption("Editor/Completion EOW Completes", &completerConfig->eowCompletes, false, &pseudoDialog->checkBoxEOWCompletes);
  580     registerOption("Editor/Completion Enable Tooltip Help", &completerConfig->tooltipHelp, true, &pseudoDialog->checkBoxToolTipHelp);
  581     registerOption("Editor/Completion Enable Tooltip Preview", &completerConfig->tooltipPreview, true, &pseudoDialog->checkBoxToolTipCompletePreview);
  582     registerOption("Editor/Completion Use Placeholders", &completerConfig->usePlaceholders, true, &pseudoDialog->checkBoxUsePlaceholders);
  583     registerOption("Editor/Completion Show Placeholders", &editorConfig->showPlaceholders, true, &pseudoDialog->checkBoxShowPlaceholders);
  584     registerOption("Editor/Completion Prefered Tab", reinterpret_cast<int *>(&completerConfig->preferedCompletionTab), 0, &pseudoDialog->comboBoxPreferedTab);
  585     registerOption("Editor/Completion Tab Relative Font Size Percent", &completerConfig->tabRelFontSizePercent, 100, &pseudoDialog->spinBoxTabRelFontSize);
  586     registerOption("Editor/Completion Auto Insert Math", &completerConfig->autoInsertMathDelimiters, true, &pseudoDialog->checkBoxAutoInsertMathDelimiters);
  587     registerOption("Editor/Completion Auto Insert Math Start", &completerConfig->startMathDelimiter,"$");
  588     registerOption("Editor/Completion Auto Insert Math Stop", &completerConfig->stopMathDelimiter,"$");
  589 
  590 
  591     registerOption("Editor/Auto Insert LRM", &editorConfig->autoInsertLRM, false, &pseudoDialog->checkBoxAutoLRM);
  592     registerOption("Editor/Visual Column Mode", &editorConfig->visualColumnMode, true, &pseudoDialog->checkBoxVisualColumnMode);
  593     registerOption("Editor/Auto Switch Language Direction", &editorConfig->switchLanguagesDirection, true, &pseudoDialog->checkBoxSwitchLanguagesDirection);
  594     registerOption("Editor/Auto Switch Language Math", &editorConfig->switchLanguagesMath, false, &pseudoDialog->checkBoxSwitchLanguagesMath);
  595 
  596     registerOption("Editor/Overwrite Opening Bracket Followed By Placeholder", &editorConfig->overwriteOpeningBracketFollowedByPlaceholder, true, &pseudoDialog->checkOverwriteOpeningBracketFollowedByPlaceholder);
  597     registerOption("Editor/Overwrite Closing Bracket Following Placeholder", &editorConfig->overwriteClosingBracketFollowingPlaceholder, true, &pseudoDialog->checkOverwriteClosingBracketFollowingPlaceholder);
  598     registerOption("Editor/Double-click Selection Includes Leading Backslash", &editorConfig->doubleClickSelectionIncludeLeadingBackslash, true, &pseudoDialog->checkBoxDoubleClickSelectionIncludeLeadingBackslash);
  599     registerOption("Editor/TripleClickSelection", &editorConfig->tripleClickSelectionIndex, 4, &pseudoDialog->comboBoxTripleClickSelection);
  600 
  601     registerOption("Editor/todo comment regExp", &editorConfig->regExpTodoComment, "%\\s*(TODO|todo)",&pseudoDialog->leRegExpTODO);
  602 
  603     registerOption("Editor/insertCiteCommand",&citeCommand,"\\cite",&pseudoDialog->lineEditCiteCommand);
  604 
  605     //table autoformating
  606     registerOption("TableAutoformat/Special Commands", &tableAutoFormatSpecialCommands, "\\hline,\\cline,\\intertext,\\shortintertext,\\toprule,\\midrule,\\bottomrule", &pseudoDialog->leTableFormatingSpecialCommands);
  607     registerOption("TableAutoformat/Special Command Position", &tableAutoFormatSpecialCommandPos, 0, &pseudoDialog->cbTableFormatingSpecialCommandPos);
  608     registerOption("TableAutoformat/One Line Per Cell", &tableAutoFormatOneLinePerCell, false, &pseudoDialog->cbTableFormatingOneLinePerCell);
  609 
  610     //grammar
  611     registerOption("Grammar/Long Repetition Check", &grammarCheckerConfig->longRangeRepetitionCheck, true, &pseudoDialog->checkBoxGrammarRepetitionCheck);
  612     registerOption("Grammar/Bad Word Check", &grammarCheckerConfig->badWordCheck, true, &pseudoDialog->checkBoxGrammarBadWordCheck);
  613     registerOption("Grammar/Long Repetition Check Distance", &grammarCheckerConfig->maxRepetitionDelta, 3, &pseudoDialog->spinBoxGrammarRepetitionDistance);
  614     registerOption("Grammar/Very Long Repetition Check Distance", &grammarCheckerConfig->maxRepetitionLongRangeDelta, 10, &pseudoDialog->spinBoxGrammarLongRangeRepetition);
  615     registerOption("Grammar/Very Long Repetition Check Min Length", &grammarCheckerConfig->maxRepetitionLongRangeMinWordLength, 6, &pseudoDialog->spinBoxGrammarLongRangeRepetitionMinLength);
  616     registerOption("Grammar/Word Lists Dir", &grammarCheckerConfig->wordlistsDir, "", &pseudoDialog->lineEditGrammarWordlists);
  617 #ifdef Q_OS_WIN
  618     registerOption("Grammar/Language Tool URL", &grammarCheckerConfig->languageToolURL, "", &pseudoDialog->lineEditGrammarLTUrl);
  619 #else
  620     registerOption("Grammar/Language Tool URL", &grammarCheckerConfig->languageToolURL, "http://localhost:8081/", &pseudoDialog->lineEditGrammarLTUrl);
  621 #endif
  622     registerOption("Grammar/Language Tool Path", &grammarCheckerConfig->languageToolPath, "", &pseudoDialog->lineEditGrammarLTPath);
  623     registerOption("Grammar/Language Tool Arguments", &grammarCheckerConfig->languageToolArguments, "org.languagetool.server.HTTPServer -p 8081", &pseudoDialog->lineEditGrammarLTArguments);
  624     registerOption("Grammar/Language Tool Java Path", &grammarCheckerConfig->languageToolJavaPath, "java", &pseudoDialog->lineEditGrammarLTJava);
  625     registerOption("Grammar/Language Tool Autorun", &grammarCheckerConfig->languageToolAutorun, true, &pseudoDialog->checkBoxGrammarLTAutorun);
  626     registerOption("Grammar/Language Tool Ignored Rules", &grammarCheckerConfig->languageToolIgnoredRules, "", &pseudoDialog->lineEditGrammarLTIgnoredRules);
  627 #define TEMP(ID) registerOption("Grammar/Special Rules" #ID, &grammarCheckerConfig->specialIds##ID, "", &pseudoDialog->lineEditGrammarSpecialRules##ID)
  628     TEMP(1);
  629     TEMP(2);
  630     TEMP(3);
  631     TEMP(4);
  632 #undef TEMP
  633 
  634     //other dialogs
  635     registerOption("Dialogs/Last Hard Wrap Column", &lastHardWrapColumn, 80);
  636     registerOption("Dialogs/Last Hard Wrap Smart Scope Selection", &lastHardWrapSmartScopeSelection, false);
  637     registerOption("Dialogs/Last Hard Wrap Join Lines", &lastHardWrapJoinLines, false);
  638 
  639     //build commands
  640     registerOption("Tools/SingleViewerInstance", &BuildManager::singleViewerInstance, false, &pseudoDialog->checkBoxSingleInstanceViewer);
  641     registerOption("Tools/Show Messages When Compiling", &showMessagesWhenCompiling, true, &pseudoDialog->checkBoxShowMessagesOnCompile);
  642     registerOption("Tools/Show Stdout", &showStdoutOption, 1, &pseudoDialog->comboBoxShowStdout);
  643     registerOption("Tools/Automatic Rerun Times", &BuildManager::autoRerunLatex, 5, &pseudoDialog->spinBoxRerunLatex);
  644     registerOption("Tools/ShowLogInCaseOfCompileError", &BuildManager::showLogInCaseOfCompileError, true, &pseudoDialog->checkBoxShowLogInCaseOfCompileError);
  645     registerOption("Tools/ReplaceEnvironmentVariables", &BuildManager::m_replaceEnvironmentVariables, true, &pseudoDialog->checkBoxReplaceEnvironmentVariables);
  646     registerOption("Tools/InterpetCommandDefinitionInMagicComment", &BuildManager::m_interpetCommandDefinitionInMagicComment, true, &pseudoDialog->checkBoxInterpetCommandDefinitionInMagicComment);
  647     registerOption("Tools/SupportShellStyleLiteralQuotes", &BuildManager::m_supportShellStyleLiteralQuotes, true);
  648 
  649     //Paths
  650 #ifdef Q_OS_MAC
  651     QString defaultSearchPaths = "/usr/local/texlive/2012/bin/x86_64-darwin"; //workaround for xelatex
  652 #else
  653     QString defaultSearchPaths;
  654 #endif
  655     registerOption("Tools/Search Paths", &BuildManager::additionalSearchPaths, defaultSearchPaths, &pseudoDialog->lineEditPathCommands);
  656     registerOption("Tools/Log Paths", &BuildManager::additionalLogPaths, "", &pseudoDialog->lineEditPathLog);
  657     registerOption("Tools/PDF Paths", &BuildManager::additionalPdfPaths, "", &pseudoDialog->lineEditPathPDF);
  658 
  659     //SVN/GIT
  660     //registerOption("Tools/Auto Checkin after Save", &autoCheckinAfterSave, false, &pseudoDialog->cbAutoCheckin);
  661     registerOption("Tools/UseVCS", &useVCS, 0, &pseudoDialog->comboBoxUseVCS);
  662     registerOption("Tools/Auto Checkin after Save level", &autoCheckinAfterSaveLevel, 0, &pseudoDialog->comboBoxAutoCheckinLevel);
  663     registerOption("Tools/SVN Undo", &svnUndo, false, &pseudoDialog->cbSVNUndo);
  664     registerOption("Tools/SVN KeywordSubstitution", &svnKeywordSubstitution, false, &pseudoDialog->cbKeywordSubstitution);
  665     registerOption("Tools/SVN Search Path Depth", &svnSearchPathDepth, 2, &pseudoDialog->sbDirSearchDepth);
  666 
  667 #ifdef INTERNAL_TERMINAL
  668     registerOption("Terminal/ColorScheme", &terminalConfig->terminalColorScheme, "Linux", &pseudoDialog->comboBoxTerminalColorScheme);
  669     registerOption("Terminal/Font Family", &terminalConfig->terminalFontFamily, "", &pseudoDialog->comboBoxTerminalFont);
  670     registerOption("Terminal/Font Size", &terminalConfig->terminalFontSize, -1, &pseudoDialog->spinBoxTerminalFontSize);
  671     registerOption("Terminal/Shell", &terminalConfig->terminalShell, "/bin/bash", &pseudoDialog->lineEditTerminalShell);
  672 #endif
  673 
  674     //interfaces
  675     int defaultStyle=0;
  676     if(systemUsesDarkMode()){
  677         // use modern style -dark in case of dark mode.
  678         // this is only relevant on the very first start-up of txs
  679         defaultStyle=2;
  680     }
  681     registerOption("GUI/Style", &modernStyle, defaultStyle, &pseudoDialog->comboBoxInterfaceModernStyle);
  682 #if defined Q_WS_X11 || defined Q_OS_LINUX
  683     interfaceFontFamily = "<later>";
  684     interfaceStyle = "<later>";
  685 #else
  686     interfaceFontFamily = QApplication::font().family();
  687     interfaceStyle = "";
  688 #endif
  689     registerOption("GUI/Texmaker Palette", &useTexmakerPalette, false, &pseudoDialog->checkBoxUseTexmakerPalette);
  690     registerOption("GUI/Use System Theme", &useSystemTheme, true, &pseudoDialog->checkBoxUseSystemTheme);
  691     registerOption("X11/Font Family", &interfaceFontFamily, interfaceFontFamily, &pseudoDialog->comboBoxInterfaceFont); //named X11 for backward compatibility
  692     registerOption("X11/Font Size", &interfaceFontSize, QApplication::font().pointSize(), &pseudoDialog->spinBoxInterfaceFontSize);
  693     registerOption("X11/Style", &interfaceStyle, interfaceStyle, &pseudoDialog->comboBoxInterfaceStyle);
  694     registerOption("GUI/ToobarIconSize", &guiToolbarIconSize, 22);
  695     registerOption("GUI/SymbolSize", &guiSymbolGridIconSize, 32);
  696     registerOption("GUI/SecondaryToobarIconSize", &guiSecondaryToolbarIconSize, 16);
  697     registerOption("GUI/PDFToobarIconSize", &guiPDFToolbarIconSize, 16);
  698     registerOption("GUI/ConfigShorcutColumnWidth", &guiConfigShortcutColumnWidth, 200);
  699 
  700     registerOption("View/ShowStatusbar", &showStatusbar, true);
  701 
  702     registerOption("Interface/Config Show Advanced Options", &configShowAdvancedOptions, false, &pseudoDialog->checkBoxShowAdvancedOptions);
  703     registerOption("Interface/Config Riddled", &configRiddled, false);
  704     registerOption("Interface/MRU Document Chooser", &mruDocumentChooser, false, &pseudoDialog->checkBoxMRUDocumentChooser);
  705 
  706     //language
  707     registerOption("Interface/Language", &language, "", &pseudoDialog->comboBoxLanguage);
  708 
  709     //preview
  710     registerOption("Preview/Mode", reinterpret_cast<int *>(&previewMode), static_cast<int>(PM_INLINE), &pseudoDialog->comboBoxPreviewMode);
  711     registerOption("Preview/Auto Preview", reinterpret_cast<int *>(&autoPreview), 1, &pseudoDialog->comboBoxAutoPreview);
  712     registerOption("Preview/Auto Preview Delay", &autoPreviewDelay, 300, &pseudoDialog->spinBoxAutoPreviewDelay);
  713     registerOption("Preview/SegmentPreviewScalePercent", &segmentPreviewScalePercent, 150, &pseudoDialog->spinBoxSegmentPreviewScalePercent);
  714 
  715     //pdf preview
  716     QRect screen = QGuiApplication::primaryScreen()->availableGeometry();
  717     registerOption("Geometries/PdfViewerLeft", &pdfDocumentConfig->windowLeft, screen.left() + screen.width() * 2 / 3);
  718     registerOption("Geometries/PdfViewerTop", &pdfDocumentConfig->windowTop, screen.top() + 10);
  719     registerOption("Geometries/PdfViewerWidth", &pdfDocumentConfig->windowWidth, screen.width() / 3 - 26);
  720     registerOption("Geometries/PdfViewerHeight", &pdfDocumentConfig->windowHeight, screen.height() - 100);
  721     registerOption("Geometries/PdfViewerMaximized", &pdfDocumentConfig->windowMaximized, false);
  722     registerOption("Geometries/PdfViewerState", &pdfDocumentConfig->windowState, QByteArray());
  723     registerOption("Preview/ToolbarVisible", &pdfDocumentConfig->toolbarVisible, true);
  724     registerOption("Preview/AnnotationPanelVisible", &pdfDocumentConfig->annotationPanelVisible, false);
  725 
  726     registerOption("Preview/CacheSize", &pdfDocumentConfig->cacheSizeMB, 512, &pseudoDialog->spinBoxCacheSizeMB);
  727     registerOption("Preview/LoadStrategy", &pdfDocumentConfig->loadStrategy, 2, &pseudoDialog->comboBoxPDFLoadStrategy);
  728     registerOption("Preview/RenderBackend", &pdfDocumentConfig->renderBackend, 0, &pseudoDialog->comboBoxPDFRenderBackend);
  729     registerOption("Preview/LimitRenderQueues", &pdfDocumentConfig->limitThreadNumber, -8); // hidden config to limit renderQueues i.e. parallel threads to render PDF. Default set numberOfThreads=qMin(8, number of cores)
  730     registerOption("Preview/DPI", &pdfDocumentConfig->dpi, QApplication::desktop()->logicalDpiX(), &pseudoDialog->spinBoxPreviewDPI);
  731     registerOption("Preview/Scale Option", &pdfDocumentConfig->scaleOption, 1, &pseudoDialog->comboBoxPreviewScale);
  732     registerOption("Preview/Scale", &pdfDocumentConfig->scale, 100, &pseudoDialog->spinBoxPreviewScale);
  733     registerOption("Preview/", &pdfDocumentConfig->disableHorizontalScrollingForFitToTextWidth, true, &pseudoDialog->checkBoxDisableHorizontalScrollingForFitToTextWidth);
  734     registerOption("Preview/ZoomStepFactor", &pdfDocumentConfig->zoomStepFactor, 1.4142135); // sqrt(2)
  735     registerOption("Preview/Magnifier Size", &pdfDocumentConfig->magnifierSize, 300, &pseudoDialog->spinBoxPreviewMagnifierSize);
  736     registerOption("Preview/Magnifier Shape", &pdfDocumentConfig->magnifierShape, 1, &pseudoDialog->comboBoxPreviewMagnifierShape);
  737     registerOption("Preview/Magnifier Border", &pdfDocumentConfig->magnifierBorder, false, &pseudoDialog->checkBoxPreviewMagnifierBorder);
  738 
  739     registerOption("Preview/PaperColor", &pdfDocumentConfig->paperColor, "#FFFFFF", &pseudoDialog->lineEditPaperColor);
  740     registerOption("Preview/HighlightColor", &pdfDocumentConfig->highlightColor, "#FFFF003F", &pseudoDialog->lineEditHighlightColor);
  741     registerOption("Preview/HighlightDuration", &pdfDocumentConfig->highlightDuration, 2000, &pseudoDialog->spinBoxHighlightDuration);
  742     registerOption("Preview/Sync File Mask", &pdfDocumentConfig->syncFileMask, "*.tex;*.tikz;*.pdf_tex;*.ctx", &pseudoDialog->lineEditPreviewSyncFileMask);
  743     registerOption("Preview/AutoHideToolbars", &pdfDocumentConfig->autoHideToolbars, false, &pseudoDialog->autoHideToolbars);
  744     registerOption("Preview/Auto Full Recompile", &editorConfig->fullCompilePreview, false, &pseudoDialog->autoRecompileFullDocument);
  745 
  746     registerOption("Preview/EnlargedEmbedded", &viewerEnlarged, false);
  747 
  748     // LogView
  749     registerOption("LogView/WarnIfFileSizeLargerMB", &logViewWarnIfFileSizeLargerMB, 2.0);
  750     registerOption("LogView/RememberChoiceLargeFile", &logViewRememberChoice, 0);
  751 
  752 #ifndef QT_NO_DEBUG
  753     registerOption("Debug/Last Application Modification", &debugLastFileModification);
  754     registerOption("Debug/Last Full Test Run", &debugLastFullTestRun);
  755 #endif
  756 
  757     // runaway limit for lexing
  758     registerOption("Editor/RUNAWAYLIMIT", &RUNAWAYLIMIT , 30);
  759 }
  760 
  761 ConfigManager::~ConfigManager()
  762 {
  763     delete editorConfig;
  764     delete completerConfig;
  765     delete webPublishDialogConfig;
  766     delete insertGraphicsConfig;
  767     delete persistentConfig;
  768     delete terminalConfig;
  769     globalConfigManager = nullptr;
  770 }
  771 
  772 QString ConfigManager::iniPath()
  773 {
  774     if (!persistentConfig) {
  775         QString configDir = configDirOverride;
  776         if (configDir.isEmpty()) configDir = portableConfigDir();
  777         return configDir + "/texstudio.ini";
  778     }
  779     return configFileName;
  780 }
  781 
  782 QString ConfigManager::portableConfigDir()
  783 {
  784     return QCoreApplication::applicationDirPath() + "/config";
  785 }
  786 
  787 bool ConfigManager::isPortableMode()
  788 {
  789     return (QDir(portableConfigDir()).exists() || !configDirOverride.isEmpty());
  790 }
  791 
  792 /*!
  793  * Returns a QSettings instance tor the location in which current settings are / should be stored.
  794  */
  795 QSettings *ConfigManager::newQSettings()
  796 {
  797     if (isPortableMode()) {
  798         return new QSettings(iniPath(), QSettings::IniFormat);
  799     } else {
  800         return new QSettings(QSettings::IniFormat, QSettings::UserScope, "texstudio", "texstudio");
  801     }
  802 }
  803 
  804 QSettings *ConfigManager::readSettings(bool reread)
  805 {
  806     //load config
  807     QSettings *config = persistentConfig;
  808     bool portableMode = isPortableMode();
  809     if (!config) {
  810         config = newQSettings();
  811         configFileName = config->fileName();
  812         configFileNameBase = configFileName;
  813         configBaseDir = ensureTrailingDirSeparator(QFileInfo(configFileName).absolutePath());
  814         completerConfig->importedCwlBaseDir = configBaseDir; // set in LatexCompleterConfig to get access from LatexDocument
  815         if (configFileNameBase.endsWith(".ini")) configFileNameBase = configFileNameBase.replace(QString(".ini"), "");
  816         persistentConfig = config;
  817         setupDirectoryStructure();
  818         moveCwls();
  819     }
  820 
  821     if (QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) && (config->value("version/written_by_Qt_version").toInt()) < QT_VERSION_CHECK(5, 9, 0)) {
  822         // workaround for bug 2175: crash when loading some old config that was created with Qt < 5.9 and now reading with Qt 5.9.
  823         // Likely a Qt bug because restoreState() should simply.
  824         // return false for invalid input. Assuming that some change in the layout of the splitter is incompatible with
  825         // a previous state. We don't know exactly what this caused and it's not worth digging into this. Therefore we just
  826         // reset this option when loading any old config.
  827         config->remove("texmaker/centralVSplitterState");
  828     }
  829 
  830     config->beginGroup("texmaker");
  831     if (config->contains("Files/Auto Detect Encoding Of Loaded Files")) { // import old setting
  832         bool b = config->value("Files/Auto Detect Encoding Of Loaded Files").toBool();
  833         if (!config->contains("Files/AutoDetectEncodingFromChars")) config->setValue("Files/AutoDetectEncodingFromChars", b);
  834         if (!config->contains("Files/AutoDetectEncodingFromLatex")) config->setValue("Files/AutoDetectEncodingFromLatex", b);
  835         config->remove("Files/Auto Detect Encoding Of Loaded Files");
  836     }
  837 
  838 
  839 
  840     //----------managed properties--------------------
  841     for (int i = 0; i < managedProperties.size(); i++)
  842         managedProperties[i].valueFromQVariant(config->value(managedProperties[i].name, managedProperties[i].def));
  843 
  844     //language
  845     QString locale = language;
  846     appTranslator = new QTranslator(this);
  847     basicTranslator = new QTranslator(this);
  848     loadTranslations(language);
  849     QCoreApplication::installTranslator(appTranslator);
  850     QCoreApplication::installTranslator(basicTranslator);
  851 
  852     //------------------files--------------------
  853     newFileEncoding = QTextCodec::codecForName(newFileEncodingName.toLatin1().data());
  854 
  855     //----------------------------dictionaries-------------------------
  856 
  857     if (spellDictDir.isEmpty()) {
  858         // non-exeistent or invalid settings for dictionary
  859         // try restore from old format where there was only one dictionary - spell_dic can be removed later when users have migrated to the new version
  860         QString dic = spell_dic;
  861         if (!QFileInfo(dic).exists()) {
  862             // fallback to defaults
  863             QStringList temp;
  864             QStringList fallBackPaths;
  865 #ifdef Q_OS_WIN32
  866             fallBackPaths << parseDir("[txs-settings-dir]/dictionaries") << parseDir("[txs-app-dir]/dictionaries");
  867 #endif
  868 #ifndef Q_OS_WIN32
  869 #ifndef PREFIX
  870 #define PREFIX
  871 #endif
  872             fallBackPaths << PREFIX"/share/hunspell" << PREFIX"/share/myspell"
  873                           << "/usr/share/hunspell" << "/usr/share/myspell"
  874                           << parseDir("[txs-app-dir]/../share/texstudio") ;
  875 #endif
  876 #ifdef Q_OS_MAC
  877             fallBackPaths << parseDir("[txs-app-dir]/Contents/Resources") << "/Applications/texstudio.app/Contents/Resources";
  878 #endif
  879             dic = findResourceFile(QString(QLocale::system().name()) + ".dic", true, temp, fallBackPaths);
  880             if (dic == "") spell_dic = findResourceFile("en_US.dic", true, temp, fallBackPaths);
  881             if (dic == "") spell_dic = findResourceFile("en_GB.dic", true, temp, fallBackPaths);
  882             if (dic == "") spell_dic = findResourceFile("fr_FR.dic", true, temp, fallBackPaths);
  883             if (dic == "") spell_dic = findResourceFile("de_DE.dic", true, temp, fallBackPaths);
  884         }
  885         QFileInfo fi(dic);
  886         if (fi.exists()) {
  887             spellDictDir = QDir::toNativeSeparators(fi.absolutePath());
  888             if (portableMode) {
  889                 spellDictDir = reverseParseDir(spellDictDir);
  890             }
  891             spellLanguage = fi.baseName();
  892         }
  893     }
  894     if (grammarCheckerConfig->wordlistsDir.isEmpty()) {
  895         QString sw = findResourceFile("de.stopWords", true, QStringList(), parseDirList(spellDictDir));
  896         if (sw == "") sw = findResourceFile("en.stopWords", true, QStringList(), QStringList() << parseDirList(spellDictDir));
  897         if (QFileInfo(sw).exists()) grammarCheckerConfig->wordlistsDir = QDir::toNativeSeparators(QFileInfo(sw).absolutePath());
  898     }
  899 
  900     if (thesaurus_database == "<dic not found>") {
  901         thesaurus_database = "";
  902     }
  903     if (thesaurus_database != "") {
  904         QFileInfo fi(parseDir(thesaurus_database));
  905         if (!fi.exists()) { // try finding the file in other directories (e.g. after update tmx->txs
  906             thesaurus_database = findResourceFile(fi.fileName(), true, QStringList(), QStringList() << parseDir(thesaurus_database));
  907         }
  908     }
  909     if (thesaurus_database == "") { // fall back to system or fixed language
  910         QStringList preferredPaths = QStringList() << parseDir("[txs-settings-dir]/dictionaries");
  911         QStringList fallBackPaths;
  912 #ifdef Q_OS_LINUX
  913         fallBackPaths << PREFIX"/share/mythes" << "/usr/share/mythes"
  914                       << parseDir("[txs-app-dir]/../share/texstudio") ;
  915 #endif
  916         thesaurus_database = findResourceFile("th_" + QString(QLocale::system().name()) + "_v2.dat", true, preferredPaths, fallBackPaths);
  917         if (thesaurus_database == "") thesaurus_database = findResourceFile("th_en_US_v2.dat", true, preferredPaths, fallBackPaths);
  918         if (thesaurus_database == "") thesaurus_database = findResourceFile("th_en_GB_v2.dat", true, preferredPaths, fallBackPaths);
  919         if (thesaurus_database == "") thesaurus_database = findResourceFile("th_fr_FR_v2.dat", true, preferredPaths, fallBackPaths);
  920         if (thesaurus_database == "") thesaurus_database = findResourceFile("th_de_DE_v2.dat", true, preferredPaths, fallBackPaths);
  921     }
  922     if (!thesaurus_database.isEmpty()) {
  923         if (portableMode) {
  924             thesaurus_database = reverseParseDir(thesaurus_database);
  925         }
  926         thesaurus_database = QDir::toNativeSeparators(thesaurus_database);
  927     }
  928 
  929 
  930     //----------------------------editor--------------------
  931 
  932     //completion
  933     QStringList cwlFiles = config->value("Editor/Completion Files", QStringList() << "tex.cwl" << "latex-document.cwl"  << "latex-dev.cwl").toStringList();
  934     //completerConfig->words=loadCwlFiles(cwlFiles,ltxCommands,completerConfig);
  935     LatexParser &latexParser = LatexParser::getInstance();
  936 
  937     // read usageCount from file of its own.
  938     // needs to be done before reading cwl files
  939     if (!reread) {
  940         QFile file(configBaseDir + "wordCount.usage");
  941         if (file.open(QIODevice::ReadOnly)) {
  942             QDataStream in(&file);
  943             quint32 magicNumer, version;
  944             in >>  magicNumer >> version;
  945             if (magicNumer == static_cast<quint32>(0xA0B0C0D0) && version == 1) {
  946                 in.setVersion(QDataStream::Qt_4_0);
  947                 uint key;
  948                 int length, usage;
  949 
  950                 completerConfig->usage.clear();
  951                 while (!in.atEnd()) {
  952                     in >> key >> length >> usage;
  953                     if (usage > 0) {
  954                         completerConfig->usage.insert(key, qMakePair(length, usage));
  955                     }
  956                 }
  957             }
  958         }
  959     }
  960 
  961     completerConfig->words.clear();
  962     QSet<QString>loadedFiles;
  963     QStringList tobeLoaded=cwlFiles;
  964     //foreach (const QString &cwlFile, cwlFiles) {
  965     while(!tobeLoaded.isEmpty()){
  966         QString cwlFile=tobeLoaded.takeFirst();
  967         if(loadedFiles.contains(cwlFile))
  968             continue;
  969         loadedFiles.insert(cwlFile);
  970         LatexPackage pck = loadCwlFile(cwlFile, completerConfig);
  971         tobeLoaded.append(pck.requiredPackages);
  972         completerConfig->words.unite(pck.completionWords);
  973         latexParser.optionCommands.unite(pck.optionCommands);
  974         latexParser.specialTreatmentCommands.unite(pck.specialTreatmentCommands);
  975         latexParser.specialDefCommands.unite(pck.specialDefCommands);
  976         latexParser.environmentAliases.unite(pck.environmentAliases);
  977         latexParser.commandDefs.unite(pck.commandDescriptions);
  978         //ltxCommands->possibleCommands.unite(pck.possibleCommands); // qt error, does not work properly
  979         foreach (const QString &elem, pck.possibleCommands.keys()) {
  980             QSet<QString> set2 = pck.possibleCommands[elem];
  981             QSet<QString> set = latexParser.possibleCommands[elem];
  982             set.unite(set2);
  983             latexParser.possibleCommands[elem] = set;
  984         }
  985     }
  986 
  987     completerConfig->setFiles(cwlFiles);
  988     // remove old solution from .ini
  989     if (config->contains("Editor/Completion Usage"))
  990         config->remove("Editor/Completion Usage");
  991     //web publish dialog
  992     webPublishDialogConfig->readSettings(*config);
  993     //insert graphics dialog
  994     insertGraphicsConfig->readSettings(*config);
  995 
  996     //build commands
  997     if (!buildManager) {
  998         UtilsUi::txsCritical("No build Manager created! => crash");
  999         return nullptr;
 1000     }
 1001     buildManager->readSettings(*config);
 1002     runLaTeXBibTeXLaTeX = config->value("Tools/After BibTeX Change", "tmx://latex && tmx://bibtex && tmx://latex").toString() != "";
 1003 
 1004     //import old key replacements or set default
 1005     QStringList keyReplace, keyReplaceAfterWord, keyReplaceBeforeWord;
 1006     if (!config->value("User/New Key Replacements Created", false).toBool()) {
 1007         int keyReplaceCount = config->value("User/KeyReplaceCount", -1).toInt();
 1008         if (keyReplaceCount == -1) {
 1009             //default
 1010             /* new system ...
 1011             keyReplace.append("\"");
 1012             QString loc=QString(QLocale::system().name()).left(2);
 1013             if (loc=="de") {
 1014             keyReplaceBeforeWord.append("\">");
 1015             keyReplaceAfterWord.append("\"<");
 1016             } else {
 1017             keyReplaceAfterWord.append("''");
 1018             keyReplaceBeforeWord.append("``");
 1019             }
 1020             */
 1021         } else for (int i = 0; i < keyReplaceCount; i++) {
 1022                 keyReplace.append(config->value("User/KeyReplace" + QVariant(i).toString(), i != 0 ? "'" : "\"").toString());
 1023                 keyReplaceAfterWord.append(config->value("User/KeyReplaceAfterWord" + QVariant(i).toString(), "").toString());
 1024                 keyReplaceBeforeWord.append(config->value("User/KeyReplaceBeforeWord" + QVariant(i).toString(), i != 0 ? "" : "\">").toString());
 1025             }
 1026     }
 1027 
 1028     //user macros
 1029     if (!reread) {
 1030         QDir dir(joinPath(configBaseDir,"macro"));
 1031         if(dir.exists()){
 1032             // use file based macros
 1033             for (int i = 0; i < ConfigManager::MAX_NUM_MACROS; i++) {
 1034                 QString fileName=joinPath(configBaseDir,"macro",QString("Macro_%1.txsMacro").arg(i));
 1035                 if(QFile(fileName).exists()){
 1036                     Macro macro;
 1037                     macro.load(fileName);
 1038                     completerConfig->userMacros.append(macro);
 1039                 }else{
 1040                     break;
 1041                 }
 1042             }
 1043         }else{
 1044             if (config->value("Macros/0").isValid()) {
 1045                 for (int i = 0; i < ConfigManager::MAX_NUM_MACROS; i++) {
 1046                     QStringList ls = config->value(QString("Macros/%1").arg(i)).toStringList();
 1047                     if (ls.isEmpty()) break;
 1048                     completerConfig->userMacros.append(Macro(ls));
 1049                 }
 1050                 for (int i = 0; i < keyReplace.size(); i++) {
 1051                     completerConfig->userMacros.append(Macro(
 1052                                                            tr("Key replacement: %1 %2").arg(keyReplace[i]).arg(tr("before word")),
 1053                                                            keyReplaceBeforeWord[i].replace("%", "%%"),
 1054                                                            "",
 1055                                                            "(?language:latex)(?<=\\s|^)" + QRegExp::escape(keyReplace[i])
 1056                                                            ));
 1057                     completerConfig->userMacros.append(Macro(
 1058                                                            tr("Key replacement: %1 %2").arg(keyReplace[i]).arg(tr("after word")),
 1059                                                            keyReplaceAfterWord[i].replace("%", "%%"),
 1060                                                            "",
 1061                                                            "(?language:latex)(?<=\\S)" + QRegExp::escape(keyReplace[i])
 1062                                                            ));
 1063                 }
 1064             } else {
 1065                 // try importing old macros
 1066                 QStringList userTags = config->value("User/Tags").toStringList();
 1067                 QStringList userNames = config->value("User/TagNames").toStringList();
 1068                 QStringList userAbbrevs = config->value("User/TagAbbrevs").toStringList();
 1069                 QStringList userTriggers = config->value("User/TagTriggers").toStringList();
 1070 
 1071                 while (userTriggers.size() < userTags.size()) userTriggers << "";
 1072 
 1073                 for (int i = 0; i < keyReplace.size(); i++) {
 1074                     userNames.append(tr("Key replacement: %1 %2").arg(keyReplace[i]).arg(tr("before word")));
 1075                     userTags.append(keyReplaceBeforeWord[i].replace("%", "%%"));
 1076                     userAbbrevs.append("");
 1077                     userTriggers.append("(?language:latex)(?<=\\s|^)" + QRegExp::escape(keyReplace[i]));
 1078 
 1079                     userNames.append(tr("Key replacement: %1 %2").arg(keyReplace[i]).arg(tr("after word")));
 1080                     userTags.append(keyReplaceAfterWord[i].replace("%", "%%"));
 1081                     userAbbrevs.append("");
 1082                     userTriggers.append("(?language:latex)(?<=\\S)" + QRegExp::escape(keyReplace[i]));
 1083                 }
 1084 
 1085                 for (int i = 0; i < userTags.size(); i++)
 1086                     completerConfig->userMacros.append(Macro(userNames.value(i, ""), userTags[i], userAbbrevs.value(i, ""), userTriggers.value(i, "")));
 1087             }
 1088         }
 1089         // import old svn setting
 1090         if (config->contains("Tools/Auto Checkin after Save")) {
 1091             bool oldSetting = config->value("Tools/Auto Checkin after Save", false).toBool();
 1092             if (oldSetting)
 1093                 autoCheckinAfterSaveLevel = 1;
 1094             config->remove("Tools/Auto Checkin after Save");
 1095         }
 1096     }
 1097     //menu shortcuts
 1098     QMap<QString, QString> aliases = QMap<QString, QString>();
 1099     // key and value may be a full command or a prefix only
 1100     aliases.insert("main/user/commands/", "main/tools/user/");
 1101     aliases.insert("main/user/tags/", "main/macros/");
 1102     aliases.insert("main/usertags/", "main/macros/");
 1103     aliases.insert("main/tools/latex", "main/tools/commands/latex");
 1104     aliases.insert("main/tools/viewdvi", "main/tools/commands/viewdvi");
 1105     aliases.insert("main/tools/dvi2ps", "main/tools/commands/dvi2ps");
 1106     aliases.insert("main/tools/viewps", "main/tools/commands/viewps");
 1107     aliases.insert("main/tools/pdflatex", "main/tools/commands/pdflatex");
 1108     aliases.insert("main/tools/viewpdf", "main/tools/commands/viewpdf");
 1109     aliases.insert("main/tools/ps2pdf", "main/tools/commands/ps2pdf");
 1110     aliases.insert("main/tools/dvipdf", "main/tools/commands/dvipdf");
 1111     aliases.insert("main/tools/makeindex", "main/tools/commands/makeindex");
 1112     aliases.insert("main/tools/metapost", "main/tools/commands/metapost");
 1113     aliases.insert("main/tools/asymptote", "main/tools/commands/asymptote");
 1114     aliases.insert("main/edit/eraseLine", "main/edit/lineoperations/eraseLine");
 1115     aliases.insert("main/edit/eraseEndLine", "main/edit/lineoperations/eraseEndLine");
 1116 
 1117     int size = config->beginReadArray("keysetting");
 1118     for (int i = 0; i < size; ++i) {
 1119         config->setArrayIndex(i);
 1120         QString id = config->value("id").toString();
 1121         for (QMap<QString, QString>::iterator it = aliases.begin(), end = aliases.end(); it != end; ++it)
 1122             if (id.startsWith(it.key())) {
 1123                 id.replace(0, it.key().length(), it.value());
 1124                 break;
 1125             }
 1126 
 1127         managedMenuNewShortcuts.append(QPair<QString, QString> (id, config->value("key").toString()));
 1128     }
 1129     config->endArray();
 1130 
 1131     //changed latex menus
 1132     manipulatedMenus = config->value("changedLatexMenus").toMap();
 1133 
 1134     //custom toolbar
 1135     for (int i = 0; i < managedToolBars.size(); i++) {
 1136         ManagedToolBar &mtb = managedToolBars[i];
 1137         mtb.actualActions = config->value(mtb.name + "ToolBar").toStringList();
 1138         for (int i = 0; i < mtb.actualActions.count(); i++) {
 1139             for (QMap<QString, QString>::iterator it = aliases.begin(), end = aliases.end(); it != end; ++it) {
 1140                 QString id = mtb.actualActions.at(i);
 1141                 if (id.startsWith(it.key())) {
 1142                     id.replace(0, it.key().length(), it.value());
 1143                     mtb.actualActions.replace(i, id);
 1144                     break;
 1145                 }
 1146             }
 1147         }
 1148         if (mtb.actualActions.empty()) mtb.actualActions = mtb.defaults;
 1149     }
 1150     replacedIconsOnMenus = config->value("customIcons").toMap();
 1151 
 1152     //custom highlighting
 1153     LatexParser::getInstance().customCommands = convertStringListtoSet(config->value("customCommands").toStringList());
 1154 
 1155     //--------------------appearance------------------------------------
 1156     QFontDatabase fdb;
 1157     QStringList xf = fdb.families();
 1158     //editor
 1159 #ifdef Q_OS_WIN32
 1160     if (editorConfig->fontFamily.isEmpty()) {
 1161         if (xf.contains("Consolas", Qt::CaseInsensitive)) editorConfig->fontFamily = "Consolas";
 1162         else if (xf.contains("Courier New", Qt::CaseInsensitive)) editorConfig->fontFamily = "Courier New";
 1163         else editorConfig->fontFamily = qApp->font().family();
 1164     }
 1165     if (editorConfig->fontSize == -1)
 1166         editorConfig->fontSize = 10;
 1167 #else
 1168     if (editorConfig->fontFamily.isEmpty()) {
 1169         if (xf.contains("DejaVu Sans Mono", Qt::CaseInsensitive)) editorConfig->fontFamily = "DejaVu Sans Mono";
 1170         //else if (xf.contains("Lucida Sans Unicode",Qt::CaseInsensitive)) editorConfig->fontFamily="Lucida Sans Unicode";
 1171         else if (xf.contains("Lucida Sans Typewriter", Qt::CaseInsensitive)) editorConfig->fontFamily = "Lucida Sans Typewriter";
 1172         else editorConfig->fontFamily = qApp->font().family();
 1173     }
 1174     if (editorConfig->fontSize == -1)
 1175         editorConfig->fontSize = qApp->font().pointSize();
 1176 #endif
 1177 
 1178 #if defined Q_WS_X11 || defined Q_OS_LINUX
 1179     if (interfaceFontFamily == "<later>") {
 1180         //use an interface like Texmaker
 1181         if (xf.contains("DejaVu Sans", Qt::CaseInsensitive)) interfaceFontFamily = "DejaVu Sans";
 1182         else if (xf.contains("DejaVu Sans LGC", Qt::CaseInsensitive)) interfaceFontFamily = "DejaVu Sans LGC";
 1183         else if (xf.contains("Bitstream Vera Sans", Qt::CaseInsensitive)) interfaceFontFamily = "Bitstream Vera Sans";
 1184         else if (xf.contains("Luxi Sans", Qt::CaseInsensitive)) interfaceFontFamily = "Luxi Sans";
 1185         else interfaceFontFamily = QApplication::font().family();
 1186     }
 1187     if (interfaceStyle == "<later>") {
 1188         if (x11desktop_env() == 0) { //no-kde
 1189             if (QStyleFactory::keys().contains("GTK+")) interfaceStyle = "GTK+"; //gtkstyle
 1190             else interfaceStyle = "Cleanlooks";
 1191         } else if ((x11desktop_env() == 5) && (QStyleFactory::keys().contains("Breeze"))) {
 1192             interfaceStyle = "Breeze"; //kde5+breeze
 1193         } else if ((x11desktop_env() == 4) && (QStyleFactory::keys().contains("Oxygen"))) {
 1194             interfaceStyle = "Oxygen"; //kde4+oxygen
 1195         } else interfaceStyle = "Plastique"; //others
 1196     }
 1197 #endif
 1198 
 1199     setInterfaceStyle();
 1200 
 1201 #ifdef Q_OS_MAC
 1202     // workaround for unwanted font changes when changing the desktop
 1203     // https://sourceforge.net/tracker/?func=detail&aid=3559432&group_id=250595&atid=1126426
 1204     if (interfaceFontFamily != QApplication::font().family()
 1205             || interfaceFontSize != QApplication::font().pointSize())
 1206         QApplication::setDesktopSettingsAware(false);
 1207 #endif
 1208     QApplication::setFont(QFont(interfaceFontFamily, interfaceFontSize));
 1209 
 1210 #ifdef INTERNAL_TERMINAL
 1211     terminalConfig->initDefaults(xf);
 1212 #endif
 1213 
 1214     config->endGroup();
 1215 
 1216     return config;
 1217 }
 1218 
 1219 QSettings *ConfigManager::saveSettings(const QString &saveName)
 1220 {
 1221     Q_ASSERT(persistentConfig);
 1222     QSettings *config = saveName.isEmpty() ? persistentConfig : (new QSettings(saveName, QSettings::IniFormat));
 1223     config->setValue("IniMode", true);
 1224 
 1225     config->beginGroup("version");
 1226     // updated on every access
 1227     config->setValue("written_by_TXS_version", TXSVERSION);
 1228     config->setValue("written_by_TXS_hg_revision", TEXSTUDIO_GIT_REVISION);
 1229     config->setValue("written_by_Qt_version", QT_VERSION);
 1230     // written just the very first time
 1231     if (!config->value("created_by_TXS_version").isValid())
 1232         config->setValue("created_by_TXS_version", TXSVERSION);
 1233     if (!config->value("created_by_TXS_hg_revision").isValid())
 1234         config->setValue("created_by_TXS_hg_revision", TEXSTUDIO_GIT_REVISION);
 1235     if (!config->value("created_by_Qt_version").isValid())
 1236         config->setValue("created_by_Qt_version", QT_VERSION);
 1237     config->endGroup();
 1238 
 1239     config->beginGroup("texmaker");
 1240 
 1241     buildManager->saveSettings(*config); //save first, so it can set managed properties
 1242 
 1243     //----------managed properties--------------------
 1244     foreach (const ManagedProperty &mp, managedProperties)
 1245         config->setValue(mp.name, mp.valueToQVariant());
 1246 
 1247     //completion
 1248     if (!completerConfig->getLoadedFiles().isEmpty())
 1249         config->setValue("Editor/Completion Files", completerConfig->getLoadedFiles());
 1250 
 1251     //web publish dialog
 1252     webPublishDialogConfig->saveSettings(*config);
 1253     insertGraphicsConfig->saveSettings(*config);
 1254 
 1255 
 1256     //---------------------build commands----------------
 1257     config->setValue("Tools/After BibTeX Change", runLaTeXBibTeXLaTeX ? "tmx://latex && tmx://bibtex && tmx://latex" : "");
 1258 
 1259     //-------------------key replacements-----------------
 1260     config->setValue("User/New Key Replacements Created", true);
 1261 
 1262     saveMacros();
 1263 
 1264     int index=0;
 1265     while (config->contains(QString("Macros/%1").arg(index))) { //remove old macros which are not used any more
 1266         config->remove(QString("Macros/%1").arg(index));
 1267         index++;
 1268     }
 1269 
 1270     // remove old Tags
 1271     config->remove("User/Tags");
 1272     config->remove("User/TagNames");
 1273     config->remove("User/TagAbbrevs");
 1274     config->remove("User/TagTriggers");
 1275 
 1276     //menu shortcuts
 1277     config->beginWriteArray("keysetting");
 1278     for (int i = 0; i < managedMenuNewShortcuts.size(); ++i) {
 1279         config->setArrayIndex(i);
 1280         if(managedMenuNewShortcuts[i].first.startsWith("main/macros/")){
 1281             continue;
 1282         }
 1283         config->setValue("id", managedMenuNewShortcuts[i].first);
 1284         config->setValue("key", managedMenuNewShortcuts[i].second);
 1285     }
 1286     config->endArray();
 1287 
 1288     //changed latex menus
 1289     config->setValue("changedLatexMenus", manipulatedMenus);
 1290     //custom toolbar
 1291     for (int i = 0; i < managedToolBars.size(); i++) {
 1292         ManagedToolBar &mtb = managedToolBars[i];
 1293         if (mtb.actualActions == mtb.defaults) config->setValue(mtb.name + "ToolBar", QStringList());
 1294         else config->setValue(mtb.name + "ToolBar", mtb.actualActions);
 1295     }
 1296     config->setValue("customIcons", replacedIconsOnMenus);
 1297     // custom highlighting
 1298     QStringList zw = LatexParser::getInstance().customCommands.values();
 1299     config->setValue("customCommands", zw);
 1300 
 1301     config->endGroup();
 1302 
 1303     config->sync();
 1304 
 1305     return config;
 1306 }
 1307 
 1308 QSettings *ConfigManager::getSettings()
 1309 {
 1310     return persistentConfig;
 1311 }
 1312 
 1313 void ConfigManager::saveMacros()
 1314 {
 1315     //user macros
 1316     bool newlyCreatedPath=!QDir(configBaseDir+"/macro").exists();
 1317     if(newlyCreatedPath){
 1318         newlyCreatedPath=QDir().mkpath(configBaseDir+"/macro");
 1319     }
 1320 
 1321     int index = 0;
 1322     foreach (Macro macro, completerConfig->userMacros) {
 1323         if (macro.name == TXS_AUTO_REPLACE_QUOTE_OPEN || macro.name == TXS_AUTO_REPLACE_QUOTE_CLOSE || macro.document)
 1324             continue;
 1325         if(newlyCreatedPath && index<10 && index!=2){
 1326             macro.setShortcut(QString("Shift+F%1").arg(index+1));
 1327         }
 1328         macro.save(QString("%1macro/Macro_%2.txsMacro").arg(configBaseDir).arg(index++));
 1329     }
 1330     // remove unused macro files
 1331     // lazy approach, only first macro is removed
 1332     QFile fn(QString("%1macro/Macro_%2.txsMacro").arg(configBaseDir).arg(index));
 1333     if(fn.exists()){
 1334         fn.remove();
 1335     }
 1336 }
 1337 
 1338 /*!
 1339  * \brief show and execute configuration dialog
 1340  * Translates internal settings into human readable settings and vice versa
 1341  * Fills the config dialog with available settings
 1342  * \param parentToDialog
 1343  * \return dialog was exited with "okay"
 1344  */
 1345 bool ConfigManager::execConfigDialog(QWidget *parentToDialog)
 1346 {
 1347     ConfigDialog *confDlg = new ConfigDialog(parentToDialog);
 1348     UtilsUi::resizeInFontHeight(confDlg, 86, 52);
 1349 
 1350     confDlg->riddled = configRiddled;
 1351     //----------managed properties--------------------
 1352     foreach (const ManagedProperty &mp, managedProperties)
 1353         if (mp.widgetOffset)
 1354             mp.writeToObject(*((QWidget **)((char *)&confDlg->ui + mp.widgetOffset))); //convert to char*, because the offset is in bytes
 1355 
 1356     //files
 1357     //if (newfile_encoding)
 1358     //  confDlg->ui.comboBoxEncoding->setCurrentIndex(confDlg->ui.comboBoxEncoding->findText(newfile_encoding->name(), Qt::MatchExactly));
 1359 
 1360     //-----------------------editor------------------------------
 1361     switch (editorConfig->showlinemultiples) {
 1362     case 0:
 1363         confDlg->ui.comboboxLineNumbers->setCurrentIndex(0);
 1364         break;
 1365     case 10:
 1366         confDlg->ui.comboboxLineNumbers->setCurrentIndex(2);
 1367         break;
 1368     default:
 1369         confDlg->ui.comboboxLineNumbers->setCurrentIndex(1);
 1370     }
 1371     if (editorConfig->autoindent && editorConfig->weakindent) confDlg->ui.comboBoxAutoIndent->setCurrentIndex(1);
 1372     else if (editorConfig->autoindent) confDlg->ui.comboBoxAutoIndent->setCurrentIndex(2);
 1373     else confDlg->ui.comboBoxAutoIndent->setCurrentIndex(0);
 1374 
 1375     lastLanguage = language;
 1376     QStringList languageFiles = findResourceFiles("translation", "texstudio_*.qm") << findResourceFiles("", "texstudio_*.qm");
 1377     for (int i = languageFiles.count() - 1; i >= 0; i--) {
 1378         QString temp = languageFiles[i].mid(languageFiles[i].indexOf("_") + 1);
 1379         temp.truncate(temp.indexOf("."));
 1380         if (languageFiles.contains(temp)) languageFiles.removeAt(i);
 1381         else languageFiles[i] = temp;
 1382     }
 1383     if (!languageFiles.contains("en")) languageFiles.append("en");
 1384     languageFiles.sort(); // insert sorted
 1385     int langId = -1;
 1386     for (int i = 0; i < languageFiles.count(); i++) {
 1387         QLocale loc(languageFiles[i]);
 1388         confDlg->ui.comboBoxLanguage->addItem(languageFiles[i]+"  ("+QLocale::languageToString(loc.language())+")");
 1389         if (languageFiles[i] == language) langId = i;
 1390     }
 1391     confDlg->ui.comboBoxLanguage->addItem(tr("default"));
 1392     if (language == "") confDlg->ui.comboBoxLanguage->setEditText(tr("default"));
 1393     else confDlg->ui.comboBoxLanguage->setEditText(language);
 1394     if (langId != -1) confDlg->ui.comboBoxLanguage->setCurrentIndex(langId);
 1395     else confDlg->ui.comboBoxLanguage->setCurrentIndex(confDlg->ui.comboBoxLanguage->count() - 1);
 1396 
 1397     QStringList files;
 1398     foreach(const QString &dirname, QDir::searchPaths("cwl")) {
 1399         if(dirname.endsWith("autogenerated"))
 1400             continue; // don't offer autogenerated cwls for gobal completion files (as they are for syntax checking only and rather bad at it)
 1401         files << QDir(dirname).entryList(QStringList("*.cwl"), QDir::Files);
 1402     }
 1403 
 1404     const QStringList &loadedFiles = completerConfig->getLoadedFiles();
 1405     foreach (const QString &elem, files) {
 1406         QListWidgetItem *item = new QListWidgetItem(elem, confDlg->ui.completeListWidget);
 1407         item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
 1408         if (loadedFiles.contains(elem)) item->setCheckState(Qt::Checked);
 1409         else  item->setCheckState(Qt::Unchecked);
 1410     }
 1411     //preview
 1412     confDlg->ui.comboBoxDvi2PngMode->setCurrentIndex(buildManager->dvi2pngMode);
 1413 
 1414     //Autosave
 1415     if (autosaveEveryMinutes == 0) confDlg->ui.comboBoxAutoSave->setCurrentIndex(0);
 1416     if (0 < autosaveEveryMinutes && autosaveEveryMinutes <= 1) confDlg->ui.comboBoxAutoSave->setCurrentIndex(1);
 1417     if (1 < autosaveEveryMinutes && autosaveEveryMinutes <= 2) confDlg->ui.comboBoxAutoSave->setCurrentIndex(2);
 1418     if (2 < autosaveEveryMinutes && autosaveEveryMinutes <= 5) confDlg->ui.comboBoxAutoSave->setCurrentIndex(3);
 1419     if (5 < autosaveEveryMinutes && autosaveEveryMinutes <= 10) confDlg->ui.comboBoxAutoSave->setCurrentIndex(4);
 1420     if (10 < autosaveEveryMinutes && autosaveEveryMinutes <= 20) confDlg->ui.comboBoxAutoSave->setCurrentIndex(5);
 1421     if (20 < autosaveEveryMinutes) confDlg->ui.comboBoxAutoSave->setCurrentIndex(6);
 1422     //--build things
 1423     //normal commands
 1424     tempCommands = buildManager->getAllCommands();
 1425     QStringList tempOrder = buildManager->getCommandsOrder();
 1426     rerunButtons.clear();
 1427     commandInputs.clear();
 1428     createCommandList(confDlg->ui.groupBoxCommands, tempOrder, false, false);
 1429     createCommandList(confDlg->ui.groupBoxMetaCommands, tempOrder, false, true);
 1430     createCommandList(confDlg->ui.groupBoxUserCommands, tempOrder, true, false);
 1431     confDlg->setBuildManger(buildManager);
 1432 
 1433     //quickbuild/more page
 1434     confDlg->ui.checkBoxReplaceBeamer->setChecked(buildManager->previewRemoveBeamer);
 1435     confDlg->ui.checkBoxPrecompilePreamble->setChecked(buildManager->previewPrecompilePreamble);
 1436 
 1437     confDlg->ui.checkBoxRunAfterBibTeXChange->setChecked(runLaTeXBibTeXLaTeX);
 1438 
 1439     QIcon fileOpenIcon = getRealIcon("document-open");
 1440     confDlg->ui.pushButtonDictDir->setIcon(fileOpenIcon);
 1441     confDlg->ui.btSelectThesaurusFileName->setIcon(fileOpenIcon);
 1442 
 1443     // grammar
 1444     confDlg->ui.pushButtonGrammarWordlists->setIcon(fileOpenIcon);
 1445     confDlg->ui.pushButtonGrammarLTPath->setIcon(fileOpenIcon);
 1446     confDlg->ui.pushButtonGrammarLTJava->setIcon(fileOpenIcon);
 1447 
 1448     //menu shortcuts
 1449     QTreeWidgetItem *menuShortcuts = new QTreeWidgetItem(static_cast<QTreeWidget *>(nullptr), QStringList() << QString(tr("Menus")));
 1450     foreach (QMenu *menu, managedMenus){
 1451         if(menu->objectName().startsWith("main"))
 1452             managedMenuToTreeWidget(menuShortcuts, menu);
 1453     }
 1454     confDlg->ui.shortcutTree->addTopLevelItem(menuShortcuts);
 1455     menuShortcuts->setExpanded(true);
 1456 #ifndef NO_POPPLER_PREVIEW
 1457     QTreeWidgetItem *menuShortcutsPDF = new QTreeWidgetItem(static_cast<QTreeWidget *>(nullptr), QStringList() << QString(tr("Menus PDF-Viewer")));
 1458     QSet<QString> usedMenus;
 1459     foreach (QMenu *menu, managedMenus){
 1460         if(usedMenus.contains(menu->objectName()))
 1461             continue; //avoid dublets
 1462         if(menu->objectName().startsWith("pdf")){
 1463             usedMenus.insert(menu->objectName());
 1464             managedMenuToTreeWidget(menuShortcutsPDF, menu);
 1465         }
 1466     }
 1467     confDlg->ui.shortcutTree->addTopLevelItem(menuShortcutsPDF);
 1468     menuShortcutsPDF->setExpanded(true);
 1469 #endif
 1470 
 1471     QTreeWidgetItem *editorItem = new QTreeWidgetItem(static_cast<QTreeWidget *>(nullptr), QStringList() << ConfigDialog::tr("Editor"));
 1472     QTreeWidgetItem *editorKeys = new QTreeWidgetItem(editorItem, QStringList() << ConfigDialog::tr("Basic Key Mapping"));
 1473     const int editorKeys_EditOperationRole = Qt::UserRole;
 1474 
 1475     Q_ASSERT(static_cast<int>(Qt::CTRL) == static_cast<int>(Qt::ControlModifier) && static_cast<int>(Qt::ALT) == static_cast<int>(Qt::AltModifier) && static_cast<int>(Qt::SHIFT) == static_cast<int>(Qt::ShiftModifier) && static_cast<int>(Qt::META) == static_cast<int>(Qt::MetaModifier));
 1476     QMultiMap<int, QString> keysReversed;
 1477     QHash<QString, int>::const_iterator it = this->editorKeys.constBegin();
 1478     while (it != this->editorKeys.constEnd()) {
 1479         keysReversed.insert(it.value(), it.key());
 1480         ++it;
 1481     }
 1482     int ht = confDlg->ui.comboBoxLanguage->minimumSizeHint().height();
 1483     foreach (const int elem, editorAvailableOperations) {
 1484         QList<QString> keys = keysReversed.values(elem);
 1485         bool listEmpty = false;
 1486         if (keys.isEmpty()) {
 1487             keys << "";
 1488             listEmpty = true;
 1489         }
 1490         foreach (const QString key, keys) {
 1491             QTreeWidgetItem *twi = nullptr;
 1492             if (listEmpty) {
 1493                 twi = new QTreeWidgetItem(editorKeys, QStringList() << LatexEditorViewConfig::translateEditOperation(elem) << "" << tr("<none>"));
 1494             } else {
 1495                 twi = new QTreeWidgetItem(editorKeys, QStringList() << LatexEditorViewConfig::translateEditOperation(elem) << "" << QKeySequence::fromString(key).toString(SHORTCUT_FORMAT));
 1496             }
 1497             twi->setData(0, editorKeys_EditOperationRole, elem);
 1498             twi->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
 1499 #ifdef Q_OS_WIN32
 1500             QSize sz = twi->sizeHint(0);
 1501             twi->setSizeHint(0, QSize(sz.width(), ht));
 1502 #endif
 1503         }
 1504 
 1505     }
 1506 
 1507     confDlg->ui.shortcutTree->addTopLevelItem(editorItem);
 1508     editorItem->setExpanded(true);
 1509     editorKeys->sortChildren(0, Qt::AscendingOrder); //sorting only works after assigning a tree widget !
 1510     QTreeWidgetItem *twi = new QTreeWidgetItem(editorKeys, QStringList() << ShortcutDelegate::addRowButton);
 1511 #ifdef Q_OS_WIN32
 1512     QSize sz = twi->sizeHint(0);
 1513     twi->setSizeHint(0, QSize(sz.width(), ht));
 1514 #else
 1515     Q_UNUSED(twi)
 1516     Q_UNUSED(ht)
 1517 #endif
 1518 
 1519     ShortcutDelegate delegate;
 1520     delegate.treeWidget = confDlg->ui.shortcutTree;
 1521     confDlg->ui.shortcutTree->setItemDelegate(&delegate); //setting in the config dialog doesn't work
 1522     delegate.connect(confDlg->ui.shortcutTree, SIGNAL(itemClicked(QTreeWidgetItem *, int)), &delegate, SLOT(treeWidgetItemClicked(QTreeWidgetItem *, int)));
 1523 
 1524     //custom menus
 1525     confDlg->menuParent = menuParent;
 1526     changedItemsList.clear();
 1527     manipulatedMenuTree.clear();
 1528     superAdvancedItems.clear();
 1529     foreach (QMenu *menu, managedMenus) {
 1530         QTreeWidgetItem *menuLatex = managedLatexMenuToTreeWidget(nullptr, menu);
 1531         if (menuLatex) {
 1532             confDlg->ui.menuTree->addTopLevelItem(menuLatex);
 1533             menuLatex->setExpanded(true);
 1534         }
 1535     }
 1536     connect(confDlg->ui.checkBoxShowAllMenus, SIGNAL(toggled(bool)), SLOT(toggleVisibleTreeItems(bool)));
 1537     toggleVisibleTreeItems(false);
 1538     connect(confDlg->ui.menuTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)), this, SLOT(menuTreeItemChanged(QTreeWidgetItem *, int)));
 1539     QAction *act = new QAction(tr("Insert New Menu Item (before)"), confDlg->ui.menuTree);
 1540     connect(act, SIGNAL(triggered()), SLOT(menuTreeNewItem()));
 1541     confDlg->ui.menuTree->addAction(act);
 1542     act = new QAction(tr("Insert New Sub Menu (before)"), confDlg->ui.menuTree);
 1543     connect(act, SIGNAL(triggered()), SLOT(menuTreeNewMenuItem()));
 1544     confDlg->ui.menuTree->addAction(act);
 1545     act = new QAction(tr("Revert/Remove User Menu Item"), confDlg->ui.menuTree);
 1546     connect(act, SIGNAL(triggered()), SLOT(menuTreeRevertItem()));
 1547     confDlg->ui.menuTree->addAction(act);
 1548     confDlg->ui.menuTree->setContextMenuPolicy(Qt::ActionsContextMenu);
 1549 
 1550     ComboBoxDelegate *cbd = new ComboBoxDelegate(confDlg->ui.menuTree);
 1551     cbd->defaultItems = possibleMenuSlots;
 1552     confDlg->ui.menuTree->setItemDelegate(cbd);
 1553 
 1554     // custom toolbars
 1555     confDlg->customizableToolbars.clear();
 1556     foreach (const ManagedToolBar &mtb, managedToolBars) {
 1557         Q_ASSERT(mtb.toolbar);
 1558         confDlg->customizableToolbars.append(mtb.actualActions);
 1559         confDlg->ui.comboBoxToolbars->addItem(qApp->translate("Texstudio", qPrintable(mtb.name)));
 1560     }
 1561     confDlg->allMenus = managedMenus;
 1562     confDlg->standardToolbarMenus = QList<QMenu *>() << getManagedMenu("main/latex") << getManagedMenu("main/math") << getManagedMenu("main/macros");
 1563     confDlg->ui.comboBoxActions->addItem(tr("Latex/Math menus"));
 1564     confDlg->ui.comboBoxActions->addItem(tr("All menus"));
 1565     confDlg->ui.comboBoxActions->addItem(tr("Special Tags"));
 1566     confDlg->replacedIconsOnMenus = &replacedIconsOnMenus;
 1567 
 1568 
 1569     //appearance
 1570     QString displayedInterfaceStyle = interfaceStyle == "" ? tr("default") : interfaceStyle;
 1571     confDlg->ui.comboBoxInterfaceStyle->clear();
 1572     QStringList availableStyles=QStyleFactory::keys();
 1573 #ifdef ADWAITA
 1574     availableStyles << "Adwaita (txs)" << "Adwaita Dark (txs)";
 1575 #endif
 1576     availableStyles << tr("default");
 1577     confDlg->ui.comboBoxInterfaceStyle->addItems(availableStyles);
 1578     confDlg->ui.comboBoxInterfaceStyle->setCurrentIndex(confDlg->ui.comboBoxInterfaceStyle->findText(displayedInterfaceStyle));
 1579     confDlg->ui.comboBoxInterfaceStyle->setEditText(displayedInterfaceStyle);
 1580 
 1581     confDlg->fmConfig->setBasePointSize( editorConfig->fontSize );
 1582     confDlg->fmConfig->addScheme("", QDocument::defaultFormatScheme());
 1583 
 1584     // set scaling sizes
 1585     confDlg->ui.horizontalSliderIcon->setValue(guiToolbarIconSize);
 1586     confDlg->ui.horizontalSliderCentraIcon->setValue(guiSecondaryToolbarIconSize);
 1587     confDlg->ui.horizontalSliderSymbol->setValue(guiSymbolGridIconSize);
 1588     confDlg->ui.horizontalSliderPDF->setValue(guiPDFToolbarIconSize);
 1589     connect(confDlg->ui.horizontalSliderIcon, SIGNAL(valueChanged(int)), SIGNAL(iconSizeChanged(int)));
 1590     connect(confDlg->ui.horizontalSliderCentraIcon, SIGNAL(valueChanged(int)), SIGNAL(secondaryIconSizeChanged(int)));
 1591     connect(confDlg->ui.horizontalSliderPDF, SIGNAL(valueChanged(int)), SIGNAL(pdfIconSizeChanged(int)));
 1592     connect(confDlg->ui.horizontalSliderSymbol, SIGNAL(valueChanged(int)), SIGNAL(symbolGridIconSizeChanged(int)));
 1593 
 1594     //EXECUTE IT
 1595     bool executed = confDlg->exec();
 1596     configRiddled = confDlg->riddled;
 1597 
 1598     //handle changes
 1599     if (executed) {
 1600         QList<void *> changedProperties;
 1601         //----------managed properties--------------------
 1602         for (int i = 0; i < managedProperties.size(); i++)
 1603             if (managedProperties[i].widgetOffset && managedProperties[i].readFromObject(*((QWidget **)((char *)&confDlg->ui + managedProperties[i].widgetOffset))))
 1604                 changedProperties << managedProperties[i].storage;
 1605 
 1606         //files
 1607         newFileEncoding = QTextCodec::codecForName(newFileEncodingName.toLatin1().data());
 1608 
 1609         if (changedProperties.contains(&maxRecentFiles) || changedProperties.contains(&maxRecentProjects))
 1610             updateRecentFiles(true);
 1611 
 1612         //editor
 1613         editorConfig->autoindent = confDlg->ui.comboBoxAutoIndent->currentIndex() != 0;
 1614         editorConfig->weakindent = (confDlg->ui.comboBoxAutoIndent->currentIndex() & 1) == 1;
 1615         switch (confDlg->ui.comboboxLineNumbers->currentIndex()) {
 1616         case 0:
 1617             editorConfig->showlinemultiples = 0;
 1618             break;
 1619         case 2:
 1620             editorConfig->showlinemultiples = 10;
 1621             break;
 1622         default:
 1623             editorConfig->showlinemultiples = 1;
 1624             break;
 1625         }
 1626 
 1627         //autosave
 1628         QList<int> times;
 1629         times << 0 << 1 << 2 << 5 << 10 << 20 << 60;
 1630         autosaveEveryMinutes = times.value(confDlg->ui.comboBoxAutoSave->currentIndex(), 0);
 1631         // update macros menu to update quote replacement
 1632         if (changedProperties.contains(&replaceQuotes)) {
 1633             bool conflict = false;
 1634             if (replaceQuotes)
 1635                 foreach (const Macro &m, completerConfig->userMacros) {
 1636                     if (m.name == TXS_AUTO_REPLACE_QUOTE_OPEN ||
 1637                             m.name == TXS_AUTO_REPLACE_QUOTE_CLOSE) continue;
 1638                     if (m.trigger == "(?language:latex)(?<=\\s|^)\"" || m.trigger == "(?language:latex)(?<=^)\"" || m.trigger == "(?language:latex)(?<=\\S)\"") {
 1639                         conflict = true;
 1640                         break;
 1641                     }
 1642                 }
 1643             if (conflict)
 1644                 if (UtilsUi::txsConfirm(tr("You have enabled auto quote replacement. However, there are macros with trigger string (?language:latex)(?<=\\s|^) or (?language:latex)(?<=\\S) which will override the new quote replacement.\nDo you want to remove them?"))) {
 1645                     for (int i = completerConfig->userMacros.count() - 1; i >= 0; i--) {
 1646                         const Macro &m = completerConfig->userMacros.at(i);
 1647                         if (m.trigger == "(?language:latex)(?<=\\s|^)\"" || m.trigger == "(?language:latex)(?<=^)\"" || m.trigger == "(?language:latex)(?<=\\S)\"")
 1648                             completerConfig->userMacros.removeAt(i);
 1649                     }
 1650                 }
 1651         }
 1652         //completion
 1653         completerConfig->enabled = confDlg->ui.checkBoxCompletion->isChecked();
 1654         completerConfig->caseSensitive = LatexCompleterConfig::CCS_CASE_INSENSITIVE;  // TODO: config removed from options due to performance issues. May be removed from completer code later on.
 1655         completerConfig->completeCommonPrefix = confDlg->ui.checkBoxCompletePrefix->isChecked();
 1656         completerConfig->eowCompletes = confDlg->ui.checkBoxEOWCompletes->isChecked();
 1657         completerConfig->tooltipHelp = confDlg->ui.checkBoxToolTipHelp->isChecked();
 1658         completerConfig->usePlaceholders = confDlg->ui.checkBoxUsePlaceholders->isChecked();
 1659         QStringList newFiles;
 1660         QListWidgetItem *elem;
 1661         for (int i = 0; i < confDlg->ui.completeListWidget->count(); i++) {
 1662             elem = confDlg->ui.completeListWidget->item(i);
 1663             if (elem->checkState() == Qt::Checked) newFiles.append(elem->text());
 1664         }
 1665         LatexParser &latexParser = LatexParser::getInstance();
 1666         latexParser.clear();
 1667         latexParser.init();
 1668         completerConfig->words.clear();
 1669         QSet<QString>loadedFiles;
 1670         QStringList tobeLoaded=newFiles;
 1671         while(!tobeLoaded.isEmpty()){
 1672             QString cwlFile=tobeLoaded.takeFirst();
 1673             if(loadedFiles.contains(cwlFile))
 1674                 continue;
 1675             loadedFiles.insert(cwlFile);
 1676             LatexPackage pck = loadCwlFile(cwlFile, completerConfig);
 1677             tobeLoaded.append(pck.requiredPackages);
 1678             completerConfig->words.unite(pck.completionWords);
 1679             latexParser.optionCommands.unite(pck.optionCommands);
 1680             latexParser.specialTreatmentCommands.unite(pck.specialTreatmentCommands);
 1681             latexParser.environmentAliases.unite(pck.environmentAliases);
 1682             latexParser.commandDefs.unite(pck.commandDescriptions);
 1683 
 1684             //ltxCommands->possibleCommands.unite(pck.possibleCommands); qt bug
 1685             foreach (const QString &elem, pck.possibleCommands.keys()) {
 1686                 QSet<QString> set2 = pck.possibleCommands[elem];
 1687                 QSet<QString> set = latexParser.possibleCommands[elem];
 1688                 set.unite(set2);
 1689                 latexParser.possibleCommands[elem] = set;
 1690             }
 1691         }
 1692         completerConfig->setFiles(newFiles);
 1693         //preview
 1694         previewMode = static_cast<PreviewMode>(confDlg->ui.comboBoxPreviewMode->currentIndex());
 1695         buildManager->dvi2pngMode = static_cast<BuildManager::Dvi2PngMode>(confDlg->ui.comboBoxDvi2PngMode->currentIndex());
 1696 #ifdef NO_POPPLER_PREVIEW
 1697         if (buildManager->dvi2pngMode == BuildManager::DPM_EMBEDDED_PDF) {
 1698             buildManager->dvi2pngMode = BuildManager::DPM_DVIPNG; //fallback when poppler is not included
 1699         }
 1700 #endif
 1701 
 1702         //build things
 1703         QStringList userOrder;
 1704         for (CommandMapping::iterator it = tempCommands.begin(), end = tempCommands.end(); it != end; )
 1705             if (it.value().user) it = tempCommands.erase(it);
 1706             else  ++it;
 1707         for (int i = 0; i < commandInputs.size(); i++) {
 1708             CommandMapping::iterator it = tempCommands.find(commandInputs[i]->property(PROPERTY_COMMAND_ID).toString());
 1709             if (it != tempCommands.end()) {
 1710                 QString text = getText(commandInputs[i]);
 1711                 QComboBox *cb = qobject_cast<QComboBox *>(commandInputs[i]);
 1712                 if (cb && !configShowAdvancedOptions) {
 1713                     // the display text has to be mappend to the actual command in case of the non-advanced dialog
 1714                     int i = cb->findText(text);
 1715                     if(i>=0)
 1716                         text = it.value().metaSuggestionList.value(i);
 1717                 }
 1718                 it.value().commandLine = text;
 1719             }
 1720         }
 1721         for (int i = 0; i < rerunButtons.size(); i++) {
 1722             CommandMapping::iterator it = tempCommands.find(getCmdID(rerunButtons[i]));
 1723             if (it != tempCommands.end()) {
 1724                 it.value().rerunCompiler = rerunButtons[i]->isChecked();
 1725             }
 1726         }
 1727         //read user commands
 1728         for (int i = 0; i < userGridLayout->count(); i++) {
 1729             QWidget *nameWidget = userGridLayout->itemAt(i)->widget();
 1730             if (!nameWidget) continue;
 1731             if (CG_ID == nameWidget->property(PROPERTY_WIDGET_TYPE).toInt()) {
 1732                 CommandInfo ci;
 1733                 QString combinedName = getText(nameWidget);
 1734                 int pos = combinedName.indexOf(":");
 1735                 ci.id = pos == -1 ? combinedName : combinedName.left(pos);
 1736                 if (ci.id.isEmpty()) ci.id = "user";
 1737                 ci.displayName = pos == -1 ? combinedName : combinedName.mid(pos + 1);
 1738                 ci.user = true;
 1739 
 1740                 while (i < userGridLayout->count() - 1) {
 1741                     i++;
 1742                     QWidget *w = userGridLayout->itemAt(i)->widget();
 1743                     if (!w) continue;
 1744                     int type = w->property(PROPERTY_WIDGET_TYPE).toInt();
 1745                     if (type == CG_ID || type == CG_ADD) {
 1746                         i--;
 1747                         break;
 1748                     } else if (type == CG_RERUN) {
 1749                         ci.rerunCompiler = static_cast<QPushButton *>(w)->isChecked();
 1750                     } else if (type == CG_CMD) {
 1751                         ci.commandLine = getText(w);
 1752                     }
 1753                 }
 1754                 tempCommands.insert(ci.id, ci);
 1755                 userOrder << ci.id;
 1756             }
 1757         }
 1758 
 1759         buildManager->setAllCommands(tempCommands, userOrder);
 1760         /*TODO for (BuildManager::LatexCommand cmd=BuildManager::CMD_LATEX; cmd < BuildManager::CMD_USER_QUICK; ++cmd){
 1761             if (!commandsToEdits.value(cmd)) continue;
 1762             buildManager->setLatexCommand(cmd,commandsToEdits.value(cmd)->text());
 1763         }
 1764 
 1765         for (BuildManager::LatexCommand cmd=BuildManager::CMD_SVN; cmd <= BuildManager::CMD_SVNADMIN; ++cmd){
 1766             if (!commandsToEdits.value(cmd)) continue;
 1767             buildManager->setLatexCommand(cmd,commandsToEdits.value(cmd)->text());
 1768         }*/
 1769 
 1770         /*Q_ASSERT(confDlg->checkboxInternalPDFViewer);
 1771         QString curPdfViewer = buildManager->getLatexCommand(BuildManager::CMD_VIEWPDF);
 1772         if (confDlg->checkboxInternalPDFViewer && confDlg->checkboxInternalPDFViewer->isChecked() != curPdfViewer.startsWith(BuildManager::TXS_INTERNAL_PDF_VIEWER)) {
 1773             //prepend/remove tmx://internal-pdf-viewer
 1774             if (confDlg->checkboxInternalPDFViewer->isChecked())
 1775                 buildManager->setLatexCommand(BuildManager::CMD_VIEWPDF , BuildManager::TXS_INTERNAL_PDF_VIEWER + "/" + curPdfViewer);
 1776             else if (curPdfViewer.startsWith(BuildManager::TXS_INTERNAL_PDF_VIEWER+"/"))
 1777                 buildManager->setLatexCommand(BuildManager::CMD_VIEWPDF , curPdfViewer.mid(BuildManager::TXS_INTERNAL_PDF_VIEWER.length() + 1));
 1778             else
 1779                 buildManager->setLatexCommand(BuildManager::CMD_VIEWPDF , curPdfViewer.mid(BuildManager::TXS_INTERNAL_PDF_VIEWER.length()));
 1780         }
 1781 
 1782         buildManager->setLatexCommand(BuildManager::CMD_USER_PRECOMPILE,confDlg->ui.lineEditExecuteBeforeCompiling->text());
 1783         buildManager->setLatexCommand(BuildManager::CMD_USER_QUICK,confDlg->ui.lineEditUserquick->text());
 1784         */
 1785         /*for (int i=0;i < confDlg->ui.quickbuildLayout->count(); i++) {
 1786             QRadioButton *rb = qobject_cast<QRadioButton*>(confDlg->ui.quickbuildLayout->itemAt(i)->widget());
 1787             if (rb && rb->isChecked()){
 1788                 buildManager->quickmode=rb->property("quickBuildMode").toInt();
 1789                 break;
 1790             }
 1791         }
 1792         */
 1793         buildManager->previewRemoveBeamer = confDlg->ui.checkBoxReplaceBeamer->isChecked();
 1794         buildManager->previewPrecompilePreamble = confDlg->ui.checkBoxPrecompilePreamble->isChecked();
 1795 
 1796         runLaTeXBibTeXLaTeX = confDlg->ui.checkBoxRunAfterBibTeXChange->isChecked();
 1797 
 1798 
 1799         //formats
 1800         confDlg->fmConfig->apply();
 1801         this->editorKeys.clear();
 1802         for (int i = 0; i < editorKeys->childCount(); i++) {
 1803             int editOperation = editorKeys->child(i)->data(0, editorKeys_EditOperationRole).toInt();
 1804             QKeySequence kSeq = QKeySequence::fromString(editorKeys->child(i)->text(2), SHORTCUT_FORMAT);
 1805             if (!kSeq.isEmpty() && editOperation > 0) /* not QEditor::Invalid or QEditor::NoOperation*/
 1806                 this->editorKeys.insert(kSeq.toString(), editOperation);
 1807         }
 1808 
 1809         //menus
 1810         managedMenuNewShortcuts.clear();
 1811 #if (QT_VERSION <= 0x050700) && (defined(Q_OS_MAC))
 1812         specialShortcuts.clear();
 1813 #endif
 1814         treeWidgetToManagedMenuTo(menuShortcuts);
 1815         updateUserMacroShortcuts(); // update macro shortcuts from menu
 1816 #ifndef NO_POPPLER_PREVIEW
 1817         treeWidgetToManagedMenuTo(menuShortcutsPDF);
 1818 #endif
 1819         treeWidgetToManagedLatexMenuTo();
 1820 
 1821         // custom toolbar
 1822         Q_ASSERT(confDlg->customizableToolbars.size() == managedToolBars.size());
 1823         for (int i = 0; i < managedToolBars.size(); i++) {
 1824             ManagedToolBar &mtb = managedToolBars[i];
 1825             mtb.actualActions = confDlg->customizableToolbars[i];
 1826         }
 1827 
 1828         //  interface
 1829         if (changedProperties.contains(&interfaceFontFamily) || changedProperties.contains(&interfaceFontSize)) {
 1830 #ifdef Q_OS_MAC
 1831             // workaround for unwanted font changes when changing the desktop
 1832             // https://sourceforge.net/tracker/?func=detail&aid=3559432&group_id=250595&atid=1126426
 1833             QApplication::setDesktopSettingsAware(false);
 1834 #endif
 1835             QApplication::setFont(QFont(interfaceFontFamily, interfaceFontSize));
 1836         }
 1837         if (changedProperties.contains(&interfaceStyle) || changedProperties.contains(&modernStyle) || changedProperties.contains(&useTexmakerPalette)) {
 1838             if (changedProperties.contains(&modernStyle))
 1839                 UtilsUi::txsInformation("Some elements cannot be adapted to the new style while the application is running. Please restart to get a consistent experience.");
 1840             if (interfaceStyle == tr("default")) interfaceStyle = "";
 1841             setInterfaceStyle();
 1842         }
 1843 
 1844         //language
 1845         if (language == tr("default")) language = "";
 1846         int i=language.indexOf("  (");
 1847         if(i>0){
 1848             language=language.left(i);
 1849         }
 1850         if (language != lastLanguage) loadTranslations(language);
 1851 
 1852         // GUI scaling
 1853         guiToolbarIconSize = confDlg->ui.horizontalSliderIcon->value();
 1854         guiSecondaryToolbarIconSize = confDlg->ui.horizontalSliderCentraIcon->value();
 1855         guiSymbolGridIconSize = confDlg->ui.horizontalSliderSymbol->value();
 1856         guiPDFToolbarIconSize = confDlg->ui.horizontalSliderPDF->value();
 1857     } else {
 1858         // GUI scaling
 1859         confDlg->ui.horizontalSliderIcon->setValue(guiToolbarIconSize);
 1860         confDlg->ui.horizontalSliderCentraIcon->setValue(guiSecondaryToolbarIconSize);
 1861         confDlg->ui.horizontalSliderSymbol->setValue(guiSymbolGridIconSize);
 1862         confDlg->ui.horizontalSliderPDF->setValue(guiPDFToolbarIconSize);
 1863 
 1864     }
 1865     delete confDlg;
 1866     return executed;
 1867 }
 1868 
 1869 bool ConfigManager::addRecentFile(const QString &fileName, bool asMaster)
 1870 {
 1871     bool changed = addMostRecent(fileName, recentFilesList, maxRecentFiles);
 1872 
 1873     if (asMaster) {
 1874         changed |= addMostRecent(fileName, recentProjectList, maxRecentProjects);
 1875     }
 1876 
 1877     if (changed) updateRecentFiles();
 1878 
 1879     return changed;
 1880 }
 1881 
 1882 void ConfigManager::activateInternalViewer(bool activated)
 1883 {
 1884     if (!activated) return;
 1885     QLineEdit *le = pdflatexEdit;
 1886     REQUIRE(le);
 1887     if (le->text().contains("synctex")) return;
 1888     if (!UtilsUi::txsConfirm(tr("To fully utilize the internal pdf-viewer, synctex has to be activated. Shall TeXstudio do it now?")))
 1889         return;
 1890     QString zw = le->text();
 1891     zw.replace("pdflatex ", "pdflatex -synctex=1 ", Qt::CaseSensitive);
 1892     le->setText(zw);
 1893 }
 1894 
 1895 void ConfigManager::updateRecentFiles(bool alwaysRecreateMenuItems)
 1896 {
 1897     QMenu *recentMenu = getManagedMenu("main/file/openrecent");
 1898     if (alwaysRecreateMenuItems || (recentMenu->actions().count() != maxRecentFiles + maxRecentProjects + 4)) {
 1899         QList<QAction *> actions = recentMenu->actions(); //recentMenu->clear() doesn't seem to delete the actions (why?)
 1900         for (int i = 0; i < actions.count(); i++)
 1901             recentMenu->removeAction(actions[i]); //neccessary or it crashes
 1902         for (int i = 0; i < maxRecentProjects; ++i)
 1903             newOrLostOldManagedAction(recentMenu, "p" + QString::number(i), tr("Recent 'Master Document' %1").arg(i), SLOT(fileOpenRecentProject()))->setVisible(false);
 1904         recentMenu->addSeparator();
 1905         for (int i = 0; i < maxRecentFiles; ++i)
 1906             newOrLostOldManagedAction(recentMenu, QString::number(i), tr("Recent File %1").arg(i), SLOT(fileOpenRecent()))->setVisible(false);
 1907         newOrLostOldManagedAction(recentMenu, "list", tr("File list"), SLOT(fileRecentList()));
 1908         newOrLostOldManagedAction(recentMenu, "firstFile", tr("Open first non-open file"), SLOT(fileOpenFirstNonOpen()));
 1909         newOrLostOldManagedAction(recentMenu, "allFiles", tr("&* Open all files"), SLOT(fileOpenAllRecent()));
 1910     }
 1911 
 1912     for (int i = 0; i < maxRecentProjects; i++) {
 1913         QAction *act = getManagedAction(QString("main/file/openrecent/p%1").arg(i));
 1914         REQUIRE(act);
 1915         if (i < recentProjectList.count()) {
 1916             act->setVisible(true);
 1917             QString temp = recentProjectList.at(i);
 1918             temp.replace("&", "&&");
 1919             act->setText(tr("Master Document: ") + (i <= 13 ? QString("&%1 ").arg(static_cast<char>('M' + i)) : "") + QDir::toNativeSeparators(temp));
 1920             act->setData(recentProjectList.at(i));
 1921         } else act->setVisible(false);
 1922     }
 1923     for (int i = 0; i < maxRecentFiles; i++) {
 1924         QAction *act = getManagedAction(QString("main/file/openrecent/%1").arg(i));
 1925         REQUIRE(act);
 1926         if (i < recentFilesList.count()) {
 1927             act->setVisible(true);
 1928             char schar = '\0';
 1929             if (i + 1 <= 9) schar = i + 1 + '0';
 1930             else if (i + 1 <= 9 + 12) schar = i + 1 + 'a' - 10;
 1931             else if (i + 1 <= 21 + 9) schar = i + 1 + '!' - 22;
 1932             else if (i + 1 <= 30 + 5) schar = i + 1 + '+' - 31;
 1933             else if (i + 1 <= 35 + 7) schar = i + 1 + ':' - 36;
 1934             else if (i + 1 <= 42 + 5) schar = i + 1 + '[' - 43;
 1935             else if (i + 1 <= 47 + 4) schar = i + 1 + '{' - 48;
 1936             QString temp = recentFilesList.at(i);
 1937             temp.replace("&", "&&");
 1938             act->setText((schar ? QString("&%1 ").arg(schar) : "") + QDir::toNativeSeparators(temp));
 1939             act->setData(recentFilesList.at(i));
 1940         } else act->setVisible(false);
 1941     }
 1942 }
 1943 
 1944 QMenu *ConfigManager::updateListMenu(const QString &menuName, const QStringList &items, const QString &namePrefix, bool prefixNumber, const char *slotName, const int baseShortCut, bool alwaysRecreateMenuItems, int additionalEntries, const QList<QVariant> data)
 1945 {
 1946     QSet<int> reservedShortcuts = QSet<int>() << Qt::SHIFT+Qt::Key_F3;  // workaround to prevent overwriting search backward
 1947     QMenu *menu = getManagedMenu(menuName);
 1948     REQUIRE_RET(menu, nullptr);
 1949     Q_ASSERT(menu->objectName() == menuName);
 1950     Q_ASSERT(data.isEmpty() || data.length()==items.length());
 1951     bool hasData = !data.isEmpty();
 1952 
 1953     QList<QAction *> actions = menu->actions();
 1954     if (!alwaysRecreateMenuItems &&
 1955             actions.count() == items.size() + additionalEntries) {
 1956         //set only title
 1957         for (int i = 0; i < items.size(); i++) {
 1958             Q_ASSERT(actions[i]->objectName() == menuName + "/" + namePrefix + QString::number(i));
 1959             actions[i]->setText(prefixNumber ? QString("%1: %2").arg(i + 1).arg(items[i]) : items[i]);
 1960         }
 1961         if (watchedMenus.contains(menuName))
 1962             emit watchedMenuChanged(menuName);
 1963         return nullptr;
 1964     }
 1965     //recreate
 1966     for (int i = 0; i < actions.count(); i++)
 1967         menu->removeAction(actions[i]); //neccessary or it crashes
 1968     for (int i = 0; i < items.size(); i++) {
 1969         QString id = namePrefix + QString::number(i);
 1970         QString completeId = menu->objectName() + "/" + id;
 1971         Q_ASSERT(completeId == menuName + "/" + namePrefix + QString::number(i));
 1972         QList<QKeySequence> shortcuts;
 1973         if (baseShortCut && i < 10 && !reservedShortcuts.contains(baseShortCut + i))
 1974             shortcuts << baseShortCut + i;
 1975         QAction *act = newOrLostOldManagedAction(menu, id, prefixNumber?QString("%1: %2").arg(i+1).arg(items[i]) : items[i], slotName, shortcuts);
 1976         if (hasData) {
 1977             act->setData(data[i]);
 1978         } else {
 1979             act->setData(i);
 1980         }
 1981     }
 1982     if (watchedMenus.contains(menuName))
 1983         emit watchedMenuChanged(menuName);
 1984     return menu;
 1985 }
 1986 
 1987 void ConfigManager::clearMenu(QMenu *menu){
 1988     QList<QObject*> lst=menu->children();
 1989     foreach(QObject *obj,lst){
 1990         QMenu *m=qobject_cast<QMenu *>(obj);
 1991         if(m){
 1992             clearMenu(m);
 1993             delete m;
 1994         }
 1995     }
 1996     menu->clear();
 1997 }
 1998 
 1999 void ConfigManager::updateUserMacroShortcuts(){
 2000     // if the macro shortcuts have been changed via options, the macros needs to be updated to reflect that shortcuts
 2001     int i=0;
 2002     for(auto &m : completerConfig->userMacros){
 2003         if (!m.document){
 2004             QString mn=m.menu;
 2005             if(!mn.isEmpty()){
 2006                 mn.append('/');
 2007             }
 2008             QString id = "main/macros/"+mn+"tag" + QString::number(i);
 2009             QAction *act = getManagedAction(id);
 2010             if(act){
 2011                 m.setShortcut(act->shortcut().toString());
 2012             }
 2013             i++;
 2014         }
 2015     }
 2016 }
 2017 
 2018 void ConfigManager::updateUserMacroMenu()
 2019 {
 2020     // remove quote replacement from list
 2021     for (int i = 0; i < completerConfig->userMacros.count(); i++) {
 2022         Macro m = completerConfig->userMacros.at(i);
 2023         if (m.name == TXS_AUTO_REPLACE_QUOTE_OPEN || m.name == TXS_AUTO_REPLACE_QUOTE_CLOSE) {
 2024             completerConfig->userMacros.removeAt(i);
 2025             i--;
 2026         }
 2027     }
 2028 
 2029     QMenu *recreatedMenu = getManagedMenu("main/macros");
 2030     clearMenu(recreatedMenu);
 2031     int i=0;
 2032     QMenu *menu=nullptr;
 2033     foreach (const Macro &m, completerConfig->userMacros){
 2034         if (!m.document){
 2035             menu=recreatedMenu;
 2036             QList<QKeySequence> shortcuts;
 2037             if(!m.shortcut().isEmpty()){
 2038                 shortcuts<<QKeySequence(m.shortcut());
 2039             }
 2040             // create/find apropriate submenu
 2041             if(!m.menu.isEmpty()){
 2042                 foreach(const QString &name,m.menu.split("/")){
 2043                     menu=newManagedMenu(menu,name,name);
 2044                 }
 2045             }
 2046 
 2047             QString id = "tag" + QString::number(i);
 2048             QAction *act = newOrLostOldManagedAction(menu, id, m.name , SLOT(insertUserTag()), shortcuts);
 2049             act->setData(i++);
 2050         }
 2051     }
 2052     recreatedMenu->addSeparator();
 2053     newOrLostOldManagedAction(recreatedMenu, "manage", QCoreApplication::translate("Texstudio", "Edit &Macros..."), SLOT(editMacros()));
 2054     // update quote replacement
 2055     const int autoQuoteCount = 10;
 2056     if (replaceQuotes >= 1 && replaceQuotes < autoQuoteCount) {
 2057         static const char *open[autoQuoteCount] = {"",  "``", "\"<", "\"`", "\\og ",  "\">", "\\enquote{", "\xE2\x80\x9C" /*“*/, ",,", "\u201E"};
 2058         static const char *close[autoQuoteCount] = {"", "''", "\">", "\"'", "\\fg{}", "\"<", "}"         , "\xE2\x80\x9D" /*”*/, "''", "\u201D"};
 2059         completerConfig->userMacros.append(Macro(TXS_AUTO_REPLACE_QUOTE_OPEN, QString::fromUtf8(open[replaceQuotes]), "", "(?language:latex)(?<=\\s|[(:]|^)\""));
 2060         completerConfig->userMacros.append(Macro(TXS_AUTO_REPLACE_QUOTE_CLOSE, QString::fromUtf8(close[replaceQuotes]), "", "(?language:latex)(?<=\\S)\""));
 2061     }
 2062 }
 2063 
 2064 QMenu *ConfigManager::newManagedMenu(const QString &id, const QString &text)
 2065 {
 2066     if (!menuParentsBar) qFatal("No menu parent bar!");
 2067     if (!menuParent) qFatal("No menu parent!");
 2068     //check if an old menu with this id exists and update it (for retranslation)
 2069     QMenu *old = menuParent->findChild<QMenu *>(id);
 2070     if (old) {
 2071         old->setTitle(text);
 2072         return old;
 2073     }
 2074     //create new
 2075     QMenu *menu = new QMenu(qobject_cast<QWidget *>(menuParent));
 2076     menuParentsBar->addMenu(menu);
 2077     menu->setTitle(text);
 2078     menu->setObjectName(id);
 2079     managedMenus.append(menu);
 2080     return menu;
 2081 }
 2082 
 2083 QMenu *ConfigManager::newManagedMenu(QWidget *menuParent,QMenuBar *menuParentsBar,const QString &id, const QString &text)
 2084 {
 2085     //if (!menuParentsBar) qFatal("No menu parent bar!");
 2086     if (!menuParent) qFatal("No menu parent!");
 2087     //check if an old menu with this id exists and update it (for retranslation)
 2088     if(!menuParents.contains(menuParent) && menuParentsBar)
 2089         menuParents.append(menuParent);
 2090     QMenu *old = menuParent->findChild<QMenu *>(id);
 2091     if (old) {
 2092         old->setTitle(text);
 2093         return old;
 2094     }
 2095     //create new
 2096     QMenu *menu = new QMenu(qobject_cast<QWidget *>(menuParent));
 2097     if(menuParentsBar){
 2098         menuParentsBar->addMenu(menu);
 2099     }
 2100     menu->setTitle(text);
 2101     menu->setObjectName(id);
 2102     managedMenus.append(menu);
 2103     return menu;
 2104 }
 2105 
 2106 QMenu *ConfigManager::newManagedMenu(QMenu *menu, const QString &id, const QString &text)
 2107 {
 2108     if (!menu) return newManagedMenu(id, text);
 2109     QString completeId = menu->objectName() + "/" + id;
 2110     //check if an old menu with this id exists and update it (for retranslation)
 2111     QMenu *old = menuParent->findChild<QMenu *>(completeId);
 2112     if (old) {
 2113         old->setTitle(text);
 2114         return old;
 2115     }
 2116     //create new
 2117     QMenu *submenu = menu->addMenu(text);
 2118     submenu->setObjectName(completeId);
 2119     return submenu;
 2120 }
 2121 
 2122 QAction *ConfigManager::newManagedAction(QWidget *menu, const QString &id, const QString &text, const char *slotName, const QList<QKeySequence> &shortCuts, const QString &iconFile)
 2123 {
 2124     if (!menuParent) qFatal("No menu parent!");
 2125     REQUIRE_RET(menu, nullptr);
 2126     QString menuId = menu->objectName();
 2127     QString completeId = menu->objectName() + "/" + id;
 2128 
 2129     QAction *old = menuParent->findChild<QAction *>(completeId);
 2130     if (old) {
 2131         old->setText(text);
 2132         if (!iconFile.isEmpty()) old->setIcon(getRealIcon(iconFile));
 2133         if (watchedMenus.contains(menuId))
 2134             emit watchedMenuChanged(menuId);
 2135         //don't set shortcut and slot!
 2136         return old;
 2137     }
 2138 
 2139     QAction *act;
 2140     if (iconFile.isEmpty()) act = new QAction(text, menuParent);
 2141     else act = new QAction(getRealIcon(iconFile), text, menuParent);
 2142 
 2143     act->setObjectName(completeId);
 2144     act->setShortcuts(shortCuts);
 2145 #if (QT_VERSION <= 0x050700) && (defined(Q_OS_MAC))
 2146     // workaround for osx not being able to use alt+key/esc as shortcut
 2147     for (int i = 0; i < shortCuts.size(); i++)
 2148         specialShortcuts.insert(shortCuts[i], act);
 2149 #endif
 2150     if (slotName) {
 2151         connect(act, SIGNAL(triggered()), menuParent, slotName);
 2152         act->setProperty("primarySlot", QString::fromLocal8Bit(slotName));
 2153     }
 2154     menu->addAction(act);
 2155     for (int i = 0; i < shortCuts.size(); i++)
 2156         managedMenuShortcuts.insert(act->objectName() + QString::number(i), shortCuts[i]);
 2157     if (watchedMenus.contains(menuId))
 2158         emit watchedMenuChanged(menuId);
 2159     return act;
 2160 }
 2161 
 2162 QAction *ConfigManager::newManagedAction(QObject *rootMenu,QWidget *menu, const QString &id, const QString &text, QObject *obj,const char *slotName, const QList<QKeySequence> &shortCuts, const QString &iconFile)
 2163 {
 2164     if (!obj) qFatal("No menu parent!");
 2165     REQUIRE_RET(menu, nullptr);
 2166     QString menuId = menu->objectName();
 2167     QString completeId = menu->objectName() + "/" + id;
 2168 
 2169     QAction *old = menu->findChild<QAction *>(completeId);
 2170     if (old) {
 2171         old->setText(text);
 2172         if (!iconFile.isEmpty()) old->setIcon(getRealIcon(iconFile));
 2173         if (watchedMenus.contains(menuId))
 2174             emit watchedMenuChanged(menuId);
 2175         //don't set shortcut and slot!
 2176         return old;
 2177     }
 2178 
 2179     QAction *act;
 2180     if (iconFile.isEmpty()) act = new QAction(text, rootMenu);
 2181     else act = new QAction(getRealIcon(iconFile), text, rootMenu);
 2182 
 2183     act->setObjectName(completeId);
 2184     act->setShortcuts(shortCuts);
 2185 #if (QT_VERSION <= 0x050700) && (defined(Q_OS_MAC))
 2186     // workaround for osx not being able to use alt+key/esc as shortcut
 2187     for (int i = 0; i < shortCuts.size(); i++)
 2188         specialShortcuts.insert(shortCuts[i], act);
 2189 #endif
 2190     if (slotName && !QString(slotName).isEmpty()) {
 2191         if(QString(slotName).contains("(bool)")){
 2192             act->setCheckable(true);
 2193             connect(act, SIGNAL(triggered(bool)), obj, slotName);
 2194         }else{
 2195             connect(act, SIGNAL(triggered()), obj, slotName);
 2196         }
 2197         act->setProperty("primarySlot", QString::fromLocal8Bit(slotName));
 2198 
 2199     }
 2200     menu->addAction(act);
 2201     for (int i = 0; i < shortCuts.size(); i++)
 2202         managedMenuShortcuts.insert(act->objectName() + QString::number(i), shortCuts[i]);
 2203     if (watchedMenus.contains(menuId))
 2204         emit watchedMenuChanged(menuId);
 2205     return act;
 2206 }
 2207 
 2208 //creates a new action or reuses an existing one (an existing one that is currently not in any menu, but has been in the given menu)
 2209 QAction *ConfigManager::newOrLostOldManagedAction(QWidget *menu, const QString &id, const QString &text, const char *slotName, const QList<QKeySequence> &shortCuts, const QString &iconFile)
 2210 {
 2211     QAction *old = menuParent->findChild<QAction *>(menu->objectName() + "/" + id);
 2212     if (!old)
 2213         return newManagedAction(menu, id, text, slotName, shortCuts, iconFile);
 2214     menu->addAction(old);
 2215     old->setText(text);
 2216     old->setShortcuts(shortCuts);
 2217     if (watchedMenus.contains(menu->objectName()))
 2218         emit watchedMenuChanged(menu->objectName());
 2219     return old;
 2220 }
 2221 
 2222 QAction *ConfigManager::newManagedAction(QWidget *menu, const QString &id, QAction *act)
 2223 {
 2224     if (!menuParent) qFatal("No menu parent!");
 2225     QString completeId = menu->objectName() + "/" + id;
 2226 
 2227     QAction *old = menuParent->findChild<QAction *>(completeId);
 2228     if (old)
 2229         return old;
 2230 
 2231 
 2232     act->setObjectName(completeId);
 2233     menu->addAction(act);
 2234     managedMenuShortcuts.insert(act->objectName() + "0", act->shortcut());
 2235     return act;
 2236 }
 2237 
 2238 QAction *ConfigManager::getManagedAction(const QString &id)
 2239 {
 2240     QAction *act = nullptr;
 2241     if(menuParents.count()>0){
 2242         for(int i=0;i<menuParents.count();i++){
 2243             QObject *obj=menuParents.at(i);
 2244             act = obj->findChild<QAction *>(id);
 2245             if(act)
 2246                 break;
 2247         }
 2248     }
 2249     if (act == nullptr) qWarning("Can't find internal action %s", id.toLatin1().data());
 2250     return act;
 2251 }
 2252 
 2253 QList<QAction *>ConfigManager::getManagedActions(const QString &id)
 2254 {
 2255     QList<QAction *>actions;
 2256     QAction *act = nullptr;
 2257     if(menuParents.count()>0){
 2258         for(int i=0;i<menuParents.count();i++){
 2259             QObject *obj=menuParents.at(i);
 2260             act = obj->findChild<QAction *>(id);
 2261             if(act)
 2262                 actions.append(act);
 2263         }
 2264     }
 2265     if (actions.isEmpty()) qWarning("Can't find internal action %s", id.toLatin1().data());
 2266     return actions;
 2267 }
 2268 
 2269 QList<QAction *> ConfigManager::getManagedActions(const QStringList &ids, const QString &commonPrefix)
 2270 {
 2271     QList<QAction *> actions;
 2272     if (!menuParent) {
 2273         qWarning("Can't find internal actions: menuParent missing.");
 2274         return actions;
 2275     }
 2276     foreach (const QString &id, ids) {
 2277         QAction *act = menuParent->findChild<QAction *>(commonPrefix + id);
 2278         if (act == nullptr) qWarning("Can't find internal action %s", id.toLatin1().data());
 2279         else actions << act;
 2280     }
 2281     return actions;
 2282 }
 2283 
 2284 QMenu *ConfigManager::getManagedMenu(const QString &id)
 2285 {
 2286     QMenu *menu = nullptr;
 2287     if (menuParent) menu = menuParent->findChild<QMenu *>(id);
 2288     if (menu == nullptr) qWarning("Can't find internal menu %s", id.toLatin1().data());
 2289     return menu;
 2290 }
 2291 
 2292 void ConfigManager::removeManagedMenus()
 2293 {
 2294     /*foreach (QMenu* menu, managedMenus){
 2295     menu->clear();
 2296     delete menu;
 2297     }
 2298     menuParentsBar->clear();*/
 2299 }
 2300 
 2301 void ConfigManager::triggerManagedAction(const QString &id)
 2302 {
 2303     QAction *act = getManagedAction(id);
 2304     if (act) act->trigger();
 2305 }
 2306 
 2307 void ConfigManager::setupDirectoryStructure()
 2308 {
 2309     QDir base(configBaseDir);
 2310     base.mkpath("completion/user");
 2311     base.mkpath("completion/autogenerated");
 2312     QDir::setSearchPaths("cwl", QStringList() << base.absoluteFilePath("completion/user") << ":/completion" << base.absoluteFilePath("completion/autogenerated"));
 2313 }
 2314 
 2315 // Move existing cwls from configBaseDir to new location at configBaseDir/completion/user or configBaseDir/completion/autogenerated
 2316 void ConfigManager::moveCwls()
 2317 {
 2318     QDir basedir(configBaseDir);
 2319     foreach (const QString &fileName, basedir.entryList(QStringList("*.cwl"), QDir::Files)) {
 2320         QFile f(basedir.filePath(fileName));
 2321         bool autogenerated = false;
 2322         if (f.open(QFile::ReadOnly)) {
 2323             autogenerated = f.readLine().startsWith("# autogenerated");
 2324             f.close();
 2325         }
 2326         if (autogenerated) {
 2327             basedir.rename(fileName, joinPath("completion/autogenerated", fileName));
 2328         } else {
 2329             basedir.rename(fileName, joinPath("completion/user", fileName));
 2330         }
 2331     }
 2332 }
 2333 
 2334 QList<QVariant> parseCommandArguments (const QString &str)
 2335 {
 2336     QString s = str;
 2337     QList<QVariant> result;
 2338     if (str == "()") return result;
 2339     s.remove(0, 1);
 2340     //                            1/-----2---\  /----3----\  /4-/5/6---------6\5\4--4\ 0
 2341     static const QRegExp args("^ *((-? *[0-9]+)|(true|false)|(\"(([^\"]*|\\\\\")*)\")) *,?");
 2342     while (args.indexIn(s) != -1) {
 2343         if (!args.cap(2).isEmpty()) result << args.cap(2).toInt();
 2344         else if (!args.cap(3).isEmpty()) result << (args.cap(3) == "true");
 2345         else if (!args.cap(5).isEmpty()) result << (args.cap(5).replace("\\\"", "\"").replace("\\n", "\n").replace("\\t", "\t").replace("\\\\", "\\"));
 2346         s.remove(0, args.matchedLength());
 2347     }
 2348     return result;
 2349 }
 2350 
 2351 void ConfigManager::connectExtendedSlot(QAction *act, const QString &slot)
 2352 {
 2353     static const char *signal = SIGNAL(triggered());
 2354     if (act->property("primarySlot").isValid())
 2355         disconnect(act, signal, menuParent, act->property("primarySlot").toByteArray().data());
 2356 
 2357     if (slot.startsWith('1') || slot.startsWith('2')) {
 2358         act->setProperty("primarySlot", slot);
 2359         connect(act, signal, menuParent, slot.toLocal8Bit());
 2360         return;
 2361     }
 2362 
 2363     if (slot.endsWith("()") && !slot.contains(':')) {
 2364         QString temp = "1" + slot;
 2365         act->setProperty("primarySlot", temp);
 2366         connect(act, signal, menuParent, temp.toLocal8Bit());
 2367         return;
 2368     }
 2369 
 2370     int argSeparator = slot.indexOf('(');
 2371     REQUIRE(argSeparator >= 0);
 2372 
 2373     QString slotName = slot.mid(0, argSeparator);
 2374     QString args = slot.mid(argSeparator);
 2375     if (slotName.contains(":")) {
 2376         act->setProperty("editorSlot", QVariant());
 2377         act->setProperty("editorViewSlot", QVariant());
 2378         if (slotName.startsWith("editorView:"))
 2379             act->setProperty("editorViewSlot", slotName.mid(strlen("editorView:")));
 2380         else if (slotName.startsWith("editor:"))
 2381             act->setProperty("editorSlot", slotName.mid(strlen("editor:")));
 2382         else REQUIRE(false);
 2383         slotName = SLOT(relayToEditorSlot());
 2384     } else {
 2385         act->setProperty("slot", slotName);
 2386         slotName = SLOT(relayToOwnSlot());
 2387     }
 2388     act->setProperty("primarySlot", slotName);
 2389     connect(act, signal, menuParent, slotName.toLocal8Bit());
 2390     act->setProperty("args", parseCommandArguments(args));
 2391 }
 2392 
 2393 QString prettySlotName(QAction *act)
 2394 {
 2395     QString primary = act->property("primarySlot").toString();
 2396     if (primary.startsWith("1")) primary = primary.mid(1);
 2397     if (primary == "relayToOwnSlot()" || primary == "relayToEditorSlot()") {
 2398         if (primary == "relayToEditorSlot()") {
 2399             if (act->property("editorViewSlot").isValid()) primary = "editorView:" + act->property("editorViewSlot").toString();
 2400             else if (act->property("editorSlot").isValid()) primary = "editor:" + act->property("editorSlot").toString();
 2401         } else primary = act->property("slot").toString();
 2402         primary += "(";
 2403         QList<QVariant> args = act->property("args").value<QList<QVariant> >();
 2404         for (int i = 0; i < args.size(); i++) {
 2405             if (i != 0) primary += ", ";
 2406             if (args[i].type() == QVariant::String) primary += '"';
 2407             primary += args[i].toString();
 2408             if (args[i].type() == QVariant::String) primary += '"';
 2409         }
 2410         primary += ")";
 2411     }
 2412     return primary;
 2413 }
 2414 
 2415 void ConfigManager::modifyMenuContents()
 2416 {
 2417     QStringList ids = manipulatedMenus.keys();
 2418     while (!ids.isEmpty()) modifyMenuContent(ids, ids.first());
 2419     modifyMenuContentsFirstCall = false;
 2420 }
 2421 
 2422 void ConfigManager::modifyMenuContent(QStringList &ids, const QString &id)
 2423 {
 2424     REQUIRE(menuParent);
 2425     int index = ids.indexOf(id);
 2426     if (index < 0) return;
 2427     ids.removeAt(index);
 2428 
 2429     QMap<QString, QVariant>::const_iterator i = manipulatedMenus.find(id);
 2430     if (i == manipulatedMenus.end()) return;
 2431 
 2432 
 2433     QStringList m = i.value().toStringList();
 2434     //qDebug() << id << ": ===> " << m.join(", ");
 2435     QAction *act = menuParent->findChild<QAction *>(id);
 2436     QMenu *mainMenu = nullptr;
 2437     if (!act) {
 2438         mainMenu = menuParent->findChild<QMenu *>(id);
 2439         if (mainMenu) act = mainMenu->menuAction();
 2440     }
 2441     bool  newlyCreated = false;
 2442     if (!act && m.value(3, "") != "") {
 2443         newlyCreated = true;
 2444         QString before = m.value(3);
 2445         modifyMenuContent(ids, before);
 2446         QAction *prevact = nullptr;
 2447         if (!before.endsWith('/'))
 2448             prevact = menuParent->findChild<QAction *>(before);
 2449         else {
 2450             before.chop(1);
 2451             QMenu *temp = menuParent->findChild<QMenu *>(before);
 2452             if (temp) prevact = temp->menuAction();
 2453         }
 2454         QString menuName = before.left(before.lastIndexOf("/"));
 2455         QMenu *menu = menuParent->findChild<QMenu *>(menuName);
 2456         if (!menu) {
 2457             modifyMenuContent(ids, menuName + "/");
 2458             menu = menuParent->findChild<QMenu *>(menuName);
 2459         }
 2460         if (!menu) return;
 2461 
 2462         Q_ASSERT(!prevact || menu->actions().contains(prevact));
 2463         QStringList temp = id.split('/');
 2464         if (temp.size() < 2) return;
 2465         if (id.endsWith('/')) {
 2466             QMenu *newMenu = newManagedMenu(menu, temp[temp.size() - 2], m.first());
 2467             act = newMenu->menuAction();
 2468         } else {
 2469             QString ownId = temp.takeLast();
 2470             QByteArray defSlot = menu->property("defaultSlot").toByteArray();
 2471             if (m.value(4).isEmpty()) {
 2472                 while (defSlot.isEmpty() && temp.size() >= 2) {
 2473                     temp.removeLast();
 2474                     QMenu *tempmenu = menuParent->findChild<QMenu *>(temp.join("/"));
 2475                     if (!tempmenu) continue;
 2476                     defSlot = tempmenu->property("defaultSlot").toByteArray();
 2477                 }
 2478             }
 2479             act = newManagedAction(menu, ownId, m.first(), defSlot.isEmpty() ? nullptr : defSlot.data());
 2480         }
 2481         if  (prevact) {
 2482             menu->removeAction(act);
 2483             menu->insertAction(prevact, act);
 2484         }
 2485     }
 2486     if (!act) return;
 2487     bool visible = !(m.value(2, "visible") == "hidden");
 2488     if (modifyMenuContentsFirstCall && !newlyCreated && visible && act->text() == m.first() && act->data().toString() == m.at(1))
 2489         manipulatedMenus.remove(mainMenu ? mainMenu->objectName() : act->objectName());
 2490     act->setText(m.first());
 2491     act->setData(m.at(1));
 2492     act->setVisible(visible);
 2493     if (!m.value(4).isEmpty()) {
 2494         if (!act->property("originalSlot").isValid())
 2495             act->setProperty("originalSlot", prettySlotName(act));
 2496         connectExtendedSlot(act, m.value(4));
 2497     } else if (act->property("originalSlot").isValid())
 2498         connectExtendedSlot(act, act->property("originalSlot").toString());
 2499 
 2500 }
 2501 
 2502 void ConfigManager::modifyManagedShortcuts(QString startsWith)
 2503 {
 2504     //modify shortcuts
 2505     for (int i = 0; i < managedMenuNewShortcuts.size(); i++) {
 2506         QString id = managedMenuNewShortcuts[i].first;
 2507         if(!startsWith.isEmpty() && !id.startsWith(startsWith))
 2508             continue;
 2509         int num = -1;
 2510         if (managedMenuNewShortcuts[i].first.endsWith("~0")) num = 0;
 2511         else if (managedMenuNewShortcuts[i].first.endsWith("~1")) num = 1;
 2512         else { } //backward compatibility
 2513         if (num != -1) id.chop(2);
 2514         QList<QAction *>actions=getManagedActions(id);
 2515         foreach(QAction *act,actions){
 2516             if (act) setManagedShortCut(act, num, QKeySequence(managedMenuNewShortcuts[i].second));
 2517         }
 2518     }
 2519 }
 2520 
 2521 void ConfigManager::setManagedShortCut(QAction *act, int num, const QKeySequence &ks)
 2522 {
 2523     REQUIRE(act);
 2524 
 2525     QList<QKeySequence> shortcuts = act->shortcuts();
 2526 
 2527     int oldIndex = -1;
 2528     for (int i = 0; i < shortcuts.size(); i++)
 2529         if (shortcuts[i].matches(ks) == QKeySequence::ExactMatch)
 2530             oldIndex = i;
 2531 
 2532     if (oldIndex != -1) {
 2533         if (oldIndex <= num)
 2534             return;
 2535         if (oldIndex > num) //allow to remove the first shortcut, by setting it to the second one
 2536             shortcuts.removeAt(oldIndex);
 2537     }
 2538     if (num < 0) num = 0;
 2539     if (num < shortcuts.size()) shortcuts[num] = ks;
 2540     else shortcuts << ks;
 2541     act->setShortcuts(shortcuts);
 2542 #if (QT_VERSION <= 0x050700) && (defined(Q_OS_MAC))
 2543     // workaround for osx not being able to use alt+key/esc as shortcut
 2544     // remove old shortcuts
 2545     QString name=act->objectName();
 2546     QMutableMapIterator<QKeySequence,QAction *> it(specialShortcuts);
 2547     while (it.hasNext()) {
 2548           it.next();
 2549           if(it.value()==act){
 2550             it.remove();
 2551           }
 2552       }
 2553     // add new ones
 2554     for (int i = 0; i < shortcuts.size(); i++)
 2555         specialShortcuts.insert(shortcuts[i], act);
 2556 #endif
 2557 }
 2558 
 2559 void ConfigManager::loadManagedMenu(QMenu *parent, const QDomElement &f)
 2560 {
 2561     QMenu *menu = newManagedMenu(parent, f.attributes().namedItem("id").nodeValue(), tr(qPrintable(f.attributes().namedItem("text").nodeValue())));
 2562     QDomNodeList children = f.childNodes();
 2563     QLocale::Language keyboardLanguage = getKeyboardLanguage();
 2564     for (int i = 0; i < children.count(); i++) {
 2565         QDomElement c = children.at(i).toElement();
 2566         if (c.nodeName() == "menu") loadManagedMenu(menu, c);
 2567         else if (c.nodeName() == "insert" || c.nodeName() == "action") {
 2568             QDomNamedNodeMap  att = c.attributes();
 2569             QByteArray ba;
 2570             const char *slotfunc;
 2571             if (c.nodeName() == "insert") slotfunc = SLOT(insertFromAction());
 2572             else {
 2573                 ba = att.namedItem("slot").nodeValue().toLocal8Bit();
 2574                 slotfunc = ba.data();
 2575             }
 2576             QKeySequence shortcut(att.namedItem("shortcut").nodeValue());
 2577             if (keyboardLanguage == QLocale::Czech) {
 2578                 shortcut = filterLocaleShortcut(shortcut);
 2579             }
 2580             QAction *act = newManagedAction(menu,
 2581                                             att.namedItem("id").nodeValue(),
 2582                                             tr(qPrintable(att.namedItem("text").nodeValue())), slotfunc,
 2583                                             QList<QKeySequence>() << shortcut,
 2584                                             att.namedItem("icon").nodeValue());
 2585             act->setWhatsThis(att.namedItem("info").nodeValue());
 2586             act->setStatusTip(att.namedItem("info").nodeValue());
 2587             act->setData(att.namedItem("insert").nodeValue());
 2588         } else if (c.nodeName() == "separator") menu->addSeparator();
 2589     }
 2590 }
 2591 
 2592 void ConfigManager::loadManagedMenus(const QString &f)
 2593 {
 2594     QFile settings(f);
 2595 
 2596     if (settings.open(QFile::ReadOnly | QFile::Text)) {
 2597         QDomDocument doc;
 2598         doc.setContent(&settings);
 2599 
 2600         QDomNodeList f = doc.documentElement().childNodes();
 2601 
 2602         for (int i = 0; i < f.count(); i++)
 2603             if (f.at(i).nodeName() == "menu")
 2604                 loadManagedMenu(nullptr, f.at(i).toElement());
 2605     }
 2606 }
 2607 
 2608 void ConfigManager::managedMenuToTreeWidget(QTreeWidgetItem *parent, QMenu *menu)
 2609 {
 2610     if (!menu) return;
 2611     QTreeWidgetItem *menuitem = new QTreeWidgetItem(parent, QStringList(menu->title().replace("&", "")));
 2612     if (menu->objectName().count("/") <= 2) menuitem->setExpanded(true);
 2613     QList<QAction *> acts = menu->actions();
 2614     for (int i = 0; i < acts.size(); i++)
 2615         if (acts[i]->menu()) managedMenuToTreeWidget(menuitem, acts[i]->menu());
 2616         else {
 2617             QTreeWidgetItem *twi = new QTreeWidgetItem(menuitem, QStringList() << acts[i]->text().replace("&", "")
 2618                     << managedMenuShortcuts.value(acts[i]->objectName() + "0", QKeySequence()).toString()
 2619                     << acts[i]->shortcut().toString(SHORTCUT_FORMAT));
 2620             if (!acts[i]->isSeparator()) {
 2621                 twi->setIcon(0, acts[i]->icon());
 2622                 twi->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled);
 2623             } else {
 2624                 twi->setIcon(0, QIcon(":/images/separator.png"));
 2625             }
 2626             twi->setData(0, Qt::UserRole, acts[i]->objectName());
 2627             if (acts[i]->shortcuts().size() > 1) twi->setText(3, acts[i]->shortcuts()[1].toString(SHORTCUT_FORMAT));
 2628         }
 2629 }
 2630 
 2631 void ConfigManager::treeWidgetToManagedMenuTo(QTreeWidgetItem *item)
 2632 {
 2633     if (item->childCount() > 0) {
 2634         for (int i = 0; i < item->childCount(); i++)
 2635             treeWidgetToManagedMenuTo(item->child(i));
 2636     } else {
 2637         QString id = item->data(0, Qt::UserRole).toString();
 2638         if (id == "") return;
 2639         QAction *act = getManagedAction(id);
 2640         if (act) {
 2641             act->setShortcuts(QList<QKeySequence>());
 2642             for (int num = 0; num < 2; num++) {
 2643                 QString mseq = item->text(2 + num);
 2644                 QString ns = QString::number(num);
 2645                 if (mseq == tr("<none>")) mseq = "";
 2646                 if (mseq == tr("<default>")) mseq = managedMenuShortcuts.value(act->objectName() + ns, QKeySequence()).toString(SHORTCUT_FORMAT);
 2647                 QKeySequence sc(mseq);
 2648                 setManagedShortCut(act, num, sc);
 2649                 if (sc != managedMenuShortcuts.value(act->objectName() + ns, QKeySequence()))
 2650                     managedMenuNewShortcuts.append(QPair<QString, QString> (id + "~" + ns, sc.toString(QKeySequence ::PortableText)));
 2651             }
 2652             //todo: what is this?
 2653             if (id == "main/view/outputview") { // special handling for outputview because of "esc"-key
 2654                 if (item->text(2).isEmpty() || (act->shortcut().matches(Qt::Key_Escape) == QKeySequence::ExactMatch)) act->setShortcutContext(Qt::WidgetShortcut);
 2655                 else act->setShortcutContext(Qt::WindowShortcut);
 2656             }
 2657         }
 2658     }
 2659 }
 2660 
 2661 void ConfigManager::loadTranslations(QString locale)
 2662 {
 2663     if (locale == "") {
 2664         locale = QString(QLocale::system().name()).left(2);
 2665         if (locale.length() < 2) locale = "en";
 2666     }
 2667     QString txsTranslationFile = findResourceFile("texstudio_" + locale + ".qm");
 2668 
 2669     if (txsTranslationFile.isEmpty()) {
 2670         txsTranslationFile = findResourceFile("translation/texstudio_" + locale + ".qm");
 2671     }
 2672     appTranslator->load(txsTranslationFile);
 2673     basicTranslator->load(findResourceFile("qt_" + locale + ".qm"));
 2674     //}
 2675 }
 2676 /*!
 2677  * \brief set txs InterfaceStyle
 2678  * Fall-back to default style if none is defined
 2679  * Also detect whether light- or dark-mode is used by checking the colour of text (white -> dark background -> dark mode)
 2680  */
 2681 void ConfigManager::setInterfaceStyle()
 2682 {
 2683     //style is controlled by the properties interfaceStyle, modernStyle and useTexmakerPalette
 2684     //default values are read from systemPalette and defaultStyleName
 2685 
 2686     QString newStyle = interfaceStyle != "" ? interfaceStyle : defaultStyleName;
 2687 
 2688     bool handled=false;
 2689 #ifdef ADWAITA
 2690     if(newStyle=="Adwaita (txs)"){
 2691         QApplication::setStyle(new Adwaita::Style(false));
 2692         handled=true;
 2693     }
 2694     if(newStyle=="Adwaita Dark (txs)"){
 2695         QApplication::setStyle(new Adwaita::Style(true));
 2696         handled=true;
 2697     }
 2698 #endif
 2699     if(!handled){
 2700         if (!QStyleFactory::keys().contains(newStyle)) newStyle = defaultStyleName;
 2701 
 2702         if (modernStyle) {
 2703             ManhattanStyle *style = new ManhattanStyle(newStyle);
 2704             if (style->isValid()) QApplication::setStyle(style);
 2705         } else
 2706             QApplication::setStyle(newStyle);
 2707     }
 2708 
 2709 
 2710     // dark mode is derived from system text color (very light => dark mode)
 2711     // however if system colors are ignored, only style manhattan - dark results in dark mode
 2712     // do the check after setting style, as the style can also activate a dark mode
 2713     if(useTexmakerPalette){
 2714         darkMode=modernStyle>1;
 2715     }else{
 2716         darkMode=systemUsesDarkMode();
 2717     }
 2718 
 2719     QPalette pal = systemPalette;
 2720     if (useTexmakerPalette) { //modify palette like texmaker does it
 2721         if(darkMode){
 2722             pal.setColor(QPalette::Active, QPalette::Highlight, QColor("#4490d8"));
 2723             pal.setColor(QPalette::Inactive, QPalette::Highlight, QColor("#4490d8"));
 2724             pal.setColor(QPalette::Disabled, QPalette::Highlight, QColor("#4490d8"));
 2725 
 2726             pal.setColor(QPalette::Active, QPalette::HighlightedText, QColor("#FFFFFF"));
 2727             pal.setColor(QPalette::Inactive, QPalette::HighlightedText, QColor("#ffffff"));
 2728             pal.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor("#ffffff"));
 2729 
 2730             pal.setColor(QPalette::Active, QPalette::Base, QColor("#303030"));
 2731             pal.setColor(QPalette::Inactive, QPalette::Base, QColor("#303030"));
 2732             pal.setColor(QPalette::Disabled, QPalette::Base, QColor("#303030"));
 2733 
 2734             pal.setColor(QPalette::Active, QPalette::WindowText, QColor("#e0e0e0"));
 2735             pal.setColor(QPalette::Inactive, QPalette::WindowText, QColor("#e0e0e0"));
 2736             pal.setColor(QPalette::Disabled, QPalette::WindowText, QColor("#e0e0e0"));
 2737 
 2738             pal.setColor( QPalette::Active, QPalette::Text, QColor("#ffffff") );
 2739             pal.setColor( QPalette::Inactive, QPalette::Text, QColor("#ffffff") );
 2740             pal.setColor( QPalette::Disabled, QPalette::Text, QColor("#ffffff") );
 2741 
 2742             pal.setColor(QPalette::Active, QPalette::ButtonText, QColor("#f0f0f0"));
 2743             pal.setColor(QPalette::Inactive, QPalette::ButtonText, QColor("#ffffff"));
 2744             pal.setColor(QPalette::Disabled, QPalette::ButtonText, QColor("#ffffff"));
 2745 
 2746             pal.setColor( QPalette::ToolTipText, QColor("#ffffff") );
 2747 
 2748             pal.setColor( QPalette::ToolTipBase, QColor("#002020") );
 2749 
 2750             pal.setColor( QPalette::Active, QPalette::Window, QColor("#000000") );
 2751             pal.setColor( QPalette::Inactive, QPalette::Window, QColor("#000000") );
 2752             pal.setColor( QPalette::Disabled, QPalette::Window, QColor("#000000") );
 2753 
 2754             pal.setColor( QPalette::Active, QPalette::Button, QColor("#2a2a2a") );
 2755             pal.setColor( QPalette::Inactive, QPalette::Button, QColor("#2a2a2a") );
 2756             pal.setColor( QPalette::Disabled, QPalette::Button, QColor("#2a2a2a") );
 2757 
 2758         }else{
 2759             pal.setColor(QPalette::Active, QPalette::Highlight, QColor("#4490d8"));
 2760             pal.setColor(QPalette::Inactive, QPalette::Highlight, QColor("#4490d8"));
 2761             pal.setColor(QPalette::Disabled, QPalette::Highlight, QColor("#4490d8"));
 2762 
 2763             pal.setColor(QPalette::Active, QPalette::HighlightedText, QColor("#ffffff"));
 2764             pal.setColor(QPalette::Inactive, QPalette::HighlightedText, QColor("#ffffff"));
 2765             pal.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor("#ffffff"));
 2766 
 2767             pal.setColor(QPalette::Active, QPalette::Base, QColor("#ffffff"));
 2768             pal.setColor(QPalette::Inactive, QPalette::Base, QColor("#ffffff"));
 2769             pal.setColor(QPalette::Disabled, QPalette::Base, QColor("#ffffff"));
 2770 
 2771             pal.setColor(QPalette::Active, QPalette::WindowText, QColor("#000000"));
 2772             pal.setColor(QPalette::Inactive, QPalette::WindowText, QColor("#000000"));
 2773             pal.setColor(QPalette::Disabled, QPalette::WindowText, QColor("#000000"));
 2774 
 2775             pal.setColor( QPalette::Active, QPalette::Text, QColor("#000000") );
 2776             pal.setColor( QPalette::Inactive, QPalette::Text, QColor("#000000") );
 2777             pal.setColor( QPalette::Disabled, QPalette::Text, QColor("#000000") );
 2778 
 2779             pal.setColor(QPalette::Active, QPalette::ButtonText, QColor("#000000"));
 2780             pal.setColor(QPalette::Inactive, QPalette::ButtonText, QColor("#000000"));
 2781             pal.setColor(QPalette::Disabled, QPalette::ButtonText, QColor("#000000"));
 2782 
 2783             pal.setColor( QPalette::ToolTipText, QColor("#000000") );
 2784 
 2785             pal.setColor( QPalette::ToolTipBase, QColor("#FFFFDC") );
 2786 
 2787             if (x11desktop_env() == 4) {
 2788                 pal.setColor(QPalette::Active, QPalette::Window, QColor("#eae9e9"));
 2789                 pal.setColor(QPalette::Inactive, QPalette::Window, QColor("#eae9e9"));
 2790                 pal.setColor(QPalette::Disabled, QPalette::Window, QColor("#eae9e9"));
 2791 
 2792                 pal.setColor(QPalette::Active, QPalette::Button, QColor("#eae9e9"));
 2793                 pal.setColor(QPalette::Inactive, QPalette::Button, QColor("#eae9e9"));
 2794                 pal.setColor(QPalette::Disabled, QPalette::Button, QColor("#eae9e9"));
 2795             } else {
 2796                 pal.setColor( QPalette::Active, QPalette::Window, QColor("#f6f3eb") );
 2797                 pal.setColor( QPalette::Inactive, QPalette::Window, QColor("#f6f3eb") );
 2798                 pal.setColor( QPalette::Disabled, QPalette::Window, QColor("#f6f3eb") );
 2799 
 2800                 pal.setColor( QPalette::Active, QPalette::Button, QColor("#f6f3eb") );
 2801                 pal.setColor( QPalette::Inactive, QPalette::Button, QColor("#f6f3eb") );
 2802                 pal.setColor( QPalette::Disabled, QPalette::Button, QColor("#f6f3eb") );
 2803 
 2804             }
 2805         }
 2806     }
 2807     QApplication::setPalette(pal);
 2808 }
 2809 
 2810 /*! GridLayout::rowCount() apparently never decreases. Instead there may be empty rows at the end
 2811     Therefore we manually keep track of the actual count of command rows
 2812     */
 2813 int ConfigManager::userCommandRowCount()
 2814 {
 2815     int index = userGridLayout->indexOf(userGridLayout->property(PROPERTY_ADD_BUTTON).value<QPushButton *>());
 2816     if (index < 0) return 0;
 2817     int r, unused;
 2818     userGridLayout->getItemPosition(index, &r, &unused, &unused, &unused);
 2819     return r;
 2820 }
 2821 
 2822 void ConfigManager::addCommandRow(QGridLayout *gl, const CommandInfo &cmd, int row)
 2823 {
 2824     static QStringList simpleMetaOptions = QStringList() << "quick" << "compile" << "view" << "view-pdf" << "bibliography";
 2825     QWidget *parent = gl->parentWidget();
 2826 
 2827     // ID
 2828     QWidget *nameWidget;
 2829     if (cmd.user) nameWidget = new QLineEdit(cmd.id + ":" + cmd.displayName, parent);
 2830     else {
 2831         QString lbl = qApp->translate("BuildManager", qPrintable(cmd.displayName));
 2832         nameWidget = new QLabel(lbl, parent);
 2833         if (configShowAdvancedOptions) nameWidget->setToolTip("ID: txs:///" + cmd.id);
 2834         nameWidget->setProperty(PROPERTY_COMMAND_ID, cmd.id);
 2835     }
 2836     nameWidget->setProperty(PROPERTY_WIDGET_TYPE, CG_ID);
 2837 
 2838 
 2839     // cmd Widget
 2840     QWidget *cmdWidget;
 2841     if (cmd.metaSuggestionList.isEmpty()) {
 2842         cmdWidget = new QLineEdit(cmd.getPrettyCommand(), parent);
 2843         if (cmd.id == "pdflatex") pdflatexEdit = qobject_cast<QLineEdit *>(cmdWidget);
 2844     } else {
 2845         cmdWidget = new QComboBox(parent);
 2846         cmdWidget->setObjectName(cmd.id);
 2847         if (!configShowAdvancedOptions && simpleMetaOptions.contains(cmd.id) && cmd.metaSuggestionList.contains(cmd.getPrettyCommand())) {
 2848             foreach (QString elem, cmd.simpleDescriptionList) {
 2849                 elem = qApp->translate("BuildManager", qPrintable(elem));
 2850                 static_cast<QComboBox *>(cmdWidget)->addItem(elem);
 2851             }
 2852             static_cast<QComboBox *>(cmdWidget)->setEditable(false);
 2853 #ifndef QT_NO_DEBUG
 2854             int i = cmd.metaSuggestionList.indexOf(cmd.getPrettyCommand());
 2855             Q_ASSERT(i >= 0);
 2856 #endif
 2857             //static_cast<QComboBox*>(w)->setEditText();
 2858         } else {
 2859             static_cast<QComboBox *>(cmdWidget)->addItems(cmd.metaSuggestionList);
 2860             static_cast<QComboBox *>(cmdWidget)->setEditable(true);
 2861             static_cast<QComboBox *>(cmdWidget)->setEditText(cmd.getPrettyCommand());
 2862         }
 2863 
 2864         int index = cmd.metaSuggestionList.indexOf(cmd.commandLine);
 2865         if (index >= 0) static_cast<QComboBox *>(cmdWidget)->setCurrentIndex(index);
 2866     }
 2867     assignNameWidget(cmdWidget, nameWidget);
 2868     cmdWidget->setProperty(PROPERTY_WIDGET_TYPE, CG_CMD);
 2869 
 2870     QList<QPushButton *> buttons;
 2871 
 2872     QPushButton *pb;
 2873     if (cmd.user || cmd.meta) {
 2874         pb = new QPushButton(getRealIcon("configure"), QString(), parent);
 2875         pb->setToolTip(tr("Configure"));
 2876         pb->setProperty(PROPERTY_WIDGET_TYPE, CG_CONFIG);
 2877         connect(pb, SIGNAL(clicked()), SLOT(editCommand()));
 2878         buttons << pb;
 2879     }
 2880 
 2881     pb = new QPushButton(getRealIcon("document-open"), "", parent);
 2882     pb->setToolTip(tr("Select Program"));
 2883     pb->setProperty(PROPERTY_WIDGET_TYPE, CG_PROGRAM);
 2884     connect(pb, SIGNAL(clicked()), SLOT(browseCommand()));
 2885     buttons << pb;
 2886 
 2887     if (!cmd.user && cmd.metaSuggestionList.isEmpty()) {
 2888         pb = new QPushButton(getRealIcon("edit-undo"), "", parent);
 2889         pb->setToolTip(tr("Restore Default"));
 2890         pb->setProperty(PROPERTY_WIDGET_TYPE, CG_RESET);
 2891         connect(pb, SIGNAL(clicked()), SLOT(undoCommand()));
 2892         buttons << pb;
 2893     }
 2894     if (cmd.user) {
 2895         pb = new QPushButton(getRealIcon("list-remove"), "", parent);
 2896         pb->setProperty(PROPERTY_WIDGET_TYPE, CG_DEL);
 2897         connect(pb, SIGNAL(clicked()), SLOT(removeCommand()));
 2898         buttons << pb;
 2899         pb = new QPushButton(getRealIcon("up"), "", parent);
 2900         if (row == 0) pb->setEnabled(false);
 2901         pb->setProperty(PROPERTY_WIDGET_TYPE, CG_MOVEUP);
 2902         connect(pb, SIGNAL(clicked()), SLOT(moveUpCommand()));
 2903         buttons << pb;
 2904         pb = new QPushButton(getRealIcon("down"), "", parent);
 2905         pb->setProperty(PROPERTY_WIDGET_TYPE, CG_MOVEDOWN);
 2906         connect(pb, SIGNAL(clicked()), SLOT(moveDownCommand()));
 2907         buttons << pb;
 2908     }
 2909     bool advanced = cmd.meta && !simpleMetaOptions.contains(cmd.id);
 2910     QList<QWidget *> temp;
 2911     temp << nameWidget << cmdWidget;
 2912     foreach (QWidget * w, buttons) temp << w;
 2913     foreach (QWidget *x, temp) {
 2914         x->setMinimumHeight(x->sizeHint().height());
 2915         if (x != cmdWidget) x->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
 2916         advanced |= (cmd.user && buttons.indexOf(static_cast<QPushButton *>(x)) >= 3);
 2917         x->setProperty("advancedOption", advanced);
 2918         if (advanced && !configShowAdvancedOptions) x->setVisible(false);
 2919     }
 2920     cmdWidget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
 2921     gl->setRowStretch(row, 1);
 2922     gl->addWidget(nameWidget, row, CG_ID);
 2923     int off = 1;
 2924 
 2925     // rerun button
 2926     static QStringList rerunnable = QStringList() << "latex" << "pdflatex" << "lualatex" << "xelatex" << "quick" << "compile" << "ps-chain" << "dvi-chain" << "pdf-chain" << "dvi-pdf-chain" << "dvi-ps-pdf-chain" << "asy-dvi-chain" << "asy-pdf-chain" << "pre-compile" << "internal-pre-compile" << "recompile-bibliography";
 2927     if (cmd.user || rerunnable.contains(cmd.id)) {
 2928         QIcon icon;
 2929         pb = new QPushButton();
 2930         //icon=getRealIcon("repeat-compile");
 2931         icon.addFile(getRealIconFile("repeat-compile"), QSize(), QIcon::Normal, QIcon::On);
 2932         icon.addFile(getRealIconFile("repeat-compile"), QSize(), QIcon::Active, QIcon::On);
 2933         icon.addFile(getRealIconFile("repeat-compile-off"), QSize(), QIcon::Normal, QIcon::Off);
 2934         icon.addFile(getRealIconFile("repeat-compile-off"), QSize(), QIcon::Active, QIcon::Off);
 2935         pb->setIcon(icon);
 2936         pb->setToolTip(tr("Repeat contained compilation commands"));
 2937         pb->setCheckable(true);
 2938         pb->setChecked(cmd.rerunCompiler);
 2939         assignNameWidget(pb, nameWidget);
 2940         pb->setProperty(PROPERTY_WIDGET_TYPE, CG_RERUN);
 2941         pb->setProperty("advancedOption", true);
 2942         if (!configShowAdvancedOptions) pb->setVisible(false);
 2943         gl->addWidget(pb, row, CG_RERUN, 1, 1);
 2944         if (!cmd.user)
 2945             rerunButtons << pb;
 2946     }
 2947 
 2948     gl->addWidget(cmdWidget, row, 1 + off, 1, 1);
 2949     for (int i = 0; i < buttons.size(); i++) {
 2950         gl->addWidget(buttons[i], row, 2 + off + i, 1, 1);
 2951         buttons[i]->setProperty(PROPERTY_ASSOCIATED_INPUT, QVariant::fromValue<QWidget *>(cmdWidget));
 2952         assignNameWidget(buttons[i], nameWidget);
 2953     }
 2954 
 2955     QPushButton *addButton = gl->property(PROPERTY_ADD_BUTTON).value<QPushButton *>();
 2956     if (cmd.user && addButton)
 2957         QWidget::setTabOrder(buttons.last(), addButton);
 2958 
 2959     if (!cmd.user)
 2960         commandInputs << cmdWidget;
 2961 }
 2962 
 2963 void ConfigManager::createCommandList(QGroupBox *box, const QStringList &order, bool user, bool meta)
 2964 {
 2965     QVBoxLayout *verticalLayout = new QVBoxLayout(box);
 2966     QScrollArea *scrollAreaCommands = new QScrollArea(box);
 2967     scrollAreaCommands->setWidgetResizable(true);
 2968     UtilsUi::enableTouchScrolling(scrollAreaCommands);
 2969 
 2970     QWidget *scrollAreaWidgetContents = new QWidget();
 2971     QGridLayout *gl = new QGridLayout(scrollAreaWidgetContents);
 2972     gl->setVerticalSpacing(2);
 2973     int row = 0;
 2974     foreach (const QString &id, order) {
 2975         const CommandInfo &cmd = tempCommands.value(id);
 2976         //bool isMeta = !cmd.metaSuggestionList.isEmpty();
 2977         bool isMeta = cmd.meta;
 2978         if (user != cmd.user) continue;
 2979         if (!user && (isMeta != meta)) continue;
 2980         addCommandRow(gl, cmd, row);
 2981         row++;
 2982     }
 2983     if (user) {
 2984         QPushButton *addButton = new QPushButton(getRealIcon("list-add"), tr("Add"), box);
 2985         addButton->setProperty(PROPERTY_WIDGET_TYPE, CG_ADD);
 2986         addButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
 2987         connect(addButton, SIGNAL(clicked()), SLOT(addCommand()));
 2988         gl->addWidget(addButton, row, 0, 1, 1, Qt::AlignLeft | Qt::AlignTop);
 2989         userGridLayout = gl;
 2990         setLastRowMoveDownEnable(false);
 2991         gl->setProperty(PROPERTY_ADD_BUTTON, QVariant::fromValue<QPushButton *>(addButton));
 2992     }
 2993     //gl->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), row, CG_RERUN);
 2994 
 2995     scrollAreaCommands->setWidget(scrollAreaWidgetContents);
 2996     verticalLayout->addWidget(scrollAreaCommands);
 2997 }
 2998 
 2999 void ConfigManager::setFirstRowMoveUpEnable(bool enable)
 3000 {
 3001     REQUIRE(userGridLayout);
 3002     int rows = userCommandRowCount();
 3003     for (int i = 0; i < rows; i++) {
 3004         QLayoutItem *li = userGridLayout->itemAtPosition(i, 6);
 3005         if (li && li->widget()) {
 3006             li->widget()->setEnabled(enable);
 3007             break;
 3008         }
 3009     }
 3010 }
 3011 
 3012 void ConfigManager::setLastRowMoveDownEnable(bool enable)
 3013 {
 3014     REQUIRE(userGridLayout);
 3015     int rows = userCommandRowCount();
 3016     for (int i = rows - 1; i >= 0; i--) {
 3017         QLayoutItem *li = userGridLayout->itemAtPosition(i, 7);
 3018         if (li && li->widget()) {
 3019             li->widget()->setEnabled(enable);
 3020             break;
 3021         }
 3022     }
 3023 }
 3024 
 3025 void ConfigManager::browseCommand()
 3026 {
 3027     QPushButton *pb = qobject_cast<QPushButton *> (sender());
 3028     REQUIRE(pb);
 3029     QWidget *w = pb->property(PROPERTY_ASSOCIATED_INPUT).value<QWidget *>();
 3030     REQUIRE(w);
 3031     QString old = getText(w);
 3032     QString path = old;
 3033     if (old.contains("|")) {
 3034         path = old.split("|").last().trimmed();
 3035         if (path.isEmpty()) path = old.split("|").first().trimmed();
 3036     }
 3037     path = path.trimmed();
 3038     if (path.contains(' ')) path.truncate(path.indexOf(' '));
 3039     if (!path.contains('/') && !path.contains('\\')) {//no absolute path
 3040         path = BuildManager::findFileInPath(path);
 3041         if (path == "") path = QDir::rootPath(); //command not found, where could it be?
 3042     } else {
 3043         //opendialog doesn't like quotation like "C:\program files\..."
 3044         if (path.startsWith('"')) path = path.remove(0, 1);
 3045         if (path.endsWith('"')) path.chop(1);
 3046     }
 3047     QString location = FileDialog::getOpenFileName(nullptr, tr("Browse program"), path, "Program (*)", nullptr, QFileDialog::DontResolveSymlinks);
 3048     if (!location.isEmpty()) {
 3049         location.replace(QString("\\"), QString("/"));
 3050         location = "\"" + location + "\" " + tempCommands.value(getCmdID(w)).defaultArgs;
 3051         if (old.contains("|")) setText(w, old + (old.trimmed().endsWith("|") ? "" : " | ") + location);
 3052         else setText(w, location);
 3053     }
 3054 }
 3055 
 3056 void ConfigManager::undoCommand()
 3057 {
 3058     QPushButton *pb = qobject_cast<QPushButton *> (sender());
 3059     REQUIRE(pb);
 3060     QWidget *w = pb->property(PROPERTY_ASSOCIATED_INPUT).value<QWidget *>();
 3061     REQUIRE(w);
 3062     setText(w, tempCommands.value(getCmdID(w)).guessCommandLine());
 3063 }
 3064 
 3065 void ConfigManager::editCommand()
 3066 {
 3067     QPushButton *pb = qobject_cast<QPushButton *> (sender());
 3068     REQUIRE(pb);
 3069     QWidget *w = pb->property(PROPERTY_ASSOCIATED_INPUT).value<QWidget *>();
 3070     REQUIRE(w);
 3071     setText(w, buildManager->editCommandList(getText(w), getCmdID(w)));
 3072 }
 3073 
 3074 void ConfigManager::addCommand()
 3075 {
 3076     QPushButton *self = qobject_cast<QPushButton *>(sender());
 3077     REQUIRE(self);
 3078     REQUIRE(userGridLayout);
 3079     CommandInfo cmd;
 3080 
 3081     // make sure the ID is unique
 3082     QStringList currentUserCmdIDs;
 3083     for (int i = 0; i < userGridLayout->count(); i++) {
 3084         QWidget *nameWidget = userGridLayout->itemAt(i)->widget();
 3085         if (!nameWidget || !(nameWidget->property(PROPERTY_WIDGET_TYPE).toInt() == CG_ID)) continue;
 3086         currentUserCmdIDs << getCmdID(nameWidget);
 3087     }
 3088     for (int i = 0; i < currentUserCmdIDs.count() + 1; i++) {
 3089         QString id = QString("user%1").arg(i);
 3090         if (!currentUserCmdIDs.contains(id)) {
 3091             cmd.id = id;
 3092             break;
 3093         }
 3094     }
 3095 
 3096     cmd.user = true;
 3097     setLastRowMoveDownEnable(true);
 3098 
 3099     int row, c, dr, dc;
 3100     userGridLayout->getItemPosition(userGridLayout->count() - 1, &row, &c, &dr, &dc);
 3101 
 3102     userGridLayout->removeWidget(self);
 3103     addCommandRow(userGridLayout, cmd, row);
 3104     userGridLayout->addWidget(self, row + 1, 0);
 3105     setLastRowMoveDownEnable(false);
 3106 }
 3107 
 3108 void ConfigManager::removeCommand()
 3109 {
 3110     // deleting widget from within the grid causes layout problems because of empty rows
 3111     // because we don't want to repopulate the whole table, we move the command to delete to the last row and delete it there
 3112     // using moveCommand is not best in performance, but easy and safe and we're not performance critical here anyway
 3113     QPushButton *self = qobject_cast<QPushButton *>(sender());
 3114     REQUIRE(self);
 3115     REQUIRE(userGridLayout);
 3116 
 3117     userGridLayout->parentWidget()->setUpdatesEnabled(false);
 3118 
 3119     int row, col, unused;
 3120     userGridLayout->getItemPosition(userGridLayout->indexOf(self), &row, &col, &unused, &unused );
 3121     REQUIRE(row >= 0);
 3122 
 3123     int rows = userCommandRowCount();
 3124     for (int r = row; r < rows - 1; r++) {
 3125         moveCommand(+1, r);
 3126     }
 3127 
 3128     QWidget *nameWidget = userGridLayout->itemAtPosition(rows - 1, 0)->widget();
 3129     QString cmdID(getCmdID(nameWidget));
 3130 
 3131     int index = userGridLayout->indexOf(nameWidget);
 3132     while (index + 1 < userGridLayout->count()) {
 3133         QLayoutItem *li = userGridLayout->itemAt(index + 1);
 3134         QWidget *w = li->widget();
 3135         if (w && nameWidget != li->widget()->property(PROPERTY_NAME_WIDGET).value<QWidget *>()) break;
 3136         userGridLayout->removeItem(li);
 3137         delete w;
 3138         delete li;
 3139     }
 3140     delete userGridLayout->takeAt(index);
 3141     delete nameWidget;
 3142 
 3143     // add button and spacer
 3144     QPushButton *addButton = userGridLayout->property(PROPERTY_ADD_BUTTON).value<QPushButton *>();
 3145     userGridLayout->removeWidget(addButton);
 3146     userGridLayout->addWidget(addButton, rows - 1, 0, 1, 1);
 3147     /*col = 0;
 3148     for (int i=index; i<userGridLayout->count(); i++) {
 3149         QLayoutItem *li = userGridLayout->takeAt(index);
 3150         qDebug() << li->widget();
 3151         userGridLayout->addItem(li, rows-1, col++, 1, 1);
 3152     }*/
 3153     qDebug() << rows << userCommandRowCount();
 3154 
 3155     /*for (int i=0; i<userGridLayout->count(); i++) {
 3156         int r, c, unused;
 3157         userGridLayout->getItemPosition(i, &r, &c, &unused, &unused);
 3158         qDebug() << i << r << c;
 3159     }*/
 3160 
 3161     setLastRowMoveDownEnable(false);
 3162     setFirstRowMoveUpEnable(false);
 3163 
 3164     userGridLayout->parentWidget()->setUpdatesEnabled(true);
 3165 }
 3166 
 3167 void exchangeProperties(QWidget *w, QWidget *w2)
 3168 {
 3169     if (!w || !w2) return;
 3170 
 3171     QLineEdit *le;
 3172     QComboBox *cb;
 3173     QPushButton *pb;
 3174     if ( (le = qobject_cast<QLineEdit *>(w)) ) {
 3175         QLineEdit *le2 = qobject_cast<QLineEdit *>(w2);
 3176         QString s = le->text();
 3177         le->setText(le2->text());
 3178         le2->setText(s);
 3179     } else if ( (cb = qobject_cast<QComboBox *>(w)) ) {
 3180         QComboBox *cb2 = qobject_cast<QComboBox *>(w2);
 3181         QString cbCurrent = cb->currentText();
 3182         QStringList cbTexts;
 3183         for (int i = 0; i < cb->count(); i++) {
 3184             cbTexts << cb->itemText(i);
 3185         }
 3186         cb->clear();
 3187         for (int i = 0; i < cb2->count(); i++) {
 3188             cb->addItem(cb2->itemText(i));
 3189         }
 3190         cb->setEditText(cb2->currentText());
 3191         cb2->clear();
 3192         cb2->addItems(cbTexts);
 3193         cb2->setEditText(cbCurrent);
 3194     } else if ((pb = qobject_cast<QPushButton *>(w)) && pb->isCheckable()) {
 3195         QPushButton *pb2 = qobject_cast<QPushButton *>(w2);
 3196         bool b = pb->isChecked();
 3197         pb->setChecked(pb2->isChecked());
 3198         pb2->setChecked(b);
 3199     }
 3200 }
 3201 
 3202 void ConfigManager::moveUpCommand()
 3203 {
 3204     moveCommand(-1);
 3205 }
 3206 
 3207 void ConfigManager::moveDownCommand()
 3208 {
 3209     moveCommand(+1);
 3210 }
 3211 
 3212 void ConfigManager::moveCommand(int dir, int atRow)
 3213 {
 3214     if (atRow < 0) {
 3215         // determine row from sending button
 3216         QPushButton *self = qobject_cast<QPushButton *>(sender());
 3217         REQUIRE(self);
 3218         REQUIRE(userGridLayout);
 3219         QWidget *w = self->property(PROPERTY_ASSOCIATED_INPUT).value<QWidget *>();
 3220         REQUIRE(w);
 3221         int col, unused;
 3222         userGridLayout->getItemPosition(userGridLayout->indexOf(self), &atRow, &col, &unused, &unused );
 3223         REQUIRE(atRow >= 0);
 3224     }
 3225     int cols = userGridLayout->columnCount();
 3226     for (int c = 0; c < cols; c++) {
 3227         QLayoutItem *li = userGridLayout->itemAtPosition(atRow, c);
 3228         QLayoutItem *li2 = userGridLayout->itemAtPosition(atRow + dir, c);
 3229         Q_ASSERT(li && li2);
 3230         QWidget *wd = li->widget();
 3231         QWidget *wd2 = li2->widget();
 3232         exchangeProperties(wd, wd2);
 3233     }
 3234     setLastRowMoveDownEnable(false);
 3235     setFirstRowMoveUpEnable(false);
 3236 }
 3237 
 3238 // manipulate latex menus
 3239 QTreeWidgetItem *ConfigManager::managedLatexMenuToTreeWidget(QTreeWidgetItem *parent, QMenu *menu)
 3240 {
 3241     if (!menu) return nullptr;
 3242     static QStringList relevantMenus = QStringList() << "main/tools" << "main/latex" << "main/math";
 3243     QTreeWidgetItem *menuitem = new QTreeWidgetItem(parent, QStringList(menu->title()));
 3244     bool advanced = false;
 3245     if (parent) advanced = parent->data(0, Qt::UserRole + 2).toBool();
 3246     else {
 3247         menuitem->setData(0, Qt::UserRole, menu->objectName());
 3248         if (manipulatedMenus.contains(menu->objectName())) {
 3249             QFont bold = menuitem->font(0);
 3250             bold.setBold(true);
 3251             for (int j = 0; j < 3; j++) menuitem->setFont(j, bold);
 3252         }
 3253         if (!relevantMenus.contains(menu->objectName())) advanced = true;
 3254     }
 3255     if (advanced) {
 3256         superAdvancedItems << menuitem;
 3257         menuitem->setData(0, Qt::UserRole + 2, true);
 3258     }
 3259     menuitem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
 3260     menuitem->setCheckState(0, menu->menuAction() && menu->menuAction()->isVisible() ? Qt::Checked : Qt::Unchecked);
 3261     if (menu->objectName().count("/") <= 2) menuitem->setExpanded(true);
 3262     QList<QAction *> acts = menu->actions();
 3263     for (int i = 0; i < acts.size(); i++) {
 3264         bool subAdvanced = advanced;
 3265         QTreeWidgetItem *twi = nullptr;
 3266         if (acts[i]->menu()) twi = managedLatexMenuToTreeWidget(menuitem, acts[i]->menu());
 3267         else {
 3268             subAdvanced |= !acts[i]->data().isValid();
 3269             QString actionData = acts[i]->data().toString();
 3270             twi = new QTreeWidgetItem(menuitem, QStringList() << QString(acts[i]->text()) << actionData << prettySlotName(acts[i]));
 3271             if (actionData.contains('\n')) {
 3272                 // limit item height to prevent vertically very large items for actions with %SCRIPTs
 3273                 twi->setSizeHint(1, QSize(-1, QFontMetrics(twi->font(1)).height()));
 3274                 twi->setTextAlignment(1, (twi->textAlignment(1)&Qt::AlignHorizontal_Mask) | Qt::AlignTop);
 3275             }
 3276             if (!acts[i]->isSeparator()) {
 3277                 twi->setIcon(0, acts[i]->icon());
 3278                 twi->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
 3279                 twi->setCheckState(0, acts[i]->isVisible() ? Qt::Checked : Qt::Unchecked);
 3280             } else {
 3281                 twi->setIcon(0, QIcon(":/images/separator.png"));
 3282             }
 3283             twi->setData(2, Qt::UserRole, acts[i]->property("originalSlot").isValid() ? acts[i]->property("originalSlot").toString() : twi->text(2));
 3284             if (manipulatedMenus.contains(acts[i]->objectName())) {
 3285                 QFont bold = twi->font(0);
 3286                 bold.setBold(true);
 3287                 for (int j = 0; j < 3; j++) twi->setFont(j, bold);
 3288             }
 3289         }
 3290         if (!twi) continue;
 3291         QString id = acts[i]->menu() ? (acts[i]->menu()->objectName() + "/") : acts[i]->objectName();
 3292         twi->setData(0, Qt::UserRole, id);
 3293         twi->setData(0, Qt::UserRole + 2, subAdvanced);
 3294         manipulatedMenuTree.insert(id, twi);
 3295         if (subAdvanced) superAdvancedItems << twi;
 3296 
 3297         int j = i + 1;
 3298         for (; j < acts.size() && acts[j]->isSeparator(); j++) ;
 3299         if (j < acts.size()) twi->setData(0, Qt::UserRole + 1, acts[j]->menu() ? acts[j]->menu()->objectName() : acts[j]->objectName());
 3300         else twi->setData(0, Qt::UserRole + 1, menu->objectName() + "/");
 3301     }
 3302     if (acts.isEmpty()) {
 3303         QTreeWidgetItem *filler = new QTreeWidgetItem(menuitem, QStringList() << QString("temporary menu end") << "");
 3304         filler->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
 3305         filler->setData(0, Qt::UserRole, menu->objectName() + "/!!end");
 3306     }
 3307     return menuitem;
 3308 }
 3309 
 3310 void ConfigManager::menuTreeItemChanged(QTreeWidgetItem *item, int )
 3311 {
 3312     if ((item->flags() & Qt::ItemIsEditable) && !changedItemsList.contains(item)) {
 3313         QFont f = item->font(0);
 3314         f.setBold(true);
 3315         for (int i = 0; i < 3; i++) item->setFont(i, f);
 3316         changedItemsList.append(item);
 3317     }
 3318 }
 3319 
 3320 void ConfigManager::menuTreeNewItem(bool menu)
 3321 {
 3322     QAction *a = qobject_cast<QAction *>(sender());
 3323     REQUIRE(a);
 3324     QTreeWidget *tw = qobject_cast<QTreeWidget *>(a->parentWidget());
 3325     REQUIRE(tw);
 3326     QTreeWidgetItem *old = tw->currentItem();
 3327     //qDebug() << old->data(0, Qt::UserRole) << old->data(0, Qt::DisplayRole);
 3328     REQUIRE(old);
 3329     if (!old->parent()) return;
 3330     QTreeWidgetItem *twi = new QTreeWidgetItem(QStringList() << QString("new item") << "");
 3331     twi->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
 3332     twi->setCheckState(0, Qt::Checked);
 3333     static int ID = 0;
 3334     QString oldID = old->data(0, Qt::UserRole).toString(), append = (menu ? "/" : "");
 3335     if (oldID.endsWith("/")) oldID.chop(1);
 3336     for (ID = 0; ID < 100000 && manipulatedMenuTree.contains(oldID + "_UII" + QString::number(ID) + append); ID++) ;
 3337     QString newId = oldID + "_UII" + QString::number(ID) + append;
 3338     twi->setData(0, Qt::UserRole, newId);
 3339     twi->setData(0, Qt::UserRole + 1, old->data(0, Qt::UserRole).toString());
 3340     old->parent()->insertChild(old->parent()->indexOfChild(old), twi);
 3341     manipulatedMenuTree.insert(newId, twi);
 3342     menuTreeItemChanged(twi, 0);
 3343     if (menu) {
 3344         QTreeWidgetItem *filler = new QTreeWidgetItem(twi, QStringList() << QString("temporary menu end") << "");
 3345         filler->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
 3346         filler->setData(0, Qt::UserRole, newId + "!!end");
 3347     }
 3348 }
 3349 
 3350 void ConfigManager::menuTreeNewMenuItem()
 3351 {
 3352     menuTreeNewItem(true);
 3353 }
 3354 
 3355 void ConfigManager::menuTreeRevertItem(){
 3356     QAction *a = qobject_cast<QAction *>(sender());
 3357     REQUIRE(a);
 3358     QTreeWidget *tw = qobject_cast<QTreeWidget *>(a->parentWidget());
 3359     REQUIRE(tw);
 3360     QTreeWidgetItem *item = tw->currentItem();
 3361     REQUIRE(item);
 3362     if (!item->parent()) return;
 3363     QString ID = item->data(0, Qt::UserRole).toString();
 3364     qDebug()<<item->text(0)<<ID;
 3365     if(ID.contains("UII")){
 3366         //user defined menu/item
 3367         QTreeWidgetItem *parent=item->parent();
 3368         parent->removeChild(item);
 3369         manipulatedMenuTree.remove(ID);
 3370     }else{ //revert
 3371         QFont bold = item->font(0);
 3372         if(bold.bold()){
 3373             // was manipulated
 3374             item->setText(1,tr("text is restored after restart"));
 3375             bold.setBold(false);
 3376             bold.setItalic(true);
 3377             for (int i = 0; i < 3; i++) item->setFont(i, bold);
 3378             manipulatedMenus.remove(ID);
 3379             changedItemsList.removeOne(item);
 3380         }
 3381     }
 3382 
 3383 }
 3384 
 3385 void ConfigManager::toggleVisibleTreeItems(bool show)
 3386 {
 3387     REQUIRE(!superAdvancedItems.isEmpty());
 3388     foreach (QTreeWidgetItem *twi, superAdvancedItems)
 3389         twi->setHidden(!show);
 3390     QTreeWidget *tw = superAdvancedItems.first()->treeWidget();
 3391     tw->setColumnHidden(2, !show);
 3392     if (show && tw->columnWidth(0) + tw->columnWidth(1) + tw->columnWidth(2) > tw->width() + 50)
 3393         tw->setColumnWidth(1, tw->width() - tw->columnWidth(0) - tw->columnWidth(2));
 3394 }
 3395 
 3396 void ConfigManager::treeWidgetToManagedLatexMenuTo()
 3397 {
 3398     manipulatedMenus.clear();
 3399     foreach (QTreeWidgetItem *item, changedItemsList) {
 3400         QString id = item->data(0, Qt::UserRole).toString();
 3401         if (id == "") continue;
 3402         QStringList m;
 3403         m << item->text(0)
 3404           << item->text(1)
 3405           << (item->checkState(0) == Qt::Checked ? "visible" : "hidden")
 3406           << item->data(0, Qt::UserRole + 1).toString()
 3407           << ((item->text(2) != item->data(2, Qt::UserRole).toString()) ? item->text(2) : "");
 3408         manipulatedMenus.insert(id, m);
 3409     }
 3410     modifyMenuContents();
 3411 }
 3412 
 3413 void ConfigManager::registerOption(const QString &name, void *storage, PropertyType type, QVariant def, void *displayWidgetOffset)
 3414 {
 3415     //#ifndef QT_NO_DEBUG
 3416     //TODO: optimize
 3417     for (int i = 0; i < managedProperties.size(); i++)
 3418         if (managedProperties[i].name == name) {
 3419             if (managedProperties[i].storage == storage)
 3420                 return;
 3421             qDebug() << "Duplicate option name" << name;
 3422             Q_ASSERT(false);
 3423         }
 3424     //#endif
 3425     ManagedProperty temp;
 3426     temp.name = name;
 3427     temp.storage = storage;
 3428     temp.type = type;
 3429     temp.def = def;
 3430     temp.widgetOffset = reinterpret_cast<ptrdiff_t>(displayWidgetOffset);
 3431     managedProperties << temp;
 3432 
 3433     if (persistentConfig) {
 3434         persistentConfig->beginGroup("texmaker");
 3435         temp.valueFromQVariant(persistentConfig->value(temp.name, temp.def));
 3436         persistentConfig->endGroup();
 3437     }
 3438 }
 3439 
 3440 void ConfigManager::registerOption(const QString &name, void *storage, PropertyType type, QVariant def)
 3441 {
 3442     registerOption(name, storage, type, def, nullptr);
 3443 }
 3444 
 3445 #define REGISTER_OPTION(TYPE, ID) \
 3446     void ConfigManager::registerOption(const QString& name, TYPE* storage, QVariant def,  void* displayWidgetOffset){ \
 3447         registerOption(name, storage, ID, def, displayWidgetOffset); \
 3448     } \
 3449     void ConfigManager::registerOption(const QString& name, TYPE* storage, QVariant def){ \
 3450         registerOption(name, storage, ID, def, nullptr); \
 3451     }
 3452 PROPERTY_TYPE_FOREACH_MACRO(REGISTER_OPTION)
 3453 #undef REGISTER_OPTION
 3454 
 3455 
 3456 void ConfigManager::setOption(const QString &name, const QVariant &value)
 3457 {
 3458     REQUIRE(persistentConfig);
 3459     QString rname = name.startsWith("/") ? name.mid(1) : ("texmaker/" + name);
 3460     ManagedProperty *option = nullptr;
 3461     if (rname.startsWith("texmaker/") && ((option = getManagedProperty(rname.mid(9))))) {
 3462         option->valueFromQVariant(value);
 3463         return;
 3464     }
 3465     persistentConfig->setValue(rname, value);
 3466 }
 3467 
 3468 QVariant ConfigManager::getOption(const QString &name, const QVariant &defaultValue) const
 3469 {
 3470     REQUIRE_RET(persistentConfig, QVariant());
 3471     QString rname = name.startsWith("/") ? name.mid(1) : ("texmaker/" + name);
 3472     const ManagedProperty *option = nullptr;
 3473     if (rname.startsWith("texmaker/") && (option = getManagedProperty(rname.mid(9))))
 3474         return option->valueToQVariant();
 3475     return persistentConfig->value(rname, defaultValue);
 3476 }
 3477 
 3478 bool ConfigManager::existsOption(const QString &name) const
 3479 {
 3480     REQUIRE_RET(persistentConfig, false);
 3481     QString rname = name.startsWith("/") ? name.mid(1) : ("texmaker/" + name);
 3482     return persistentConfig->contains(rname);
 3483 }
 3484 
 3485 void ConfigManager::linkOptionToDialogWidget(const void *optionStorage, QWidget *widget)
 3486 {
 3487     ManagedProperty *property = getManagedProperty(optionStorage);
 3488     REQUIRE(property);
 3489 
 3490     QWidget *parentWidget = widget->parentWidget();
 3491     while (parentWidget && !qobject_cast<QDialog *>(parentWidget)) parentWidget = parentWidget->parentWidget();
 3492     Q_ASSERT(parentWidget);
 3493     QDialog *parentDialog = qobject_cast<QDialog *>(parentWidget);
 3494     Q_ASSERT(parentDialog);
 3495 
 3496     if (managedOptionDialogs.contains(parentDialog)) {
 3497         (*managedOptionDialogs.find(parentDialog)) << widget;
 3498     } else {
 3499         managedOptionDialogs.insert(parentDialog, QList<QWidget *>() << widget);
 3500         connect(parentDialog, SIGNAL(accepted()), SLOT(managedOptionDialogAccepted()));
 3501     }
 3502 
 3503     property->writeToObject(widget);
 3504     widget->setProperty("managedProperty", QVariant::fromValue<void *>(property->storage));
 3505 }
 3506 
 3507 void ConfigManager::linkOptionToObject(const void *optionStorage, QObject *object, LinkOptions options)
 3508 {
 3509     ManagedProperty *property = getManagedProperty(optionStorage);
 3510     REQUIRE(property);
 3511     REQUIRE((options & LO_DIRECT_OVERRIDE) || property->type == PT_BOOL);
 3512     if (managedOptionObjects.contains(property)) {
 3513         Q_ASSERT(managedOptionObjects[property].first == options);
 3514         managedOptionObjects[property].second << object;
 3515     } else {
 3516         managedOptionObjects.insert(property, QPair<LinkOptions, QList<QObject *> >(options, QList<QObject *>() << object));
 3517     }
 3518     property->writeToObject(object);
 3519     object->setProperty("managedProperty", QVariant::fromValue<ManagedProperty *>(property));
 3520     connect(object, SIGNAL(destroyed(QObject *)), SLOT(managedOptionObjectDestroyed(QObject *)));
 3521     if (qobject_cast<QAction *>(object) || qobject_cast<QCheckBox *>(object) || qobject_cast<QToolButton *>(object))
 3522         connect(object, SIGNAL(toggled(bool)), SLOT(managedOptionBoolToggled()));
 3523 }
 3524 
 3525 void ConfigManager::updateAllLinkedObjects(const void *optionStorage)
 3526 {
 3527     ManagedProperty *property = getManagedProperty(optionStorage);
 3528     REQUIRE(property);
 3529     updateManagedOptionObjects(property);
 3530 }
 3531 
 3532 ManagedProperty *ConfigManager::getManagedProperty(const void *storage)
 3533 {
 3534     for (int i = 0; i < managedProperties.size(); i++)
 3535         if (managedProperties[i].storage == storage) return &managedProperties[i];
 3536     return nullptr;
 3537 }
 3538 
 3539 ManagedProperty *ConfigManager::getManagedProperty(const QString &name)
 3540 {
 3541     for (int i = 0; i < managedProperties.size(); i++)
 3542         if (managedProperties[i].name == name) return &managedProperties[i];
 3543     return nullptr;
 3544 }
 3545 
 3546 const ManagedProperty *ConfigManager::getManagedProperty(const QString &name) const
 3547 {
 3548     for (int i = 0; i < managedProperties.size(); i++)
 3549         if (managedProperties[i].name == name) return &managedProperties[i];
 3550     return nullptr;
 3551 }
 3552 
 3553 void ConfigManager::getDefaultEncoding(const QByteArray &, QTextCodec *&guess, int &sure)
 3554 {
 3555     if (sure >= 100) return;
 3556     if (!newFileEncoding) return;
 3557 
 3558     using namespace Encoding;
 3559     //guess is utf-8 or latin1, no latex encoding definition detected
 3560     if (guess && guess->mibEnum() == MIB_UTF8) return; //trust utf8 detection
 3561     //guess is latin1, no latex encoding definition detected
 3562     if (sure == 0) {
 3563         //ascii file
 3564         if (newFileEncoding->mibEnum() == MIB_UTF16BE || newFileEncoding->mibEnum() == MIB_UTF16LE) {
 3565             guess = QTextCodec::codecForMib(MIB_UTF8);
 3566             return;
 3567         }
 3568         guess = newFileEncoding;
 3569         return;
 3570     } else {
 3571         //file containing invalid utf-8 characters
 3572         if (newFileEncoding->mibEnum() == MIB_UTF16BE || newFileEncoding->mibEnum() == MIB_UTF16LE || newFileEncoding->mibEnum() == MIB_UTF8) {
 3573             guess = QTextCodec::codecForMib(MIB_LATIN1);
 3574             return;
 3575         }
 3576         guess = newFileEncoding;
 3577         return;
 3578     }
 3579 }
 3580 
 3581 /*!
 3582  * Replace "[txs-settings-dir]" and "[txs-app-dir]"  with the config and application paths.
 3583  */
 3584 QString ConfigManager::parseDir(QString s) const
 3585 {
 3586     s.replace("[txs-settings-dir]", removePathDelim(configBaseDir));
 3587     s.replace("[txs-app-dir]", removePathDelim(QCoreApplication::applicationDirPath()));
 3588     return s;
 3589 }
 3590 
 3591 QStringList ConfigManager::parseDirList(const QString &s) const
 3592 {
 3593     return parseDir(s).split(";");
 3594 }
 3595 
 3596 /*!
 3597  * Replace the config and application path with "[txs-settings-dir]" and "[txs-app-dir]" respectively.
 3598  */
 3599 QString ConfigManager::reverseParseDir(QString s) const
 3600 {
 3601     s.replace(removePathDelim(configBaseDir), "[txs-settings-dir]");
 3602     s.replace(removePathDelim(QCoreApplication::applicationDirPath()), "[txs-app-dir]");
 3603     return s;
 3604 }
 3605 
 3606 QString ConfigManager::reverseParseDir(const QStringList &s) const
 3607 {
 3608     return reverseParseDir(s.join(";"));
 3609 }
 3610 
 3611 void ConfigManager::managedOptionDialogAccepted()
 3612 {
 3613     QDialog *dialog = qobject_cast<QDialog *>(sender());
 3614     REQUIRE(dialog);
 3615     foreach (const QWidget *widget, managedOptionDialogs.value(dialog, QList<QWidget *>())) {
 3616         ManagedProperty *prop = getManagedProperty(widget->property("managedProperty").value<void *>());
 3617         Q_ASSERT(prop);
 3618         prop->readFromObject(widget);
 3619     }
 3620     managedOptionDialogs.remove(dialog);
 3621 }
 3622 
 3623 void ConfigManager::managedOptionObjectDestroyed(QObject *obj)
 3624 {
 3625     REQUIRE(obj);
 3626     ManagedProperty *property = obj->property("managedProperty").value<ManagedProperty *>();
 3627     REQUIRE(property);
 3628     Q_ASSERT(managedOptionObjects.contains(property));
 3629     Q_ASSERT(managedOptionObjects[property].second.contains(obj));
 3630     managedOptionObjects[property].second.removeAll(obj);
 3631     if (managedOptionObjects[property].second.isEmpty())
 3632         managedOptionObjects.remove(property);
 3633 }
 3634 
 3635 int isChecked(const QObject *obj)
 3636 {
 3637     const QAction *act = qobject_cast<const QAction *>(obj);
 3638     if (act) return (act->isChecked() ? 1 : -1);
 3639     const QCheckBox *cb = qobject_cast<const QCheckBox *>(obj);
 3640     if (cb) return (cb->isChecked() ? 1 : -1);
 3641     const QToolButton *tb = qobject_cast<const QToolButton *>(obj);
 3642     if (tb) return (tb->isChecked() ? 1 : -1);
 3643     return 0;
 3644 }
 3645 
 3646 void ConfigManager::managedOptionBoolToggled()
 3647 {
 3648     int state = isChecked(sender());
 3649     REQUIRE(state);
 3650     ManagedProperty *property = sender()->property("managedProperty").value<ManagedProperty *>();
 3651     REQUIRE(property);
 3652     REQUIRE(property->type == PT_BOOL);
 3653     if ((state > 0) == *static_cast<bool *>(property->storage)) return;
 3654     if (managedOptionObjects[property].first & LO_DIRECT_OVERRIDE) {
 3655         //full sync
 3656         *static_cast<bool *>(property->storage) = (state > 0);
 3657     } else {
 3658         //voting
 3659         int totalState = 0;
 3660         foreach (const QObject *o, managedOptionObjects[property].second)
 3661             totalState += isChecked(o);
 3662 
 3663         if (totalState == 0) totalState = state;
 3664         if ((totalState > 0) == *static_cast<bool *>(property->storage)) return;
 3665         *static_cast<bool *>(property->storage) = (totalState > 0);
 3666     }
 3667     updateManagedOptionObjects(property);
 3668 }
 3669 
 3670 void ConfigManager::updateManagedOptionObjects(ManagedProperty *property)
 3671 {
 3672     if (!(managedOptionObjects[property].first & LO_UPDATE_ALL))
 3673         return;
 3674     foreach (QObject *o, managedOptionObjects[property].second)
 3675         property->writeToObject(o);
 3676 }