1 #include "Settings.h"
2
3 #include <utility>
4
5 #include "model/FormatDefinitions.h"
6 #include "util/DeviceListHelper.h"
7
8 #include "ButtonConfig.h"
9 #include "Util.h"
10 #include "filesystem.h"
11 #include "i18n.h"
12
13 constexpr auto const* DEFAULT_FONT = "Sans";
14 constexpr auto DEFAULT_FONT_SIZE = 12;
15
16 #define SAVE_BOOL_PROP(var) xmlNode = saveProperty((const char*)#var, (var) ? "true" : "false", root)
17 #define SAVE_STRING_PROP(var) xmlNode = saveProperty((const char*)#var, (var).empty() ? "" : (var).c_str(), root)
18 #define SAVE_INT_PROP(var) xmlNode = saveProperty((const char*)#var, var, root)
19 #define SAVE_UINT_PROP(var) xmlNode = savePropertyUnsigned((const char*)#var, var, root)
20 #define SAVE_DOUBLE_PROP(var) xmlNode = savePropertyDouble((const char*)#var, var, root)
21 #define ATTACH_COMMENT(var) \
22 com = xmlNewComment((const xmlChar*)(var)); \
23 xmlAddPrevSibling(xmlNode, com);
24
Settings(fs::path filepath)25 Settings::Settings(fs::path filepath): filepath(std::move(filepath)) { loadDefault(); }
26
~Settings()27 Settings::~Settings() {
28 for (auto& i: this->buttonConfig) {
29 delete i;
30 i = nullptr;
31 }
32 }
33
loadDefault()34 void Settings::loadDefault() {
35 this->pressureSensitivity = true;
36 this->minimumPressure = 0.05;
37 this->pressureMultiplier = 1.0;
38 this->pressureGuessing = false;
39 this->zoomGesturesEnabled = true;
40
41 this->maximized = false;
42 this->showPairedPages = false;
43 this->presentationMode = false;
44
45 this->numColumns = 1; // only one of these applies at a time
46 this->numRows = 1;
47 this->viewFixedRows = false;
48
49 this->layoutVertical = false;
50 this->layoutRightToLeft = false;
51 this->layoutBottomToTop = false;
52
53 this->numPairsOffset = 1;
54
55 this->zoomStep = 10.0;
56 this->zoomStepScroll = 2.0;
57
58 this->displayDpi = 72;
59
60 this->font.setName(DEFAULT_FONT);
61 this->font.setSize(DEFAULT_FONT_SIZE);
62
63 this->mainWndWidth = 800;
64 this->mainWndHeight = 600;
65
66 this->showSidebar = true;
67 this->sidebarWidth = 150;
68
69 this->showToolbar = true;
70
71 this->sidebarOnRight = false;
72
73 this->scrollbarOnLeft = false;
74
75 this->menubarVisible = true;
76
77 this->autoloadMostRecent = false;
78 this->autoloadPdfXoj = true;
79
80 this->stylusCursorType = STYLUS_CURSOR_DOT;
81 this->highlightPosition = false;
82 this->cursorHighlightColor = 0x80FFFF00; // Yellow with 50% opacity
83 this->cursorHighlightRadius = 30.0;
84 this->cursorHighlightBorderColor = 0x800000FF; // Blue with 50% opacity
85 this->cursorHighlightBorderWidth = 0.0;
86 this->darkTheme = false;
87 this->useStockIcons = false;
88 this->scrollbarHideType = SCROLLBAR_HIDE_NONE;
89 this->disableScrollbarFadeout = false;
90
91 // Set this for autosave frequency in minutes.
92 this->autosaveTimeout = 3;
93 this->autosaveEnabled = true;
94
95 this->addHorizontalSpace = false;
96 this->addHorizontalSpaceAmount = 150;
97 this->addVerticalSpace = false;
98 this->addVerticalSpaceAmount = 150;
99
100 // Drawing direction emulates modifier keys
101 this->drawDirModsRadius = 50;
102 this->drawDirModsEnabled = false;
103
104 this->snapRotation = true;
105 this->snapRotationTolerance = 0.30;
106
107 this->snapGrid = true;
108 this->snapGridTolerance = 0.50;
109 this->snapGridSize = DEFAULT_GRID_SIZE;
110
111 this->touchDrawing = false;
112
113 this->defaultSaveName = _("%F-Note-%H-%M");
114
115 // Eraser
116 this->buttonConfig[BUTTON_ERASER] =
117 new ButtonConfig(TOOL_ERASER, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
118 // Middle button
119 this->buttonConfig[BUTTON_MOUSE_MIDDLE] =
120 new ButtonConfig(TOOL_HAND, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
121 // Right button
122 this->buttonConfig[BUTTON_MOUSE_RIGHT] =
123 new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
124 // Touch
125 this->buttonConfig[BUTTON_TOUCH] =
126 new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
127 // Default config
128 this->buttonConfig[BUTTON_DEFAULT] =
129 new ButtonConfig(TOOL_PEN, Color{0x000000U}, TOOL_SIZE_FINE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
130 // Pen button 1
131 this->buttonConfig[BUTTON_STYLUS_ONE] =
132 new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
133 // Pen button 2
134 this->buttonConfig[BUTTON_STYLUS_TWO] =
135 new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
136
137 this->fullscreenHideElements = "mainMenubar";
138 this->presentationHideElements = "mainMenubar,sidebarContents";
139
140 this->touchZoomStartThreshold = 0.0;
141
142 this->pageRerenderThreshold = 5.0;
143 this->pdfPageCacheSize = 10;
144 this->preloadPagesBefore = 3U;
145 this->preloadPagesAfter = 5U;
146 this->eagerPageCleanup = true;
147
148 this->selectionBorderColor = 0xff0000U; // red
149 this->selectionMarkerColor = 0x729fcfU; // light blue
150
151 this->backgroundColor = 0xdcdad5U;
152
153 // clang-format off
154 this->pageTemplate = "xoj/template\ncopyLastPageSettings=true\nsize=595.275591x841.889764\nbackgroundType=lined\nbackgroundColor=#ffffff\n";
155 // clang-format on
156
157 this->audioSampleRate = 44100.0;
158 this->audioInputDevice = -1;
159 this->audioOutputDevice = -1;
160 this->audioGain = 1.0;
161 this->defaultSeekTime = 5;
162
163 this->pluginEnabled = "";
164 this->pluginDisabled = "";
165
166 this->numIgnoredStylusEvents = 0;
167
168 this->inputSystemTPCButton = false;
169 this->inputSystemDrawOutsideWindow = true;
170
171 this->strokeFilterIgnoreTime = 150;
172 this->strokeFilterIgnoreLength = 1;
173 this->strokeFilterSuccessiveTime = 500;
174 this->strokeFilterEnabled = false;
175 this->doActionOnStrokeFiltered = false;
176 this->trySelectOnStrokeFiltered = false;
177
178 this->snapRecognizedShapesEnabled = false;
179 this->restoreLineWidthEnabled = false;
180
181 this->inTransaction = false;
182
183 /**
184 * Stabilizer related settings
185 */
186 this->stabilizerAveragingMethod = StrokeStabilizer::AveragingMethod::NONE;
187 this->stabilizerPreprocessor = StrokeStabilizer::Preprocessor::NONE;
188 this->stabilizerBuffersize = 20;
189 this->stabilizerSigma = 0.5;
190 this->stabilizerDeadzoneRadius = 1.3;
191 this->stabilizerCuspDetection = true;
192 this->stabilizerDrag = 0.4;
193 this->stabilizerMass = 5.0;
194 this->stabilizerFinalizeStroke = true;
195 /**/
196 }
197
198 /**
199 * tempg_ascii_strtod
200 * Transition to using g_ascii_strtod to minimize disruption. May, 2019.
201 * Delete this and replace calls to this function with calls to g_ascii_strtod() in 2020.
202 * See: https://developer.gnome.org/glib/stable/glib-String-Utility-Functions.html#g-strtod
203 */
tempg_ascii_strtod(const gchar * txt,gchar ** endptr)204 auto tempg_ascii_strtod(const gchar* txt, gchar** endptr) -> double {
205 return g_strtod(txt,
206 endptr); // makes best guess between locale formatted and C formatted numbers. See link above.
207 }
208
209
parseData(xmlNodePtr cur,SElement & elem)210 void Settings::parseData(xmlNodePtr cur, SElement& elem) {
211 for (xmlNodePtr x = cur->children; x != nullptr; x = x->next) {
212 if (!xmlStrcmp(x->name, reinterpret_cast<const xmlChar*>("data"))) {
213 xmlChar* name = xmlGetProp(x, reinterpret_cast<const xmlChar*>("name"));
214 parseData(x, elem.child(reinterpret_cast<const char*>(name)));
215 xmlFree(name);
216 } else if (!xmlStrcmp(x->name, reinterpret_cast<const xmlChar*>("attribute"))) {
217 xmlChar* name = xmlGetProp(x, reinterpret_cast<const xmlChar*>("name"));
218 xmlChar* value = xmlGetProp(x, reinterpret_cast<const xmlChar*>("value"));
219 xmlChar* type = xmlGetProp(x, reinterpret_cast<const xmlChar*>("type"));
220
221 string sType = reinterpret_cast<const char*>(type);
222
223 if (sType == "int") {
224 int i = atoi(reinterpret_cast<const char*>(value));
225 elem.setInt(reinterpret_cast<const char*>(name), i);
226 } else if (sType == "double") {
227 double d = tempg_ascii_strtod(reinterpret_cast<const char*>(value),
228 nullptr); // g_ascii_strtod ignores locale setting.
229 elem.setDouble(reinterpret_cast<const char*>(name), d);
230 } else if (sType == "hex") {
231 int i = 0;
232 if (sscanf(reinterpret_cast<const char*>(value), "%x", &i)) {
233 elem.setIntHex(reinterpret_cast<const char*>(name), i);
234 } else {
235 g_warning("Settings::Unknown hex value: %s:%s\n", name, value);
236 }
237 } else if (sType == "string") {
238 elem.setString(reinterpret_cast<const char*>(name), reinterpret_cast<const char*>(value));
239 } else if (sType == "boolean") {
240 elem.setBool(reinterpret_cast<const char*>(name),
241 strcmp(reinterpret_cast<const char*>(value), "true") == 0);
242 } else {
243 g_warning("Settings::Unknown datatype: %s\n", sType.c_str());
244 }
245
246 xmlFree(name);
247 xmlFree(type);
248 xmlFree(value);
249 } else {
250 g_warning("Settings::parseData: Unknown XML node: %s\n", x->name);
251 continue;
252 }
253 }
254 }
255
parseItem(xmlDocPtr doc,xmlNodePtr cur)256 void Settings::parseItem(xmlDocPtr doc, xmlNodePtr cur) {
257 // Parse data map
258 if (!xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>("data"))) {
259 xmlChar* name = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("name"));
260 if (name == nullptr) {
261 g_warning("Settings::%s:No name property!\n", cur->name);
262 return;
263 }
264
265 parseData(cur, data[reinterpret_cast<const char*>(name)]);
266
267 xmlFree(name);
268 return;
269 }
270
271 if (cur->type == XML_COMMENT_NODE) {
272 return;
273 }
274
275 if (xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>("property"))) {
276 g_warning("Settings::Unknown XML node: %s\n", cur->name);
277 return;
278 }
279
280 xmlChar* name = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("name"));
281 if (name == nullptr) {
282 g_warning("Settings::%s:No name property!\n", cur->name);
283 return;
284 }
285
286 if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("font")) == 0) {
287 xmlFree(name);
288 xmlChar* font = nullptr;
289 xmlChar* size = nullptr;
290
291 font = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("font"));
292 if (font) {
293 this->font.setName(reinterpret_cast<const char*>(font));
294 xmlFree(font);
295 }
296
297 size = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("size"));
298 if (size) {
299 double dSize = DEFAULT_FONT_SIZE;
300 if (sscanf(reinterpret_cast<const char*>(size), "%lf", &dSize) == 1) {
301 this->font.setSize(dSize);
302 }
303 xmlFree(size);
304 }
305 return;
306 }
307
308 xmlChar* value = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("value"));
309 if (value == nullptr) {
310 xmlFree(name);
311 g_warning("No value property!\n");
312 return;
313 }
314
315 // TODO(fabian): remove this typo fix in 2-3 release cycles
316 if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("presureSensitivity")) == 0) {
317 this->pressureSensitivity = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
318 }
319 if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pressureSensitivity")) == 0) {
320 this->pressureSensitivity = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
321 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("minimumPressure")) == 0) {
322 this->minimumPressure = g_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
323 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pressureMultiplier")) == 0) {
324 this->pressureMultiplier = g_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
325 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("zoomGesturesEnabled")) == 0) {
326 this->zoomGesturesEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
327 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("selectedToolbar")) == 0) {
328 this->selectedToolbar = reinterpret_cast<const char*>(value);
329 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("lastSavePath")) == 0) {
330 this->lastSavePath = fs::u8path(reinterpret_cast<const char*>(value));
331 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("lastOpenPath")) == 0) {
332 this->lastOpenPath = fs::u8path(reinterpret_cast<const char*>(value));
333 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("lastImagePath")) == 0) {
334 this->lastImagePath = fs::u8path(reinterpret_cast<const char*>(value));
335 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("zoomStep")) == 0) {
336 this->zoomStep = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
337 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("zoomStepScroll")) == 0) {
338 this->zoomStepScroll = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
339 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("displayDpi")) == 0) {
340 this->displayDpi = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
341 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("mainWndWidth")) == 0) {
342 this->mainWndWidth = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
343 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("mainWndHeight")) == 0) {
344 this->mainWndHeight = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
345 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("maximized")) == 0) {
346 this->maximized = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
347 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("showToolbar")) == 0) {
348 this->showToolbar = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
349 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("showSidebar")) == 0) {
350 this->showSidebar = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
351 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("sidebarWidth")) == 0) {
352 this->sidebarWidth = std::max<int>(g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10), 50);
353 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("sidebarOnRight")) == 0) {
354 this->sidebarOnRight = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
355 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("scrollbarOnLeft")) == 0) {
356 this->scrollbarOnLeft = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
357 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("menubarVisible")) == 0) {
358 this->menubarVisible = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
359 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numColumns")) == 0) {
360 this->numColumns = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
361 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numRows")) == 0) {
362 this->numRows = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
363 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("viewFixedRows")) == 0) {
364 this->viewFixedRows = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
365 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("layoutVertical")) == 0) {
366 this->layoutVertical = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
367 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("layoutRightToLeft")) == 0) {
368 this->layoutRightToLeft = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
369 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("layoutBottomToTop")) == 0) {
370 this->layoutBottomToTop = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
371 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("showPairedPages")) == 0) {
372 this->showPairedPages = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
373 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numPairsOffset")) == 0) {
374 this->numPairsOffset = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
375 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("presentationMode")) == 0) {
376 this->presentationMode = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
377 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("autoloadMostRecent")) == 0) {
378 this->autoloadMostRecent = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
379 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("autoloadPdfXoj")) == 0) {
380 this->autoloadPdfXoj = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
381 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stylusCursorType")) == 0) {
382 this->stylusCursorType = stylusCursorTypeFromString(reinterpret_cast<const char*>(value));
383 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("highlightPosition")) == 0) {
384 this->highlightPosition = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
385 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("cursorHighlightColor")) == 0) {
386 this->cursorHighlightColor = g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10);
387 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("cursorHighlightRadius")) == 0) {
388 this->cursorHighlightRadius = g_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
389 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("cursorHighlightBorderColor")) == 0) {
390 this->cursorHighlightBorderColor = g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10);
391 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("cursorHighlightBorderWidth")) == 0) {
392 this->cursorHighlightBorderWidth = g_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
393 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("darkTheme")) == 0) {
394 this->darkTheme = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
395 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("useStockIcons")) == 0) {
396 this->useStockIcons = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
397 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("defaultSaveName")) == 0) {
398 this->defaultSaveName = reinterpret_cast<const char*>(value);
399 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pluginEnabled")) == 0) {
400 this->pluginEnabled = reinterpret_cast<const char*>(value);
401 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pluginDisabled")) == 0) {
402 this->pluginDisabled = reinterpret_cast<const char*>(value);
403 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pageTemplate")) == 0) {
404 this->pageTemplate = reinterpret_cast<const char*>(value);
405 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("sizeUnit")) == 0) {
406 this->sizeUnit = reinterpret_cast<const char*>(value);
407 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioFolder")) == 0) {
408 this->audioFolder = reinterpret_cast<const char*>(value);
409 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("autosaveEnabled")) == 0) {
410 this->autosaveEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
411 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("autosaveTimeout")) == 0) {
412 this->autosaveTimeout = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
413 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("fullscreenHideElements")) == 0) {
414 this->fullscreenHideElements = reinterpret_cast<const char*>(value);
415 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("presentationHideElements")) == 0) {
416 this->presentationHideElements = reinterpret_cast<const char*>(value);
417 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("touchZoomStartThreshold")) == 0) {
418 this->touchZoomStartThreshold = g_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
419 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pageRerenderThreshold")) == 0) {
420 this->pageRerenderThreshold = g_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
421 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pdfPageCacheSize")) == 0) {
422 this->pdfPageCacheSize = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
423 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("preloadPagesBefore")) == 0) {
424 this->preloadPagesBefore = g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10);
425 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("preloadPagesAfter")) == 0) {
426 this->preloadPagesAfter = g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10);
427 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("eagerPageCleanup")) == 0) {
428 this->eagerPageCleanup = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
429 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("selectionBorderColor")) == 0) {
430 this->selectionBorderColor = Color(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10));
431 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("selectionMarkerColor")) == 0) {
432 this->selectionMarkerColor = Color(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10));
433 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("backgroundColor")) == 0) {
434 this->backgroundColor = Color(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10));
435 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("addHorizontalSpace")) == 0) {
436 this->addHorizontalSpace = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
437 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("addHorizontalSpaceAmount")) == 0) {
438 this->addHorizontalSpaceAmount = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
439 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("addVerticalSpace")) == 0) {
440 this->addVerticalSpace = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
441 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("addVerticalSpaceAmount")) == 0) {
442 this->addVerticalSpaceAmount = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
443 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("drawDirModsEnabled")) == 0) {
444 this->drawDirModsEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
445 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("drawDirModsRadius")) == 0) {
446 this->drawDirModsRadius = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
447 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapRotation")) == 0) {
448 this->snapRotation = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
449 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapRotationTolerance")) == 0) {
450 this->snapRotationTolerance = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
451 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapGrid")) == 0) {
452 this->snapGrid = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
453 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapGridSize")) == 0) {
454 this->snapGridSize = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
455 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapGridTolerance")) == 0) {
456 this->snapGridTolerance = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
457 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("touchDrawing")) == 0) {
458 this->touchDrawing = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
459 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pressureGuessing")) == 0) {
460 this->pressureGuessing = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
461 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("scrollbarHideType")) == 0) {
462 if (xmlStrcmp(value, reinterpret_cast<const xmlChar*>("both")) == 0) {
463 this->scrollbarHideType = SCROLLBAR_HIDE_BOTH;
464 } else if (xmlStrcmp(value, reinterpret_cast<const xmlChar*>("horizontal")) == 0) {
465 this->scrollbarHideType = SCROLLBAR_HIDE_HORIZONTAL;
466 } else if (xmlStrcmp(value, reinterpret_cast<const xmlChar*>("vertical")) == 0) {
467 this->scrollbarHideType = SCROLLBAR_HIDE_VERTICAL;
468 } else {
469 this->scrollbarHideType = SCROLLBAR_HIDE_NONE;
470 }
471 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("disableScrollbarFadeout")) == 0) {
472 this->disableScrollbarFadeout = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
473 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioSampleRate")) == 0) {
474 this->audioSampleRate = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
475 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioGain")) == 0) {
476 this->audioGain = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
477 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("defaultSeekTime")) == 0) {
478 this->defaultSeekTime = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
479 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioInputDevice")) == 0) {
480 this->audioInputDevice = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
481 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioOutputDevice")) == 0) {
482 this->audioOutputDevice = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
483 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numIgnoredStylusEvents")) == 0) {
484 this->numIgnoredStylusEvents =
485 std::max<int>(g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10), 0);
486 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("inputSystemTPCButton")) == 0) {
487 this->inputSystemTPCButton = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
488 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("inputSystemDrawOutsideWindow")) == 0) {
489 this->inputSystemDrawOutsideWindow = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
490 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("strokeFilterIgnoreTime")) == 0) {
491 this->strokeFilterIgnoreTime = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
492 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("strokeFilterIgnoreLength")) == 0) {
493 this->strokeFilterIgnoreLength = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
494 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("strokeFilterSuccessiveTime")) == 0) {
495 this->strokeFilterSuccessiveTime = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
496 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("strokeFilterEnabled")) == 0) {
497 this->strokeFilterEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
498 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("doActionOnStrokeFiltered")) == 0) {
499 this->doActionOnStrokeFiltered = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
500 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("trySelectOnStrokeFiltered")) == 0) {
501 this->trySelectOnStrokeFiltered = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
502 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("latexSettings.autoCheckDependencies")) == 0) {
503 this->latexSettings.autoCheckDependencies = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
504 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("latexSettings.globalTemplatePath")) == 0) {
505 std::string v(reinterpret_cast<char*>(value));
506 this->latexSettings.globalTemplatePath = fs::u8path(v);
507 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("latexSettings.genCmd")) == 0) {
508 this->latexSettings.genCmd = reinterpret_cast<char*>(value);
509 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapRecognizedShapesEnabled")) == 0) {
510 this->snapRecognizedShapesEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
511 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("restoreLineWidthEnabled")) == 0) {
512 this->restoreLineWidthEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
513 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("preferredLocale")) == 0) {
514 this->preferredLocale = reinterpret_cast<char*>(value);
515 /**
516 * Stabilizer related settings
517 */
518 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerAveragingMethod")) == 0) {
519 this->stabilizerAveragingMethod =
520 (StrokeStabilizer::AveragingMethod)g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
521 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerPreprocessor")) == 0) {
522 this->stabilizerPreprocessor =
523 (StrokeStabilizer::Preprocessor)g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
524 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerBuffersize")) == 0) {
525 this->stabilizerBuffersize = g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10);
526 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerSigma")) == 0) {
527 this->stabilizerSigma = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
528 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerDeadzoneRadius")) == 0) {
529 this->stabilizerDeadzoneRadius = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
530 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerDrag")) == 0) {
531 this->stabilizerDrag = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
532 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerMass")) == 0) {
533 this->stabilizerMass = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
534 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerCuspDetection")) == 0) {
535 this->stabilizerCuspDetection = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
536 } else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stabilizerFinalizeStroke")) == 0) {
537 this->stabilizerFinalizeStroke = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
538 }
539 /**/
540
541 xmlFree(name);
542 xmlFree(value);
543 }
544
loadDeviceClasses()545 void Settings::loadDeviceClasses() {
546 SElement& s = getCustomElement("deviceClasses");
547 for (auto device: s.children()) {
548 SElement& deviceNode = device.second;
549 int deviceClass = 0;
550 int deviceSource = 0;
551 deviceNode.getInt("deviceClass", deviceClass);
552 deviceNode.getInt("deviceSource", deviceSource);
553 inputDeviceClasses.emplace(device.first, std::make_pair(static_cast<InputDeviceTypeOption>(deviceClass),
554 static_cast<GdkInputSource>(deviceSource)));
555 }
556 }
557
loadButtonConfig()558 void Settings::loadButtonConfig() {
559 SElement& s = getCustomElement("buttonConfig");
560
561 for (int i = 0; i < BUTTON_COUNT; i++) {
562 SElement& e = s.child(buttonToString(static_cast<Button>(i)));
563 ButtonConfig* cfg = buttonConfig[i];
564
565 string sType;
566 if (e.getString("tool", sType)) {
567 ToolType type = toolTypeFromString(sType);
568 cfg->action = type;
569
570 if (type == TOOL_PEN || type == TOOL_HIGHLIGHTER) {
571 string drawingType;
572 if (e.getString("drawingType", drawingType)) {
573 cfg->drawingType = drawingTypeFromString(drawingType);
574 }
575
576 string sSize;
577 if (e.getString("size", sSize)) {
578 cfg->size = toolSizeFromString(sSize);
579 } else {
580 // If not specified: do not change
581 cfg->size = TOOL_SIZE_NONE;
582 }
583 }
584
585 if (type == TOOL_PEN || type == TOOL_HIGHLIGHTER || type == TOOL_TEXT) {
586 if (int iColor; e.getInt("color", iColor)) {
587 cfg->color = Color(iColor);
588 }
589 }
590
591 if (type == TOOL_ERASER) {
592 string sEraserMode;
593 if (e.getString("eraserMode", sEraserMode)) {
594 cfg->eraserMode = eraserTypeFromString(sEraserMode);
595 } else {
596 // If not specified: do not change
597 cfg->eraserMode = ERASER_TYPE_NONE;
598 }
599
600 string sSize;
601 if (e.getString("size", sSize)) {
602 cfg->size = toolSizeFromString(sSize);
603 } else {
604 // If not specified: do not change
605 cfg->size = TOOL_SIZE_NONE;
606 }
607 }
608
609 // Touch device
610 if (i == BUTTON_TOUCH) {
611 if (!e.getString("device", cfg->device)) {
612 cfg->device = "";
613 }
614
615 e.getBool("disableDrawing", cfg->disableDrawing);
616 }
617 } else {
618 continue;
619 }
620 }
621 }
622
load()623 auto Settings::load() -> bool {
624 xmlKeepBlanksDefault(0);
625
626 if (!fs::exists(filepath)) {
627 g_warning("configfile does not exist %s\n", filepath.string().c_str());
628 return false;
629 }
630
631 xmlDocPtr doc = xmlParseFile(filepath.u8string().c_str());
632
633 if (doc == nullptr) {
634 g_warning("Settings::load:: doc == null, could not load Settings!\n");
635 return false;
636 }
637
638 xmlNodePtr cur = xmlDocGetRootElement(doc);
639 if (cur == nullptr) {
640 g_message("The settings file \"%s\" is empty", filepath.string().c_str());
641 xmlFreeDoc(doc);
642
643 return false;
644 }
645
646 if (xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>("settings"))) {
647 g_message("File \"%s\" is of the wrong type", filepath.string().c_str());
648 xmlFreeDoc(doc);
649
650 return false;
651 }
652
653 cur = xmlDocGetRootElement(doc);
654 cur = cur->xmlChildrenNode;
655
656 while (cur != nullptr) {
657 parseItem(doc, cur);
658
659 cur = cur->next;
660 }
661
662 xmlFreeDoc(doc);
663
664 loadButtonConfig();
665 loadDeviceClasses();
666
667 return true;
668 }
669
savePropertyDouble(const gchar * key,double value,xmlNodePtr parent)670 auto Settings::savePropertyDouble(const gchar* key, double value, xmlNodePtr parent) -> xmlNodePtr {
671 char text[G_ASCII_DTOSTR_BUF_SIZE];
672 // g_ascii_ version uses C locale always.
673 g_ascii_formatd(text, G_ASCII_DTOSTR_BUF_SIZE, Util::PRECISION_FORMAT_STRING, value);
674 xmlNodePtr xmlNode = saveProperty(key, text, parent);
675 return xmlNode;
676 }
677
saveProperty(const gchar * key,int value,xmlNodePtr parent)678 auto Settings::saveProperty(const gchar* key, int value, xmlNodePtr parent) -> xmlNodePtr {
679 char* text = g_strdup_printf("%i", value);
680 xmlNodePtr xmlNode = saveProperty(key, text, parent);
681 g_free(text);
682 return xmlNode;
683 }
684
savePropertyUnsigned(const gchar * key,unsigned int value,xmlNodePtr parent)685 auto Settings::savePropertyUnsigned(const gchar* key, unsigned int value, xmlNodePtr parent) -> xmlNodePtr {
686 char* text = g_strdup_printf("%u", value);
687 xmlNodePtr xmlNode = saveProperty(key, text, parent);
688 g_free(text);
689 return xmlNode;
690 }
691
saveProperty(const gchar * key,const gchar * value,xmlNodePtr parent)692 auto Settings::saveProperty(const gchar* key, const gchar* value, xmlNodePtr parent) -> xmlNodePtr {
693 xmlNodePtr xmlNode = xmlNewChild(parent, nullptr, reinterpret_cast<const xmlChar*>("property"), nullptr);
694
695 xmlSetProp(xmlNode, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>(key));
696
697 xmlSetProp(xmlNode, reinterpret_cast<const xmlChar*>("value"), reinterpret_cast<const xmlChar*>(value));
698
699 return xmlNode;
700 }
701
saveDeviceClasses()702 void Settings::saveDeviceClasses() {
703 SElement& s = getCustomElement("deviceClasses");
704
705 for (auto& device: inputDeviceClasses) {
706 const std::string& name = device.first;
707 InputDeviceTypeOption& deviceClass = device.second.first;
708 GdkInputSource& source = device.second.second;
709 SElement& e = s.child(name);
710 e.setInt("deviceClass", static_cast<int>(deviceClass));
711 e.setInt("deviceSource", source);
712 }
713 }
714
saveButtonConfig()715 void Settings::saveButtonConfig() {
716 SElement& s = getCustomElement("buttonConfig");
717 s.clear();
718
719 for (int i = 0; i < BUTTON_COUNT; i++) {
720 SElement& e = s.child(buttonToString(static_cast<Button>(i)));
721 ButtonConfig* cfg = buttonConfig[i];
722
723 ToolType type = cfg->action;
724 e.setString("tool", toolTypeToString(type));
725
726 if (type == TOOL_PEN || type == TOOL_HIGHLIGHTER) {
727 e.setString("drawingType", drawingTypeToString(cfg->drawingType));
728 e.setString("size", toolSizeToString(cfg->size));
729 } // end if pen or highlighter
730
731 if (type == TOOL_PEN || type == TOOL_HIGHLIGHTER || type == TOOL_TEXT) {
732 e.setIntHex("color", int32_t(cfg->color));
733 }
734
735 if (type == TOOL_ERASER) {
736 e.setString("eraserMode", eraserTypeToString(cfg->eraserMode));
737 e.setString("size", toolSizeToString(cfg->size));
738 }
739
740 // Touch device
741 if (i == BUTTON_TOUCH) {
742 e.setString("device", cfg->device);
743 e.setBool("disableDrawing", cfg->disableDrawing);
744 }
745 }
746 }
747
748 /**
749 * Do not save settings until transactionEnd() is called
750 */
transactionStart()751 void Settings::transactionStart() { inTransaction = true; }
752
753 /**
754 * Stop transaction and save settings
755 */
transactionEnd()756 void Settings::transactionEnd() {
757 inTransaction = false;
758 save();
759 }
760
save()761 void Settings::save() {
762 if (inTransaction) {
763 return;
764 }
765
766 xmlDocPtr doc = nullptr;
767 xmlNodePtr root = nullptr;
768 xmlNodePtr xmlNode = nullptr;
769
770 xmlIndentTreeOutput = true;
771
772 doc = xmlNewDoc(reinterpret_cast<const xmlChar*>("1.0"));
773 if (doc == nullptr) {
774 return;
775 }
776
777 saveButtonConfig();
778 saveDeviceClasses();
779
780 /* Create metadata root */
781 root = xmlNewDocNode(doc, nullptr, reinterpret_cast<const xmlChar*>("settings"), nullptr);
782 xmlDocSetRootElement(doc, root);
783 xmlNodePtr com = xmlNewComment(
784 reinterpret_cast<const xmlChar*>("The Xournal++ settings file. Do not edit this file! "
785 "Most settings are available in the Settings dialog, "
786 "the others are commented in this file, but handle with care!"));
787 xmlAddPrevSibling(root, com);
788
789 SAVE_BOOL_PROP(pressureSensitivity);
790 SAVE_DOUBLE_PROP(minimumPressure);
791 SAVE_DOUBLE_PROP(pressureMultiplier);
792
793 SAVE_BOOL_PROP(zoomGesturesEnabled);
794
795 SAVE_STRING_PROP(selectedToolbar);
796
797 auto lastSavePath = this->lastSavePath.u8string();
798 auto lastOpenPath = this->lastOpenPath.u8string();
799 auto lastImagePath = this->lastImagePath.u8string();
800 SAVE_STRING_PROP(lastSavePath);
801 SAVE_STRING_PROP(lastOpenPath);
802 SAVE_STRING_PROP(lastImagePath);
803
804 SAVE_DOUBLE_PROP(zoomStep);
805 SAVE_DOUBLE_PROP(zoomStepScroll);
806 SAVE_INT_PROP(displayDpi);
807 SAVE_INT_PROP(mainWndWidth);
808 SAVE_INT_PROP(mainWndHeight);
809 SAVE_BOOL_PROP(maximized);
810
811 SAVE_BOOL_PROP(showToolbar);
812
813 SAVE_BOOL_PROP(showSidebar);
814 SAVE_INT_PROP(sidebarWidth);
815
816 SAVE_BOOL_PROP(sidebarOnRight);
817 SAVE_BOOL_PROP(scrollbarOnLeft);
818 SAVE_BOOL_PROP(menubarVisible);
819 SAVE_INT_PROP(numColumns);
820 SAVE_INT_PROP(numRows);
821 SAVE_BOOL_PROP(viewFixedRows);
822 SAVE_BOOL_PROP(showPairedPages);
823 SAVE_BOOL_PROP(layoutVertical);
824 SAVE_BOOL_PROP(layoutRightToLeft);
825 SAVE_BOOL_PROP(layoutBottomToTop);
826 SAVE_INT_PROP(numPairsOffset);
827 SAVE_BOOL_PROP(presentationMode);
828
829 SAVE_STRING_PROP(fullscreenHideElements);
830 ATTACH_COMMENT("Which gui elements are hidden if you are in Fullscreen mode, separated by a colon (,)");
831
832 SAVE_STRING_PROP(presentationHideElements);
833 ATTACH_COMMENT("Which gui elements are hidden if you are in Presentation mode, separated by a colon (,)");
834
835 xmlNode = saveProperty("stylusCursorType", stylusCursorTypeToString(this->stylusCursorType), root);
836 ATTACH_COMMENT("The cursor icon used with a stylus, allowed values are \"none\", \"dot\", \"big\"");
837
838 SAVE_BOOL_PROP(highlightPosition);
839 SAVE_UINT_PROP(cursorHighlightColor);
840 SAVE_UINT_PROP(cursorHighlightBorderColor);
841 SAVE_DOUBLE_PROP(cursorHighlightRadius);
842 SAVE_DOUBLE_PROP(cursorHighlightBorderWidth);
843 SAVE_BOOL_PROP(darkTheme);
844 SAVE_BOOL_PROP(useStockIcons);
845
846 SAVE_BOOL_PROP(disableScrollbarFadeout);
847
848 if (this->scrollbarHideType == SCROLLBAR_HIDE_BOTH) {
849 xmlNode = saveProperty("scrollbarHideType", "both", root);
850 } else if (this->scrollbarHideType == SCROLLBAR_HIDE_HORIZONTAL) {
851 xmlNode = saveProperty("scrollbarHideType", "horizontal", root);
852 } else if (this->scrollbarHideType == SCROLLBAR_HIDE_VERTICAL) {
853 xmlNode = saveProperty("scrollbarHideType", "vertical", root);
854 } else {
855 xmlNode = saveProperty("scrollbarHideType", "none", root);
856 }
857 ATTACH_COMMENT(
858 "Hides scroolbars in the main window, allowed values: \"none\", \"horizontal\", \"vertical\", \"both\"");
859
860 SAVE_BOOL_PROP(autoloadMostRecent);
861 SAVE_BOOL_PROP(autoloadPdfXoj);
862 SAVE_STRING_PROP(defaultSaveName);
863
864 SAVE_BOOL_PROP(autosaveEnabled);
865 SAVE_INT_PROP(autosaveTimeout);
866
867 SAVE_BOOL_PROP(addHorizontalSpace);
868 SAVE_INT_PROP(addHorizontalSpaceAmount);
869 SAVE_BOOL_PROP(addVerticalSpace);
870 SAVE_INT_PROP(addVerticalSpaceAmount);
871
872 SAVE_BOOL_PROP(drawDirModsEnabled);
873 SAVE_INT_PROP(drawDirModsRadius);
874
875
876 SAVE_BOOL_PROP(snapRotation);
877 SAVE_DOUBLE_PROP(snapRotationTolerance);
878 SAVE_BOOL_PROP(snapGrid);
879 SAVE_DOUBLE_PROP(snapGridTolerance);
880 SAVE_DOUBLE_PROP(snapGridSize);
881
882 SAVE_BOOL_PROP(touchDrawing);
883 SAVE_BOOL_PROP(pressureGuessing);
884
885 SAVE_UINT_PROP(selectionBorderColor);
886 SAVE_UINT_PROP(backgroundColor);
887 SAVE_UINT_PROP(selectionMarkerColor);
888
889 SAVE_DOUBLE_PROP(touchZoomStartThreshold);
890 SAVE_DOUBLE_PROP(pageRerenderThreshold);
891
892 SAVE_INT_PROP(pdfPageCacheSize);
893 ATTACH_COMMENT("The count of rendered PDF pages which will be cached.");
894 SAVE_UINT_PROP(preloadPagesBefore);
895 SAVE_UINT_PROP(preloadPagesAfter);
896 SAVE_BOOL_PROP(eagerPageCleanup);
897
898 SAVE_STRING_PROP(pageTemplate);
899 ATTACH_COMMENT("Config for new pages");
900
901 SAVE_STRING_PROP(sizeUnit);
902
903 SAVE_STRING_PROP(audioFolder);
904 SAVE_INT_PROP(audioInputDevice);
905 SAVE_INT_PROP(audioOutputDevice);
906 SAVE_DOUBLE_PROP(audioSampleRate);
907 SAVE_DOUBLE_PROP(audioGain);
908 SAVE_INT_PROP(defaultSeekTime);
909
910 SAVE_STRING_PROP(pluginEnabled);
911 SAVE_STRING_PROP(pluginDisabled);
912
913 SAVE_INT_PROP(strokeFilterIgnoreTime);
914 SAVE_DOUBLE_PROP(strokeFilterIgnoreLength);
915 SAVE_INT_PROP(strokeFilterSuccessiveTime);
916 SAVE_BOOL_PROP(strokeFilterEnabled);
917 SAVE_BOOL_PROP(doActionOnStrokeFiltered);
918 SAVE_BOOL_PROP(trySelectOnStrokeFiltered);
919
920 SAVE_BOOL_PROP(snapRecognizedShapesEnabled);
921 SAVE_BOOL_PROP(restoreLineWidthEnabled);
922
923 SAVE_INT_PROP(numIgnoredStylusEvents);
924
925 SAVE_BOOL_PROP(inputSystemTPCButton);
926 SAVE_BOOL_PROP(inputSystemDrawOutsideWindow);
927
928 SAVE_STRING_PROP(preferredLocale);
929
930 /**
931 * Stabilizer related settings
932 */
933 saveProperty("stabilizerAveragingMethod", static_cast<int>(stabilizerAveragingMethod), root);
934 saveProperty("stabilizerPreprocessor", static_cast<int>(stabilizerPreprocessor), root);
935 SAVE_UINT_PROP(stabilizerBuffersize);
936 SAVE_DOUBLE_PROP(stabilizerSigma);
937 SAVE_DOUBLE_PROP(stabilizerDeadzoneRadius);
938 SAVE_DOUBLE_PROP(stabilizerDrag);
939 SAVE_DOUBLE_PROP(stabilizerMass);
940 SAVE_BOOL_PROP(stabilizerCuspDetection);
941 SAVE_BOOL_PROP(stabilizerFinalizeStroke);
942 /**/
943
944 SAVE_BOOL_PROP(latexSettings.autoCheckDependencies);
945 // Inline SAVE_STRING_PROP(latexSettings.globalTemplatePath) since it
946 // breaks on Windows due to the native character representation being
947 // wchar_t instead of char
948 fs::path& p = latexSettings.globalTemplatePath;
949 xmlNode = saveProperty("latexSettings.globalTemplatePath", p.empty() ? "" : p.u8string().c_str(), root);
950 SAVE_STRING_PROP(latexSettings.genCmd);
951
952 xmlNodePtr xmlFont = nullptr;
953 xmlFont = xmlNewChild(root, nullptr, reinterpret_cast<const xmlChar*>("property"), nullptr);
954 xmlSetProp(xmlFont, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>("font"));
955 xmlSetProp(xmlFont, reinterpret_cast<const xmlChar*>("font"),
956 reinterpret_cast<const xmlChar*>(this->font.getName().c_str()));
957
958 char sSize[G_ASCII_DTOSTR_BUF_SIZE];
959
960 g_ascii_formatd(sSize, G_ASCII_DTOSTR_BUF_SIZE, Util::PRECISION_FORMAT_STRING, this->font.getSize()); // no locale
961 xmlSetProp(xmlFont, reinterpret_cast<const xmlChar*>("size"), reinterpret_cast<const xmlChar*>(sSize));
962
963
964 for (std::map<string, SElement>::value_type p: data) { saveData(root, p.first, p.second); }
965
966 xmlSaveFormatFileEnc(filepath.u8string().c_str(), doc, "UTF-8", 1);
967 xmlFreeDoc(doc);
968 }
969
saveData(xmlNodePtr root,const string & name,SElement & elem)970 void Settings::saveData(xmlNodePtr root, const string& name, SElement& elem) {
971 xmlNodePtr xmlNode = xmlNewChild(root, nullptr, reinterpret_cast<const xmlChar*>("data"), nullptr);
972
973 xmlSetProp(xmlNode, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>(name.c_str()));
974
975 for (auto const& [aname, attrib]: elem.attributes()) {
976 string type;
977 string value;
978
979 if (attrib.type == ATTRIBUTE_TYPE_BOOLEAN) {
980 type = "boolean";
981
982 if (attrib.iValue) {
983 value = "true";
984 } else {
985 value = "false";
986 }
987 } else if (attrib.type == ATTRIBUTE_TYPE_INT) {
988 type = "int";
989
990 char* tmp = g_strdup_printf("%i", attrib.iValue);
991 value = tmp;
992 g_free(tmp);
993 } else if (attrib.type == ATTRIBUTE_TYPE_DOUBLE) {
994 type = "double";
995
996 char tmp[G_ASCII_DTOSTR_BUF_SIZE];
997 g_ascii_formatd(tmp, G_ASCII_DTOSTR_BUF_SIZE, Util::PRECISION_FORMAT_STRING, attrib.dValue);
998 value = tmp;
999 } else if (attrib.type == ATTRIBUTE_TYPE_INT_HEX) {
1000 type = "hex";
1001
1002 char* tmp = g_strdup_printf("%06x", attrib.iValue);
1003 value = tmp;
1004 g_free(tmp);
1005 } else if (attrib.type == ATTRIBUTE_TYPE_STRING) {
1006 type = "string";
1007 value = attrib.sValue;
1008 } else {
1009 // Unknown type or empty attribute
1010 continue;
1011 }
1012
1013 xmlNodePtr at = nullptr;
1014 at = xmlNewChild(xmlNode, nullptr, reinterpret_cast<const xmlChar*>("attribute"), nullptr);
1015
1016 xmlSetProp(at, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>(aname.c_str()));
1017 xmlSetProp(at, reinterpret_cast<const xmlChar*>("type"), reinterpret_cast<const xmlChar*>(type.c_str()));
1018 xmlSetProp(at, reinterpret_cast<const xmlChar*>("value"), reinterpret_cast<const xmlChar*>(value.c_str()));
1019
1020 if (!attrib.comment.empty()) {
1021 xmlNodePtr com = xmlNewComment(reinterpret_cast<const xmlChar*>(attrib.comment.c_str()));
1022 xmlAddPrevSibling(xmlNode, com);
1023 }
1024 }
1025
1026 for (std::map<string, SElement>::value_type p: elem.children()) { saveData(xmlNode, p.first, p.second); }
1027 }
1028
1029 // Getter- / Setter
isPressureSensitivity() const1030 auto Settings::isPressureSensitivity() const -> bool { return this->pressureSensitivity; }
1031
isZoomGesturesEnabled() const1032 auto Settings::isZoomGesturesEnabled() const -> bool { return this->zoomGesturesEnabled; }
1033
setZoomGesturesEnabled(bool enable)1034 void Settings::setZoomGesturesEnabled(bool enable) {
1035 if (this->zoomGesturesEnabled == enable) {
1036 return;
1037 }
1038 this->zoomGesturesEnabled = enable;
1039 save();
1040 }
1041
isSidebarOnRight() const1042 auto Settings::isSidebarOnRight() const -> bool { return this->sidebarOnRight; }
1043
setSidebarOnRight(bool right)1044 void Settings::setSidebarOnRight(bool right) {
1045 if (this->sidebarOnRight == right) {
1046 return;
1047 }
1048
1049 this->sidebarOnRight = right;
1050
1051 save();
1052 }
1053
isScrollbarOnLeft() const1054 auto Settings::isScrollbarOnLeft() const -> bool { return this->scrollbarOnLeft; }
1055
setScrollbarOnLeft(bool right)1056 void Settings::setScrollbarOnLeft(bool right) {
1057 if (this->scrollbarOnLeft == right) {
1058 return;
1059 }
1060
1061 this->scrollbarOnLeft = right;
1062
1063 save();
1064 }
1065
isMenubarVisible() const1066 auto Settings::isMenubarVisible() const -> bool { return this->menubarVisible; }
1067
setMenubarVisible(bool visible)1068 void Settings::setMenubarVisible(bool visible) {
1069 if (this->menubarVisible == visible) {
1070 return;
1071 }
1072
1073 this->menubarVisible = visible;
1074
1075 save();
1076 }
1077
getAutosaveTimeout() const1078 auto Settings::getAutosaveTimeout() const -> int { return this->autosaveTimeout; }
1079
setAutosaveTimeout(int autosave)1080 void Settings::setAutosaveTimeout(int autosave) {
1081 if (this->autosaveTimeout == autosave) {
1082 return;
1083 }
1084
1085 this->autosaveTimeout = autosave;
1086
1087 save();
1088 }
1089
isAutosaveEnabled() const1090 auto Settings::isAutosaveEnabled() const -> bool { return this->autosaveEnabled; }
1091
setAutosaveEnabled(bool autosave)1092 void Settings::setAutosaveEnabled(bool autosave) {
1093 if (this->autosaveEnabled == autosave) {
1094 return;
1095 }
1096
1097 this->autosaveEnabled = autosave;
1098
1099 save();
1100 }
1101
getAddVerticalSpace() const1102 auto Settings::getAddVerticalSpace() const -> bool { return this->addVerticalSpace; }
1103
setAddVerticalSpace(bool space)1104 void Settings::setAddVerticalSpace(bool space) { this->addVerticalSpace = space; }
1105
getAddVerticalSpaceAmount() const1106 auto Settings::getAddVerticalSpaceAmount() const -> int { return this->addVerticalSpaceAmount; }
1107
setAddVerticalSpaceAmount(int pixels)1108 void Settings::setAddVerticalSpaceAmount(int pixels) {
1109 if (this->addVerticalSpaceAmount == pixels) {
1110 return;
1111 }
1112
1113 this->addVerticalSpaceAmount = pixels;
1114 save();
1115 }
1116
1117
getAddHorizontalSpace() const1118 auto Settings::getAddHorizontalSpace() const -> bool { return this->addHorizontalSpace; }
1119
setAddHorizontalSpace(bool space)1120 void Settings::setAddHorizontalSpace(bool space) { this->addHorizontalSpace = space; }
1121
getAddHorizontalSpaceAmount() const1122 auto Settings::getAddHorizontalSpaceAmount() const -> int { return this->addHorizontalSpaceAmount; }
1123
setAddHorizontalSpaceAmount(int pixels)1124 void Settings::setAddHorizontalSpaceAmount(int pixels) {
1125 if (this->addHorizontalSpaceAmount == pixels) {
1126 return;
1127 }
1128
1129 this->addHorizontalSpaceAmount = pixels;
1130 save();
1131 }
1132
1133
getDrawDirModsEnabled() const1134 auto Settings::getDrawDirModsEnabled() const -> bool { return this->drawDirModsEnabled; }
1135
setDrawDirModsEnabled(bool enable)1136 void Settings::setDrawDirModsEnabled(bool enable) { this->drawDirModsEnabled = enable; }
1137
getDrawDirModsRadius() const1138 auto Settings::getDrawDirModsRadius() const -> int { return this->drawDirModsRadius; }
1139
setDrawDirModsRadius(int pixels)1140 void Settings::setDrawDirModsRadius(int pixels) {
1141 if (this->drawDirModsRadius == pixels) {
1142 return;
1143 }
1144
1145 this->drawDirModsRadius = pixels;
1146 save();
1147 }
1148
getStylusCursorType() const1149 auto Settings::getStylusCursorType() const -> StylusCursorType { return this->stylusCursorType; }
1150
setStylusCursorType(StylusCursorType type)1151 void Settings::setStylusCursorType(StylusCursorType type) {
1152 if (this->stylusCursorType == type) {
1153 return;
1154 }
1155
1156 this->stylusCursorType = type;
1157
1158 save();
1159 }
1160
isHighlightPosition() const1161 auto Settings::isHighlightPosition() const -> bool { return this->highlightPosition; }
1162
setHighlightPosition(bool highlight)1163 void Settings::setHighlightPosition(bool highlight) {
1164 if (this->highlightPosition == highlight) {
1165 return;
1166 }
1167
1168 this->highlightPosition = highlight;
1169 save();
1170 }
1171
getCursorHighlightColor() const1172 auto Settings::getCursorHighlightColor() const -> Color { return this->cursorHighlightColor; }
1173
setCursorHighlightColor(Color color)1174 void Settings::setCursorHighlightColor(Color color) {
1175 if (this->cursorHighlightColor != color) {
1176 this->cursorHighlightColor = color;
1177 save();
1178 }
1179 }
1180
getCursorHighlightRadius() const1181 auto Settings::getCursorHighlightRadius() const -> double { return this->cursorHighlightRadius; }
1182
setCursorHighlightRadius(double radius)1183 void Settings::setCursorHighlightRadius(double radius) {
1184 if (this->cursorHighlightRadius != radius) {
1185 this->cursorHighlightRadius = radius;
1186 save();
1187 }
1188 }
1189
getCursorHighlightBorderColor() const1190 auto Settings::getCursorHighlightBorderColor() const -> Color { return this->cursorHighlightBorderColor; }
1191
setCursorHighlightBorderColor(Color color)1192 void Settings::setCursorHighlightBorderColor(Color color) {
1193 if (this->cursorHighlightBorderColor != color) {
1194 this->cursorHighlightBorderColor = color;
1195 save();
1196 }
1197 }
1198
getCursorHighlightBorderWidth() const1199 auto Settings::getCursorHighlightBorderWidth() const -> double { return this->cursorHighlightBorderWidth; }
1200
setCursorHighlightBorderWidth(double radius)1201 void Settings::setCursorHighlightBorderWidth(double radius) {
1202 if (this->cursorHighlightBorderWidth != radius) {
1203 this->cursorHighlightBorderWidth = radius;
1204 save();
1205 }
1206 }
1207
isSnapRotation() const1208 auto Settings::isSnapRotation() const -> bool { return this->snapRotation; }
1209
setSnapRotation(bool b)1210 void Settings::setSnapRotation(bool b) {
1211 if (this->snapRotation == b) {
1212 return;
1213 }
1214
1215 this->snapRotation = b;
1216 save();
1217 }
1218
getSnapRotationTolerance() const1219 auto Settings::getSnapRotationTolerance() const -> double { return this->snapRotationTolerance; }
1220
setSnapRotationTolerance(double tolerance)1221 void Settings::setSnapRotationTolerance(double tolerance) {
1222 this->snapRotationTolerance = tolerance;
1223 save();
1224 }
1225
isSnapGrid() const1226 auto Settings::isSnapGrid() const -> bool { return this->snapGrid; }
1227
setSnapGrid(bool b)1228 void Settings::setSnapGrid(bool b) {
1229 if (this->snapGrid == b) {
1230 return;
1231 }
1232
1233 this->snapGrid = b;
1234 save();
1235 }
1236
setSnapGridTolerance(double tolerance)1237 void Settings::setSnapGridTolerance(double tolerance) {
1238 this->snapGridTolerance = tolerance;
1239 save();
1240 }
1241
getSnapGridTolerance() const1242 auto Settings::getSnapGridTolerance() const -> double { return this->snapGridTolerance; }
getSnapGridSize() const1243 auto Settings::getSnapGridSize() const -> double { return this->snapGridSize; };
setSnapGridSize(double gridSize)1244 void Settings::setSnapGridSize(double gridSize) {
1245 if (this->snapGridSize == gridSize) {
1246 return;
1247 }
1248 this->snapGridSize = gridSize;
1249 save();
1250 }
1251
getTouchDrawingEnabled() const1252 auto Settings::getTouchDrawingEnabled() const -> bool { return this->touchDrawing; }
1253
setTouchDrawingEnabled(bool b)1254 void Settings::setTouchDrawingEnabled(bool b) {
1255 if (this->touchDrawing == b) {
1256 return;
1257 }
1258
1259 this->touchDrawing = b;
1260 save();
1261 }
1262
isPressureGuessingEnabled() const1263 auto Settings::isPressureGuessingEnabled() const -> bool { return this->pressureGuessing; }
setPressureGuessingEnabled(bool b)1264 void Settings::setPressureGuessingEnabled(bool b) {
1265 if (this->pressureGuessing == b) {
1266 return;
1267 }
1268
1269 this->pressureGuessing = b;
1270 save();
1271 }
1272
getMinimumPressure() const1273 double Settings::getMinimumPressure() const { return this->minimumPressure; }
setMinimumPressure(double minimumPressure)1274 void Settings::setMinimumPressure(double minimumPressure) {
1275 if (this->minimumPressure == minimumPressure) {
1276 return;
1277 }
1278
1279 this->minimumPressure = minimumPressure;
1280 save();
1281 }
1282
getPressureMultiplier() const1283 double Settings::getPressureMultiplier() const { return this->pressureMultiplier; }
setPressureMultiplier(double multiplier)1284 void Settings::setPressureMultiplier(double multiplier) {
1285 if (this->pressureMultiplier == multiplier) {
1286 return;
1287 }
1288
1289 this->pressureMultiplier = multiplier;
1290 save();
1291 }
1292
getScrollbarHideType() const1293 auto Settings::getScrollbarHideType() const -> ScrollbarHideType { return this->scrollbarHideType; }
1294
setScrollbarHideType(ScrollbarHideType type)1295 void Settings::setScrollbarHideType(ScrollbarHideType type) {
1296 if (this->scrollbarHideType == type) {
1297 return;
1298 }
1299
1300 this->scrollbarHideType = type;
1301
1302 save();
1303 }
1304
isAutoloadMostRecent() const1305 auto Settings::isAutoloadMostRecent() const -> bool { return this->autoloadMostRecent; }
1306
setAutoloadMostRecent(bool load)1307 void Settings::setAutoloadMostRecent(bool load) {
1308 if (this->autoloadMostRecent == load) {
1309 return;
1310 }
1311 this->autoloadMostRecent = load;
1312 save();
1313 }
1314
isAutoloadPdfXoj() const1315 auto Settings::isAutoloadPdfXoj() const -> bool { return this->autoloadPdfXoj; }
1316
setAutoloadPdfXoj(bool load)1317 void Settings::setAutoloadPdfXoj(bool load) {
1318 if (this->autoloadPdfXoj == load) {
1319 return;
1320 }
1321 this->autoloadPdfXoj = load;
1322 save();
1323 }
1324
getDefaultSaveName() const1325 auto Settings::getDefaultSaveName() const -> string const& { return this->defaultSaveName; }
1326
setDefaultSaveName(const string & name)1327 void Settings::setDefaultSaveName(const string& name) {
1328 if (this->defaultSaveName == name) {
1329 return;
1330 }
1331
1332 this->defaultSaveName = name;
1333
1334 save();
1335 }
1336
getPageTemplate() const1337 auto Settings::getPageTemplate() const -> string const& { return this->pageTemplate; }
1338
setPageTemplate(const string & pageTemplate)1339 void Settings::setPageTemplate(const string& pageTemplate) {
1340 if (this->pageTemplate == pageTemplate) {
1341 return;
1342 }
1343
1344 this->pageTemplate = pageTemplate;
1345
1346 save();
1347 }
1348
getAudioFolder() const1349 auto Settings::getAudioFolder() const -> string const& { return this->audioFolder; }
1350
setAudioFolder(const string & audioFolder)1351 void Settings::setAudioFolder(const string& audioFolder) {
1352 if (this->audioFolder == audioFolder) {
1353 return;
1354 }
1355
1356 this->audioFolder = audioFolder;
1357
1358 save();
1359 }
1360
getSizeUnit() const1361 auto Settings::getSizeUnit() const -> string const& { return sizeUnit; }
1362
setSizeUnit(const string & sizeUnit)1363 void Settings::setSizeUnit(const string& sizeUnit) {
1364 if (this->sizeUnit == sizeUnit) {
1365 return;
1366 }
1367
1368 this->sizeUnit = sizeUnit;
1369
1370 save();
1371 }
1372
1373 /**
1374 * Get size index in XOJ_UNITS
1375 */
getSizeUnitIndex() const1376 auto Settings::getSizeUnitIndex() const -> int {
1377 string unit = getSizeUnit();
1378
1379 for (int i = 0; i < XOJ_UNIT_COUNT; i++) {
1380 if (unit == XOJ_UNITS[i].name) {
1381 return i;
1382 }
1383 }
1384
1385 return 0;
1386 }
1387
1388 /**
1389 * Set size index in XOJ_UNITS
1390 */
setSizeUnitIndex(int sizeUnitId)1391 void Settings::setSizeUnitIndex(int sizeUnitId) {
1392 if (sizeUnitId < 0 || sizeUnitId >= XOJ_UNIT_COUNT) {
1393 sizeUnitId = 0;
1394 }
1395
1396 setSizeUnit(XOJ_UNITS[sizeUnitId].name);
1397 }
1398
setShowPairedPages(bool showPairedPages)1399 void Settings::setShowPairedPages(bool showPairedPages) {
1400 if (this->showPairedPages == showPairedPages) {
1401 return;
1402 }
1403
1404 this->showPairedPages = showPairedPages;
1405 save();
1406 }
1407
isShowPairedPages() const1408 auto Settings::isShowPairedPages() const -> bool { return this->showPairedPages; }
1409
setPresentationMode(bool presentationMode)1410 void Settings::setPresentationMode(bool presentationMode) {
1411 if (this->presentationMode == presentationMode) {
1412 return;
1413 }
1414
1415 this->presentationMode = presentationMode;
1416 save();
1417 }
1418
isPresentationMode() const1419 auto Settings::isPresentationMode() const -> bool { return this->presentationMode; }
1420
setPressureSensitivity(gboolean presureSensitivity)1421 void Settings::setPressureSensitivity(gboolean presureSensitivity) {
1422 if (this->pressureSensitivity == presureSensitivity) {
1423 return;
1424 }
1425 this->pressureSensitivity = presureSensitivity;
1426
1427 save();
1428 }
1429
setPairsOffset(int numOffset)1430 void Settings::setPairsOffset(int numOffset) {
1431 if (this->numPairsOffset == numOffset) {
1432 return;
1433 }
1434
1435 this->numPairsOffset = numOffset;
1436 save();
1437 }
1438
getPairsOffset() const1439 auto Settings::getPairsOffset() const -> int { return this->numPairsOffset; }
1440
setViewColumns(int numColumns)1441 void Settings::setViewColumns(int numColumns) {
1442 if (this->numColumns == numColumns) {
1443 return;
1444 }
1445
1446 this->numColumns = numColumns;
1447 save();
1448 }
1449
getViewColumns() const1450 auto Settings::getViewColumns() const -> int { return this->numColumns; }
1451
1452
setViewRows(int numRows)1453 void Settings::setViewRows(int numRows) {
1454 if (this->numRows == numRows) {
1455 return;
1456 }
1457
1458 this->numRows = numRows;
1459 save();
1460 }
1461
getViewRows() const1462 auto Settings::getViewRows() const -> int { return this->numRows; }
1463
setViewFixedRows(bool viewFixedRows)1464 void Settings::setViewFixedRows(bool viewFixedRows) {
1465 if (this->viewFixedRows == viewFixedRows) {
1466 return;
1467 }
1468
1469 this->viewFixedRows = viewFixedRows;
1470 save();
1471 }
1472
isViewFixedRows() const1473 auto Settings::isViewFixedRows() const -> bool { return this->viewFixedRows; }
1474
setViewLayoutVert(bool vert)1475 void Settings::setViewLayoutVert(bool vert) {
1476 if (this->layoutVertical == vert) {
1477 return;
1478 }
1479
1480 this->layoutVertical = vert;
1481 save();
1482 }
1483
getViewLayoutVert() const1484 auto Settings::getViewLayoutVert() const -> bool { return this->layoutVertical; }
1485
setViewLayoutR2L(bool r2l)1486 void Settings::setViewLayoutR2L(bool r2l) {
1487 if (this->layoutRightToLeft == r2l) {
1488 return;
1489 }
1490
1491 this->layoutRightToLeft = r2l;
1492 save();
1493 }
1494
getViewLayoutR2L() const1495 auto Settings::getViewLayoutR2L() const -> bool { return this->layoutRightToLeft; }
1496
setViewLayoutB2T(bool b2t)1497 void Settings::setViewLayoutB2T(bool b2t) {
1498 if (this->layoutBottomToTop == b2t) {
1499 return;
1500 }
1501
1502 this->layoutBottomToTop = b2t;
1503 save();
1504 }
1505
getViewLayoutB2T() const1506 auto Settings::getViewLayoutB2T() const -> bool { return this->layoutBottomToTop; }
1507
setLastSavePath(fs::path p)1508 void Settings::setLastSavePath(fs::path p) {
1509 this->lastSavePath = std::move(p);
1510 save();
1511 }
1512
getLastSavePath() const1513 auto Settings::getLastSavePath() const -> fs::path const& { return this->lastSavePath; }
1514
setLastOpenPath(fs::path p)1515 void Settings::setLastOpenPath(fs::path p) {
1516 this->lastOpenPath = std::move(p);
1517 save();
1518 }
1519
getLastOpenPath() const1520 auto Settings::getLastOpenPath() const -> fs::path const& { return this->lastOpenPath; }
1521
setLastImagePath(const fs::path & path)1522 void Settings::setLastImagePath(const fs::path& path) {
1523 if (this->lastImagePath == path) {
1524 return;
1525 }
1526 this->lastImagePath = path;
1527 save();
1528 }
1529
getLastImagePath() const1530 auto Settings::getLastImagePath() const -> fs::path const& { return this->lastImagePath; }
1531
setZoomStep(double zoomStep)1532 void Settings::setZoomStep(double zoomStep) {
1533 if (this->zoomStep == zoomStep) {
1534 return;
1535 }
1536 this->zoomStep = zoomStep;
1537 save();
1538 }
1539
getZoomStep() const1540 auto Settings::getZoomStep() const -> double { return this->zoomStep; }
1541
setZoomStepScroll(double zoomStepScroll)1542 void Settings::setZoomStepScroll(double zoomStepScroll) {
1543 if (this->zoomStepScroll == zoomStepScroll) {
1544 return;
1545 }
1546 this->zoomStepScroll = zoomStepScroll;
1547 save();
1548 }
1549
getZoomStepScroll() const1550 auto Settings::getZoomStepScroll() const -> double { return this->zoomStepScroll; }
1551
setDisplayDpi(int dpi)1552 void Settings::setDisplayDpi(int dpi) {
1553 if (this->displayDpi == dpi) {
1554 return;
1555 }
1556 this->displayDpi = dpi;
1557 save();
1558 }
1559
getDisplayDpi() const1560 auto Settings::getDisplayDpi() const -> int { return this->displayDpi; }
1561
setDarkTheme(bool dark)1562 void Settings::setDarkTheme(bool dark) {
1563 if (this->darkTheme == dark) {
1564 return;
1565 }
1566 this->darkTheme = dark;
1567 save();
1568 }
1569
isDarkTheme() const1570 auto Settings::isDarkTheme() const -> bool { return this->darkTheme; }
1571
setAreStockIconsUsed(bool use)1572 void Settings::setAreStockIconsUsed(bool use) {
1573 if (this->useStockIcons == use) {
1574 return;
1575 }
1576 this->useStockIcons = use;
1577 save();
1578 }
1579
areStockIconsUsed() const1580 auto Settings::areStockIconsUsed() const -> bool { return this->useStockIcons; }
1581
isSidebarVisible() const1582 auto Settings::isSidebarVisible() const -> bool { return this->showSidebar; }
1583
setSidebarVisible(bool visible)1584 void Settings::setSidebarVisible(bool visible) {
1585 if (this->showSidebar == visible) {
1586 return;
1587 }
1588 this->showSidebar = visible;
1589 save();
1590 }
1591
isToolbarVisible() const1592 auto Settings::isToolbarVisible() const -> bool { return this->showToolbar; }
1593
setToolbarVisible(bool visible)1594 void Settings::setToolbarVisible(bool visible) {
1595 if (this->showToolbar == visible) {
1596 return;
1597 }
1598 this->showToolbar = visible;
1599 save();
1600 }
1601
getSidebarWidth() const1602 auto Settings::getSidebarWidth() const -> int { return this->sidebarWidth; }
1603
setSidebarWidth(int width)1604 void Settings::setSidebarWidth(int width) {
1605 width = std::max(width, 50);
1606
1607 if (this->sidebarWidth == width) {
1608 return;
1609 }
1610 this->sidebarWidth = width;
1611 save();
1612 }
1613
setMainWndSize(int width,int height)1614 void Settings::setMainWndSize(int width, int height) {
1615 this->mainWndWidth = width;
1616 this->mainWndHeight = height;
1617
1618 save();
1619 }
1620
getMainWndWidth() const1621 auto Settings::getMainWndWidth() const -> int { return this->mainWndWidth; }
1622
getMainWndHeight() const1623 auto Settings::getMainWndHeight() const -> int { return this->mainWndHeight; }
1624
isMainWndMaximized() const1625 auto Settings::isMainWndMaximized() const -> bool { return this->maximized; }
1626
setMainWndMaximized(bool max)1627 void Settings::setMainWndMaximized(bool max) { this->maximized = max; }
1628
setSelectedToolbar(const string & name)1629 void Settings::setSelectedToolbar(const string& name) {
1630 if (this->selectedToolbar == name) {
1631 return;
1632 }
1633 this->selectedToolbar = name;
1634 save();
1635 }
1636
getSelectedToolbar() const1637 auto Settings::getSelectedToolbar() const -> string const& { return this->selectedToolbar; }
1638
getCustomElement(const string & name)1639 auto Settings::getCustomElement(const string& name) -> SElement& { return this->data[name]; }
1640
customSettingsChanged()1641 void Settings::customSettingsChanged() { save(); }
1642
getButtonConfig(int id)1643 auto Settings::getButtonConfig(int id) -> ButtonConfig* {
1644 if (id < 0 || id >= BUTTON_COUNT) {
1645 g_error("Settings::getButtonConfig try to get id=%i out of range!", id);
1646 return nullptr;
1647 }
1648 return this->buttonConfig[id];
1649 }
1650
getFullscreenHideElements() const1651 auto Settings::getFullscreenHideElements() const -> string const& { return this->fullscreenHideElements; }
1652
setFullscreenHideElements(string elements)1653 void Settings::setFullscreenHideElements(string elements) {
1654 this->fullscreenHideElements = std::move(elements);
1655 save();
1656 }
1657
getPresentationHideElements() const1658 auto Settings::getPresentationHideElements() const -> string const& { return this->presentationHideElements; }
1659
setPresentationHideElements(string elements)1660 void Settings::setPresentationHideElements(string elements) {
1661 this->presentationHideElements = std::move(elements);
1662 save();
1663 }
1664
getTouchZoomStartThreshold() const1665 auto Settings::getTouchZoomStartThreshold() const -> double { return this->touchZoomStartThreshold; }
setTouchZoomStartThreshold(double threshold)1666 void Settings::setTouchZoomStartThreshold(double threshold) {
1667 if (this->touchZoomStartThreshold == threshold) {
1668 return;
1669 }
1670
1671 this->touchZoomStartThreshold = threshold;
1672 save();
1673 }
1674
1675
getPDFPageRerenderThreshold() const1676 auto Settings::getPDFPageRerenderThreshold() const -> double { return this->pageRerenderThreshold; }
setPDFPageRerenderThreshold(double threshold)1677 void Settings::setPDFPageRerenderThreshold(double threshold) {
1678 if (this->pageRerenderThreshold == threshold) {
1679 return;
1680 }
1681
1682 this->pageRerenderThreshold = threshold;
1683 save();
1684 }
1685
getPdfPageCacheSize() const1686 auto Settings::getPdfPageCacheSize() const -> int { return this->pdfPageCacheSize; }
1687
setPdfPageCacheSize(int size)1688 void Settings::setPdfPageCacheSize(int size) {
1689 if (this->pdfPageCacheSize == size) {
1690 return;
1691 }
1692 this->pdfPageCacheSize = size;
1693 save();
1694 }
1695
getPreloadPagesBefore() const1696 auto Settings::getPreloadPagesBefore() const -> unsigned int { return this->preloadPagesBefore; }
1697
setPreloadPagesBefore(unsigned int n)1698 void Settings::setPreloadPagesBefore(unsigned int n) {
1699 if (this->preloadPagesBefore == n) {
1700 return;
1701 }
1702 this->preloadPagesBefore = n;
1703 save();
1704 }
1705
getPreloadPagesAfter() const1706 auto Settings::getPreloadPagesAfter() const -> unsigned int { return this->preloadPagesAfter; }
1707
setPreloadPagesAfter(unsigned int n)1708 void Settings::setPreloadPagesAfter(unsigned int n) {
1709 if (this->preloadPagesAfter == n) {
1710 return;
1711 }
1712 this->preloadPagesAfter = n;
1713 save();
1714 }
1715
isEagerPageCleanup() const1716 auto Settings::isEagerPageCleanup() const -> bool { return this->eagerPageCleanup; }
1717
setEagerPageCleanup(bool b)1718 void Settings::setEagerPageCleanup(bool b) {
1719 if (this->eagerPageCleanup == b) {
1720 return;
1721 }
1722 this->eagerPageCleanup = b;
1723 save();
1724 }
1725
getBorderColor() const1726 auto Settings::getBorderColor() const -> Color { return this->selectionBorderColor; }
1727
setBorderColor(Color color)1728 void Settings::setBorderColor(Color color) {
1729 if (this->selectionBorderColor == color) {
1730 return;
1731 }
1732 this->selectionBorderColor = color;
1733 save();
1734 }
1735
getSelectionColor() const1736 auto Settings::getSelectionColor() const -> Color { return this->selectionMarkerColor; }
1737
setSelectionColor(Color color)1738 void Settings::setSelectionColor(Color color) {
1739 if (this->selectionMarkerColor == color) {
1740 return;
1741 }
1742 this->selectionMarkerColor = color;
1743 save();
1744 }
1745
getBackgroundColor() const1746 auto Settings::getBackgroundColor() const -> Color { return this->backgroundColor; }
1747
setBackgroundColor(Color color)1748 void Settings::setBackgroundColor(Color color) {
1749 if (this->backgroundColor == color) {
1750 return;
1751 }
1752 this->backgroundColor = color;
1753 save();
1754 }
1755
getFont()1756 auto Settings::getFont() -> XojFont& { return this->font; }
1757
setFont(const XojFont & font)1758 void Settings::setFont(const XojFont& font) {
1759 this->font = font;
1760 save();
1761 }
1762
1763
getAudioInputDevice() const1764 auto Settings::getAudioInputDevice() const -> PaDeviceIndex { return this->audioInputDevice; }
1765
setAudioInputDevice(PaDeviceIndex deviceIndex)1766 void Settings::setAudioInputDevice(PaDeviceIndex deviceIndex) {
1767 if (this->audioInputDevice == deviceIndex) {
1768 return;
1769 }
1770 this->audioInputDevice = deviceIndex;
1771 save();
1772 }
1773
getAudioOutputDevice() const1774 auto Settings::getAudioOutputDevice() const -> PaDeviceIndex { return this->audioOutputDevice; }
1775
setAudioOutputDevice(PaDeviceIndex deviceIndex)1776 void Settings::setAudioOutputDevice(PaDeviceIndex deviceIndex) {
1777 if (this->audioOutputDevice == deviceIndex) {
1778 return;
1779 }
1780 this->audioOutputDevice = deviceIndex;
1781 save();
1782 }
1783
getAudioSampleRate() const1784 auto Settings::getAudioSampleRate() const -> double { return this->audioSampleRate; }
1785
setAudioSampleRate(double sampleRate)1786 void Settings::setAudioSampleRate(double sampleRate) {
1787 if (this->audioSampleRate == sampleRate) {
1788 return;
1789 }
1790 this->audioSampleRate = sampleRate;
1791 save();
1792 }
1793
getAudioGain() const1794 auto Settings::getAudioGain() const -> double { return this->audioGain; }
1795
setAudioGain(double gain)1796 void Settings::setAudioGain(double gain) {
1797 if (this->audioGain == gain) {
1798 return;
1799 }
1800 this->audioGain = gain;
1801 save();
1802 }
1803
getDefaultSeekTime() const1804 auto Settings::getDefaultSeekTime() const -> unsigned int { return this->defaultSeekTime; }
1805
setDefaultSeekTime(unsigned int t)1806 void Settings::setDefaultSeekTime(unsigned int t) {
1807 if (this->defaultSeekTime == t) {
1808 return;
1809 }
1810 this->defaultSeekTime = t;
1811 save();
1812 }
1813
getPluginEnabled() const1814 auto Settings::getPluginEnabled() const -> string const& { return this->pluginEnabled; }
1815
setPluginEnabled(const string & pluginEnabled)1816 void Settings::setPluginEnabled(const string& pluginEnabled) {
1817 if (this->pluginEnabled == pluginEnabled) {
1818 return;
1819 }
1820 this->pluginEnabled = pluginEnabled;
1821 save();
1822 }
1823
getPluginDisabled() const1824 auto Settings::getPluginDisabled() const -> string const& { return this->pluginDisabled; }
1825
setPluginDisabled(const string & pluginDisabled)1826 void Settings::setPluginDisabled(const string& pluginDisabled) {
1827 if (this->pluginDisabled == pluginDisabled) {
1828 return;
1829 }
1830 this->pluginDisabled = pluginDisabled;
1831 save();
1832 }
1833
1834
getStrokeFilter(int * ignoreTime,double * ignoreLength,int * successiveTime) const1835 void Settings::getStrokeFilter(int* ignoreTime, double* ignoreLength, int* successiveTime) const {
1836 *ignoreTime = this->strokeFilterIgnoreTime;
1837 *ignoreLength = this->strokeFilterIgnoreLength;
1838 *successiveTime = this->strokeFilterSuccessiveTime;
1839 }
1840
setStrokeFilter(int ignoreTime,double ignoreLength,int successiveTime)1841 void Settings::setStrokeFilter(int ignoreTime, double ignoreLength, int successiveTime) {
1842 this->strokeFilterIgnoreTime = ignoreTime;
1843 this->strokeFilterIgnoreLength = ignoreLength;
1844 this->strokeFilterSuccessiveTime = successiveTime;
1845 }
1846
setStrokeFilterEnabled(bool enabled)1847 void Settings::setStrokeFilterEnabled(bool enabled) { this->strokeFilterEnabled = enabled; }
1848
getStrokeFilterEnabled() const1849 auto Settings::getStrokeFilterEnabled() const -> bool { return this->strokeFilterEnabled; }
1850
setDoActionOnStrokeFiltered(bool enabled)1851 void Settings::setDoActionOnStrokeFiltered(bool enabled) { this->doActionOnStrokeFiltered = enabled; }
1852
getDoActionOnStrokeFiltered() const1853 auto Settings::getDoActionOnStrokeFiltered() const -> bool { return this->doActionOnStrokeFiltered; }
1854
setTrySelectOnStrokeFiltered(bool enabled)1855 void Settings::setTrySelectOnStrokeFiltered(bool enabled) { this->trySelectOnStrokeFiltered = enabled; }
1856
getTrySelectOnStrokeFiltered() const1857 auto Settings::getTrySelectOnStrokeFiltered() const -> bool { return this->trySelectOnStrokeFiltered; }
1858
setSnapRecognizedShapesEnabled(bool enabled)1859 void Settings::setSnapRecognizedShapesEnabled(bool enabled) { this->snapRecognizedShapesEnabled = enabled; }
1860
getSnapRecognizedShapesEnabled() const1861 auto Settings::getSnapRecognizedShapesEnabled() const -> bool { return this->snapRecognizedShapesEnabled; }
1862
1863
setRestoreLineWidthEnabled(bool enabled)1864 void Settings::setRestoreLineWidthEnabled(bool enabled) { this->restoreLineWidthEnabled = enabled; }
1865
getRestoreLineWidthEnabled() const1866 auto Settings::getRestoreLineWidthEnabled() const -> bool { return this->restoreLineWidthEnabled; }
1867
setPreferredLocale(std::string const & locale)1868 auto Settings::setPreferredLocale(std::string const& locale) -> void { this->preferredLocale = locale; }
1869
getPreferredLocale() const1870 auto Settings::getPreferredLocale() const -> std::string { return this->preferredLocale; }
1871
setIgnoredStylusEvents(int numEvents)1872 void Settings::setIgnoredStylusEvents(int numEvents) {
1873 if (this->numIgnoredStylusEvents == numEvents) {
1874 return;
1875 }
1876 this->numIgnoredStylusEvents = std::max<int>(numEvents, 0);
1877 save();
1878 }
1879
getIgnoredStylusEvents() const1880 auto Settings::getIgnoredStylusEvents() const -> int { return this->numIgnoredStylusEvents; }
1881
setInputSystemTPCButtonEnabled(bool tpcButtonEnabled)1882 void Settings::setInputSystemTPCButtonEnabled(bool tpcButtonEnabled) {
1883 if (this->inputSystemTPCButton == tpcButtonEnabled) {
1884 return;
1885 }
1886 this->inputSystemTPCButton = tpcButtonEnabled;
1887 save();
1888 }
1889
getInputSystemTPCButtonEnabled() const1890 auto Settings::getInputSystemTPCButtonEnabled() const -> bool { return this->inputSystemTPCButton; }
1891
setInputSystemDrawOutsideWindowEnabled(bool drawOutsideWindowEnabled)1892 void Settings::setInputSystemDrawOutsideWindowEnabled(bool drawOutsideWindowEnabled) {
1893 if (this->inputSystemDrawOutsideWindow == drawOutsideWindowEnabled) {
1894 return;
1895 }
1896 this->inputSystemDrawOutsideWindow = drawOutsideWindowEnabled;
1897 save();
1898 }
1899
getInputSystemDrawOutsideWindowEnabled() const1900 auto Settings::getInputSystemDrawOutsideWindowEnabled() const -> bool { return this->inputSystemDrawOutsideWindow; }
1901
setDeviceClassForDevice(GdkDevice * device,InputDeviceTypeOption deviceClass)1902 void Settings::setDeviceClassForDevice(GdkDevice* device, InputDeviceTypeOption deviceClass) {
1903 this->setDeviceClassForDevice(gdk_device_get_name(device), gdk_device_get_source(device), deviceClass);
1904 }
1905
setDeviceClassForDevice(const string & deviceName,GdkInputSource deviceSource,InputDeviceTypeOption deviceClass)1906 void Settings::setDeviceClassForDevice(const string& deviceName, GdkInputSource deviceSource,
1907 InputDeviceTypeOption deviceClass) {
1908 auto it = inputDeviceClasses.find(deviceName);
1909 if (it != inputDeviceClasses.end()) {
1910 it->second.first = deviceClass;
1911 it->second.second = deviceSource;
1912 } else {
1913 inputDeviceClasses.emplace(deviceName, std::make_pair(deviceClass, deviceSource));
1914 }
1915 }
1916
getKnownInputDevices() const1917 auto Settings::getKnownInputDevices() const -> std::vector<InputDevice> {
1918 std::vector<InputDevice> inputDevices;
1919 for (auto pair: inputDeviceClasses) {
1920 const std::string& name = pair.first;
1921 GdkInputSource& source = pair.second.second;
1922 inputDevices.emplace_back(name, source);
1923 }
1924 return inputDevices;
1925 }
1926
getDeviceClassForDevice(GdkDevice * device) const1927 auto Settings::getDeviceClassForDevice(GdkDevice* device) const -> InputDeviceTypeOption {
1928 return this->getDeviceClassForDevice(gdk_device_get_name(device), gdk_device_get_source(device));
1929 }
1930
getDeviceClassForDevice(const string & deviceName,GdkInputSource deviceSource) const1931 auto Settings::getDeviceClassForDevice(const string& deviceName, GdkInputSource deviceSource) const
1932 -> InputDeviceTypeOption {
1933 auto search = inputDeviceClasses.find(deviceName);
1934 if (search != inputDeviceClasses.end()) {
1935 return search->second.first;
1936 }
1937
1938
1939 InputDeviceTypeOption deviceType = InputDeviceTypeOption::Disabled;
1940 switch (deviceSource) {
1941 case GDK_SOURCE_CURSOR:
1942 #if (GDK_MAJOR_VERSION >= 3 && GDK_MINOR_VERSION >= 22)
1943 case GDK_SOURCE_TABLET_PAD:
1944 #endif
1945 case GDK_SOURCE_KEYBOARD:
1946 deviceType = InputDeviceTypeOption::Disabled;
1947 break;
1948 case GDK_SOURCE_MOUSE:
1949 case GDK_SOURCE_TOUCHPAD:
1950 #if (GDK_MAJOR_VERSION >= 3 && GDK_MINOR_VERSION >= 22)
1951 case GDK_SOURCE_TRACKPOINT:
1952 #endif
1953 deviceType = InputDeviceTypeOption::Mouse;
1954 break;
1955 case GDK_SOURCE_PEN:
1956 deviceType = InputDeviceTypeOption::Pen;
1957 break;
1958 case GDK_SOURCE_ERASER:
1959 deviceType = InputDeviceTypeOption::Eraser;
1960 break;
1961 case GDK_SOURCE_TOUCHSCREEN:
1962 deviceType = InputDeviceTypeOption::Touchscreen;
1963 break;
1964 default:
1965 deviceType = InputDeviceTypeOption::Disabled;
1966 }
1967 return deviceType;
1968 }
1969
isScrollbarFadeoutDisabled() const1970 auto Settings::isScrollbarFadeoutDisabled() const -> bool { return disableScrollbarFadeout; }
1971
setScrollbarFadeoutDisabled(bool disable)1972 void Settings::setScrollbarFadeoutDisabled(bool disable) {
1973 if (disableScrollbarFadeout == disable) {
1974 return;
1975 }
1976 disableScrollbarFadeout = disable;
1977 save();
1978 }
1979
1980 //////////////////////////////////////////////////
1981
SAttribute()1982 SAttribute::SAttribute() {
1983 this->dValue = 0;
1984 this->iValue = 0;
1985 this->type = ATTRIBUTE_TYPE_NONE;
1986 }
1987
SAttribute(const SAttribute & attrib)1988 SAttribute::SAttribute(const SAttribute& attrib) { *this = attrib; }
1989
~SAttribute()1990 SAttribute::~SAttribute() {
1991 this->iValue = 0;
1992 this->type = ATTRIBUTE_TYPE_NONE;
1993 }
1994
1995 //////////////////////////////////////////////////
1996
attributes()1997 auto SElement::attributes() -> std::map<string, SAttribute>& { return this->element->attributes; }
1998
children()1999 auto SElement::children() -> std::map<string, SElement>& { return this->element->children; }
2000
clear()2001 void SElement::clear() {
2002 this->element->attributes.clear();
2003 this->element->children.clear();
2004 }
2005
child(const string & name)2006 auto SElement::child(const string& name) -> SElement& { return this->element->children[name]; }
2007
setComment(const string & name,const string & comment)2008 void SElement::setComment(const string& name, const string& comment) {
2009 SAttribute& attrib = this->element->attributes[name];
2010 attrib.comment = comment;
2011 }
2012
setIntHex(const string & name,const int value)2013 void SElement::setIntHex(const string& name, const int value) {
2014 SAttribute& attrib = this->element->attributes[name];
2015 attrib.iValue = value;
2016 attrib.type = ATTRIBUTE_TYPE_INT_HEX;
2017 }
2018
setInt(const string & name,const int value)2019 void SElement::setInt(const string& name, const int value) {
2020 SAttribute& attrib = this->element->attributes[name];
2021 attrib.iValue = value;
2022 attrib.type = ATTRIBUTE_TYPE_INT;
2023 }
2024
setBool(const string & name,const bool value)2025 void SElement::setBool(const string& name, const bool value) {
2026 SAttribute& attrib = this->element->attributes[name];
2027 attrib.iValue = value;
2028 attrib.type = ATTRIBUTE_TYPE_BOOLEAN;
2029 }
2030
setString(const string & name,const string & value)2031 void SElement::setString(const string& name, const string& value) {
2032 SAttribute& attrib = this->element->attributes[name];
2033 attrib.sValue = value;
2034 attrib.type = ATTRIBUTE_TYPE_STRING;
2035 }
2036
setDouble(const string & name,const double value)2037 void SElement::setDouble(const string& name, const double value) {
2038 SAttribute& attrib = this->element->attributes[name];
2039 attrib.dValue = value;
2040 attrib.type = ATTRIBUTE_TYPE_DOUBLE;
2041 }
2042
getDouble(const string & name,double & value)2043 auto SElement::getDouble(const string& name, double& value) -> bool {
2044 SAttribute& attrib = this->element->attributes[name];
2045 if (attrib.type == ATTRIBUTE_TYPE_NONE) {
2046 this->element->attributes.erase(name);
2047 return false;
2048 }
2049
2050 if (attrib.type != ATTRIBUTE_TYPE_DOUBLE) {
2051 return false;
2052 }
2053
2054 value = attrib.dValue;
2055
2056 return true;
2057 }
2058
getInt(const string & name,int & value)2059 auto SElement::getInt(const string& name, int& value) -> bool {
2060 SAttribute& attrib = this->element->attributes[name];
2061 if (attrib.type == ATTRIBUTE_TYPE_NONE) {
2062 this->element->attributes.erase(name);
2063 return false;
2064 }
2065
2066 if (attrib.type != ATTRIBUTE_TYPE_INT && attrib.type != ATTRIBUTE_TYPE_INT_HEX) {
2067 return false;
2068 }
2069
2070 value = attrib.iValue;
2071
2072 return true;
2073 }
2074
getBool(const string & name,bool & value)2075 auto SElement::getBool(const string& name, bool& value) -> bool {
2076 SAttribute& attrib = this->element->attributes[name];
2077 if (attrib.type == ATTRIBUTE_TYPE_NONE) {
2078 this->element->attributes.erase(name);
2079 return false;
2080 }
2081
2082 if (attrib.type != ATTRIBUTE_TYPE_BOOLEAN) {
2083 return false;
2084 }
2085
2086 value = attrib.iValue;
2087
2088 return true;
2089 }
2090
getString(const string & name,string & value)2091 auto SElement::getString(const string& name, string& value) -> bool {
2092 SAttribute& attrib = this->element->attributes[name];
2093 if (attrib.type == ATTRIBUTE_TYPE_NONE) {
2094 this->element->attributes.erase(name);
2095 return false;
2096 }
2097
2098 if (attrib.type != ATTRIBUTE_TYPE_STRING) {
2099 return false;
2100 }
2101
2102 value = attrib.sValue;
2103
2104 return true;
2105 }
2106
2107 /**
2108 * Stabilizer related getters and setters
2109 */
getStabilizerCuspDetection() const2110 auto Settings::getStabilizerCuspDetection() const -> bool { return stabilizerCuspDetection; }
getStabilizerFinalizeStroke() const2111 auto Settings::getStabilizerFinalizeStroke() const -> bool { return stabilizerFinalizeStroke; }
getStabilizerBuffersize() const2112 auto Settings::getStabilizerBuffersize() const -> size_t { return stabilizerBuffersize; }
getStabilizerDeadzoneRadius() const2113 auto Settings::getStabilizerDeadzoneRadius() const -> double { return stabilizerDeadzoneRadius; }
getStabilizerDrag() const2114 auto Settings::getStabilizerDrag() const -> double { return stabilizerDrag; }
getStabilizerMass() const2115 auto Settings::getStabilizerMass() const -> double { return stabilizerMass; }
getStabilizerSigma() const2116 auto Settings::getStabilizerSigma() const -> double { return stabilizerSigma; }
getStabilizerAveragingMethod() const2117 auto Settings::getStabilizerAveragingMethod() const -> StrokeStabilizer::AveragingMethod {
2118 return stabilizerAveragingMethod;
2119 }
getStabilizerPreprocessor() const2120 auto Settings::getStabilizerPreprocessor() const -> StrokeStabilizer::Preprocessor { return stabilizerPreprocessor; }
2121
setStabilizerCuspDetection(bool cuspDetection)2122 void Settings::setStabilizerCuspDetection(bool cuspDetection) {
2123 if (stabilizerCuspDetection == cuspDetection) {
2124 return;
2125 }
2126 stabilizerCuspDetection = cuspDetection;
2127 save();
2128 }
setStabilizerFinalizeStroke(bool finalizeStroke)2129 void Settings::setStabilizerFinalizeStroke(bool finalizeStroke) {
2130 if (stabilizerFinalizeStroke == finalizeStroke) {
2131 return;
2132 }
2133 stabilizerFinalizeStroke = finalizeStroke;
2134 save();
2135 }
setStabilizerBuffersize(size_t buffersize)2136 void Settings::setStabilizerBuffersize(size_t buffersize) {
2137 if (stabilizerBuffersize == buffersize) {
2138 return;
2139 }
2140 stabilizerBuffersize = buffersize;
2141 save();
2142 }
setStabilizerDeadzoneRadius(double deadzoneRadius)2143 void Settings::setStabilizerDeadzoneRadius(double deadzoneRadius) {
2144 if (stabilizerDeadzoneRadius == deadzoneRadius) {
2145 return;
2146 }
2147 stabilizerDeadzoneRadius = deadzoneRadius;
2148 save();
2149 }
setStabilizerDrag(double drag)2150 void Settings::setStabilizerDrag(double drag) {
2151 if (stabilizerDrag == drag) {
2152 return;
2153 }
2154 stabilizerDrag = drag;
2155 save();
2156 }
setStabilizerMass(double mass)2157 void Settings::setStabilizerMass(double mass) {
2158 if (stabilizerMass == mass) {
2159 return;
2160 }
2161 stabilizerMass = mass;
2162 save();
2163 }
setStabilizerSigma(double sigma)2164 void Settings::setStabilizerSigma(double sigma) {
2165 if (stabilizerSigma == sigma) {
2166 return;
2167 }
2168 stabilizerSigma = sigma;
2169 save();
2170 }
setStabilizerAveragingMethod(StrokeStabilizer::AveragingMethod averagingMethod)2171 void Settings::setStabilizerAveragingMethod(StrokeStabilizer::AveragingMethod averagingMethod) {
2172 const StrokeStabilizer::AveragingMethod method =
2173 StrokeStabilizer::isValid(averagingMethod) ? averagingMethod : StrokeStabilizer::AveragingMethod::NONE;
2174
2175 if (stabilizerAveragingMethod == method) {
2176 return;
2177 }
2178 stabilizerAveragingMethod = method;
2179 save();
2180 }
setStabilizerPreprocessor(StrokeStabilizer::Preprocessor preprocessor)2181 void Settings::setStabilizerPreprocessor(StrokeStabilizer::Preprocessor preprocessor) {
2182 const StrokeStabilizer::Preprocessor p =
2183 StrokeStabilizer::isValid(preprocessor) ? preprocessor : StrokeStabilizer::Preprocessor::NONE;
2184
2185 if (stabilizerPreprocessor == p) {
2186 return;
2187 }
2188 stabilizerPreprocessor = p;
2189 save();
2190 }
2191