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