You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1948 lines
69 KiB

#include "Settings.h"
#include <utility>
#include <PathUtil.h>
#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<const xmlChar*>("data"))) {
xmlChar* name = xmlGetProp(x, reinterpret_cast<const xmlChar*>("name"));
parseData(x, elem.child(reinterpret_cast<const char*>(name)));
xmlFree(name);
} else if (!xmlStrcmp(x->name, reinterpret_cast<const xmlChar*>("attribute"))) {
xmlChar* name = xmlGetProp(x, reinterpret_cast<const xmlChar*>("name"));
xmlChar* value = xmlGetProp(x, reinterpret_cast<const xmlChar*>("value"));
xmlChar* type = xmlGetProp(x, reinterpret_cast<const xmlChar*>("type"));
string sType = reinterpret_cast<const char*>(type);
if (sType == "int") {
int i = atoi(reinterpret_cast<const char*>(value));
elem.setInt(reinterpret_cast<const char*>(name), i);
} else if (sType == "double") {
double d = tempg_ascii_strtod(reinterpret_cast<const char*>(value),
nullptr); // g_ascii_strtod ignores locale setting.
elem.setDouble(reinterpret_cast<const char*>(name), d);
} else if (sType == "hex") {
int i = 0;
if (sscanf(reinterpret_cast<const char*>(value), "%x", &i)) {
elem.setIntHex(reinterpret_cast<const char*>(name), i);
} else {
g_warning("Settings::Unknown hex value: %s:%s\n", name, value);
}
} else if (sType == "string") {
elem.setString(reinterpret_cast<const char*>(name), reinterpret_cast<const char*>(value));
} else if (sType == "boolean") {
elem.setBool(reinterpret_cast<const char*>(name),
strcmp(reinterpret_cast<const char*>(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<const xmlChar*>("data"))) {
xmlChar* name = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("name"));
if (name == nullptr) {
g_warning("Settings::%s:No name property!\n", cur->name);
return;
}
parseData(cur, data[reinterpret_cast<const char*>(name)]);
xmlFree(name);
return;
}
if (cur->type == XML_COMMENT_NODE) {
return;
}
if (xmlStrcmp(cur->name, reinterpret_cast<const xmlChar*>("property"))) {
g_warning("Settings::Unknown XML node: %s\n", cur->name);
return;
}
xmlChar* name = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("name"));
if (name == nullptr) {
g_warning("Settings::%s:No name property!\n", cur->name);
return;
}
if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("font")) == 0) {
xmlFree(name);
xmlChar* font = nullptr;
xmlChar* size = nullptr;
font = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("font"));
if (font) {
this->font.setName(reinterpret_cast<const char*>(font));
xmlFree(font);
}
size = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("size"));
if (size) {
double dSize = DEFAULT_FONT_SIZE;
if (sscanf(reinterpret_cast<const char*>(size), "%lf", &dSize) == 1) {
this->font.setSize(dSize);
}
xmlFree(size);
}
return;
}
xmlChar* value = xmlGetProp(cur, reinterpret_cast<const xmlChar*>("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<const xmlChar*>("presureSensitivity")) == 0) {
this->pressureSensitivity = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
}
if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pressureSensitivity")) == 0) {
this->pressureSensitivity = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("zoomGesturesEnabled")) == 0) {
this->zoomGesturesEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("selectedToolbar")) == 0) {
this->selectedToolbar = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("lastSavePath")) == 0) {
this->lastSavePath = fs::u8path(reinterpret_cast<const char*>(value));
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("lastOpenPath")) == 0) {
this->lastOpenPath = fs::u8path(reinterpret_cast<const char*>(value));
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("lastImagePath")) == 0) {
this->lastImagePath = fs::u8path(reinterpret_cast<const char*>(value));
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("zoomStep")) == 0) {
this->zoomStep = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("zoomStepScroll")) == 0) {
this->zoomStepScroll = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("displayDpi")) == 0) {
this->displayDpi = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("mainWndWidth")) == 0) {
this->mainWndWidth = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("mainWndHeight")) == 0) {
this->mainWndHeight = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("maximized")) == 0) {
this->maximized = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("showToolbar")) == 0) {
this->showToolbar = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("showSidebar")) == 0) {
this->showSidebar = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("sidebarWidth")) == 0) {
this->sidebarWidth = std::max<int>(g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10), 50);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("sidebarOnRight")) == 0) {
this->sidebarOnRight = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("scrollbarOnLeft")) == 0) {
this->scrollbarOnLeft = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("menubarVisible")) == 0) {
this->menubarVisible = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numColumns")) == 0) {
this->numColumns = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numRows")) == 0) {
this->numRows = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("viewFixedRows")) == 0) {
this->viewFixedRows = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("layoutVertical")) == 0) {
this->layoutVertical = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("layoutRightToLeft")) == 0) {
this->layoutRightToLeft = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("layoutBottomToTop")) == 0) {
this->layoutBottomToTop = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("showPairedPages")) == 0) {
this->showPairedPages = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numPairsOffset")) == 0) {
this->numPairsOffset = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("presentationMode")) == 0) {
this->presentationMode = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("autoloadPdfXoj")) == 0) {
this->autoloadPdfXoj = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("stylusCursorType")) == 0) {
this->stylusCursorType = stylusCursorTypeFromString(reinterpret_cast<const char*>(value));
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("highlightPosition")) == 0) {
this->highlightPosition = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("cursorHighlightColor")) == 0) {
this->cursorHighlightColor = g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("cursorHighlightRadius")) == 0) {
this->cursorHighlightRadius = g_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("cursorHighlightBorderColor")) == 0) {
this->cursorHighlightBorderColor = g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("cursorHighlightBorderWidth")) == 0) {
this->cursorHighlightBorderWidth = g_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("darkTheme")) == 0) {
this->darkTheme = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("defaultSaveName")) == 0) {
this->defaultSaveName = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pluginEnabled")) == 0) {
this->pluginEnabled = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pluginDisabled")) == 0) {
this->pluginDisabled = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pageTemplate")) == 0) {
this->pageTemplate = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("sizeUnit")) == 0) {
this->sizeUnit = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioFolder")) == 0) {
this->audioFolder = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("autosaveEnabled")) == 0) {
this->autosaveEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("autosaveTimeout")) == 0) {
this->autosaveTimeout = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("fullscreenHideElements")) == 0) {
this->fullscreenHideElements = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("presentationHideElements")) == 0) {
this->presentationHideElements = reinterpret_cast<const char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("pdfPageCacheSize")) == 0) {
this->pdfPageCacheSize = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("selectionBorderColor")) == 0) {
this->selectionBorderColor = Color(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10));
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("selectionMarkerColor")) == 0) {
this->selectionMarkerColor = Color(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10));
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("backgroundColor")) == 0) {
this->backgroundColor = Color(g_ascii_strtoull(reinterpret_cast<const char*>(value), nullptr, 10));
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("addHorizontalSpace")) == 0) {
this->addHorizontalSpace = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("addHorizontalSpaceAmount")) == 0) {
this->addHorizontalSpaceAmount = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("addVerticalSpace")) == 0) {
this->addVerticalSpace = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("addVerticalSpaceAmount")) == 0) {
this->addVerticalSpaceAmount = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("drawDirModsEnabled")) == 0) {
this->drawDirModsEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("drawDirModsRadius")) == 0) {
this->drawDirModsRadius = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapRotation")) == 0) {
this->snapRotation = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapRotationTolerance")) == 0) {
this->snapRotationTolerance = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapGrid")) == 0) {
this->snapGrid = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapGridSize")) == 0) {
this->snapGridSize = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapGridTolerance")) == 0) {
this->snapGridTolerance = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("touchWorkaround")) == 0) {
this->touchWorkaround = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("scrollbarHideType")) == 0) {
if (xmlStrcmp(value, reinterpret_cast<const xmlChar*>("both")) == 0) {
this->scrollbarHideType = SCROLLBAR_HIDE_BOTH;
} else if (xmlStrcmp(value, reinterpret_cast<const xmlChar*>("horizontal")) == 0) {
this->scrollbarHideType = SCROLLBAR_HIDE_HORIZONTAL;
} else if (xmlStrcmp(value, reinterpret_cast<const xmlChar*>("vertical")) == 0) {
this->scrollbarHideType = SCROLLBAR_HIDE_VERTICAL;
} else {
this->scrollbarHideType = SCROLLBAR_HIDE_NONE;
}
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("disableScrollbarFadeout")) == 0) {
this->disableScrollbarFadeout = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioSampleRate")) == 0) {
this->audioSampleRate = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioGain")) == 0) {
this->audioGain = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("defaultSeekTime")) == 0) {
this->defaultSeekTime = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioInputDevice")) == 0) {
this->audioInputDevice = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("audioOutputDevice")) == 0) {
this->audioOutputDevice = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("numIgnoredStylusEvents")) == 0) {
this->numIgnoredStylusEvents =
std::max<int>(g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10), 0);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("newInputSystemEnabled")) == 0) {
this->newInputSystemEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("inputSystemTPCButton")) == 0) {
this->inputSystemTPCButton = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("inputSystemDrawOutsideWindow")) == 0) {
this->inputSystemDrawOutsideWindow = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("strokeFilterIgnoreTime")) == 0) {
this->strokeFilterIgnoreTime = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("strokeFilterIgnoreLength")) == 0) {
this->strokeFilterIgnoreLength = tempg_ascii_strtod(reinterpret_cast<const char*>(value), nullptr);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("strokeFilterSuccessiveTime")) == 0) {
this->strokeFilterSuccessiveTime = g_ascii_strtoll(reinterpret_cast<const char*>(value), nullptr, 10);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("strokeFilterEnabled")) == 0) {
this->strokeFilterEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("doActionOnStrokeFiltered")) == 0) {
this->doActionOnStrokeFiltered = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("trySelectOnStrokeFiltered")) == 0) {
this->trySelectOnStrokeFiltered = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("latexSettings.autoCheckDependencies")) == 0) {
this->latexSettings.autoCheckDependencies = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("latexSettings.globalTemplatePath")) == 0) {
std::string v(reinterpret_cast<char*>(value));
this->latexSettings.globalTemplatePath = fs::u8path(v);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("latexSettings.genCmd")) == 0) {
this->latexSettings.genCmd = reinterpret_cast<char*>(value);
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("snapRecognizedShapesEnabled")) == 0) {
this->snapRecognizedShapesEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("restoreLineWidthEnabled")) == 0) {
this->restoreLineWidthEnabled = xmlStrcmp(value, reinterpret_cast<const xmlChar*>("true")) == 0;
} else if (xmlStrcmp(name, reinterpret_cast<const xmlChar*>("preferredLocale")) == 0) {
this->preferredLocale = reinterpret_cast<char*>(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<InputDeviceTypeOption>(deviceClass),
static_cast<GdkInputSource>(deviceSource)));
}
}
void Settings::loadButtonConfig() {
SElement& s = getCustomElement("buttonConfig");
for (int i = 0; i < BUTTON_COUNT; i++) {
SElement& e = s.child(buttonToString(static_cast<Buttons>(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<const xmlChar*>("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<const xmlChar*>("property"), nullptr);
xmlSetProp(xmlNode, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>(key));
xmlSetProp(xmlNode, reinterpret_cast<const xmlChar*>("value"), reinterpret_cast<const xmlChar*>(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<int>(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<Buttons>(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<const xmlChar*>("1.0"));
if (doc == nullptr) {
return;
}
saveButtonConfig();
saveDeviceClasses();
/* Create metadata root */
root = xmlNewDocNode(doc, nullptr, reinterpret_cast<const xmlChar*>("settings"), nullptr);
xmlDocSetRootElement(doc, root);
xmlNodePtr com = xmlNewComment(
reinterpret_cast<const xmlChar*>("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<const xmlChar*>("property"), nullptr);
xmlSetProp(xmlFont, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>("font"));
xmlSetProp(xmlFont, reinterpret_cast<const xmlChar*>("font"),
reinterpret_cast<const xmlChar*>(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<const xmlChar*>("size"), reinterpret_cast<const xmlChar*>(sSize));
for (std::map<string, SElement>::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<const xmlChar*>("data"), nullptr);
xmlSetProp(xmlNode, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>(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<const xmlChar*>("attribute"), nullptr);
xmlSetProp(at, reinterpret_cast<const xmlChar*>("name"), reinterpret_cast<const xmlChar*>(aname.c_str()));
xmlSetProp(at, reinterpret_cast<const xmlChar*>("type"), reinterpret_cast<const xmlChar*>(type.c_str()));
xmlSetProp(at, reinterpret_cast<const xmlChar*>("value"), reinterpret_cast<const xmlChar*>(value.c_str()));
if (!attrib.comment.empty()) {
xmlNodePtr com = xmlNewComment(reinterpret_cast<const xmlChar*>(attrib.comment.c_str()));
xmlAddPrevSibling(xmlNode, com);
}
}
for (std::map<string, SElement>::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<int>(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<InputDevice> {
std::vector<InputDevice> 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<string, SAttribute>& { return this->element->attributes; }
auto SElement::children() -> std::map<string, SElement>& { 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;
}