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", &centralVisible, 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", &macroEditorUsesLineWrap, 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