1 
2 
3 #include "toonz/preferences.h"
4 
5 // TnzLib includes
6 #include "toonz/tscenehandle.h"
7 #include "toonz/toonzscene.h"
8 #include "toonz/toonzfolders.h"
9 #include "toonz/tcamera.h"
10 #include "toonz/txshleveltypes.h"
11 
12 // TnzBase includes
13 #include "tenv.h"
14 #include "tunit.h"
15 
16 // TnzCore includes
17 #include "tsystem.h"
18 #include "tconvert.h"
19 #include "tundo.h"
20 #include "tbigmemorymanager.h"
21 #include "tfilepath.h"
22 #include "timage_io.h"
23 
24 // Qt includes
25 #include <QSettings>
26 #include <QStringList>
27 #include <QAction>
28 #include <QColor>
29 #include <QTextStream>
30 
31 // boost includes
32 #include <boost/bind.hpp>
33 
34 //**********************************************************************************
35 //    Local namespace  stuff
36 //**********************************************************************************
37 
38 namespace {
39 
40 typedef Preferences::LevelFormat LevelFormat;
41 typedef std::vector<LevelFormat> LevelFormatVector;
42 
43 //-----------------------------------------------------------------
44 
45 const char *s_levelFormats = "levelFormats";
46 
47 const char *s_name = "name", *s_regexp = "regexp", *s_priority = "priority";
48 
49 const char *s_dpiPolicy = "dpiPolicy", *s_dpi = "dpi",
50            *s_subsampling = "subsampling", *s_antialias = "antialias",
51            *s_premultiply = "premultiply", *s_whiteTransp = "whiteTransp";
52 
53 //=================================================================
54 
colorToString(const QColor & color)55 inline QString colorToString(const QColor &color) {
56   return QString("%1 %2 %3 %4")
57       .arg(color.red())
58       .arg(color.green())
59       .arg(color.blue())
60       .arg(color.alpha());
61 }
62 
stringToColor(const QString & str)63 inline QColor stringToColor(const QString &str) {
64   QStringList values = str.split(' ');
65   return QColor(values[0].toInt(), values[1].toInt(), values[2].toInt(),
66                 values[3].toInt());
67 }
68 
colorToTPixel(const QColor & color)69 inline TPixel colorToTPixel(const QColor &color) {
70   return TPixel(color.red(), color.green(), color.blue(), color.alpha());
71 }
72 
73 //-----------------------------------------------------------------
74 
setCurrentUnits(std::string measureName,std::string units)75 static void setCurrentUnits(std::string measureName, std::string units) {
76   TMeasure *m = TMeasureManager::instance()->get(measureName);
77   if (!m) return;
78   TUnit *u = m->getUnit(::to_wstring(units));
79   if (!u) return;
80   m->setCurrentUnit(u);
81 }
82 
83 //-----------------------------------------------------------------
84 
formatLess(const Preferences::LevelFormat & a,const Preferences::LevelFormat & b)85 inline bool formatLess(const Preferences::LevelFormat &a,
86                        const Preferences::LevelFormat &b) {
87   return (
88       a.m_priority > b.m_priority  // Observe '>' used here - we want inverse
89       || (!(b.m_priority >
90             a.m_priority)  // sorting on priority, higher priorities come first
91           && a.m_name < b.m_name));
92 }
93 
94 //=================================================================
95 
getDefaultLevelFormats(LevelFormatVector & lfv)96 void getDefaultLevelFormats(LevelFormatVector &lfv) {
97   lfv.resize(3);
98   {
99     LevelFormat &lf = lfv[0];
100 
101     lf.m_name       = Preferences::tr("Retas Level Format");
102     lf.m_pathFormat = QRegExp(".+[0-9]{4,4}\\.tga", Qt::CaseInsensitive);
103     lf.m_options.m_whiteTransp = true;
104     lf.m_options.m_antialias   = 70;
105 
106     // for all PSD files, set the premultiply options to layers
107     lfv[1].m_name                  = Preferences::tr("Adobe Photoshop");
108     lfv[1].m_pathFormat            = QRegExp("..*\\.psd", Qt::CaseInsensitive);
109     lfv[1].m_options.m_premultiply = true;
110 
111     // for all PNG files, set premultiply by default
112     // UPDATE : from V1.5, PNG images are premultiplied on loading
113     // lfv[2].m_name                  = Preferences::tr("PNG");
114     // lfv[2].m_pathFormat            = QRegExp("..*\\.png",
115     // Qt::CaseInsensitive); lfv[2].m_options.m_premultiply = true;
116   }
117 }
118 
119 //=================================================================
120 
setValue(QSettings & settings,const LevelOptions & lo)121 void setValue(QSettings &settings, const LevelOptions &lo) {
122   settings.setValue(s_dpiPolicy, int(lo.m_dpiPolicy));
123   settings.setValue(s_dpi, lo.m_dpi);
124   settings.setValue(s_subsampling, lo.m_subsampling);
125   settings.setValue(s_antialias, lo.m_antialias);
126   settings.setValue(s_premultiply, int(lo.m_premultiply));
127   settings.setValue(s_whiteTransp, int(lo.m_whiteTransp));
128 }
129 
130 //-----------------------------------------------------------------
131 
getValue(const QSettings & settings,LevelOptions & lo)132 void getValue(const QSettings &settings, LevelOptions &lo) {
133   int dpiPolicy    = settings.value(s_dpiPolicy, int(lo.m_dpiPolicy)).toInt();
134   lo.m_dpiPolicy   = LevelOptions::DpiPolicy(dpiPolicy);
135   lo.m_dpi         = settings.value(s_dpi, lo.m_dpi).toDouble();
136   lo.m_subsampling = settings.value(s_subsampling, lo.m_subsampling).toInt();
137   lo.m_antialias   = settings.value(s_antialias, lo.m_antialias).toInt();
138   lo.m_premultiply =
139       (settings.value(s_premultiply, lo.m_premultiply).toInt() != 0);
140   lo.m_whiteTransp =
141       (settings.value(s_whiteTransp, lo.m_whiteTransp).toInt() != 0);
142 }
143 
144 //-----------------------------------------------------------------
145 
setValue(QSettings & settings,const LevelFormat & lf)146 void setValue(QSettings &settings, const LevelFormat &lf) {
147   settings.setValue(s_name, lf.m_name);
148   settings.setValue(s_regexp, lf.m_pathFormat.pattern());
149   settings.setValue(s_priority, lf.m_priority);
150   setValue(settings, lf.m_options);
151 }
152 
153 //-----------------------------------------------------------------
154 
getValue(const QSettings & settings,LevelFormat & lf)155 void getValue(const QSettings &settings, LevelFormat &lf) {
156   lf.m_name = settings.value(s_name, lf.m_name).toString();
157   lf.m_pathFormat =
158       QRegExp(settings.value(s_regexp, lf.m_pathFormat).toString(),
159               Qt::CaseInsensitive);
160   lf.m_priority = settings.value(s_priority, lf.m_priority).toInt();
161   getValue(settings, lf.m_options);
162 }
163 
164 //-----------------------------------------------------------------
165 
_setValue(QSettings & settings,const LevelFormatVector & lfv)166 void _setValue(QSettings &settings, const LevelFormatVector &lfv) {
167   int lf, lfCount = int(lfv.size());
168 
169   settings.remove(s_levelFormats);
170 
171   settings.beginWriteArray(s_levelFormats, lfCount);
172   {
173     for (lf = 0; lf != lfCount; ++lf) {
174       settings.setArrayIndex(lf);
175       setValue(settings, lfv[lf]);
176     }
177   }
178   settings.endArray();
179 }
180 
181 //-----------------------------------------------------------------
182 
getValue(QSettings & settings,LevelFormatVector & lfv)183 void getValue(QSettings &settings,
184               LevelFormatVector &lfv)  // Why does QSettings' interface require
185 {  // non-const access on reading arrays/groups?
186   if (!settings.childGroups().contains(s_levelFormats))
187     return;  // Default is no level formats - use builtins
188 
189   int lfCount = settings.beginReadArray(s_levelFormats);  // lfCount could be 0
190   lfv.resize(lfCount);
191 
192   for (int lf = 0; lf != lfCount; ++lf) {
193     settings.setArrayIndex(lf);
194     getValue(settings, lfv[lf]);
195   }
196   settings.endArray();
197 
198   // from OT V1.5, PNG images are premultiplied on loading.
199   // Leaving the premultiply option will cause unwanted double operation.
200   // So, check the loaded options and modify it "silently".
201   bool changed                   = false;
202   LevelFormatVector::iterator it = lfv.begin();
203   while (it != lfv.end()) {
204     if ((*it).m_name == Preferences::tr("PNG") &&
205         (*it).m_pathFormat == QRegExp("..*\\.png", Qt::CaseInsensitive) &&
206         (*it).m_options.m_premultiply == true) {
207       LevelOptions defaultValue;
208       defaultValue.m_premultiply = true;
209       // if other parameters are the same as deafault, just erase the item
210       if ((*it).m_options == defaultValue) it = lfv.erase(it);
211       // if there are some adjustments by user, then disable only premultiply
212       // option
213       else {
214         (*it).m_options.m_premultiply = false;
215         ++it;
216       }
217       changed = true;
218     } else
219       ++it;
220   }
221   // overwrite the setting
222   if (changed) _setValue(settings, lfv);
223 }
224 
225 }  // namespace
226 
227 //**********************************************************************************
228 //    Preferences::LevelFormat  implementation
229 //**********************************************************************************
230 
matches(const TFilePath & fp) const231 bool Preferences::LevelFormat::matches(const TFilePath &fp) const {
232   return m_pathFormat.exactMatch(fp.getQString());
233 }
234 
235 //**********************************************************************************
236 //    Preferences  implementation
237 //**********************************************************************************
238 
Preferences()239 Preferences::Preferences() {
240   // load preference file
241   TFilePath layoutDir = ToonzFolder::getMyModuleDir();
242   TFilePath prefPath  = layoutDir + TFilePath("preferences.ini");
243   // In case the personal settings is not exist (for new users)
244   if (!TFileStatus(prefPath).doesExist()) {
245     TFilePath templatePath =
246         ToonzFolder::getTemplateModuleDir() + TFilePath("preferences.ini");
247     // If there is the template, copy it to the personal one
248     if (TFileStatus(templatePath).doesExist())
249       TSystem::copyFile(prefPath, templatePath);
250   }
251   m_settings.reset(new QSettings(
252       QString::fromStdWString(prefPath.getWideString()), QSettings::IniFormat));
253 
254   initializeOptions();
255 
256   definePreferenceItems();
257   // resolve compatibility for deprecated items
258   resolveCompatibility();
259 
260   // initialize environment based on loaded preferences
261   setUnits();
262   setCameraUnits();
263   setUndoMemorySize();
264 
265   // Load level formats
266   getDefaultLevelFormats(m_levelFormats);
267   getValue(*m_settings, m_levelFormats);
268   std::sort(m_levelFormats.begin(),
269             m_levelFormats.end(),  // Format sorting must be
270             formatLess);           // enforced
271 
272   if (m_roomMaps.key(getStringValue(CurrentRoomChoice), -1) == -1) {
273     assert(!m_roomMaps.isEmpty());
274     setValue(CurrentRoomChoice, m_roomMaps[0]);
275   }
276 
277   if (!m_styleSheetList.contains(getStringValue(CurrentStyleSheetName)))
278     setValue(CurrentStyleSheetName, "Default");
279 
280   if (!m_languageList.contains(getStringValue(CurrentLanguageName)))
281     setValue(CurrentLanguageName, "English");
282 
283   TImageWriter::setBackgroundColor(getColorValue(rasterBackgroundColor));
284 }
285 
286 //-----------------------------------------------------------------
287 
~Preferences()288 Preferences::~Preferences() {
289   // DO NOT REMOVE
290 }
291 
292 //-----------------------------------------------------------------
293 
instance()294 Preferences *Preferences::instance() {
295   static Preferences _instance;
296   return &_instance;
297 }
298 
299 //-----------------------------------------------------------------
300 // load and initialize options for languages, styles and rooms
301 
initializeOptions()302 void Preferences::initializeOptions() {
303   // load languages
304   TFilePath lang_path = TEnv::getConfigDir() + "loc";
305   TFilePathSet lang_fpset;
306   m_languageList.append("English");
307   // m_currentLanguage=0;
308   try {
309     TFileStatus langPathFs(lang_path);
310 
311     if (langPathFs.doesExist() && langPathFs.isDirectory()) {
312       TSystem::readDirectory(lang_fpset, lang_path, true, false);
313     }
314 
315     int i = 0;
316     for (auto const &newPath : lang_fpset) {
317       ++i;
318       if (newPath == lang_path) continue;
319       if (TFileStatus(newPath).isDirectory()) {
320         QString string = QString::fromStdWString(newPath.getWideName());
321         m_languageList.append(string);
322       }
323     }
324   } catch (...) {
325   }
326 
327   // load styles
328   TFilePath path(TEnv::getConfigDir() + "qss");
329   TFilePathSet fpset;
330   try {
331     TSystem::readDirectory(fpset, path, true, false);
332     int i = -1;
333     for (auto const &newPath : fpset) {
334       ++i;
335       if (newPath == path) continue;
336       QString fpName = QString::fromStdWString(newPath.getWideName());
337       m_styleSheetList.append(fpName);
338     }
339   } catch (...) {
340   }
341 
342   // load rooms or layouts
343   TFilePath room_path(ToonzFolder::getRoomsDir());
344   TFilePathSet room_fpset;
345   try {
346     TSystem::readDirectory(room_fpset, room_path, true, false);
347     TFilePathSet::iterator it = room_fpset.begin();
348     for (int i = 0; it != room_fpset.end(); it++, i++) {
349       TFilePath newPath = *it;
350       if (newPath == room_path) continue;
351       if (TFileStatus(newPath).isDirectory()) {
352         QString string = QString::fromStdWString(newPath.getWideName());
353         m_roomMaps[i]  = string;
354       }
355     }
356   } catch (...) {
357   }
358 }
359 
360 //-----------------------------------------------------------------
361 
definePreferenceItems()362 void Preferences::definePreferenceItems() {
363   // General
364   define(defaultViewerEnabled, "defaultViewerEnabled", QMetaType::Bool, false);
365   define(rasterOptimizedMemory, "rasterOptimizedMemory", QMetaType::Bool,
366          false);
367   define(startupPopupEnabled, "startupPopupEnabled", QMetaType::Bool, true);
368   define(undoMemorySize, "undoMemorySize", QMetaType::Int, 100, 0, 2000);
369   define(taskchunksize, "taskchunksize", QMetaType::Int, 10, 1, 2000);
370   define(sceneNumberingEnabled, "sceneNumberingEnabled", QMetaType::Bool,
371          false);
372   define(watchFileSystemEnabled, "watchFileSystemEnabled", QMetaType::Bool,
373          true);
374   define(projectRoot, "projectRoot", QMetaType::Int, 0x08);
375   define(customProjectRoot, "customProjectRoot", QMetaType::QString, "");
376   define(pathAliasPriority, "pathAliasPriority", QMetaType::Int,
377          (int)ProjectFolderOnly);
378 
379   setCallBack(undoMemorySize, &Preferences::setUndoMemorySize);
380 
381   // Interface
382   define(CurrentStyleSheetName, "CurrentStyleSheetName", QMetaType::QString,
383          "Default");
384 
385   // Qt has a bug in recent versions that Menu item Does not show correctly
386   // (QTBUG-90242) Since the current OT is made to handle such issue, so we need
387   // to apply an extra adjustment when it is run on the older versions (5.9.x)
388   // of Qt
389   // Update: confirmed that the bug does not appear at least in Qt 5.12.8
390   QString defaultAditionalSheet = "";
391 #if QT_VERSION < QT_VERSION_CHECK(5, 12, 9)
392   defaultAditionalSheet = "QMenu::Item{ padding: 3 28 3 28; }";
393 #endif
394 
395   define(additionalStyleSheet, "additionalStyleSheet", QMetaType::QString,
396          defaultAditionalSheet);
397   define(iconTheme, "iconTheme", QMetaType::Bool, false);
398   define(pixelsOnly, "pixelsOnly", QMetaType::Bool, false);
399   define(oldUnits, "oldUnits", QMetaType::QString, "mm");
400   define(oldCameraUnits, "oldCameraUnits", QMetaType::QString, "inch");
401   define(linearUnits, "linearUnits", QMetaType::QString, "mm");
402   define(cameraUnits, "cameraUnits", QMetaType::QString, "inch");
403   define(CurrentRoomChoice, "CurrentRoomChoice", QMetaType::QString, "Default");
404   define(functionEditorToggle, "functionEditorToggle", QMetaType::Int,
405          (int)ShowGraphEditorInPopup);
406   define(moveCurrentFrameByClickCellArea, "moveCurrentFrameByClickCellArea",
407          QMetaType::Bool, true);
408   define(actualPixelViewOnSceneEditingMode, "actualPixelViewOnSceneEditingMode",
409          QMetaType::Bool, false);
410   define(levelNameOnEachMarkerEnabled, "levelNameOnEachMarkerEnabled",
411          QMetaType::Bool, false);
412   define(showRasterImagesDarkenBlendedInViewer,
413          "showRasterImagesDarkenBlendedInViewer", QMetaType::Bool, false);
414   define(showFrameNumberWithLetters, "showFrameNumberWithLetters",
415          QMetaType::Bool, false);
416   define(iconSize, "iconSize", QMetaType::QSize, QSize(80, 45), QSize(10, 10),
417          QSize(400, 400));
418   define(viewShrink, "viewShrink", QMetaType::Int, 1, 1, 20);
419   define(viewStep, "viewStep", QMetaType::Int, 1, 1, 20);
420   define(viewerZoomCenter, "viewerZoomCenter", QMetaType::Int,
421          0);  // Mouse Cursor
422   define(CurrentLanguageName, "CurrentLanguageName", QMetaType::QString,
423          "English");
424 #ifdef _WIN32
425   QString defaultFont("Segoe UI");
426 #elif defined Q_OS_MACOS
427   QString defaultFont("Helvetica Neue");
428 #else
429   QString defaultFont("Helvetica");
430 #endif
431   define(interfaceFont, "interfaceFont", QMetaType::QString, defaultFont);
432   define(interfaceFontStyle, "interfaceFontStyle", QMetaType::QString,
433          "Regular");
434   define(colorCalibrationEnabled, "colorCalibrationEnabled", QMetaType::Bool,
435          false);
436   define(colorCalibrationLutPaths, "colorCalibrationLutPaths",
437          QMetaType::QVariantMap, QVariantMap());
438 
439   // hide menu icons by default in macOS since the icon color may not match with
440   // the system color theme
441 #ifdef Q_OS_MACOS
442   bool defIconsVisible = false;
443 #else
444   bool defIconsVisible = true;
445 #endif
446   define(showIconsInMenu, "showIconsInMenu", QMetaType::Bool, defIconsVisible);
447 
448   setCallBack(pixelsOnly, &Preferences::setPixelsOnly);
449   setCallBack(linearUnits, &Preferences::setUnits);
450   setCallBack(cameraUnits, &Preferences::setCameraUnits);
451 
452   // Visualization
453   define(show0ThickLines, "show0ThickLines", QMetaType::Bool, true);
454   define(regionAntialias, "regionAntialias", QMetaType::Bool, false);
455 
456   // Loading
457   define(importPolicy, "importPolicy", QMetaType::Int, 0);  // Always ask
458   define(autoExposeEnabled, "autoExposeEnabled", QMetaType::Bool, true);
459   define(subsceneFolderEnabled, "subsceneFolderEnabled", QMetaType::Bool, true);
460   define(removeSceneNumberFromLoadedLevelName,
461          "removeSceneNumberFromLoadedLevelName", QMetaType::Bool, false);
462   define(IgnoreImageDpi, "IgnoreImageDpi", QMetaType::Bool, false);
463   define(initialLoadTlvCachingBehavior, "initialLoadTlvCachingBehavior",
464          QMetaType::Int, 0);  // On Demand
465   define(columnIconLoadingPolicy, "columnIconLoadingPolicy", QMetaType::Int,
466          (int)LoadAtOnce);
467   //"levelFormats" need to be handle separately
468 
469   // Saving
470   define(autosaveEnabled, "autosaveEnabled", QMetaType::Bool, false);
471   define(autosavePeriod, "autosavePeriod", QMetaType::Int, 15, 1, 60);
472   define(autosaveSceneEnabled, "autosaveSceneEnabled", QMetaType::Bool, true);
473   define(autosaveOtherFilesEnabled, "autosaveOtherFilesEnabled",
474          QMetaType::Bool, true);
475   define(replaceAfterSaveLevelAs, "replaceAfterSaveLevelAs", QMetaType::Bool,
476          true);
477   define(backupEnabled, "backupEnabled", QMetaType::Bool, true);
478   define(backupKeepCount, "backupKeepCount", QMetaType::Int, 1, 1,
479          std::numeric_limits<int>::max());
480   define(rasterBackgroundColor, "rasterBackgroundColor", QMetaType::QColor,
481          QColor(Qt::white));
482   define(resetUndoOnSavingLevel, "resetUndoOnSavingLevel", QMetaType::Bool,
483          true);
484 
485   setCallBack(rasterBackgroundColor, &Preferences::setRasterBackgroundColor);
486   setCallBack(autosaveEnabled, &Preferences::enableAutosave);
487   setCallBack(autosavePeriod, &Preferences::setAutosavePeriod);
488 
489   // Import / Export
490   define(ffmpegPath, "ffmpegPath", QMetaType::QString, "");
491   define(ffmpegTimeout, "ffmpegTimeout", QMetaType::Int, 600, 1,
492          std::numeric_limits<int>::max());
493   define(fastRenderPath, "fastRenderPath", QMetaType::QString, "desktop");
494 
495   // Drawing
496   define(scanLevelType, "scanLevelType", QMetaType::QString, "tif");
497   define(DefLevelType, "DefLevelType", QMetaType::Int, TZP_XSHLEVEL);
498   define(newLevelSizeToCameraSizeEnabled, "newLevelSizeToCameraSizeEnabled",
499          QMetaType::Bool, false);
500   define(DefLevelWidth, "DefLevelWidth", QMetaType::Double,
501          TCamera().getSize().lx, 0.1, std::numeric_limits<double>::max());
502   define(DefLevelHeight, "DefLevelHeight", QMetaType::Double,
503          TCamera().getSize().ly, 0.1, std::numeric_limits<double>::max());
504   define(DefLevelDpi, "DefLevelDpi", QMetaType::Double, TCamera().getDpi().x,
505          0.1, std::numeric_limits<double>::max());
506 
507   define(EnableAutocreation, "EnableAutocreation", QMetaType::Bool, true);
508   define(NumberingSystem, "NumberingSystem", QMetaType::Int, 0);  // Incremental
509   define(EnableAutoStretch, "EnableAutoStretch", QMetaType::Bool, true);
510   define(EnableCreationInHoldCells, "EnableCreationInHoldCells",
511          QMetaType::Bool, true);
512   define(EnableAutoRenumber, "EnableAutoRenumber", QMetaType::Bool, true);
513 
514   define(vectorSnappingTarget, "vectorSnappingTarget", QMetaType::Int,
515          (int)SnapAll);
516   define(saveUnpaintedInCleanup, "saveUnpaintedInCleanup", QMetaType::Bool,
517          true);
518   define(minimizeSaveboxAfterEditing, "minimizeSaveboxAfterEditing",
519          QMetaType::Bool, true);
520   define(useNumpadForSwitchingStyles, "useNumpadForSwitchingStyles",
521          QMetaType::Bool, true);
522   define(downArrowInLevelStripCreatesNewFrame,
523          "downArrowInLevelStripCreatesNewFrame", QMetaType::Bool, true);
524   define(keepFillOnVectorSimplify, "keepFillOnVectorSimplify", QMetaType::Bool,
525          true);
526   define(useHigherDpiOnVectorSimplify, "useHigherDpiOnVectorSimplify",
527          QMetaType::Bool, false);
528 
529   // Tools
530   define(dropdownShortcutsCycleOptions, "dropdownShortcutsCycleOptions",
531          QMetaType::Int,
532          1);  // Cycle through the available options (changed from bool to int)
533   define(FillOnlysavebox, "FillOnlysavebox", QMetaType::Bool, false);
534   define(multiLayerStylePickerEnabled, "multiLayerStylePickerEnabled",
535          QMetaType::Bool, false);
536   define(cursorBrushType, "cursorBrushType", QMetaType::QString, "Small");
537   define(cursorBrushStyle, "cursorBrushStyle", QMetaType::QString, "Default");
538   define(cursorOutlineEnabled, "cursorOutlineEnabled", QMetaType::Bool, true);
539   define(levelBasedToolsDisplay, "levelBasedToolsDisplay", QMetaType::Int,
540          0);  // Default
541   define(useCtrlAltToResizeBrush, "useCtrlAltToResizeBrush", QMetaType::Bool,
542          true);
543   define(tempToolSwitchTimer, "tempToolSwitchTimer", QMetaType::Int, 500, 1,
544          std::numeric_limits<int>::max());
545 
546   // Xsheet
547   define(xsheetLayoutPreference, "xsheetLayoutPreference", QMetaType::QString,
548          "Classic-revised");
549   define(xsheetStep, "xsheetStep", QMetaType::Int, 10, 0,
550          std::numeric_limits<int>::max());
551   define(xsheetAutopanEnabled, "xsheetAutopanEnabled", QMetaType::Bool, true);
552   define(DragCellsBehaviour, "DragCellsBehaviour", QMetaType::Int,
553          1);  // Cells and Column Data
554   define(ignoreAlphaonColumn1Enabled, "ignoreAlphaonColumn1Enabled",
555          QMetaType::Bool, false);
556   define(showKeyframesOnXsheetCellArea, "showKeyframesOnXsheetCellArea",
557          QMetaType::Bool, true);
558   define(showXsheetCameraColumn, "showXsheetCameraColumn", QMetaType::Bool,
559          true);
560   define(useArrowKeyToShiftCellSelection, "useArrowKeyToShiftCellSelection",
561          QMetaType::Bool, true);
562   define(inputCellsWithoutDoubleClickingEnabled,
563          "inputCellsWithoutDoubleClickingEnabled", QMetaType::Bool, false);
564   define(shortcutCommandsWhileRenamingCellEnabled,
565          "shortcutCommandsWhileRenamingCellEnabled", QMetaType::Bool, false);
566   define(showXSheetToolbar, "showXSheetToolbar", QMetaType::Bool, true);
567   define(expandFunctionHeader, "expandFunctionHeader", QMetaType::Bool, false);
568   define(showColumnNumbers, "showColumnNumbers", QMetaType::Bool, false);
569   define(syncLevelRenumberWithXsheet, "syncLevelRenumberWithXsheet",
570          QMetaType::Bool, true);
571   define(currentTimelineEnabled, "currentTimelineEnabled", QMetaType::Bool,
572          true);
573   define(currentColumnColor, "currentColumnColor", QMetaType::QColor,
574          QColor(Qt::yellow));
575 
576   // Animation
577   define(keyframeType, "keyframeType", QMetaType::Int, 2);  // Linear
578   define(animationStep, "animationStep", QMetaType::Int, 1, 1, 500);
579   define(modifyExpressionOnMovingReferences,
580          "modifyExpressionOnMovingReferences", QMetaType::Bool, false);
581 
582   // Preview
583   define(blanksCount, "blanksCount", QMetaType::Int, 0, 0, 1000);
584   define(blankColor, "blankColor", QMetaType::QColor, QColor(Qt::white));
585   define(rewindAfterPlayback, "rewindAfterPlayback", QMetaType::Bool, true);
586   define(shortPlayFrameCount, "shortPlayFrameCount", QMetaType::Int, 8, 1, 100);
587   define(previewAlwaysOpenNewFlip, "previewAlwaysOpenNewFlip", QMetaType::Bool,
588          false);
589   define(fitToFlipbook, "fitToFlipbook", QMetaType::Bool, false);
590   define(generatedMovieViewEnabled, "generatedMovieViewEnabled",
591          QMetaType::Bool, true);
592 
593   // Onion Skin
594   define(onionSkinEnabled, "onionSkinEnabled", QMetaType::Bool, true);
595   define(onionPaperThickness, "onionPaperThickness", QMetaType::Int, 50, 0,
596          100);
597   define(backOnionColor, "backOnionColor", QMetaType::QColor, QColor(Qt::red));
598   define(frontOnionColor, "frontOnionColor", QMetaType::QColor,
599          QColor(Qt::green));
600   define(onionInksOnly, "onionInksOnly", QMetaType::Bool, false);
601   define(onionSkinDuringPlayback, "onionSkinDuringPlayback", QMetaType::Bool,
602          false);
603   define(useOnionColorsForShiftAndTraceGhosts,
604          "useOnionColorsForShiftAndTraceGhosts", QMetaType::Bool, true);
605   define(animatedGuidedDrawing, "animatedGuidedDrawing", QMetaType::Int,
606          0);  // Arrow Markers (changed from bool to int)
607 
608   // Colors
609   define(viewerBGColor, "viewerBGColor", QMetaType::QColor,
610          QColor(128, 128, 128));
611   define(previewBGColor, "previewBGColor", QMetaType::QColor,
612          QColor(64, 64, 64));
613   define(levelEditorBoxColor, "levelEditorBoxColor", QMetaType::QColor,
614          QColor(128, 128, 128));
615   define(chessboardColor1, "chessboardColor1", QMetaType::QColor,
616          QColor(180, 180, 180));
617   define(chessboardColor2, "chessboardColor2", QMetaType::QColor,
618          QColor(230, 230, 230));
619   define(transpCheckInkOnWhite, "transpCheckInkOnWhite", QMetaType::QColor,
620          QColor(Qt::black));
621   define(transpCheckInkOnBlack, "transpCheckInkOnBlack", QMetaType::QColor,
622          QColor(Qt::white));
623   define(transpCheckPaint, "transpCheckPaint", QMetaType::QColor,
624          QColor(127, 127, 127));
625 
626   // Version Control
627   define(SVNEnabled, "SVNEnabled", QMetaType::Bool, false);
628   define(automaticSVNFolderRefreshEnabled, "automaticSVNFolderRefreshEnabled",
629          QMetaType::Bool, true);
630   define(latestVersionCheckEnabled, "latestVersionCheckEnabled",
631          QMetaType::Bool, true);
632 
633   // Touch / Tablet Settings
634   // TounchGestureControl // Touch Gesture is a checkable command and not in
635   // preferences.ini
636   define(winInkEnabled, "winInkEnabled", QMetaType::Bool, false);
637   // This option will be shown & available only when WITH_WINTAB is defined
638   define(useQtNativeWinInk, "useQtNativeWinInk", QMetaType::Bool, false);
639 
640   // Others (not appeared in the popup)
641   // Shortcut popup settings
642   define(shortcutPreset, "shortcutPreset", QMetaType::QString, "defopentoonz");
643   // Viewer context menu
644   define(guidedDrawingType, "guidedDrawingType", QMetaType::Int, 0);  // Off
645   define(guidedAutoInbetween, "guidedAutoInbetween", QMetaType::Bool,
646          false);  // Off
647   define(guidedInterpolationType, "guidedInterpolationType", QMetaType::Int,
648          1);  // Linear
649 #if defined(MACOSX) && defined(__LP64__)
650   // OSX shared memory settings
651   define(shmmax, "shmmax", QMetaType::Int, -1);
652   define(shmseg, "shmseg", QMetaType::Int, -1);
653   define(shmall, "shmall", QMetaType::Int, -1);
654   define(shmmni, "shmmni", QMetaType::Int, -1);
655 #endif
656 }
657 
658 //-----------------------------------------------------------------
659 
define(PreferencesItemId id,QString idString,QMetaType::Type type,QVariant defaultValue,QVariant min,QVariant max)660 void Preferences::define(PreferencesItemId id, QString idString,
661                          QMetaType::Type type, QVariant defaultValue,
662                          QVariant min, QVariant max) {
663   // load value
664   QVariant value(defaultValue);
665   switch (type) {
666   case QMetaType::Bool:
667   case QMetaType::Int:
668   case QMetaType::Double:
669   case QMetaType::QString:
670     if (m_settings->contains(idString) &&
671         m_settings->value(idString).canConvert(type))
672       value = m_settings->value(idString);
673     break;
674   case QMetaType::QSize:  // used in iconSize
675     if (m_settings->contains(idString) &&
676         m_settings->value(idString).canConvert(QMetaType::QSize))
677       value = m_settings->value(idString);
678     // to keep compatibility with older versions
679     else if (m_settings->contains(idString + "X")) {
680       QSize size = value.toSize();
681       size.setWidth(m_settings->value(idString + "X", size.width()).toInt());
682       size.setHeight(m_settings->value(idString + "Y", size.height()).toInt());
683       value.setValue(size);
684     }
685     break;
686   case QMetaType::QMetaType::QColor:
687     if (m_settings->contains(idString)) {
688       QString str = m_settings->value(idString).toString();
689       value.setValue(stringToColor(str));
690     }
691     // following two conditions are to keep compatibility with older versions
692     else if (m_settings->contains(idString + "_R")) {
693       QColor color = value.value<QColor>();
694       color.setRed(m_settings->value(idString + "_R", color.red()).toInt());
695       color.setGreen(m_settings->value(idString + "_G", color.green()).toInt());
696       color.setBlue(m_settings->value(idString + "_B", color.blue()).toInt());
697       color.setAlpha(m_settings->value(idString + "_M", color.alpha()).toInt());
698       value.setValue(color);
699     } else if (m_settings->contains(idString + ".r")) {
700       QColor color = value.value<QColor>();
701       color.setRed(m_settings->value(idString + ".r", color.red()).toInt());
702       color.setGreen(m_settings->value(idString + ".g", color.green()).toInt());
703       color.setBlue(m_settings->value(idString + ".b", color.blue()).toInt());
704       color.setAlpha(255);
705       value.setValue(color);
706     }
707     break;
708   case QMetaType::QVariantMap:  // used in colorCalibrationLutPaths
709     if (m_settings->contains(idString) &&
710         m_settings->value(idString).canConvert(type)) {
711       QMap<QString, QString> pathMap;
712       QAssociativeIterable iterable =
713           m_settings->value(idString).value<QAssociativeIterable>();
714       QAssociativeIterable::const_iterator it        = iterable.begin();
715       const QAssociativeIterable::const_iterator end = iterable.end();
716       for (; it != end; ++it)
717         pathMap.insert(it.key().toString(), it.value().toString());
718       value.setValue(pathMap);
719     }
720     break;
721   default:
722     std::cout << "Unsupported type detected" << std::endl;
723     // load anyway
724     value = m_settings->value(idString, value);
725     break;
726   }
727 
728   m_items.insert(id, PreferencesItem(idString, type, value, min, max));
729 }
730 
731 //-----------------------------------------------------------------
732 
setCallBack(const PreferencesItemId id,OnEditedFunc func)733 void Preferences::setCallBack(const PreferencesItemId id, OnEditedFunc func) {
734   getItem(id).onEditedFunc = func;
735 }
736 
737 //-----------------------------------------------------------------
738 
resolveCompatibility()739 void Preferences::resolveCompatibility() {
740   // autocreation type is divided into "EnableAutocreation" and
741   // "NumberingSystem"
742   if (m_settings->contains("AutocreationType") &&
743       !m_settings->contains("EnableAutocreation")) {
744     int type = m_settings->value("AutocreationType").toInt();
745     switch (type) {
746     case 0:  // former "Disabled"
747       setValue(EnableAutocreation, false);
748       break;
749     case 1:  // former "Enabled"
750       setValue(EnableAutocreation, true);
751       setValue(NumberingSystem, 0);  // set numbering system to "Incremental"
752       break;
753     case 2:  // former "Use Xsheet as Animation Sheet"
754       setValue(EnableAutocreation, true);
755       setValue(NumberingSystem, 1);
756       break;
757     }
758   }
759 }
760 
761 //-----------------------------------------------------------------
762 
getItem(const PreferencesItemId id)763 PreferencesItem &Preferences::getItem(const PreferencesItemId id) {
764   assert(m_items.contains(id));
765   return m_items[id];
766 }
767 
768 //-----------------------------------------------------------------
769 
getBoolValue(const PreferencesItemId id) const770 bool Preferences::getBoolValue(const PreferencesItemId id) const {
771   assert(m_items.contains(id));
772   if (!m_items.contains(id)) return false;
773   PreferencesItem item = m_items.value(id);
774   assert(item.type == QMetaType::Bool);
775   if (item.type != QMetaType::Bool) return false;
776 
777   return item.value.toBool();
778 }
779 
780 //-----------------------------------------------------------------
781 
getIntValue(const PreferencesItemId id) const782 int Preferences::getIntValue(const PreferencesItemId id) const {
783   assert(m_items.contains(id));
784   if (!m_items.contains(id)) return -1;
785   PreferencesItem item = m_items.value(id);
786   assert(item.type == QMetaType::Int);
787   if (item.type != QMetaType::Int) return -1;
788 
789   return item.value.toInt();
790 }
791 
792 //-----------------------------------------------------------------
793 
getDoubleValue(const PreferencesItemId id) const794 double Preferences::getDoubleValue(const PreferencesItemId id) const {
795   assert(m_items.contains(id));
796   if (!m_items.contains(id)) return -1.0;
797   PreferencesItem item = m_items.value(id);
798   assert(item.type == QMetaType::Double);
799   if (item.type != QMetaType::Double) return -1.0;
800 
801   return item.value.toDouble();
802 }
803 
804 //-----------------------------------------------------------------
805 
getStringValue(const PreferencesItemId id) const806 QString Preferences::getStringValue(const PreferencesItemId id) const {
807   assert(m_items.contains(id));
808   if (!m_items.contains(id)) return QString();
809   PreferencesItem item = m_items.value(id);
810   assert(item.type == QMetaType::QString);
811   if (item.type != QMetaType::QString) return QString();
812 
813   return item.value.toString();
814 }
815 
816 //-----------------------------------------------------------------
817 
getColorValue(const PreferencesItemId id) const818 TPixel Preferences::getColorValue(const PreferencesItemId id) const {
819   assert(m_items.contains(id));
820   if (!m_items.contains(id)) return TPixel();
821   PreferencesItem item = m_items.value(id);
822   assert(item.type == QMetaType::QColor);
823   if (item.type != QMetaType::QColor) return TPixel();
824 
825   return colorToTPixel(item.value.value<QColor>());
826 }
827 
828 //-----------------------------------------------------------------
829 
getSizeValue(const PreferencesItemId id) const830 TDimension Preferences::getSizeValue(const PreferencesItemId id) const {
831   assert(m_items.contains(id));
832   if (!m_items.contains(id)) return TDimension();
833   PreferencesItem item = m_items.value(id);
834   assert(item.type == QMetaType::QSize);
835   if (item.type != QMetaType::QSize) return TDimension();
836   QSize size = item.value.toSize();
837   return TDimension(size.width(), size.height());
838 }
839 
840 //-----------------------------------------------------------------
841 // saveToFile is true by default, becomes false when dragging color field
setValue(const PreferencesItemId id,QVariant value,bool saveToFile)842 void Preferences::setValue(const PreferencesItemId id, QVariant value,
843                            bool saveToFile) {
844   assert(m_items.contains(id));
845   if (!m_items.contains(id)) return;
846   m_items[id].value = value;
847   if (saveToFile) {
848     if (m_items[id].type ==
849         QMetaType::QColor)  // write in human-readable format
850       m_settings->setValue(m_items[id].idString,
851                            colorToString(value.value<QColor>()));
852     else if (m_items[id].type ==
853              QMetaType::Bool)  // write 1/0 instead of true/false to keep
854                                // compatibility
855       m_settings->setValue(m_items[id].idString, value.toBool() ? "1" : "0");
856     else
857       m_settings->setValue(m_items[id].idString, value);
858   }
859 
860   // execute callback
861   if (m_items[id].onEditedFunc) (this->*(m_items[id].onEditedFunc))();
862 }
863 
864 //-----------------------------------------------------------------
865 
enableAutosave()866 void Preferences::enableAutosave() {
867   bool autoSaveOn = getBoolValue(autosaveEnabled);
868   if (autoSaveOn)
869     emit startAutoSave();
870   else
871     emit stopAutoSave();
872 }
873 
874 //-----------------------------------------------------------------
875 
setAutosavePeriod()876 void Preferences::setAutosavePeriod() {
877   emit stopAutoSave();
878   emit startAutoSave();
879   emit autoSavePeriodChanged();
880 }
881 
882 //-----------------------------------------------------------------
883 
setUndoMemorySize()884 void Preferences::setUndoMemorySize() {
885   int memorySize = getIntValue(undoMemorySize);
886   TUndoManager::manager()->setUndoMemorySize(memorySize);
887 }
888 
889 //-----------------------------------------------------------------
890 
setPixelsOnly()891 void Preferences::setPixelsOnly() {
892   bool pixelSelected = getBoolValue(pixelsOnly);
893   if (pixelSelected)
894     storeOldUnits();
895   else
896     resetOldUnits();
897 }
898 
899 //-----------------------------------------------------------------
900 
setUnits()901 void Preferences::setUnits() {
902   std::string units = getStringValue(linearUnits).toStdString();
903   setCurrentUnits("length", units);
904   setCurrentUnits("length.x", units);
905   setCurrentUnits("length.y", units);
906   setCurrentUnits("length.lx", units);
907   setCurrentUnits("length.ly", units);
908   setCurrentUnits("fxLength", units);
909   setCurrentUnits("pippo", units);
910 }
911 
912 //-----------------------------------------------------------------
913 
setCameraUnits()914 void Preferences::setCameraUnits() {
915   std::string units = getStringValue(cameraUnits).toStdString();
916   setCurrentUnits("camera.lx", units);
917   setCurrentUnits("camera.ly", units);
918 }
919 
920 //-----------------------------------------------------------------
921 
setRasterBackgroundColor()922 void Preferences::setRasterBackgroundColor() {
923   TPixel color = getColorValue(rasterBackgroundColor);
924   TImageWriter::setBackgroundColor(color);
925 }
926 
927 //-----------------------------------------------------------------
928 
storeOldUnits()929 void Preferences::storeOldUnits() {
930   setValue(oldUnits, getStringValue(linearUnits));
931   setValue(oldCameraUnits, getStringValue(cameraUnits));
932 }
933 
934 //-----------------------------------------------------------------
935 
resetOldUnits()936 void Preferences::resetOldUnits() {
937   QString oldLinearU = getStringValue(oldUnits);
938   QString oldCameraU = getStringValue(oldCameraUnits);
939   if (oldLinearU != "" && oldCameraU != "") {
940     setValue(linearUnits, oldLinearU);
941     setValue(cameraUnits, oldCameraU);
942   }
943 }
944 
945 //-----------------------------------------------------------------
946 
getCurrentLanguage() const947 QString Preferences::getCurrentLanguage() const {
948   QString lang = getStringValue(CurrentLanguageName);
949   if (m_languageList.contains(lang)) return lang;
950   // If no valid option selected, then return English
951   return m_languageList[0];
952 }
953 
954 //-----------------------------------------------------------------
955 
getCurrentStyleSheet() const956 QString Preferences::getCurrentStyleSheet() const {
957   QString currentStyleSheetName = getStringValue(CurrentStyleSheetName);
958   if (currentStyleSheetName.isEmpty()) return QString();
959   TFilePath path(TEnv::getConfigDir() + "qss");
960   QString string = currentStyleSheetName + QString("/") +
961                    currentStyleSheetName + QString(".qss");
962   QString styleSheetPath = path.getQString() + "/" + string;
963 
964   QString additionalSheetStr = getStringValue(additionalStyleSheet);
965   // if there is no additional style sheet, return the path and let
966   // Qt to load and parse it
967   if (additionalSheetStr.isEmpty()) return QString("file:///" + styleSheetPath);
968 
969   // if there is any additional style sheet, load the style sheet
970   // from the file and combine with it
971   QString styleSheetStr;
972   QFile f(styleSheetPath);
973   if (f.open(QFile::ReadOnly | QFile::Text)) {
974     QTextStream ts(&f);
975     styleSheetStr = ts.readAll();
976   }
977   styleSheetStr += additionalSheetStr;
978 
979   // here we will convert all relative paths to absolute paths
980   // or Qt will look for images relative to the current working directory
981   // since it has no idea where the style sheet comes from.
982 
983   QString currentStyleFolderPath =
984       path.getQString().replace("\\", "/") + "/" + currentStyleSheetName;
985 
986   styleSheetStr.replace(QRegExp("url\\(['\"]([^'\"]+)['\"]\\)"),
987                         "url(\"" + currentStyleFolderPath + QString("/\\1\")"));
988 
989   return styleSheetStr;
990 }
991 
992 //-----------------------------------------------------------------
993 
setPrecompute(bool enabled)994 void Preferences::setPrecompute(bool enabled) { m_precompute = enabled; }
995 
996 //-----------------------------------------------------------------
997 
addLevelFormat(const LevelFormat & format)998 int Preferences::addLevelFormat(const LevelFormat &format) {
999   LevelFormatVector::iterator lft = m_levelFormats.insert(
1000       std::upper_bound(m_levelFormats.begin(), m_levelFormats.end(), format,
1001                        formatLess),
1002       format);
1003 
1004   int formatIdx = int(
1005       lft -
1006       m_levelFormats.begin());  // NOTE: Must be disjoint from the instruction
1007   //       above, since operator-'s param evaluation
1008   //       order is unspecified
1009   _setValue(*m_settings, m_levelFormats);
1010 
1011   return formatIdx;
1012 }
1013 
1014 //-----------------------------------------------------------------
1015 
removeLevelFormat(int formatIdx)1016 void Preferences::removeLevelFormat(int formatIdx) {
1017   assert(0 <= formatIdx && formatIdx < int(m_levelFormats.size()));
1018   m_levelFormats.erase(m_levelFormats.begin() + formatIdx);
1019 
1020   _setValue(*m_settings, m_levelFormats);
1021 }
1022 
1023 //-----------------------------------------------------------------
1024 
levelFormat(int formatIdx) const1025 const Preferences::LevelFormat &Preferences::levelFormat(int formatIdx) const {
1026   assert(0 <= formatIdx && formatIdx < int(m_levelFormats.size()));
1027   return m_levelFormats[formatIdx];
1028 }
1029 
1030 //-----------------------------------------------------------------
1031 
levelFormatsCount() const1032 int Preferences::levelFormatsCount() const {
1033   return int(m_levelFormats.size());
1034 }
1035 
1036 //-----------------------------------------------------------------
1037 
matchLevelFormat(const TFilePath & fp) const1038 int Preferences::matchLevelFormat(const TFilePath &fp) const {
1039   LevelFormatVector::const_iterator lft =
1040       std::find_if(m_levelFormats.begin(), m_levelFormats.end(),
1041                    boost::bind(&LevelFormat::matches, _1, boost::cref(fp)));
1042 
1043   return (lft != m_levelFormats.end()) ? lft - m_levelFormats.begin() : -1;
1044 }
1045 
1046 //-----------------------------------------------------------------
1047 
setColorCalibrationLutPath(QString monitorName,QString path)1048 void Preferences::setColorCalibrationLutPath(QString monitorName,
1049                                              QString path) {
1050   PreferencesItem item = m_items.value(colorCalibrationLutPaths);
1051   QMap<QString, QVariant> lutPathMap =
1052       item.value.value<QMap<QString, QVariant>>();
1053   lutPathMap.insert(monitorName, path);
1054   setValue(colorCalibrationLutPaths, lutPathMap);
1055 }
1056 
1057 //-----------------------------------------------------------------
1058 
getColorCalibrationLutPath(QString & monitorName) const1059 QString Preferences::getColorCalibrationLutPath(QString &monitorName) const {
1060   PreferencesItem item = m_items.value(colorCalibrationLutPaths);
1061   QMap<QString, QVariant> lutPathMap =
1062       item.value.value<QMap<QString, QVariant>>();
1063 
1064   return lutPathMap.value(monitorName).toString();
1065 }
1066