#include "Settings.h" #include #include #include "model/FormatDefinitions.h" #include "util/DeviceListHelper.h" #include "ButtonConfig.h" #include "Util.h" #include "filesystem.h" #include "i18n.h" constexpr auto const* DEFAULT_FONT = "Sans"; constexpr auto DEFAULT_FONT_SIZE = 12; #define WRITE_BOOL_PROP(var) xmlNode = saveProperty((const char*)#var, (var) ? "true" : "false", root) #define WRITE_STRING_PROP(var) xmlNode = saveProperty((const char*)#var, (var).empty() ? "" : (var).c_str(), root) #define WRITE_INT_PROP(var) xmlNode = saveProperty((const char*)#var, var, root) #define WRITE_UINT_PROP(var) xmlNode = savePropertyUnsigned((const char*)#var, var, root) #define WRITE_DOUBLE_PROP(var) xmlNode = savePropertyDouble((const char*)#var, var, root) #define WRITE_COMMENT(var) \ com = xmlNewComment((const xmlChar*)(var)); \ xmlAddPrevSibling(xmlNode, com); Settings::Settings(fs::path filepath): filepath(std::move(filepath)) { loadDefault(); } Settings::~Settings() { for (auto& i: this->buttonConfig) { delete i; i = nullptr; } } void Settings::loadDefault() { this->pressureSensitivity = true; this->zoomGesturesEnabled = true; this->maximized = false; this->showPairedPages = false; this->presentationMode = false; this->numColumns = 1; // only one of these applies at a time this->numRows = 1; this->viewFixedRows = false; this->layoutVertical = false; this->layoutRightToLeft = false; this->layoutBottomToTop = false; this->numPairsOffset = 1; this->zoomStep = 10.0; this->zoomStepScroll = 2.0; this->displayDpi = 72; this->font.setName(DEFAULT_FONT); this->font.setSize(DEFAULT_FONT_SIZE); this->mainWndWidth = 800; this->mainWndHeight = 600; this->showSidebar = true; this->sidebarWidth = 150; this->showToolbar = true; this->sidebarOnRight = false; this->scrollbarOnLeft = false; this->menubarVisible = true; this->autoloadPdfXoj = true; this->stylusCursorType = STYLUS_CURSOR_DOT; this->highlightPosition = false; this->cursorHighlightColor = 0x80FFFF00; // Yellow with 50% opacity this->cursorHighlightRadius = 30.0; this->cursorHighlightBorderColor = 0x800000FF; // Blue with 50% opacity this->cursorHighlightBorderWidth = 0.0; this->darkTheme = false; this->scrollbarHideType = SCROLLBAR_HIDE_NONE; this->disableScrollbarFadeout = false; // Set this for autosave frequency in minutes. this->autosaveTimeout = 3; this->autosaveEnabled = true; this->addHorizontalSpace = false; this->addHorizontalSpaceAmount = 150; this->addVerticalSpace = false; this->addVerticalSpaceAmount = 150; // Drawing direction emulates modifier keys this->drawDirModsRadius = 50; this->drawDirModsEnabled = false; this->snapRotation = true; this->snapRotationTolerance = 0.30; this->snapGrid = true; this->snapGridTolerance = 0.50; this->snapGridSize = DEFAULT_GRID_SIZE; this->touchWorkaround = false; this->defaultSaveName = _("%F-Note-%H-%M"); // Eraser this->buttonConfig[BUTTON_ERASER] = new ButtonConfig(TOOL_ERASER, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE); // Middle button this->buttonConfig[BUTTON_MIDDLE] = new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE); // Right button this->buttonConfig[BUTTON_RIGHT] = new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE); // Touch this->buttonConfig[BUTTON_TOUCH] = new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE); // Default config this->buttonConfig[BUTTON_DEFAULT] = new ButtonConfig(TOOL_PEN, Color{0x000000U}, TOOL_SIZE_FINE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE); // Pen button 1 this->buttonConfig[BUTTON_STYLUS] = new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE); // Pen button 2 this->buttonConfig[BUTTON_STYLUS2] = new ButtonConfig(TOOL_NONE, Color{0x000000U}, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE); this->fullscreenHideElements = "mainMenubar"; this->presentationHideElements = "mainMenubar,sidebarContents"; this->pdfPageCacheSize = 10; this->selectionBorderColor = 0xff0000U; // red this->selectionMarkerColor = 0x729fcfU; // light blue this->backgroundColor = 0xdcdad5U; // clang-format off this->pageTemplate = "xoj/template\ncopyLastPageSettings=true\nsize=595.275591x841.889764\nbackgroundType=lined\nbackgroundColor=#ffffff\n"; // clang-format on this->audioSampleRate = 44100.0; this->audioInputDevice = -1; this->audioOutputDevice = -1; this->audioGain = 1.0; this->defaultSeekTime = 5; this->pluginEnabled = ""; this->pluginDisabled = ""; this->numIgnoredStylusEvents = 0; this->newInputSystemEnabled = true; this->inputSystemTPCButton = false; this->inputSystemDrawOutsideWindow = true; this->strokeFilterIgnoreTime = 150; this->strokeFilterIgnoreLength = 1; this->strokeFilterSuccessiveTime = 500; this->strokeFilterEnabled = false; this->doActionOnStrokeFiltered = false; this->trySelectOnStrokeFiltered = false; this->snapRecognizedShapesEnabled = false; this->restoreLineWidthEnabled = false; this->inTransaction = false; } /** * tempg_ascii_strtod * Transition to using g_ascii_strtod to minimize disruption. May, 2019. * Delete this and replace calls to this function with calls to g_ascii_strtod() in 2020. * See: https://developer.gnome.org/glib/stable/glib-String-Utility-Functions.html#g-strtod */ auto tempg_ascii_strtod(const gchar* txt, gchar** endptr) -> double { return g_strtod(txt, endptr); // makes best guess between locale formatted and C formatted numbers. See link above. } void Settings::parseData(xmlNodePtr cur, SElement& elem) { for (xmlNodePtr x = cur->children; x != nullptr; x = x->next) { if (!xmlStrcmp(x->name, reinterpret_cast("data"))) { xmlChar* name = xmlGetProp(x, reinterpret_cast("name")); parseData(x, elem.child(reinterpret_cast(name))); xmlFree(name); } else if (!xmlStrcmp(x->name, reinterpret_cast("attribute"))) { xmlChar* name = xmlGetProp(x, reinterpret_cast("name")); xmlChar* value = xmlGetProp(x, reinterpret_cast("value")); xmlChar* type = xmlGetProp(x, reinterpret_cast("type")); string sType = reinterpret_cast(type); if (sType == "int") { int i = atoi(reinterpret_cast(value)); elem.setInt(reinterpret_cast(name), i); } else if (sType == "double") { double d = tempg_ascii_strtod(reinterpret_cast(value), nullptr); // g_ascii_strtod ignores locale setting. elem.setDouble(reinterpret_cast(name), d); } else if (sType == "hex") { int i = 0; if (sscanf(reinterpret_cast(value), "%x", &i)) { elem.setIntHex(reinterpret_cast(name), i); } else { g_warning("Settings::Unknown hex value: %s:%s\n", name, value); } } else if (sType == "string") { elem.setString(reinterpret_cast(name), reinterpret_cast(value)); } else if (sType == "boolean") { elem.setBool(reinterpret_cast(name), strcmp(reinterpret_cast(value), "true") == 0); } else { g_warning("Settings::Unknown datatype: %s\n", sType.c_str()); } xmlFree(name); xmlFree(type); xmlFree(value); } else { g_warning("Settings::parseData: Unknown XML node: %s\n", x->name); continue; } } } void Settings::parseItem(xmlDocPtr doc, xmlNodePtr cur) { // Parse data map if (!xmlStrcmp(cur->name, reinterpret_cast("data"))) { xmlChar* name = xmlGetProp(cur, reinterpret_cast("name")); if (name == nullptr) { g_warning("Settings::%s:No name property!\n", cur->name); return; } parseData(cur, data[reinterpret_cast(name)]); xmlFree(name); return; } if (cur->type == XML_COMMENT_NODE) { return; } if (xmlStrcmp(cur->name, reinterpret_cast("property"))) { g_warning("Settings::Unknown XML node: %s\n", cur->name); return; } xmlChar* name = xmlGetProp(cur, reinterpret_cast("name")); if (name == nullptr) { g_warning("Settings::%s:No name property!\n", cur->name); return; } if (xmlStrcmp(name, reinterpret_cast("font")) == 0) { xmlFree(name); xmlChar* font = nullptr; xmlChar* size = nullptr; font = xmlGetProp(cur, reinterpret_cast("font")); if (font) { this->font.setName(reinterpret_cast(font)); xmlFree(font); } size = xmlGetProp(cur, reinterpret_cast("size")); if (size) { double dSize = DEFAULT_FONT_SIZE; if (sscanf(reinterpret_cast(size), "%lf", &dSize) == 1) { this->font.setSize(dSize); } xmlFree(size); } return; } xmlChar* value = xmlGetProp(cur, reinterpret_cast("value")); if (value == nullptr) { xmlFree(name); g_warning("No value property!\n"); return; } // TODO(fabian): remove this typo fix in 2-3 release cycles if (xmlStrcmp(name, reinterpret_cast("presureSensitivity")) == 0) { this->pressureSensitivity = xmlStrcmp(value, reinterpret_cast("true")) == 0; } if (xmlStrcmp(name, reinterpret_cast("pressureSensitivity")) == 0) { this->pressureSensitivity = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("zoomGesturesEnabled")) == 0) { this->zoomGesturesEnabled = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("selectedToolbar")) == 0) { this->selectedToolbar = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("lastSavePath")) == 0) { this->lastSavePath = fs::u8path(reinterpret_cast(value)); } else if (xmlStrcmp(name, reinterpret_cast("lastOpenPath")) == 0) { this->lastOpenPath = fs::u8path(reinterpret_cast(value)); } else if (xmlStrcmp(name, reinterpret_cast("lastImagePath")) == 0) { this->lastImagePath = fs::u8path(reinterpret_cast(value)); } else if (xmlStrcmp(name, reinterpret_cast("zoomStep")) == 0) { this->zoomStep = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("zoomStepScroll")) == 0) { this->zoomStepScroll = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("displayDpi")) == 0) { this->displayDpi = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("mainWndWidth")) == 0) { this->mainWndWidth = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("mainWndHeight")) == 0) { this->mainWndHeight = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("maximized")) == 0) { this->maximized = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("showToolbar")) == 0) { this->showToolbar = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("showSidebar")) == 0) { this->showSidebar = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("sidebarWidth")) == 0) { this->sidebarWidth = std::max(g_ascii_strtoll(reinterpret_cast(value), nullptr, 10), 50); } else if (xmlStrcmp(name, reinterpret_cast("sidebarOnRight")) == 0) { this->sidebarOnRight = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("scrollbarOnLeft")) == 0) { this->scrollbarOnLeft = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("menubarVisible")) == 0) { this->menubarVisible = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("numColumns")) == 0) { this->numColumns = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("numRows")) == 0) { this->numRows = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("viewFixedRows")) == 0) { this->viewFixedRows = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("layoutVertical")) == 0) { this->layoutVertical = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("layoutRightToLeft")) == 0) { this->layoutRightToLeft = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("layoutBottomToTop")) == 0) { this->layoutBottomToTop = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("showPairedPages")) == 0) { this->showPairedPages = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("numPairsOffset")) == 0) { this->numPairsOffset = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("presentationMode")) == 0) { this->presentationMode = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("autoloadPdfXoj")) == 0) { this->autoloadPdfXoj = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("stylusCursorType")) == 0) { this->stylusCursorType = stylusCursorTypeFromString(reinterpret_cast(value)); } else if (xmlStrcmp(name, reinterpret_cast("highlightPosition")) == 0) { this->highlightPosition = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("cursorHighlightColor")) == 0) { this->cursorHighlightColor = g_ascii_strtoull(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("cursorHighlightRadius")) == 0) { this->cursorHighlightRadius = g_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("cursorHighlightBorderColor")) == 0) { this->cursorHighlightBorderColor = g_ascii_strtoull(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("cursorHighlightBorderWidth")) == 0) { this->cursorHighlightBorderWidth = g_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("darkTheme")) == 0) { this->darkTheme = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("defaultSaveName")) == 0) { this->defaultSaveName = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("pluginEnabled")) == 0) { this->pluginEnabled = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("pluginDisabled")) == 0) { this->pluginDisabled = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("pageTemplate")) == 0) { this->pageTemplate = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("sizeUnit")) == 0) { this->sizeUnit = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("audioFolder")) == 0) { this->audioFolder = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("autosaveEnabled")) == 0) { this->autosaveEnabled = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("autosaveTimeout")) == 0) { this->autosaveTimeout = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("fullscreenHideElements")) == 0) { this->fullscreenHideElements = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("presentationHideElements")) == 0) { this->presentationHideElements = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("pdfPageCacheSize")) == 0) { this->pdfPageCacheSize = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("selectionBorderColor")) == 0) { this->selectionBorderColor = Color(g_ascii_strtoull(reinterpret_cast(value), nullptr, 10)); } else if (xmlStrcmp(name, reinterpret_cast("selectionMarkerColor")) == 0) { this->selectionMarkerColor = Color(g_ascii_strtoull(reinterpret_cast(value), nullptr, 10)); } else if (xmlStrcmp(name, reinterpret_cast("backgroundColor")) == 0) { this->backgroundColor = Color(g_ascii_strtoull(reinterpret_cast(value), nullptr, 10)); } else if (xmlStrcmp(name, reinterpret_cast("addHorizontalSpace")) == 0) { this->addHorizontalSpace = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("addHorizontalSpaceAmount")) == 0) { this->addHorizontalSpaceAmount = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("addVerticalSpace")) == 0) { this->addVerticalSpace = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("addVerticalSpaceAmount")) == 0) { this->addVerticalSpaceAmount = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("drawDirModsEnabled")) == 0) { this->drawDirModsEnabled = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("drawDirModsRadius")) == 0) { this->drawDirModsRadius = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("snapRotation")) == 0) { this->snapRotation = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("snapRotationTolerance")) == 0) { this->snapRotationTolerance = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("snapGrid")) == 0) { this->snapGrid = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("snapGridSize")) == 0) { this->snapGridSize = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("snapGridTolerance")) == 0) { this->snapGridTolerance = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("touchWorkaround")) == 0) { this->touchWorkaround = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("scrollbarHideType")) == 0) { if (xmlStrcmp(value, reinterpret_cast("both")) == 0) { this->scrollbarHideType = SCROLLBAR_HIDE_BOTH; } else if (xmlStrcmp(value, reinterpret_cast("horizontal")) == 0) { this->scrollbarHideType = SCROLLBAR_HIDE_HORIZONTAL; } else if (xmlStrcmp(value, reinterpret_cast("vertical")) == 0) { this->scrollbarHideType = SCROLLBAR_HIDE_VERTICAL; } else { this->scrollbarHideType = SCROLLBAR_HIDE_NONE; } } else if (xmlStrcmp(name, reinterpret_cast("disableScrollbarFadeout")) == 0) { this->disableScrollbarFadeout = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("audioSampleRate")) == 0) { this->audioSampleRate = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("audioGain")) == 0) { this->audioGain = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("defaultSeekTime")) == 0) { this->defaultSeekTime = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("audioInputDevice")) == 0) { this->audioInputDevice = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("audioOutputDevice")) == 0) { this->audioOutputDevice = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("numIgnoredStylusEvents")) == 0) { this->numIgnoredStylusEvents = std::max(g_ascii_strtoll(reinterpret_cast(value), nullptr, 10), 0); } else if (xmlStrcmp(name, reinterpret_cast("newInputSystemEnabled")) == 0) { this->newInputSystemEnabled = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("inputSystemTPCButton")) == 0) { this->inputSystemTPCButton = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("inputSystemDrawOutsideWindow")) == 0) { this->inputSystemDrawOutsideWindow = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("strokeFilterIgnoreTime")) == 0) { this->strokeFilterIgnoreTime = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("strokeFilterIgnoreLength")) == 0) { this->strokeFilterIgnoreLength = tempg_ascii_strtod(reinterpret_cast(value), nullptr); } else if (xmlStrcmp(name, reinterpret_cast("strokeFilterSuccessiveTime")) == 0) { this->strokeFilterSuccessiveTime = g_ascii_strtoll(reinterpret_cast(value), nullptr, 10); } else if (xmlStrcmp(name, reinterpret_cast("strokeFilterEnabled")) == 0) { this->strokeFilterEnabled = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("doActionOnStrokeFiltered")) == 0) { this->doActionOnStrokeFiltered = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("trySelectOnStrokeFiltered")) == 0) { this->trySelectOnStrokeFiltered = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("latexSettings.autoCheckDependencies")) == 0) { this->latexSettings.autoCheckDependencies = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("latexSettings.globalTemplatePath")) == 0) { std::string v(reinterpret_cast(value)); this->latexSettings.globalTemplatePath = fs::u8path(v); } else if (xmlStrcmp(name, reinterpret_cast("latexSettings.genCmd")) == 0) { this->latexSettings.genCmd = reinterpret_cast(value); } else if (xmlStrcmp(name, reinterpret_cast("snapRecognizedShapesEnabled")) == 0) { this->snapRecognizedShapesEnabled = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("restoreLineWidthEnabled")) == 0) { this->restoreLineWidthEnabled = xmlStrcmp(value, reinterpret_cast("true")) == 0; } else if (xmlStrcmp(name, reinterpret_cast("preferredLocale")) == 0) { this->preferredLocale = reinterpret_cast(value); } xmlFree(name); xmlFree(value); } void Settings::loadDeviceClasses() { SElement& s = getCustomElement("deviceClasses"); for (auto device: s.children()) { SElement& deviceNode = device.second; int deviceClass = 0; int deviceSource = 0; deviceNode.getInt("deviceClass", deviceClass); deviceNode.getInt("deviceSource", deviceSource); inputDeviceClasses.emplace(device.first, std::make_pair(static_cast(deviceClass), static_cast(deviceSource))); } } void Settings::loadButtonConfig() { SElement& s = getCustomElement("buttonConfig"); for (int i = 0; i < BUTTON_COUNT; i++) { SElement& e = s.child(buttonToString(static_cast(i))); ButtonConfig* cfg = buttonConfig[i]; string sType; if (e.getString("tool", sType)) { ToolType type = toolTypeFromString(sType); cfg->action = type; if (type == TOOL_PEN || type == TOOL_HILIGHTER) { string drawingType; if (e.getString("drawingType", drawingType)) { cfg->drawingType = drawingTypeFromString(drawingType); } string sSize; if (e.getString("size", sSize)) { cfg->size = toolSizeFromString(sSize); } else { // If not specified: do not change cfg->size = TOOL_SIZE_NONE; } } if (type == TOOL_PEN || type == TOOL_HILIGHTER || type == TOOL_TEXT) { if (int iColor; e.getInt("color", iColor)) { cfg->color = Color(iColor); } } if (type == TOOL_ERASER) { string sEraserMode; if (e.getString("eraserMode", sEraserMode)) { cfg->eraserMode = eraserTypeFromString(sEraserMode); } else { // If not specified: do not change cfg->eraserMode = ERASER_TYPE_NONE; } string sSize; if (e.getString("size", sSize)) { cfg->size = toolSizeFromString(sSize); } else { // If not specified: do not change cfg->size = TOOL_SIZE_NONE; } } // Touch device if (i == BUTTON_TOUCH) { if (!e.getString("device", cfg->device)) { cfg->device = ""; } e.getBool("disableDrawing", cfg->disableDrawing); } } else { continue; } } } auto Settings::load() -> bool { xmlKeepBlanksDefault(0); if (!fs::exists(filepath)) { g_warning("configfile does not exist %s\n", filepath.string().c_str()); return false; } xmlDocPtr doc = xmlParseFile(filepath.u8string().c_str()); if (doc == nullptr) { g_warning("Settings::load:: doc == null, could not load Settings!\n"); return false; } xmlNodePtr cur = xmlDocGetRootElement(doc); if (cur == nullptr) { g_message("The settings file \"%s\" is empty", filepath.string().c_str()); xmlFreeDoc(doc); return false; } if (xmlStrcmp(cur->name, reinterpret_cast("settings"))) { g_message("File \"%s\" is of the wrong type", filepath.string().c_str()); xmlFreeDoc(doc); return false; } cur = xmlDocGetRootElement(doc); cur = cur->xmlChildrenNode; while (cur != nullptr) { parseItem(doc, cur); cur = cur->next; } xmlFreeDoc(doc); loadButtonConfig(); loadDeviceClasses(); paletteFilePath = PALETTE_FILE; auto paletteFile = Util::getConfigFile(paletteFilePath); if (!fs::exists(paletteFile)) { Palette::create_default(paletteFile); } this->palette = new Palette(std::move(paletteFile)); this->palette->load(); // load Palette return true; } auto Settings::savePropertyDouble(const gchar* key, double value, xmlNodePtr parent) -> xmlNodePtr { char text[G_ASCII_DTOSTR_BUF_SIZE]; // g_ascii_ version uses C locale always. g_ascii_formatd(text, G_ASCII_DTOSTR_BUF_SIZE, Util::PRECISION_FORMAT_STRING, value); xmlNodePtr xmlNode = saveProperty(key, text, parent); return xmlNode; } auto Settings::saveProperty(const gchar* key, int value, xmlNodePtr parent) -> xmlNodePtr { char* text = g_strdup_printf("%i", value); xmlNodePtr xmlNode = saveProperty(key, text, parent); g_free(text); return xmlNode; } auto Settings::savePropertyUnsigned(const gchar* key, unsigned int value, xmlNodePtr parent) -> xmlNodePtr { char* text = g_strdup_printf("%u", value); xmlNodePtr xmlNode = saveProperty(key, text, parent); g_free(text); return xmlNode; } auto Settings::saveProperty(const gchar* key, const gchar* value, xmlNodePtr parent) -> xmlNodePtr { xmlNodePtr xmlNode = xmlNewChild(parent, nullptr, reinterpret_cast("property"), nullptr); xmlSetProp(xmlNode, reinterpret_cast("name"), reinterpret_cast(key)); xmlSetProp(xmlNode, reinterpret_cast("value"), reinterpret_cast(value)); return xmlNode; } void Settings::saveDeviceClasses() { SElement& s = getCustomElement("deviceClasses"); for (auto& device: inputDeviceClasses) { const std::string& name = device.first; InputDeviceTypeOption& deviceClass = device.second.first; GdkInputSource& source = device.second.second; SElement& e = s.child(name); e.setInt("deviceClass", static_cast(deviceClass)); e.setInt("deviceSource", source); } } void Settings::saveButtonConfig() { SElement& s = getCustomElement("buttonConfig"); s.clear(); for (int i = 0; i < BUTTON_COUNT; i++) { SElement& e = s.child(buttonToString(static_cast(i))); ButtonConfig* cfg = buttonConfig[i]; ToolType type = cfg->action; e.setString("tool", toolTypeToString(type)); if (type == TOOL_PEN || type == TOOL_HILIGHTER) { e.setString("drawingType", drawingTypeToString(cfg->drawingType)); e.setString("size", toolSizeToString(cfg->size)); } // end if pen or highlighter if (type == TOOL_PEN || type == TOOL_HILIGHTER || type == TOOL_TEXT) { e.setIntHex("color", int32_t(cfg->color)); } if (type == TOOL_ERASER) { e.setString("eraserMode", eraserTypeToString(cfg->eraserMode)); e.setString("size", toolSizeToString(cfg->size)); } // Touch device if (i == BUTTON_TOUCH) { e.setString("device", cfg->device); e.setBool("disableDrawing", cfg->disableDrawing); } } } /** * Do not save settings until transactionEnd() is called */ void Settings::transactionStart() { inTransaction = true; } /** * Stop transaction and save settings */ void Settings::transactionEnd() { inTransaction = false; save(); } void Settings::save() { if (inTransaction) { return; } xmlDocPtr doc = nullptr; xmlNodePtr root = nullptr; xmlNodePtr xmlNode = nullptr; xmlIndentTreeOutput = true; doc = xmlNewDoc(reinterpret_cast("1.0")); if (doc == nullptr) { return; } saveButtonConfig(); saveDeviceClasses(); /* Create metadata root */ root = xmlNewDocNode(doc, nullptr, reinterpret_cast("settings"), nullptr); xmlDocSetRootElement(doc, root); xmlNodePtr com = xmlNewComment( reinterpret_cast("The Xournal++ settings file. Do not edit this file! " "The most settings are available in the Settings dialog, " "the others are commented in this file, but handle with care!")); xmlAddPrevSibling(root, com); WRITE_BOOL_PROP(pressureSensitivity); WRITE_BOOL_PROP(zoomGesturesEnabled); WRITE_STRING_PROP(selectedToolbar); auto lastSavePath = this->lastSavePath.u8string(); auto lastOpenPath = this->lastOpenPath.u8string(); auto lastImagePath = this->lastImagePath.u8string(); WRITE_STRING_PROP(lastSavePath); WRITE_STRING_PROP(lastOpenPath); WRITE_STRING_PROP(lastImagePath); WRITE_DOUBLE_PROP(zoomStep); WRITE_DOUBLE_PROP(zoomStepScroll); WRITE_INT_PROP(displayDpi); WRITE_INT_PROP(mainWndWidth); WRITE_INT_PROP(mainWndHeight); WRITE_BOOL_PROP(maximized); WRITE_BOOL_PROP(showToolbar); WRITE_BOOL_PROP(showSidebar); WRITE_INT_PROP(sidebarWidth); WRITE_BOOL_PROP(sidebarOnRight); WRITE_BOOL_PROP(scrollbarOnLeft); WRITE_BOOL_PROP(menubarVisible); WRITE_INT_PROP(numColumns); WRITE_INT_PROP(numRows); WRITE_BOOL_PROP(viewFixedRows); WRITE_BOOL_PROP(showPairedPages); WRITE_BOOL_PROP(layoutVertical); WRITE_BOOL_PROP(layoutRightToLeft); WRITE_BOOL_PROP(layoutBottomToTop); WRITE_INT_PROP(numPairsOffset); WRITE_BOOL_PROP(presentationMode); WRITE_STRING_PROP(fullscreenHideElements); WRITE_COMMENT("Which gui elements are hidden if you are in Fullscreen mode, separated by a colon (,)"); WRITE_STRING_PROP(presentationHideElements); WRITE_COMMENT("Which gui elements are hidden if you are in Presentation mode, separated by a colon (,)"); xmlNode = saveProperty("stylusCursorType", stylusCursorTypeToString(this->stylusCursorType), root); WRITE_COMMENT("The cursor icon used with a stylus, allowed values are \"none\", \"dot\", \"big\""); WRITE_BOOL_PROP(highlightPosition); WRITE_UINT_PROP(cursorHighlightColor); WRITE_UINT_PROP(cursorHighlightBorderColor); WRITE_DOUBLE_PROP(cursorHighlightRadius); WRITE_DOUBLE_PROP(cursorHighlightBorderWidth); WRITE_BOOL_PROP(darkTheme); WRITE_BOOL_PROP(disableScrollbarFadeout); if (this->scrollbarHideType == SCROLLBAR_HIDE_BOTH) { saveProperty("scrollbarHideType", "both", root); } else if (this->scrollbarHideType == SCROLLBAR_HIDE_HORIZONTAL) { saveProperty("scrollbarHideType", "horizontal", root); } else if (this->scrollbarHideType == SCROLLBAR_HIDE_VERTICAL) { saveProperty("scrollbarHideType", "vertical", root); } else { saveProperty("scrollbarHideType", "none", root); } WRITE_BOOL_PROP(autoloadPdfXoj); WRITE_COMMENT( "Hides scroolbars in the main window, allowed values: \"none\", \"horizontal\", \"vertical\", \"both\""); WRITE_STRING_PROP(defaultSaveName); WRITE_BOOL_PROP(autosaveEnabled); WRITE_INT_PROP(autosaveTimeout); WRITE_BOOL_PROP(addHorizontalSpace); WRITE_INT_PROP(addHorizontalSpaceAmount); WRITE_BOOL_PROP(addVerticalSpace); WRITE_INT_PROP(addVerticalSpaceAmount); WRITE_BOOL_PROP(drawDirModsEnabled); WRITE_INT_PROP(drawDirModsRadius); WRITE_BOOL_PROP(snapRotation); WRITE_DOUBLE_PROP(snapRotationTolerance); WRITE_BOOL_PROP(snapGrid); WRITE_DOUBLE_PROP(snapGridTolerance); WRITE_DOUBLE_PROP(snapGridSize); WRITE_BOOL_PROP(touchWorkaround); WRITE_UINT_PROP(selectionBorderColor); WRITE_UINT_PROP(backgroundColor); WRITE_UINT_PROP(selectionMarkerColor); WRITE_INT_PROP(pdfPageCacheSize); WRITE_COMMENT("The count of rendered PDF pages which will be cached."); WRITE_COMMENT("Config for new pages"); WRITE_STRING_PROP(pageTemplate); WRITE_STRING_PROP(sizeUnit); WRITE_STRING_PROP(audioFolder); WRITE_INT_PROP(audioInputDevice); WRITE_INT_PROP(audioOutputDevice); WRITE_DOUBLE_PROP(audioSampleRate); WRITE_DOUBLE_PROP(audioGain); WRITE_INT_PROP(defaultSeekTime); WRITE_STRING_PROP(pluginEnabled); WRITE_STRING_PROP(pluginDisabled); WRITE_INT_PROP(strokeFilterIgnoreTime); WRITE_DOUBLE_PROP(strokeFilterIgnoreLength); WRITE_INT_PROP(strokeFilterSuccessiveTime); WRITE_BOOL_PROP(strokeFilterEnabled); WRITE_BOOL_PROP(doActionOnStrokeFiltered); WRITE_BOOL_PROP(trySelectOnStrokeFiltered); WRITE_BOOL_PROP(snapRecognizedShapesEnabled); WRITE_BOOL_PROP(restoreLineWidthEnabled); WRITE_INT_PROP(numIgnoredStylusEvents); WRITE_BOOL_PROP(newInputSystemEnabled); WRITE_BOOL_PROP(inputSystemTPCButton); WRITE_BOOL_PROP(inputSystemDrawOutsideWindow); WRITE_STRING_PROP(preferredLocale); WRITE_BOOL_PROP(latexSettings.autoCheckDependencies); // Inline WRITE_STRING_PROP(latexSettings.globalTemplatePath) since it // breaks on Windows due to the native character representation being // wchar_t instead of char fs::path& p = latexSettings.globalTemplatePath; xmlNode = saveProperty("latexSettings.globalTemplatePath", p.empty() ? "" : p.u8string().c_str(), root); WRITE_STRING_PROP(latexSettings.genCmd); xmlNodePtr xmlFont = nullptr; xmlFont = xmlNewChild(root, nullptr, reinterpret_cast("property"), nullptr); xmlSetProp(xmlFont, reinterpret_cast("name"), reinterpret_cast("font")); xmlSetProp(xmlFont, reinterpret_cast("font"), reinterpret_cast(this->font.getName().c_str())); char sSize[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_formatd(sSize, G_ASCII_DTOSTR_BUF_SIZE, Util::PRECISION_FORMAT_STRING, this->font.getSize()); // no locale xmlSetProp(xmlFont, reinterpret_cast("size"), reinterpret_cast(sSize)); for (std::map::value_type p: data) { saveData(root, p.first, p.second); } xmlSaveFormatFileEnc(filepath.u8string().c_str(), doc, "UTF-8", 1); xmlFreeDoc(doc); } void Settings::saveData(xmlNodePtr root, const string& name, SElement& elem) { xmlNodePtr xmlNode = xmlNewChild(root, nullptr, reinterpret_cast("data"), nullptr); xmlSetProp(xmlNode, reinterpret_cast("name"), reinterpret_cast(name.c_str())); for (auto const& [aname, attrib]: elem.attributes()) { string type; string value; if (attrib.type == ATTRIBUTE_TYPE_BOOLEAN) { type = "boolean"; if (attrib.iValue) { value = "true"; } else { value = "false"; } } else if (attrib.type == ATTRIBUTE_TYPE_INT) { type = "int"; char* tmp = g_strdup_printf("%i", attrib.iValue); value = tmp; g_free(tmp); } else if (attrib.type == ATTRIBUTE_TYPE_DOUBLE) { type = "double"; char tmp[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_formatd(tmp, G_ASCII_DTOSTR_BUF_SIZE, Util::PRECISION_FORMAT_STRING, attrib.dValue); value = tmp; } else if (attrib.type == ATTRIBUTE_TYPE_INT_HEX) { type = "hex"; char* tmp = g_strdup_printf("%06x", attrib.iValue); value = tmp; g_free(tmp); } else if (attrib.type == ATTRIBUTE_TYPE_STRING) { type = "string"; value = attrib.sValue; } else { // Unknown type or empty attribute continue; } xmlNodePtr at = nullptr; at = xmlNewChild(xmlNode, nullptr, reinterpret_cast("attribute"), nullptr); xmlSetProp(at, reinterpret_cast("name"), reinterpret_cast(aname.c_str())); xmlSetProp(at, reinterpret_cast("type"), reinterpret_cast(type.c_str())); xmlSetProp(at, reinterpret_cast("value"), reinterpret_cast(value.c_str())); if (!attrib.comment.empty()) { xmlNodePtr com = xmlNewComment(reinterpret_cast(attrib.comment.c_str())); xmlAddPrevSibling(xmlNode, com); } } for (std::map::value_type p: elem.children()) { saveData(xmlNode, p.first, p.second); } } // Getter- / Setter auto Settings::isPressureSensitivity() const -> bool { return this->pressureSensitivity; } auto Settings::isZoomGesturesEnabled() const -> bool { return this->zoomGesturesEnabled; } void Settings::setZoomGesturesEnabled(bool enable) { if (this->zoomGesturesEnabled == enable) { return; } this->zoomGesturesEnabled = enable; save(); } auto Settings::isSidebarOnRight() const -> bool { return this->sidebarOnRight; } void Settings::setSidebarOnRight(bool right) { if (this->sidebarOnRight == right) { return; } this->sidebarOnRight = right; save(); } auto Settings::isScrollbarOnLeft() const -> bool { return this->scrollbarOnLeft; } void Settings::setScrollbarOnLeft(bool right) { if (this->scrollbarOnLeft == right) { return; } this->scrollbarOnLeft = right; save(); } auto Settings::isMenubarVisible() const -> bool { return this->menubarVisible; } void Settings::setMenubarVisible(bool visible) { this->menubarVisible = visible; save(); } auto Settings::getAutosaveTimeout() const -> int { return this->autosaveTimeout; } void Settings::setAutosaveTimeout(int autosave) { if (this->autosaveTimeout == autosave) { return; } this->autosaveTimeout = autosave; save(); } auto Settings::isAutosaveEnabled() const -> bool { return this->autosaveEnabled; } void Settings::setAutosaveEnabled(bool autosave) { if (this->autosaveEnabled == autosave) { return; } this->autosaveEnabled = autosave; save(); } auto Settings::getAddVerticalSpace() const -> bool { return this->addVerticalSpace; } void Settings::setAddVerticalSpace(bool space) { this->addVerticalSpace = space; } auto Settings::getAddVerticalSpaceAmount() const -> int { return this->addVerticalSpaceAmount; } void Settings::setAddVerticalSpaceAmount(int pixels) { if (this->addVerticalSpaceAmount == pixels) { return; } this->addVerticalSpaceAmount = pixels; save(); } auto Settings::getAddHorizontalSpace() const -> bool { return this->addHorizontalSpace; } void Settings::setAddHorizontalSpace(bool space) { this->addHorizontalSpace = space; } auto Settings::getAddHorizontalSpaceAmount() const -> int { return this->addHorizontalSpaceAmount; } void Settings::setAddHorizontalSpaceAmount(int pixels) { if (this->addHorizontalSpaceAmount == pixels) { return; } this->addHorizontalSpaceAmount = pixels; save(); } auto Settings::getDrawDirModsEnabled() const -> bool { return this->drawDirModsEnabled; } void Settings::setDrawDirModsEnabled(bool enable) { this->drawDirModsEnabled = enable; } auto Settings::getDrawDirModsRadius() const -> int { return this->drawDirModsRadius; } void Settings::setDrawDirModsRadius(int pixels) { if (this->drawDirModsRadius == pixels) { return; } this->drawDirModsRadius = pixels; save(); } auto Settings::getStylusCursorType() const -> StylusCursorType { return this->stylusCursorType; } void Settings::setStylusCursorType(StylusCursorType type) { if (this->stylusCursorType == type) { return; } this->stylusCursorType = type; save(); } auto Settings::isHighlightPosition() const -> bool { return this->highlightPosition; } void Settings::setHighlightPosition(bool highlight) { if (this->highlightPosition == highlight) { return; } this->highlightPosition = highlight; save(); } auto Settings::getCursorHighlightColor() const -> Color { return this->cursorHighlightColor; } void Settings::setCursorHighlightColor(Color color) { if (this->cursorHighlightColor != color) { this->cursorHighlightColor = color; save(); } } auto Settings::getCursorHighlightRadius() const -> double { return this->cursorHighlightRadius; } void Settings::setCursorHighlightRadius(double radius) { if (this->cursorHighlightRadius != radius) { this->cursorHighlightRadius = radius; save(); } } auto Settings::getCursorHighlightBorderColor() const -> Color { return this->cursorHighlightBorderColor; } void Settings::setCursorHighlightBorderColor(Color color) { if (this->cursorHighlightBorderColor != color) { this->cursorHighlightBorderColor = color; save(); } } auto Settings::getCursorHighlightBorderWidth() const -> double { return this->cursorHighlightBorderWidth; } void Settings::setCursorHighlightBorderWidth(double radius) { if (this->cursorHighlightBorderWidth != radius) { this->cursorHighlightBorderWidth = radius; save(); } } auto Settings::isSnapRotation() const -> bool { return this->snapRotation; } void Settings::setSnapRotation(bool b) { if (this->snapRotation == b) { return; } this->snapRotation = b; save(); } auto Settings::getSnapRotationTolerance() const -> double { return this->snapRotationTolerance; } void Settings::setSnapRotationTolerance(double tolerance) { this->snapRotationTolerance = tolerance; save(); } auto Settings::isSnapGrid() const -> bool { return this->snapGrid; } void Settings::setSnapGrid(bool b) { if (this->snapGrid == b) { return; } this->snapGrid = b; save(); } void Settings::setSnapGridTolerance(double tolerance) { this->snapGridTolerance = tolerance; save(); } auto Settings::getSnapGridTolerance() const -> double { return this->snapGridTolerance; } auto Settings::getSnapGridSize() const -> double { return this->snapGridSize; }; void Settings::setSnapGridSize(double gridSize) { if (this->snapGridSize == gridSize) { return; } this->snapGridSize = gridSize; save(); } auto Settings::isTouchWorkaround() const -> bool { return this->touchWorkaround; } void Settings::setTouchWorkaround(bool b) { if (this->touchWorkaround == b) { return; } this->touchWorkaround = b; save(); } auto Settings::getScrollbarHideType() const -> ScrollbarHideType { return this->scrollbarHideType; } void Settings::setScrollbarHideType(ScrollbarHideType type) { if (this->scrollbarHideType == type) { return; } this->scrollbarHideType = type; save(); } auto Settings::isAutloadPdfXoj() const -> bool { return this->autoloadPdfXoj; } void Settings::setAutoloadPdfXoj(bool load) { if (this->autoloadPdfXoj == load) { return; } this->autoloadPdfXoj = load; save(); } auto Settings::getDefaultSaveName() const -> string const& { return this->defaultSaveName; } void Settings::setDefaultSaveName(const string& name) { if (this->defaultSaveName == name) { return; } this->defaultSaveName = name; save(); } auto Settings::getPageTemplate() const -> string const& { return this->pageTemplate; } void Settings::setPageTemplate(const string& pageTemplate) { if (this->pageTemplate == pageTemplate) { return; } this->pageTemplate = pageTemplate; save(); } auto Settings::getAudioFolder() const -> string const& { return this->audioFolder; } void Settings::setAudioFolder(const string& audioFolder) { if (this->audioFolder == audioFolder) { return; } this->audioFolder = audioFolder; save(); } auto Settings::getSizeUnit() const -> string const& { return sizeUnit; } void Settings::setSizeUnit(const string& sizeUnit) { if (this->sizeUnit == sizeUnit) { return; } this->sizeUnit = sizeUnit; save(); } /** * Get size index in XOJ_UNITS */ auto Settings::getSizeUnitIndex() const -> int { string unit = getSizeUnit(); for (int i = 0; i < XOJ_UNIT_COUNT; i++) { if (unit == XOJ_UNITS[i].name) { return i; } } return 0; } /** * Set size index in XOJ_UNITS */ void Settings::setSizeUnitIndex(int sizeUnitId) { if (sizeUnitId < 0 || sizeUnitId >= XOJ_UNIT_COUNT) { sizeUnitId = 0; } setSizeUnit(XOJ_UNITS[sizeUnitId].name); } void Settings::setShowPairedPages(bool showPairedPages) { if (this->showPairedPages == showPairedPages) { return; } this->showPairedPages = showPairedPages; save(); } auto Settings::isShowPairedPages() const -> bool { return this->showPairedPages; } void Settings::setPresentationMode(bool presentationMode) { if (this->presentationMode == presentationMode) { return; } this->presentationMode = presentationMode; save(); } auto Settings::isPresentationMode() const -> bool { return this->presentationMode; } void Settings::setPressureSensitivity(gboolean presureSensitivity) { if (this->pressureSensitivity == presureSensitivity) { return; } this->pressureSensitivity = presureSensitivity; save(); } void Settings::setPairsOffset(int numOffset) { if (this->numPairsOffset == numOffset) { return; } this->numPairsOffset = numOffset; save(); } auto Settings::getPairsOffset() const -> int { return this->numPairsOffset; } void Settings::setViewColumns(int numColumns) { if (this->numColumns == numColumns) { return; } this->numColumns = numColumns; save(); } auto Settings::getViewColumns() const -> int { return this->numColumns; } void Settings::setViewRows(int numRows) { if (this->numRows == numRows) { return; } this->numRows = numRows; save(); } auto Settings::getViewRows() const -> int { return this->numRows; } void Settings::setViewFixedRows(bool viewFixedRows) { if (this->viewFixedRows == viewFixedRows) { return; } this->viewFixedRows = viewFixedRows; save(); } auto Settings::isViewFixedRows() const -> bool { return this->viewFixedRows; } void Settings::setViewLayoutVert(bool vert) { if (this->layoutVertical == vert) { return; } this->layoutVertical = vert; save(); } auto Settings::getViewLayoutVert() const -> bool { return this->layoutVertical; } void Settings::setViewLayoutR2L(bool r2l) { if (this->layoutRightToLeft == r2l) { return; } this->layoutRightToLeft = r2l; save(); } auto Settings::getViewLayoutR2L() const -> bool { return this->layoutRightToLeft; } void Settings::setViewLayoutB2T(bool b2t) { if (this->layoutBottomToTop == b2t) { return; } this->layoutBottomToTop = b2t; save(); } auto Settings::getViewLayoutB2T() const -> bool { return this->layoutBottomToTop; } void Settings::setLastSavePath(fs::path p) { this->lastSavePath = std::move(p); save(); } auto Settings::getLastSavePath() const -> fs::path const& { return this->lastSavePath; } void Settings::setLastOpenPath(fs::path p) { this->lastOpenPath = std::move(p); save(); } auto Settings::getLastOpenPath() const -> fs::path const& { return this->lastOpenPath; } void Settings::setLastImagePath(const fs::path& path) { if (this->lastImagePath == path) { return; } this->lastImagePath = path; save(); } auto Settings::getLastImagePath() const -> fs::path const& { return this->lastImagePath; } void Settings::setZoomStep(double zoomStep) { if (this->zoomStep == zoomStep) { return; } this->zoomStep = zoomStep; save(); } auto Settings::getZoomStep() const -> double { return this->zoomStep; } void Settings::setZoomStepScroll(double zoomStepScroll) { if (this->zoomStepScroll == zoomStepScroll) { return; } this->zoomStepScroll = zoomStepScroll; save(); } auto Settings::getZoomStepScroll() const -> double { return this->zoomStepScroll; } void Settings::setDisplayDpi(int dpi) { if (this->displayDpi == dpi) { return; } this->displayDpi = dpi; save(); } auto Settings::getDisplayDpi() const -> int { return this->displayDpi; } void Settings::setDarkTheme(bool dark) { if (this->darkTheme == dark) { return; } this->darkTheme = dark; save(); } auto Settings::isDarkTheme() const -> bool { return this->darkTheme; } auto Settings::isSidebarVisible() const -> bool { return this->showSidebar; } void Settings::setSidebarVisible(bool visible) { if (this->showSidebar == visible) { return; } this->showSidebar = visible; save(); } auto Settings::isToolbarVisible() const -> bool { return this->showToolbar; } void Settings::setToolbarVisible(bool visible) { if (this->showToolbar == visible) { return; } this->showToolbar = visible; save(); } auto Settings::getSidebarWidth() const -> int { return this->sidebarWidth; } void Settings::setSidebarWidth(int width) { width = std::max(width, 50); if (this->sidebarWidth == width) { return; } this->sidebarWidth = width; save(); } void Settings::setMainWndSize(int width, int height) { this->mainWndWidth = width; this->mainWndHeight = height; save(); } auto Settings::getMainWndWidth() const -> int { return this->mainWndWidth; } auto Settings::getMainWndHeight() const -> int { return this->mainWndHeight; } auto Settings::isMainWndMaximized() const -> bool { return this->maximized; } void Settings::setMainWndMaximized(bool max) { this->maximized = max; } void Settings::setSelectedToolbar(const string& name) { if (this->selectedToolbar == name) { return; } this->selectedToolbar = name; save(); } auto Settings::getSelectedToolbar() const -> string const& { return this->selectedToolbar; } auto Settings::getCustomElement(const string& name) -> SElement& { return this->data[name]; } void Settings::customSettingsChanged() { save(); } auto Settings::getButtonConfig(int id) -> ButtonConfig* { if (id < 0 || id >= BUTTON_COUNT) { g_error("Settings::getButtonConfig try to get id=%i out of range!", id); return nullptr; } return this->buttonConfig[id]; } auto Settings::getEraserButtonConfig() -> ButtonConfig* { return this->buttonConfig[BUTTON_ERASER]; } auto Settings::getMiddleButtonConfig() -> ButtonConfig* { return this->buttonConfig[BUTTON_MIDDLE]; } auto Settings::getRightButtonConfig() -> ButtonConfig* { return this->buttonConfig[BUTTON_RIGHT]; } auto Settings::getTouchButtonConfig() -> ButtonConfig* { return this->buttonConfig[BUTTON_TOUCH]; } auto Settings::getDefaultButtonConfig() -> ButtonConfig* { return this->buttonConfig[BUTTON_DEFAULT]; } auto Settings::getStylusButton1Config() -> ButtonConfig* { return this->buttonConfig[BUTTON_STYLUS]; } auto Settings::getStylusButton2Config() -> ButtonConfig* { return this->buttonConfig[BUTTON_STYLUS2]; } auto Settings::getFullscreenHideElements() const -> string const& { return this->fullscreenHideElements; } void Settings::setFullscreenHideElements(string elements) { this->fullscreenHideElements = std::move(elements); save(); } auto Settings::getPresentationHideElements() const -> string const& { return this->presentationHideElements; } void Settings::setPresentationHideElements(string elements) { this->presentationHideElements = std::move(elements); save(); } auto Settings::getPdfPageCacheSize() const -> int { return this->pdfPageCacheSize; } void Settings::setPdfPageCacheSize(int size) { if (this->pdfPageCacheSize == size) { return; } this->pdfPageCacheSize = size; save(); } auto Settings::getBorderColor() const -> Color { return this->selectionBorderColor; } void Settings::setBorderColor(Color color) { if (this->selectionBorderColor == color) { return; } this->selectionBorderColor = color; save(); } auto Settings::getSelectionColor() const -> Color { return this->selectionMarkerColor; } void Settings::setSelectionColor(Color color) { if (this->selectionMarkerColor == color) { return; } this->selectionMarkerColor = color; save(); } auto Settings::getBackgroundColor() const -> Color { return this->backgroundColor; } void Settings::setBackgroundColor(Color color) { if (this->backgroundColor == color) { return; } this->backgroundColor = color; save(); } auto Settings::getFont() -> XojFont& { return this->font; } void Settings::setFont(const XojFont& font) { this->font = font; save(); } auto Settings::getAudioInputDevice() const -> PaDeviceIndex { return this->audioInputDevice; } void Settings::setAudioInputDevice(PaDeviceIndex deviceIndex) { if (this->audioInputDevice == deviceIndex) { return; } this->audioInputDevice = deviceIndex; save(); } auto Settings::getAudioOutputDevice() const -> PaDeviceIndex { return this->audioOutputDevice; } void Settings::setAudioOutputDevice(PaDeviceIndex deviceIndex) { if (this->audioOutputDevice == deviceIndex) { return; } this->audioOutputDevice = deviceIndex; save(); } auto Settings::getAudioSampleRate() const -> double { return this->audioSampleRate; } void Settings::setAudioSampleRate(double sampleRate) { if (this->audioSampleRate == sampleRate) { return; } this->audioSampleRate = sampleRate; save(); } auto Settings::getAudioGain() const -> double { return this->audioGain; } void Settings::setAudioGain(double gain) { if (this->audioGain == gain) { return; } this->audioGain = gain; save(); } auto Settings::getDefaultSeekTime() const -> unsigned int { return this->defaultSeekTime; } void Settings::setDefaultSeekTime(unsigned int t) { if (this->defaultSeekTime == t) { return; } this->defaultSeekTime = t; save(); } auto Settings::getPluginEnabled() const -> string const& { return this->pluginEnabled; } void Settings::setPluginEnabled(const string& pluginEnabled) { if (this->pluginEnabled == pluginEnabled) { return; } this->pluginEnabled = pluginEnabled; save(); } auto Settings::getPluginDisabled() const -> string const& { return this->pluginDisabled; } void Settings::setPluginDisabled(const string& pluginDisabled) { if (this->pluginDisabled == pluginDisabled) { return; } this->pluginDisabled = pluginDisabled; save(); } void Settings::getStrokeFilter(int* ignoreTime, double* ignoreLength, int* successiveTime) const { *ignoreTime = this->strokeFilterIgnoreTime; *ignoreLength = this->strokeFilterIgnoreLength; *successiveTime = this->strokeFilterSuccessiveTime; } void Settings::setStrokeFilter(int ignoreTime, double ignoreLength, int successiveTime) { this->strokeFilterIgnoreTime = ignoreTime; this->strokeFilterIgnoreLength = ignoreLength; this->strokeFilterSuccessiveTime = successiveTime; } void Settings::setStrokeFilterEnabled(bool enabled) { this->strokeFilterEnabled = enabled; } auto Settings::getStrokeFilterEnabled() const -> bool { return this->strokeFilterEnabled; } void Settings::setDoActionOnStrokeFiltered(bool enabled) { this->doActionOnStrokeFiltered = enabled; } auto Settings::getDoActionOnStrokeFiltered() const -> bool { return this->doActionOnStrokeFiltered; } void Settings::setTrySelectOnStrokeFiltered(bool enabled) { this->trySelectOnStrokeFiltered = enabled; } auto Settings::getTrySelectOnStrokeFiltered() const -> bool { return this->trySelectOnStrokeFiltered; } void Settings::setSnapRecognizedShapesEnabled(bool enabled) { this->snapRecognizedShapesEnabled = enabled; } auto Settings::getSnapRecognizedShapesEnabled() const -> bool { return this->snapRecognizedShapesEnabled; } void Settings::setRestoreLineWidthEnabled(bool enabled) { this->restoreLineWidthEnabled = enabled; } auto Settings::getRestoreLineWidthEnabled() const -> bool { return this->restoreLineWidthEnabled; } auto Settings::setPreferredLocale(std::string const& locale) -> void { this->preferredLocale = locale; } auto Settings::getPreferredLocale() const -> std::string { return this->preferredLocale; } void Settings::setIgnoredStylusEvents(int numEvents) { if (this->numIgnoredStylusEvents == numEvents) { return; } this->numIgnoredStylusEvents = std::max(numEvents, 0); save(); } auto Settings::getIgnoredStylusEvents() const -> int { return this->numIgnoredStylusEvents; } void Settings::setExperimentalInputSystemEnabled(bool systemEnabled) { if (this->newInputSystemEnabled == systemEnabled) { return; } this->newInputSystemEnabled = systemEnabled; save(); } auto Settings::getExperimentalInputSystemEnabled() const -> bool { return this->newInputSystemEnabled; } void Settings::setInputSystemTPCButtonEnabled(bool tpcButtonEnabled) { if (this->inputSystemTPCButton == tpcButtonEnabled) { return; } this->inputSystemTPCButton = tpcButtonEnabled; save(); } auto Settings::getInputSystemTPCButtonEnabled() const -> bool { return this->inputSystemTPCButton; } void Settings::setInputSystemDrawOutsideWindowEnabled(bool drawOutsideWindowEnabled) { if (this->inputSystemDrawOutsideWindow == drawOutsideWindowEnabled) { return; } this->inputSystemDrawOutsideWindow = drawOutsideWindowEnabled; save(); } auto Settings::getInputSystemDrawOutsideWindowEnabled() const -> bool { return this->inputSystemDrawOutsideWindow; } void Settings::setDeviceClassForDevice(GdkDevice* device, InputDeviceTypeOption deviceClass) { this->setDeviceClassForDevice(gdk_device_get_name(device), gdk_device_get_source(device), deviceClass); } void Settings::setDeviceClassForDevice(const string& deviceName, GdkInputSource deviceSource, InputDeviceTypeOption deviceClass) { auto it = inputDeviceClasses.find(deviceName); if (it != inputDeviceClasses.end()) { it->second.first = deviceClass; it->second.second = deviceSource; } else { inputDeviceClasses.emplace(deviceName, std::make_pair(deviceClass, deviceSource)); } } auto Settings::getKnownInputDevices() const -> std::vector { std::vector inputDevices; for (auto pair: inputDeviceClasses) { const std::string& name = pair.first; GdkInputSource& source = pair.second.second; inputDevices.emplace_back(name, source); } return inputDevices; } auto Settings::getDeviceClassForDevice(GdkDevice* device) const -> InputDeviceTypeOption { return this->getDeviceClassForDevice(gdk_device_get_name(device), gdk_device_get_source(device)); } auto Settings::getDeviceClassForDevice(const string& deviceName, GdkInputSource deviceSource) const -> InputDeviceTypeOption { auto search = inputDeviceClasses.find(deviceName); if (search != inputDeviceClasses.end()) { return search->second.first; } InputDeviceTypeOption deviceType = InputDeviceTypeOption::Disabled; switch (deviceSource) { case GDK_SOURCE_CURSOR: #if (GDK_MAJOR_VERSION >= 3 && GDK_MINOR_VERSION >= 22) case GDK_SOURCE_TABLET_PAD: #endif case GDK_SOURCE_KEYBOARD: deviceType = InputDeviceTypeOption::Disabled; break; case GDK_SOURCE_MOUSE: case GDK_SOURCE_TOUCHPAD: #if (GDK_MAJOR_VERSION >= 3 && GDK_MINOR_VERSION >= 22) case GDK_SOURCE_TRACKPOINT: #endif deviceType = InputDeviceTypeOption::Mouse; break; case GDK_SOURCE_PEN: deviceType = InputDeviceTypeOption::Pen; break; case GDK_SOURCE_ERASER: deviceType = InputDeviceTypeOption::Eraser; break; case GDK_SOURCE_TOUCHSCREEN: deviceType = InputDeviceTypeOption::Touchscreen; break; default: deviceType = InputDeviceTypeOption::Disabled; } return deviceType; } auto Settings::isScrollbarFadeoutDisabled() const -> bool { return disableScrollbarFadeout; } void Settings::setScrollbarFadeoutDisabled(bool disable) { if (disableScrollbarFadeout == disable) { return; } disableScrollbarFadeout = disable; save(); } ////////////////////////////////////////////////// SAttribute::SAttribute() { this->dValue = 0; this->iValue = 0; this->type = ATTRIBUTE_TYPE_NONE; } SAttribute::SAttribute(const SAttribute& attrib) { *this = attrib; } SAttribute::~SAttribute() { this->iValue = 0; this->type = ATTRIBUTE_TYPE_NONE; } ////////////////////////////////////////////////// auto SElement::attributes() -> std::map& { return this->element->attributes; } auto SElement::children() -> std::map& { return this->element->children; } void SElement::clear() { this->element->attributes.clear(); this->element->children.clear(); } auto SElement::child(const string& name) -> SElement& { return this->element->children[name]; } void SElement::setComment(const string& name, const string& comment) { SAttribute& attrib = this->element->attributes[name]; attrib.comment = comment; } void SElement::setIntHex(const string& name, const int value) { SAttribute& attrib = this->element->attributes[name]; attrib.iValue = value; attrib.type = ATTRIBUTE_TYPE_INT_HEX; } void SElement::setInt(const string& name, const int value) { SAttribute& attrib = this->element->attributes[name]; attrib.iValue = value; attrib.type = ATTRIBUTE_TYPE_INT; } void SElement::setBool(const string& name, const bool value) { SAttribute& attrib = this->element->attributes[name]; attrib.iValue = value; attrib.type = ATTRIBUTE_TYPE_BOOLEAN; } void SElement::setString(const string& name, const string& value) { SAttribute& attrib = this->element->attributes[name]; attrib.sValue = value; attrib.type = ATTRIBUTE_TYPE_STRING; } void SElement::setDouble(const string& name, const double value) { SAttribute& attrib = this->element->attributes[name]; attrib.dValue = value; attrib.type = ATTRIBUTE_TYPE_DOUBLE; } auto SElement::getDouble(const string& name, double& value) -> bool { SAttribute& attrib = this->element->attributes[name]; if (attrib.type == ATTRIBUTE_TYPE_NONE) { this->element->attributes.erase(name); return false; } if (attrib.type != ATTRIBUTE_TYPE_DOUBLE) { return false; } value = attrib.dValue; return true; } auto SElement::getInt(const string& name, int& value) -> bool { SAttribute& attrib = this->element->attributes[name]; if (attrib.type == ATTRIBUTE_TYPE_NONE) { this->element->attributes.erase(name); return false; } if (attrib.type != ATTRIBUTE_TYPE_INT && attrib.type != ATTRIBUTE_TYPE_INT_HEX) { return false; } value = attrib.iValue; return true; } auto SElement::getBool(const string& name, bool& value) -> bool { SAttribute& attrib = this->element->attributes[name]; if (attrib.type == ATTRIBUTE_TYPE_NONE) { this->element->attributes.erase(name); return false; } if (attrib.type != ATTRIBUTE_TYPE_BOOLEAN) { return false; } value = attrib.iValue; return true; } auto SElement::getString(const string& name, string& value) -> bool { SAttribute& attrib = this->element->attributes[name]; if (attrib.type == ATTRIBUTE_TYPE_NONE) { this->element->attributes.erase(name); return false; } if (attrib.type != ATTRIBUTE_TYPE_STRING) { return false; } value = attrib.sValue; return true; }