1 /*
2  * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014-2019 <tsujan2000@gmail.com>
3  *
4  * FeatherPad is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * FeatherPad is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12  * See the GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program.  If not, see <http://www.gnu.org/licenses/>.
16  *
17  * @license GPL-3.0+ <https://spdx.org/licenses/GPL-3.0+.html>
18  */
19 
20 #include "config.h"
21 //#include <QFileInfo>
22 #include <QKeySequence>
23 
24 namespace FeatherPad {
25 
Config()26 Config::Config():
27     remSize_ (true),
28     remPos_ (false),
29     remSplitterPos_ (true),
30     noToolbar_ (false),
31     noMenubar_ (false),
32     hideSearchbar_ (false),
33     showStatusbar_ (true),
34     showCursorPos_ (false),
35     showLangSelector_ (false),
36     sidePaneMode_ (false),
37     remFont_ (true),
38     wrapByDefault_ (true),
39     indentByDefault_ (true),
40     autoReplace_ (false),
41     autoBracket_ (false),
42     lineByDefault_ (false),
43     syntaxByDefault_ (true),
44     showWhiteSpace_ (false),
45     showEndings_ (false),
46     isMaxed_ (false),
47     isFull_ (false),
48     darkColScheme_ (false),
49     thickCursor_ (false),
50     tabWrapAround_ (false),
51     hideSingleTab_ (false),
52     executeScripts_ (false),
53     appendEmptyLine_ (true),
54     removeTrailingSpaces_ (false),
55     openInWindows_ (false),
56     nativeDialog_ (true),
57     inertialScrolling_ (false),
58     autoSave_ (false),
59     skipNonText_ (true),
60     saveUnmodified_ (false),
61     selectionHighlighting_ (false),
62     pastePaths_ (false),
63     closeWithLastTab_ (false),
64     sharedSearchHistory_ (false),
65     disableMenubarAccel_ (false),
66     vLineDistance_ (-80),
67     tabPosition_ (0),
68     maxSHSize_ (2),
69     lightBgColorValue_ (255),
70     darkBgColorValue_ (15),
71     recentFilesNumber_ (10),
72     curRecentFilesNumber_ (10), // not needed
73     autoSaveInterval_ (1), // not needed
74     textTabSize_ (4), // not needed
75     winSize_ (QSize (700, 500)),
76     startSize_ (QSize (700, 500)),
77     winPos_ (QPoint (0, 0)),
78     splitterPos_ (20), // percentage
79     font_ (QFont ("Monospace")),
80     recentOpened_ (false),
81     saveLastFilesList_ (false),
82     cursorPosRetrieved_ (false),
83     spellCheckFromStart_ (false),
84     whiteSpaceValue_ (180),
85     curLineHighlight_ (-1) {}
86 /*************************/
~Config()87 Config::~Config() {}
88 /*************************/
readConfig()89 void Config::readConfig()
90 {
91     QVariant v;
92     Settings settings ("featherpad", "fp");
93 
94     /**************
95      *** Window ***
96      **************/
97 
98     settings.beginGroup ("window");
99 
100     if (settings.value ("size") == "none")
101         remSize_ = false; // true by default
102     else
103     {
104         winSize_ = settings.value ("size", QSize (700, 500)).toSize();
105         if (!winSize_.isValid() || winSize_.isNull())
106             winSize_ = QSize (700, 500);
107         isMaxed_ = settings.value ("max", false).toBool();
108         isFull_ = settings.value ("fullscreen", false).toBool();
109     }
110     startSize_ = settings.value ("startSize", QSize (700, 500)).toSize();
111     if (!startSize_.isValid() || startSize_.isNull())
112         startSize_ = QSize (700, 500);
113 
114     v = settings.value ("position");
115     if (v.isValid() && settings.value ("position") != "none")
116     {
117         remPos_ = true; // false by default
118         winPos_ = settings.value ("position", QPoint (0, 0)).toPoint();
119     }
120 
121     if (settings.value ("splitterPos") == "none")
122         remSplitterPos_ = false; // true by default
123     else
124         splitterPos_ = qMin (qMax (settings.value ("splitterPos", 20).toInt(), 0), 100);
125 
126     prefSize_ = settings.value ("prefSize").toSize();
127 
128     if (settings.value ("noToolbar").toBool())
129         noToolbar_ = true; // false by default
130 
131     if (settings.value ("noMenubar").toBool())
132         noMenubar_ = true; // false by default
133 
134     if (noToolbar_ && noMenubar_)
135     { // we don't want to hide all actions
136         noToolbar_ = false;
137         noMenubar_ = true;
138     }
139 
140     if (settings.value ("hideSearchbar").toBool())
141         hideSearchbar_ = true; // false by default
142 
143     v = settings.value ("showStatusbar");
144     if (v.isValid()) // true by default
145         showStatusbar_ = v.toBool();
146 
147     if (settings.value ("showCursorPos").toBool())
148         showCursorPos_ = true; // false by default
149 
150     if (settings.value ("showLangSelector").toBool())
151         showLangSelector_ = true; // false by default
152 
153     if (settings.value ("sidePaneMode").toBool())
154         sidePaneMode_ = true; // false by default
155 
156     int pos = settings.value ("tabPosition").toInt();
157     if (pos > 0 && pos <= 3)
158         tabPosition_ = pos; // 0 by default
159 
160     if (settings.value ("tabWrapAround").toBool())
161         tabWrapAround_ = true; // false by default
162 
163     if (settings.value ("hideSingleTab").toBool())
164         hideSingleTab_ = true; // false by default
165 
166     if (settings.value ("openInWindows").toBool())
167         openInWindows_ = true; // false by default
168 
169     v = settings.value ("nativeDialog");
170     if (v.isValid()) // true by default
171         nativeDialog_ = v.toBool();
172 
173     if (settings.value ("closeWithLastTab").toBool())
174         closeWithLastTab_ = true; // false by default
175 
176     if (settings.value ("sharedSearchHistory").toBool())
177         sharedSearchHistory_ = true; // false by default
178 
179     if (settings.value ("disableMenubarAccel").toBool())
180         disableMenubarAccel_ = true; // false by default
181 
182     settings.endGroup();
183 
184     /************
185      *** Text ***
186      ************/
187 
188     settings.beginGroup ("text");
189 
190     if (settings.value ("font") == "none")
191     {
192         remFont_ = false; // true by default
193         font_.setPointSize (qMax (QFont().pointSize(), 9));
194     }
195     else
196     {
197         QString fontStr = settings.value ("font").toString();
198         if (!fontStr.isEmpty())
199             font_.fromString (fontStr);
200         else
201             font_.setPointSize (qMax (QFont().pointSize(), 9));
202     }
203 
204     if (settings.value ("noWrap").toBool())
205         wrapByDefault_ = false; // true by default
206 
207     if (settings.value ("noIndent").toBool())
208         indentByDefault_ = false; // true by default
209 
210     if (settings.value ("autoReplace").toBool())
211         autoReplace_ = true; // false by default
212 
213     if (settings.value ("autoBracket").toBool())
214         autoBracket_ = true; // false by default
215 
216     if (settings.value ("lineNumbers").toBool())
217         lineByDefault_ = true; // false by default
218 
219     if (settings.value ("noSyntaxHighlighting").toBool())
220         syntaxByDefault_ = false; // true by default
221 
222     if (settings.value ("showWhiteSpace").toBool())
223         showWhiteSpace_ = true; // false by default
224 
225     if (settings.value ("showEndings").toBool())
226         showEndings_ = true; // false by default
227 
228     if (settings.value ("darkColorScheme").toBool())
229         darkColScheme_ = true; // false by default
230 
231     if (settings.value ("thickCursor").toBool())
232         thickCursor_ = true; // false by default
233 
234     if (settings.value ("inertialScrolling").toBool())
235         inertialScrolling_ = true; // false by default
236 
237     if (settings.value ("autoSave").toBool())
238         autoSave_ = true; // false by default
239 
240     int distance = settings.value ("vLineDistance").toInt();
241     if (qAbs (distance) >= 10 && qAbs (distance) < 1000)
242         vLineDistance_ = distance; // -80 by default
243 
244     v = settings.value ("skipNonText");
245     if (v.isValid()) // true by default
246         skipNonText_ = v.toBool();
247 
248     if (settings.value ("saveUnmodified").toBool())
249         saveUnmodified_ = true; // false by default
250 
251     if (settings.value ("selectionHighlighting").toBool())
252         selectionHighlighting_ = true; // false by default
253 
254     if (settings.value ("pastePaths").toBool())
255         pastePaths_ = true; // false by default
256 
257     maxSHSize_ = qBound (1, settings.value ("maxSHSize", 2).toInt(), 10);
258 
259     /* don't let the dark bg be darker than #e6e6e6 */
260     lightBgColorValue_ = qBound (230, settings.value ("lightBgColorValue", 255).toInt(), 255);
261 
262     /* don't let the dark bg be lighter than #323232 */
263     darkBgColorValue_ = qBound (0, settings.value ("darkBgColorValue", 15).toInt(), 50);
264 
265     dateFormat_ = settings.value ("dateFormat").toString();
266 
267     if (settings.value ("executeScripts").toBool())
268         executeScripts_ = true; // false by default
269     executeCommand_ = settings.value ("executeCommand").toString();
270 
271     v = settings.value ("appendEmptyLine");
272     if (v.isValid()) // true by default
273         appendEmptyLine_ = v.toBool();
274 
275     if (settings.value ("removeTrailingSpaces").toBool())
276         removeTrailingSpaces_ = true; // false by default
277 
278     recentFilesNumber_ = qBound (0, settings.value ("recentFilesNumber", 10).toInt(), 20);
279     curRecentFilesNumber_ = recentFilesNumber_; // fixed
280     recentFiles_ = settings.value ("recentFiles").toStringList();
281     recentFiles_.removeAll ("");
282     recentFiles_.removeDuplicates();
283     while (recentFiles_.count() > recentFilesNumber_)
284         recentFiles_.removeLast();
285     if (settings.value ("recentOpened").toBool())
286         recentOpened_ = true; // false by default
287 
288     if (settings.value ("saveLastFilesList").toBool())
289         saveLastFilesList_ = true; // false by default
290 
291     autoSaveInterval_ = qBound (1, settings.value ("autoSaveInterval", 1).toInt(), 60);
292 
293     textTabSize_ = qBound (2, settings.value ("textTabSize", 4).toInt(), 10);
294 
295     dictPath_ = settings.value ("dictionaryPath").toString();
296     spellCheckFromStart_ = settings.value ("spellCheckFromStart").toBool();
297 
298     settings.endGroup();
299 
300     readSyntaxColors();
301 }
302 /*************************/
resetFont()303 void Config::resetFont()
304 {
305     font_ = QFont ("Monospace");
306     font_.setPointSize (qMax (QFont().pointSize(), 9));
307 }
308 /*************************/
readShortcuts()309 void Config::readShortcuts()
310 {
311     /* NOTE: We don't read the custom shortcuts from global config files
312              because we want the user to be able to restore their default values. */
313     Settings tmp ("featherpad", "fp");
314     Settings settings (tmp.fileName(), QSettings::NativeFormat);
315 
316     settings.beginGroup ("shortcuts");
317     QStringList actions = settings.childKeys();
318     for (int i = 0; i < actions.size(); ++i)
319     {
320         QVariant v = settings.value (actions.at (i));
321         bool isValid;
322         QString vs = validatedShortcut (v, &isValid);
323         if (isValid)
324             setActionShortcut (actions.at (i), vs);
325         else // remove the key on writing config
326             removedActions_ << actions.at (i);
327     }
328     settings.endGroup();
329 }
330 /*************************/
getLastFiles()331 QStringList Config::getLastFiles()
332 {
333     if (!saveLastFilesList_) // it's already decided
334         return QStringList();
335 
336     Settings settingsLastCur ("featherpad", "fp_last_cursor_pos");
337     lasFilesCursorPos_ = settingsLastCur.value ("cursorPositions").toHash();
338 
339     QStringList lastFiles = lasFilesCursorPos_.keys();
340     lastFiles.removeAll ("");
341     lastFiles.removeDuplicates();
342     while (lastFiles.count() > 50) // never more than 50 files
343         lastFiles.removeLast();
344     return lastFiles;
345 }
346 /*************************/
readSyntaxColors()347 void Config::readSyntaxColors()// may be called multiple times
348 {
349     setDfaultSyntaxColors();
350     customSyntaxColors_.clear();
351 
352     /* NOTE: We don't read the custom syntax colors from global config files
353              because we want the user to be able to restore their default values. */
354     Settings tmp ("featherpad", darkColScheme_ ? "fp_dark_syntax_colors" : "fp_light_syntax_colors");
355     Settings settingsColors (tmp.fileName(), QSettings::NativeFormat);
356 
357     settingsColors.beginGroup ("curLineHighlight");
358     curLineHighlight_ = qBound (-1, settingsColors.value ("value", -1).toInt(), 255);
359     settingsColors.endGroup();
360     if (curLineHighlight_ >= 0
361         && (darkColScheme_ ? curLineHighlight_ > 70
362                            : curLineHighlight_ < 210))
363     {
364         curLineHighlight_ = -1;
365     }
366 
367 
368     settingsColors.beginGroup ("whiteSpace");
369     int ws = settingsColors.value ("value").toInt();
370     settingsColors.endGroup();
371     if (ws < getMinWhiteSpaceValue() || ws > getMaxWhiteSpaceValue())
372         whiteSpaceValue_ = getDefaultWhiteSpaceValue();
373     else
374         whiteSpaceValue_ = ws;
375 
376     const auto syntaxes = defaultLightSyntaxColors_.keys();
377     QList<QColor> l;
378     l << (darkColScheme_ ? QColor (Qt::white) : QColor (Qt::black));
379     l << QColor (whiteSpaceValue_, whiteSpaceValue_, whiteSpaceValue_);
380     for (auto &syntax : syntaxes)
381     {
382         QColor col;
383         col.setNamedColor (settingsColors.value (syntax).toString());
384         if (col.isValid())
385             col.setAlpha (255); // only opaque custom colors
386         if (!col.isValid() || l.contains (col))
387         { // an invalid or repeated color shows a corrupted configuration
388             customSyntaxColors_.clear();
389             break;
390         }
391         l << col;
392         customSyntaxColors_.insert (syntax, col);
393     }
394 }
395 /*************************/
writeConfig()396 void Config::writeConfig()
397 {
398     Settings settings ("featherpad", "fp");
399     if (!settings.isWritable()) return;
400 
401     /**************
402      *** Window ***
403      **************/
404 
405     settings.beginGroup ("window");
406 
407     if (remSize_)
408     {
409         settings.setValue ("size", winSize_);
410         settings.setValue ("max", isMaxed_);
411         settings.setValue ("fullscreen", isFull_);
412     }
413     else
414     {
415         settings.setValue ("size", "none");
416         settings.remove ("max");
417         settings.remove ("fullscreen");
418     }
419 
420     if (remPos_)
421         settings.setValue ("position", winPos_);
422     else
423         settings.setValue ("position", "none");
424 
425     if (remSplitterPos_)
426         settings.setValue ("splitterPos", splitterPos_);
427     else
428         settings.setValue ("splitterPos", "none");
429 
430     settings.setValue ("prefSize", prefSize_);
431 
432     settings.setValue ("startSize", startSize_);
433     settings.setValue ("noToolbar", noToolbar_);
434     settings.setValue ("noMenubar", noMenubar_);
435     settings.setValue ("hideSearchbar", hideSearchbar_);
436     settings.setValue ("showStatusbar", showStatusbar_);
437     settings.setValue ("showCursorPos", showCursorPos_);
438     settings.setValue ("showLangSelector", showLangSelector_);
439     settings.setValue ("sidePaneMode", sidePaneMode_);
440     settings.setValue ("tabPosition", tabPosition_);
441     settings.setValue ("tabWrapAround", tabWrapAround_);
442     settings.setValue ("hideSingleTab", hideSingleTab_);
443     settings.setValue ("openInWindows", openInWindows_);
444     settings.setValue ("nativeDialog", nativeDialog_);
445     settings.setValue ("closeWithLastTab", closeWithLastTab_);
446     settings.setValue ("sharedSearchHistory", sharedSearchHistory_);
447     settings.setValue ("disableMenubarAccel", disableMenubarAccel_);
448 
449     settings.endGroup();
450 
451     /************
452      *** Text ***
453      ************/
454 
455     settings.beginGroup ("text");
456 
457     if (remFont_)
458         settings.setValue ("font", font_.toString());
459     else
460         settings.setValue ("font", "none");
461 
462     settings.setValue ("noWrap", !wrapByDefault_);
463     settings.setValue ("noIndent", !indentByDefault_);
464     settings.setValue ("autoReplace", autoReplace_);
465     settings.setValue ("autoBracket", autoBracket_);
466     settings.setValue ("lineNumbers", lineByDefault_);
467     settings.setValue ("noSyntaxHighlighting", !syntaxByDefault_);
468     settings.setValue ("showWhiteSpace", showWhiteSpace_);
469     settings.setValue ("showEndings", showEndings_);
470     settings.setValue ("darkColorScheme", darkColScheme_);
471     settings.setValue ("thickCursor", thickCursor_);
472     settings.setValue ("inertialScrolling", inertialScrolling_);
473     settings.setValue ("autoSave", autoSave_);
474     settings.setValue ("skipNonText", skipNonText_);
475     settings.setValue ("saveUnmodified", saveUnmodified_);
476     settings.setValue ("selectionHighlighting", selectionHighlighting_);
477     settings.setValue ("pastePaths", pastePaths_);
478     settings.setValue ("maxSHSize", maxSHSize_);
479 
480     settings.setValue ("lightBgColorValue", lightBgColorValue_);
481     settings.setValue ("dateFormat", dateFormat_);
482     settings.setValue ("darkBgColorValue", darkBgColorValue_);
483     settings.setValue ("executeScripts", executeScripts_);
484     settings.setValue ("appendEmptyLine", appendEmptyLine_);
485     settings.setValue ("removeTrailingSpaces", removeTrailingSpaces_);
486 
487     settings.setValue ("vLineDistance", vLineDistance_);
488 
489     settings.setValue ("recentFilesNumber", recentFilesNumber_);
490     settings.setValue ("executeCommand", executeCommand_);
491     while (recentFiles_.count() > recentFilesNumber_) // recentFilesNumber_ may have decreased
492         recentFiles_.removeLast();
493     if (recentFiles_.isEmpty()) // don't save "@Invalid()"
494         settings.setValue ("recentFiles", "");
495     else
496         settings.setValue ("recentFiles", recentFiles_);
497     settings.setValue ("recentOpened", recentOpened_);
498 
499     settings.setValue ("saveLastFilesList", saveLastFilesList_);
500 
501     settings.setValue ("autoSaveInterval", autoSaveInterval_);
502 
503     settings.setValue ("textTabSize", textTabSize_);
504 
505     settings.setValue ("dictionaryPath", dictPath_);
506     settings.setValue ("spellCheckFromStart", spellCheckFromStart_);
507 
508     settings.endGroup();
509 
510     /*****************
511      *** Shortcuts ***
512      *****************/
513 
514     settings.beginGroup ("shortcuts");
515 
516     for (int i = 0; i < removedActions_.size(); ++i)
517         settings.remove (removedActions_.at (i));
518 
519     QHash<QString, QString>::const_iterator it = actions_.constBegin();
520     while (it != actions_.constEnd())
521     {
522         settings.setValue (it.key(), it.value());
523         ++it;
524     }
525 
526     settings.endGroup();
527 
528     writeCursorPos();
529 
530     writeSyntaxColors();
531 }
532 /*************************/
readCursorPos()533 void Config::readCursorPos()
534 {
535     if (!cursorPosRetrieved_)
536     {
537         Settings settings ("featherpad", "fp_cursor_pos");
538         cursorPos_ = settings.value ("cursorPositions").toHash();
539         cursorPosRetrieved_ = true;
540     }
541 }
542 /*************************/
writeCursorPos()543 void Config::writeCursorPos()
544 {
545     Settings settings ("featherpad", "fp_cursor_pos");
546     if (settings.isWritable())
547     {
548         if (!cursorPos_.isEmpty())
549         {
550             /* no need to clean up the config file here because
551            it's more or less cleaned by the session dialog */
552             /*QHash<QString, QVariant>::iterator it = cursorPos_.begin();
553             while (it != cursorPos_.end())
554             {
555                 if (!QFileInfo (it.key()).isFile())
556                     it = cursorPos_.erase (it);
557                 else ++it;
558             }*/
559             settings.setValue ("cursorPositions", cursorPos_);
560         }
561     }
562 
563     Settings settingsLastCur ("featherpad", "fp_last_cursor_pos");
564     if (settingsLastCur.isWritable())
565     {
566         if (saveLastFilesList_ && !lasFilesCursorPos_.isEmpty())
567             settingsLastCur.setValue ("cursorPositions", lasFilesCursorPos_);
568         else
569             settingsLastCur.remove ("cursorPositions");
570     }
571 }
572 /*************************/
writeSyntaxColors()573 void Config::writeSyntaxColors()
574 {
575     Settings settingsColors ("featherpad", darkColScheme_ ? "fp_dark_syntax_colors" : "fp_light_syntax_colors");
576 
577     if (customSyntaxColors_.isEmpty())
578     { // avoid redundant writing as far as possible
579         if (whiteSpaceValue_ != getDefaultWhiteSpaceValue()
580             || curLineHighlight_ != -1)
581         {
582             if (settingsColors.allKeys().size() > 2)
583                 settingsColors.clear();
584         }
585         else
586         {
587             settingsColors.clear();
588             return;
589         }
590     }
591     else
592     {
593         QHash<QString, QColor>::const_iterator it = customSyntaxColors_.constBegin();
594         while (it != customSyntaxColors_.constEnd())
595         {
596             settingsColors.setValue (it.key(), it.value().name());
597             ++it;
598         }
599     }
600 
601     /* NOTE: QSettings has a strange bug that makes it unreliable. If the config file can
602        have a subkey but has none, QSettings might empty the file when a new window is
603        opened. This happens when nothing is written to the config file by the code. It
604        should be related to some kind of cache because I've also seen cases, where a key
605        has been removed from the code but is created after reading the config file. Since
606        we write the settings on quitting, the bug has no effect under usual circumstances,
607        but if a crash happens or the system is shut down inappropriately, the settings
608        might be lost. So, we always add a subkey if there is color customization. */
609     settingsColors.beginGroup ("whiteSpace");
610     settingsColors.setValue ("value", whiteSpaceValue_);
611     settingsColors.endGroup();
612 
613     settingsColors.beginGroup ("curLineHighlight");
614     settingsColors.setValue ("value", curLineHighlight_);
615     settingsColors.endGroup();
616 }
617 /*************************/
setWhiteSpaceValue(int value)618 void Config::setWhiteSpaceValue (int value)
619 {
620     value = qBound (getMinWhiteSpaceValue(), value, getMaxWhiteSpaceValue());
621     QList<QColor> colors;
622     colors << (darkColScheme_ ? QColor (Qt::white) : QColor (Qt::black));
623     if (!customSyntaxColors_.isEmpty())
624         colors = customSyntaxColors_.values();
625     else if (darkColScheme_)
626         colors = defaultDarkSyntaxColors_.values();
627     else
628         colors = defaultLightSyntaxColors_.values();
629     const int average = (getMinWhiteSpaceValue() + getMaxWhiteSpaceValue()) / 2;
630     QColor ws (value, value, value);
631     while (colors.contains (ws))
632     {
633         int r = ws.red();
634         if (value >= average) --r;
635         else ++r;
636         ws = QColor (r, r, r);
637     }
638     whiteSpaceValue_ = ws.red();
639 }
640 /*************************/
setCurLineHighlight(int value)641 void Config::setCurLineHighlight (int value)
642 {
643     if (value < getMinCurLineHighlight() || value > getMaxCurLineHighlight())
644         curLineHighlight_ = -1;
645     else
646         curLineHighlight_ = value;
647 }
648 /*************************/
addRecentFile(const QString & file)649 void Config::addRecentFile (const QString& file)
650 {
651     if (curRecentFilesNumber_ > 0)
652     {
653         recentFiles_.removeAll (file);
654         recentFiles_.prepend (file);
655         while (recentFiles_.count() > curRecentFilesNumber_)
656             recentFiles_.removeLast();
657     }
658 }
659 /*************************/
validatedShortcut(const QVariant v,bool * isValid)660 QString Config::validatedShortcut (const QVariant v, bool *isValid)
661 {
662     static QStringList added;
663     if (v.isValid())
664     {
665         QString str = v.toString();
666         if (str.isEmpty())
667         { // it means the removal of a shortcut
668             *isValid = true;
669             return QString();
670         }
671 
672         /* NOTE: In older versions of FeatherPad, shorcuts were saved in
673            the NativeText format. For the sake of backward compatibility,
674            we convert them into the PortableText format. */
675         QKeySequence keySeq (str);
676         if (str == keySeq.toString (QKeySequence::NativeText))
677             str = keySeq.toString();
678 
679         if (!QKeySequence (str, QKeySequence::PortableText).toString().isEmpty())
680         {
681             if (!reservedShortcuts_.contains (str)
682                 // prevent ambiguous shortcuts at startup as far as possible
683                 && !added.contains (str))
684             {
685                 *isValid = true;
686                 added << str;
687                 return str;
688             }
689         }
690     }
691 
692     *isValid = false;
693     return QString();
694 }
695 /*************************/
setDfaultSyntaxColors()696 void Config::setDfaultSyntaxColors()
697 {
698     if (defaultLightSyntaxColors_.isEmpty())
699     {
700         defaultLightSyntaxColors_.insert ("function", QColor (Qt::blue));
701         defaultLightSyntaxColors_.insert ("BuiltinFunction", QColor (Qt::magenta));
702         defaultLightSyntaxColors_.insert ("comment", QColor (Qt::red));
703         defaultLightSyntaxColors_.insert ("quote", QColor (Qt::darkGreen));
704         defaultLightSyntaxColors_.insert ("type", QColor (Qt::darkMagenta));
705         defaultLightSyntaxColors_.insert ("keyWord", QColor (Qt::darkBlue));
706         defaultLightSyntaxColors_.insert ("number", QColor (150, 85, 0));
707         defaultLightSyntaxColors_.insert ("regex", QColor (150, 0, 0));
708         defaultLightSyntaxColors_.insert ("xmlElement", QColor (126, 0, 230));
709         defaultLightSyntaxColors_.insert ("cssValue", QColor (0, 110, 110));
710         defaultLightSyntaxColors_.insert ("other", QColor (100, 100, 0));
711 
712         defaultDarkSyntaxColors_.insert ("function", QColor (85, 227, 255));
713         defaultDarkSyntaxColors_.insert ("BuiltinFunction", QColor (Qt::magenta));
714         defaultDarkSyntaxColors_.insert ("comment", QColor (255, 120, 120));
715         defaultDarkSyntaxColors_.insert ("quote", QColor (Qt::green));
716         defaultDarkSyntaxColors_.insert ("type", QColor (255, 153, 255));
717         defaultDarkSyntaxColors_.insert ("keyWord", QColor (65, 154, 255));
718         defaultDarkSyntaxColors_.insert ("number", QColor (255, 205, 0));
719         defaultDarkSyntaxColors_.insert ("regex", QColor (255, 160, 0));
720         defaultDarkSyntaxColors_.insert ("xmlElement", QColor (255, 255, 1));
721         defaultDarkSyntaxColors_.insert ("cssValue", QColor (150, 255, 0));
722         defaultDarkSyntaxColors_.insert ("other", QColor (Qt::yellow));
723     }
724 }
725 
726 }
727