#include "Settings.h"

#include "ButtonConfig.h"
#include "model/FormatDefinitions.h"

#include <config.h>
#include <i18n.h>
#include <Util.h>
#include <util/DeviceListHelper.h>
#include <utility>
#define DEFAULT_FONT "Sans"
#define 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_DOUBLE_PROP(var) xmlNode = savePropertyDouble((const char *)#var, var, root)
#define WRITE_COMMENT(var) com = xmlNewComment((const xmlChar *)var); xmlAddPrevSibling(xmlNode, com);

const char* BUTTON_NAMES[] = {"middle", "right", "eraser", "touch", "default", "stylus", "stylus2"};

Settings::Settings(Path filename)
 : filename(std::move(filename))
{
	XOJ_INIT_TYPE(Settings);
	loadDefault();
}

Settings::~Settings()
{
	XOJ_CHECK_TYPE(Settings);

	for (int i = 0; i < BUTTON_COUNT; i++)
	{
		delete this->buttonConfig[i];
		this->buttonConfig[i] = NULL;
	}

	XOJ_RELEASE_TYPE(Settings);
}

void Settings::loadDefault()
{
	XOJ_CHECK_TYPE(Settings);

	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->sidebarOnRight = false;

	this->scrollbarOnLeft = false;

	this->menubarVisible = true;

	this->autoloadPdfXoj = true;
	this->showBigCursor = false;
	this->highlightPosition = false;
	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.20;

	this->snapGrid = true;
	this->snapGridTolerance = 0.25;

	this->touchWorkaround = false;

	this->defaultSaveName = _("%F-Note-%H-%M");

	// Eraser
	this->buttonConfig[0] = new ButtonConfig(TOOL_ERASER, 0, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
	// Middle button
	this->buttonConfig[1] = new ButtonConfig(TOOL_NONE, 0, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
	// Right button
	this->buttonConfig[2] = new ButtonConfig(TOOL_NONE, 0, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
	// Touch
	this->buttonConfig[3] = new ButtonConfig(TOOL_NONE, 0, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
	// Default config
	this->buttonConfig[4] = new ButtonConfig(TOOL_PEN, 0, TOOL_SIZE_FINE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
	// Pen button 1
	this->buttonConfig[5] = new ButtonConfig(TOOL_NONE, 0, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);
	// Pen button 2
	this->buttonConfig[6] = new ButtonConfig(TOOL_NONE, 0, TOOL_SIZE_NONE, DRAWING_TYPE_DEFAULT, ERASER_TYPE_NONE);

	this->fullscreenHideElements = "mainMenubar";
	this->presentationHideElements = "mainMenubar,sidebarContents";

	this->pdfPageCacheSize = 10;

	this->selectionBorderColor = 0xff0000; // red
	this->selectionMarkerColor = 0x729FCF; // light blue

	this->backgroundColor = 0xDCDAD5;

	// 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->pluginEnabled = "";
	this->pluginDisabled = "";

	this->experimentalInputSystemEnabled = false;
	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->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
*/
double tempg_ascii_strtod( const gchar* txt, gchar ** endptr )
{
	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)
{
	XOJ_CHECK_TYPE(Settings);

	for (xmlNodePtr x = cur->children; x != NULL; x = x->next)
	{
		if (!xmlStrcmp(x->name, (const xmlChar*) "data"))
		{
			xmlChar* name = xmlGetProp(x, (const xmlChar*) "name");
			parseData(x, elem.child((const char*) name));
			xmlFree(name);
		}
		else if (!xmlStrcmp(x->name, (const xmlChar*) "attribute"))
		{
			xmlChar* name = xmlGetProp(x, (const xmlChar*) "name");
			xmlChar* value = xmlGetProp(x, (const xmlChar*) "value");
			xmlChar* type = xmlGetProp(x, (const xmlChar*) "type");

			string sType = (const char*) type;

			if (sType == "int")
			{
				int i = atoi((const char*) value);
				elem.setInt((const char*) name, i);
			}
			else if (sType == "double")
			{
				double d = tempg_ascii_strtod((const char*) value, NULL);	//g_ascii_strtod ignores locale setting.
				elem.setDouble((const char*) name, d);
			}
			else if (sType == "hex")
			{
				int i = 0;
				if (sscanf((const char*) value, "%x", &i))
				{
					elem.setIntHex((const char*) name, i);
				}
				else
				{
					g_warning("Settings::Unknown hex value: %s:%s\n", name, value);
				}
			}
			else if (sType == "string")
			{
				elem.setString((const char*) name, (const char*) value);
			}
			else if (sType == "boolean")
			{
				elem.setBool((const char*) name, strcmp((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)
{
	XOJ_CHECK_TYPE(Settings);

	// Parse data map
	if (!xmlStrcmp(cur->name, (const xmlChar*) "data"))
	{
		xmlChar* name = xmlGetProp(cur, (const xmlChar*) "name");
		if (name == NULL)
		{
			g_warning("Settings::%s:No name property!\n", cur->name);
			return;
		}

		parseData(cur, data[(const char*) name]);

		xmlFree(name);
		return;
	}

	if (cur->type == XML_COMMENT_NODE)
	{
		return;
	}

	if (xmlStrcmp(cur->name, (const xmlChar*) "property"))
	{
		g_warning("Settings::Unknown XML node: %s\n", cur->name);
		return;
	}

	xmlChar* name = xmlGetProp(cur, (const xmlChar*) "name");
	if (name == NULL)
	{
		g_warning("Settings::%s:No name property!\n", cur->name);
		return;
	}

	if (xmlStrcmp(name, (const xmlChar*) "font") == 0)
	{
		xmlFree(name);
		xmlChar* font;
		xmlChar* size;

		font = xmlGetProp(cur, (const xmlChar*) "font");
		if (font)
		{
			this->font.setName((const char*) font);
			xmlFree(font);
		}

		size = xmlGetProp(cur, (const xmlChar*) "size");
		if (size)
		{
			double dSize = DEFAULT_FONT_SIZE;
			if (sscanf((const char*) size, "%lf", &dSize) == 1)
			{
				this->font.setSize(dSize);
			}

			xmlFree(size);
		}

		return;
	}

	xmlChar* value = xmlGetProp(cur, (const xmlChar*) "value");
	if (value == NULL)
	{
		xmlFree(name);
		g_warning("No value property!\n");
		return;
	}

	// TODO: remove this typo fix in 2-3 release cycles
	if (xmlStrcmp(name, (const xmlChar*) "presureSensitivity") == 0)
	{
		setPressureSensitivity(xmlStrcmp(value, (const xmlChar*) "true") ? false : true);
	}
	if (xmlStrcmp(name, (const xmlChar*) "pressureSensitivity") == 0)
	{
		setPressureSensitivity(xmlStrcmp(value, (const xmlChar*) "true") ? false : true);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "zoomGesturesEnabled") == 0)
	{
		this->zoomGesturesEnabled = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "selectedToolbar") == 0)
	{
		this->selectedToolbar = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "lastSavePath") == 0)
	{
		this->lastSavePath = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "lastOpenPath") == 0)
	{
		this->lastOpenPath = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "lastImagePath") == 0)
	{
		this->lastImagePath = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "zoomStep") == 0)
	{
		this->zoomStep = tempg_ascii_strtod((const char*) value, NULL);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "zoomStepScroll") == 0)
	{
		this->zoomStepScroll = tempg_ascii_strtod((const char*) value, NULL);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "displayDpi") == 0)
	{
		this->displayDpi = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "mainWndWidth") == 0)
	{
		this->mainWndWidth = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "mainWndHeight") == 0)
	{
		this->mainWndHeight = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "maximized") == 0)
	{
		this->maximized = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "showSidebar") == 0)
	{
		this->showSidebar = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "sidebarWidth") == 0)
	{
		this->sidebarWidth = std::max<int>(g_ascii_strtoll((const char*) value, NULL, 10), 50);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "sidebarOnRight") == 0)
	{
		this->sidebarOnRight = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "scrollbarOnLeft") == 0)
	{
		this->scrollbarOnLeft = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "menubarVisible") == 0)
	{
		this->menubarVisible = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "numColumns") == 0)
	{
		this->numColumns = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "numRows") == 0)
	{
		this->numRows = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "viewFixedRows") == 0)
	{
		this->viewFixedRows = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "layoutVertical") == 0)
	{
		this->layoutVertical = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "layoutRightToLeft") == 0)
	{
		this->layoutRightToLeft = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "layoutBottomToTop") == 0)
	{
		this->layoutBottomToTop = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "showPairedPages") == 0)
	{
		this->showPairedPages = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "numPairsOffset") == 0)
	{
		this->numPairsOffset = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "presentationMode") == 0)
	{
		this->presentationMode = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "autoloadPdfXoj") == 0)
	{
		this->autoloadPdfXoj = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "showBigCursor") == 0)
	{
		this->showBigCursor = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "highlightPosition") == 0)
	{
		this->highlightPosition = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "darkTheme") == 0)
	{
		this->darkTheme = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "defaultSaveName") == 0)
	{
		this->defaultSaveName = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "pluginEnabled") == 0)
	{
		this->pluginEnabled = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "pluginDisabled") == 0)
	{
		this->pluginDisabled = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "pageTemplate") == 0)
	{
		this->pageTemplate = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "sizeUnit") == 0)
	{
		this->sizeUnit = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "audioFolder") == 0)
	{
		this->audioFolder = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "autosaveEnabled") == 0)
	{
		this->autosaveEnabled = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "autosaveTimeout") == 0)
	{
		this->autosaveTimeout = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "fullscreenHideElements") == 0)
	{
		this->fullscreenHideElements = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "presentationHideElements") == 0)
	{
		this->presentationHideElements = (const char*) value;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "pdfPageCacheSize") == 0)
	{
		this->pdfPageCacheSize = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "selectionBorderColor") == 0)
	{
		this->selectionBorderColor = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "selectionMarkerColor") == 0)
	{
		this->selectionMarkerColor = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "backgroundColor") == 0)
	{
		this->backgroundColor = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "addHorizontalSpace") == 0)
	{
		this->addHorizontalSpace = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "addHorizontalSpaceAmount") == 0)
	{
		this->addHorizontalSpaceAmount = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "addVerticalSpace") == 0)
	{
		this->addVerticalSpace = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "addVerticalSpaceAmount") == 0)
	{
		this->addVerticalSpaceAmount = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "drawDirModsEnabled") == 0)
	{
		this->drawDirModsEnabled = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "drawDirModsRadius") == 0)
	{
		this->drawDirModsRadius = g_ascii_strtoll((const char*) value, NULL, 10);
	}	
	else if (xmlStrcmp(name, (const xmlChar*) "snapRotation") == 0)
	{
		this->snapRotation = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "snapRotationTolerance") == 0)
	{
		this->snapRotationTolerance = tempg_ascii_strtod((const char*) value, NULL);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "snapGrid") == 0)
	{
		this->snapGrid = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "snapGrid") == 0)
	{
		this->snapGrid = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "snapGridTolerance") == 0)
	{
		this->snapGridTolerance = tempg_ascii_strtod((const char*) value, NULL);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "touchWorkaround") == 0)
	{
		this->touchWorkaround = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "scrollbarHideType") == 0)
	{
		if (xmlStrcmp(value, (const xmlChar*) "both") == 0)
		{
			this->scrollbarHideType = SCROLLBAR_HIDE_BOTH;
		}
		else if (xmlStrcmp(value, (const xmlChar*) "horizontal") == 0)
		{
			this->scrollbarHideType = SCROLLBAR_HIDE_HORIZONTAL;
		}
		else if (xmlStrcmp(value, (const xmlChar*) "vertical") == 0)
		{
			this->scrollbarHideType = SCROLLBAR_HIDE_VERTICAL;
		}
		else
		{
			this->scrollbarHideType = SCROLLBAR_HIDE_NONE;
		}
	}
	else if (xmlStrcmp(name, (const xmlChar*) "disableScrollbarFadeout") == 0)
	{
		this->disableScrollbarFadeout = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "audioSampleRate") == 0)
	{
		this->audioSampleRate = tempg_ascii_strtod((const char *) value, NULL);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "audioGain") == 0)
	{
		this->audioGain = tempg_ascii_strtod((const char *) value, NULL);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "audioInputDevice") == 0)
	{
		this->audioInputDevice = g_ascii_strtoll((const char *) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "audioOutputDevice") == 0)
	{
		this->audioOutputDevice = g_ascii_strtoll((const char *) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "experimentalInputSystemEnabled") == 0)
	{
		this->experimentalInputSystemEnabled = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "inputSystemTPCButton") == 0)
	{
		this->inputSystemTPCButton = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "inputSystemDrawOutsideWindow") == 0)
	{
		this->inputSystemDrawOutsideWindow = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "strokeFilterIgnoreTime") == 0)
	{
		this->strokeFilterIgnoreTime = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "strokeFilterIgnoreLength") == 0)
	{
		this->strokeFilterIgnoreLength = tempg_ascii_strtod((const char*) value, NULL);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "strokeFilterSuccessiveTime") == 0)
	{
		this->strokeFilterSuccessiveTime = g_ascii_strtoll((const char*) value, NULL, 10);
	}
	else if (xmlStrcmp(name, (const xmlChar*) "strokeFilterEnabled") == 0)
	{
		this->strokeFilterEnabled = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "doActionOnStrokeFiltered") == 0)
	{
		this->doActionOnStrokeFiltered = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}
	else if (xmlStrcmp(name, (const xmlChar*) "trySelectOnStrokeFiltered") == 0)
	{
		this->trySelectOnStrokeFiltered = xmlStrcmp(value, (const xmlChar*) "true") ? false : true;
	}

	xmlFree(name);
	xmlFree(value);
}

void Settings::loadDeviceClasses()
{
	SElement& s = getCustomElement("deviceClasses");
	for (auto device : s.children())
	{
		SElement& deviceNode = device.second;
		int deviceClass;
		int deviceSource;
		deviceNode.getInt("deviceClass", deviceClass);
		deviceNode.getInt("deviceSource", deviceSource);
		inputDeviceClasses.insert(std::pair<string, std::pair<int, GdkInputSource>>(
		        device.first, std::pair<int, GdkInputSource>(deviceClass, (GdkInputSource) deviceSource)));
	}
}

void Settings::loadButtonConfig()
{
	XOJ_CHECK_TYPE(Settings);

	SElement& s = getCustomElement("buttonConfig");

	for (int i = 0; i < BUTTON_COUNT; i++)
	{
		SElement& e = s.child(BUTTON_NAMES[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)
			{
				e.getInt("color", cfg->color);
			}

			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 == 3)
			{
				if (!e.getString("device", cfg->device))
				{
					cfg->device = "";
				}

				e.getBool("disableDrawing", cfg->disableDrawing);
			}
		}
		else
		{
			continue;
		}
	}
}

bool Settings::load()
{
	
	XOJ_CHECK_TYPE(Settings);

	xmlKeepBlanksDefault(0);

	if (!filename.exists())
	{
		g_warning("configfile does not exist %s\n", filename.c_str());
		return false;
	}

	xmlDocPtr doc = xmlParseFile(filename.c_str());

	if (doc == NULL)
	{
		g_warning("Settings::load:: doc == null, could not load Settings!\n");
		return false;
	}

	xmlNodePtr cur = xmlDocGetRootElement(doc);
	if (cur == NULL)
	{
		g_message("The settings file \"%s\" is empty", filename.c_str());
		xmlFreeDoc(doc);

		return false;
	}

	if (xmlStrcmp(cur->name, (const xmlChar*) "settings"))
	{
		g_message("File \"%s\" is of the wrong type", filename.c_str());
		xmlFreeDoc(doc);

		return false;
	}

	cur = xmlDocGetRootElement(doc);
	cur = cur->xmlChildrenNode;

	while (cur != NULL)
	{
		parseItem(doc, cur);

		cur = cur->next;
	}

	xmlFreeDoc(doc);

	loadButtonConfig();
	loadDeviceClasses();

	return true;
}

xmlNodePtr Settings::savePropertyDouble(const gchar* key, double value, xmlNodePtr parent)
{
	XOJ_CHECK_TYPE(Settings);

	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;
}

xmlNodePtr Settings::saveProperty(const gchar* key, int value, xmlNodePtr parent)
{
	XOJ_CHECK_TYPE(Settings);

	char* text = g_strdup_printf("%i", value);
	xmlNodePtr xmlNode = saveProperty(key, text, parent);
	g_free(text);
	return xmlNode;
}

xmlNodePtr Settings::saveProperty(const gchar* key, const gchar* value, xmlNodePtr parent)
{
	XOJ_CHECK_TYPE(Settings);

	xmlNodePtr xmlNode = xmlNewChild(parent, NULL, (const xmlChar*) "property", NULL);

	xmlSetProp(xmlNode, (const xmlChar*) "name", (const xmlChar*) key);

	xmlSetProp(xmlNode, (const xmlChar*) "value", (const xmlChar*) value);

	return xmlNode;
}

void Settings::saveDeviceClasses()
{
	XOJ_CHECK_TYPE(Settings);

	SElement& s = getCustomElement("deviceClasses");

	for (const std::map<string, std::pair<int, GdkInputSource>>::value_type& device: inputDeviceClasses)
	{
		SElement& e = s.child(device.first);
		e.setInt("deviceClass", device.second.first);
		e.setInt("deviceSource", device.second.second);
	}
}

void Settings::saveButtonConfig()
{
	XOJ_CHECK_TYPE(Settings);

	SElement& s = getCustomElement("buttonConfig");
	s.clear();

	for (int i = 0; i < BUTTON_COUNT; i++)
	{
		SElement& e = s.child(BUTTON_NAMES[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", cfg->color);
		}

		if (type == TOOL_ERASER)
		{
			e.setString("eraserMode", eraserTypeToString(cfg->eraserMode));
			e.setString("size", toolSizeToString(cfg->size));
		}

		// Touch device
		if (i == 3)
		{
			e.setString("device", cfg->device);
			e.setBool("disableDrawing", cfg->disableDrawing);
		}
	}
}

/**
 * Do not save settings until transactionEnd() is called
 */
void Settings::transactionStart()
{
	XOJ_CHECK_TYPE(Settings);

	inTransaction = true;
}

/**
 * Stop transaction and save settings
 */
void Settings::transactionEnd()
{
	XOJ_CHECK_TYPE(Settings);

	inTransaction = false;
	save();
}

void Settings::save()
{
	XOJ_CHECK_TYPE(Settings);

	if (inTransaction)
	{
		return;
	}

	xmlDocPtr doc;
	xmlNodePtr root;
	xmlNodePtr xmlNode;

	xmlIndentTreeOutput = TRUE;

	doc = xmlNewDoc((const xmlChar*) "1.0");
	if (doc == NULL)
	{
		return;
	}

	saveButtonConfig();
	saveDeviceClasses();

	/* Create metadata root */
	root = xmlNewDocNode(doc, NULL, (const xmlChar*) "settings", NULL);
	xmlDocSetRootElement(doc, root);
	xmlNodePtr com = xmlNewComment((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.str();
	auto lastOpenPath = this->lastOpenPath.str();
	auto lastImagePath = this->lastImagePath.str();
	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(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 (,)");

	WRITE_BOOL_PROP(showBigCursor);
	WRITE_BOOL_PROP(highlightPosition);
	WRITE_BOOL_PROP(darkTheme);

	WRITE_BOOL_PROP(disableScrollbarFadeout);

	if (this->scrollbarHideType == SCROLLBAR_HIDE_BOTH)
	{
		saveProperty((const char*) "scrollbarHideType", "both", root);
	}
	else if (this->scrollbarHideType == SCROLLBAR_HIDE_HORIZONTAL)
	{
		saveProperty((const char*) "scrollbarHideType", "horizontal", root);
	}
	else if (this->scrollbarHideType == SCROLLBAR_HIDE_VERTICAL)
	{
		saveProperty((const char*) "scrollbarHideType", "vertical", root);
	}
	else
	{
		saveProperty((const char*) "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_BOOL_PROP(touchWorkaround);

	WRITE_INT_PROP(selectionBorderColor);
	WRITE_INT_PROP(backgroundColor);
	WRITE_INT_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_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(experimentalInputSystemEnabled);
	WRITE_BOOL_PROP(inputSystemTPCButton);
	WRITE_BOOL_PROP(inputSystemDrawOutsideWindow);

	xmlNodePtr xmlFont;
	xmlFont = xmlNewChild(root, NULL, (const xmlChar*) "property", NULL);
	xmlSetProp(xmlFont, (const xmlChar*) "name", (const xmlChar*) "font");
	xmlSetProp(xmlFont, (const xmlChar*) "font", (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, (const xmlChar*) "size", (const xmlChar*) sSize);


	for (std::map<string, SElement>::value_type p: data)
	{
		saveData(root, p.first, p.second);
	}

	xmlSaveFormatFileEnc(filename.c_str(), doc, "UTF-8", 1);
	xmlFreeDoc(doc);
}

void Settings::saveData(xmlNodePtr root, string name, SElement& elem)
{
	XOJ_CHECK_TYPE(Settings);

	xmlNodePtr xmlNode = xmlNewChild(root, NULL, (const xmlChar*) "data", NULL);

	xmlSetProp(xmlNode, (const xmlChar*) "name", (const xmlChar*) name.c_str());

	for (std::map<string, SAttribute>::value_type p : elem.attributes())
	{
		string aname = p.first;
		SAttribute& attrib = p.second;

		XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

		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;
		at = xmlNewChild(xmlNode, NULL, (const xmlChar*) "attribute", NULL);

		xmlSetProp(at, (const xmlChar*) "name", (const xmlChar*) aname.c_str());
		xmlSetProp(at, (const xmlChar*) "type", (const xmlChar*) type.c_str());
		xmlSetProp(at, (const xmlChar*) "value", (const xmlChar*) value.c_str());

		if (!attrib.comment.empty())
		{
			xmlNodePtr com = xmlNewComment((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
bool Settings::isPressureSensitivity() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->pressureSensitivity;
}

bool Settings::isZoomGesturesEnabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->zoomGesturesEnabled;
}

void Settings::setZoomGesturesEnabled(bool enable)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->zoomGesturesEnabled == enable)
	{
		return;
	}
	this->zoomGesturesEnabled = enable;
	save();
}

bool Settings::isSidebarOnRight() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->sidebarOnRight;
}

void Settings::setSidebarOnRight(bool right)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->sidebarOnRight == right)
	{
		return;
	}

	this->sidebarOnRight = right;

	save();
}

bool Settings::isScrollbarOnLeft() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->scrollbarOnLeft;
}

void Settings::setScrollbarOnLeft(bool right)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->scrollbarOnLeft == right)
	{
		return;
	}

	this->scrollbarOnLeft = right;

	save();
}

bool Settings::isMenubarVisible() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->menubarVisible;
}

void Settings::setMenubarVisible(bool visible)
{
	XOJ_CHECK_TYPE(Settings);

	this->menubarVisible = visible;

	save();
}

int Settings::getAutosaveTimeout() const
{
	return this->autosaveTimeout;
}

void Settings::setAutosaveTimeout(int autosave)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->autosaveTimeout == autosave)
	{
		return;
	}

	this->autosaveTimeout = autosave;

	save();
}

bool Settings::isAutosaveEnabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->autosaveEnabled;
}

void Settings::setAutosaveEnabled(bool autosave)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->autosaveEnabled == autosave)
	{
		return;
	}

	this->autosaveEnabled = autosave;

	save();
}

bool Settings::getAddVerticalSpace() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->addVerticalSpace;
}

void Settings::setAddVerticalSpace(bool space)
{
	XOJ_CHECK_TYPE(Settings);

	this->addVerticalSpace = space;
}

int Settings::getAddVerticalSpaceAmount() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->addVerticalSpaceAmount;
}

void Settings::setAddVerticalSpaceAmount(int pixels)
{
	XOJ_CHECK_TYPE(Settings);
	
	if (this->addVerticalSpaceAmount == pixels)
	{
		return;
	}

	this->addVerticalSpaceAmount = pixels;
	save();
}


bool Settings::getAddHorizontalSpace() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->addHorizontalSpace;
}

void Settings::setAddHorizontalSpace(bool space)
{
	XOJ_CHECK_TYPE(Settings);

	this->addHorizontalSpace = space;
}

int Settings::getAddHorizontalSpaceAmount() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->addHorizontalSpaceAmount;
}

void Settings::setAddHorizontalSpaceAmount(int pixels)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->addHorizontalSpaceAmount == pixels)
	{
		return;
	}

	this->addHorizontalSpaceAmount = pixels;
	save();
}


bool Settings::getDrawDirModsEnabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->drawDirModsEnabled;
}

void Settings::setDrawDirModsEnabled(bool enable)
{
	XOJ_CHECK_TYPE(Settings);

	this->drawDirModsEnabled = enable;
}

int Settings::getDrawDirModsRadius() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->drawDirModsRadius;
}

void Settings::setDrawDirModsRadius(int pixels)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->drawDirModsRadius == pixels)
	{
		return;
	}

	this->drawDirModsRadius = pixels;
	save();
}


bool Settings::isShowBigCursor() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->showBigCursor;
}

void Settings::setShowBigCursor(bool b)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->showBigCursor == b)
	{
		return;
	}

	this->showBigCursor = b;
	save();
}

bool Settings::isHighlightPosition() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->highlightPosition;

}

void Settings::setHighlightPosition(bool highlight)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->highlightPosition == highlight)
	{
		return;
	}

	this->highlightPosition = highlight;
	save();
}

bool Settings::isSnapRotation() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->snapRotation;
}

void Settings::setSnapRotation(bool b)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->snapRotation == b)
	{
		return;
	}

	this->snapRotation = b;
	save();
}

double Settings::getSnapRotationTolerance() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->snapRotationTolerance;
}

void Settings::setSnapRotationTolerance(double tolerance)
{
	XOJ_CHECK_TYPE(Settings);

	this->snapRotationTolerance = tolerance;
	save();
}

bool Settings::isSnapGrid() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->snapGrid;
}

void Settings::setSnapGrid(bool b)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->snapGrid == b)
	{
		return;
	}

	this->snapGrid = b;
	save();
}

void Settings::setSnapGridTolerance(double tolerance)
{
	XOJ_CHECK_TYPE(Settings);

	this->snapGridTolerance = tolerance;
	save();
}

double Settings::getSnapGridTolerance() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->snapGridTolerance;
}

bool Settings::isTouchWorkaround() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->touchWorkaround;
}

void Settings::setTouchWorkaround(bool b)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->touchWorkaround == b)
	{
		return;
	}

	this->touchWorkaround = b;
	save();
}

ScrollbarHideType Settings::getScrollbarHideType() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->scrollbarHideType;
}

void Settings::setScrollbarHideType(ScrollbarHideType type)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->scrollbarHideType == type)
	{
		return;
	}

	this->scrollbarHideType = type;

	save();
}

bool Settings::isAutloadPdfXoj() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->autoloadPdfXoj;
}

void Settings::setAutoloadPdfXoj(bool load)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->autoloadPdfXoj == load)
	{
		return;
	}
	this->autoloadPdfXoj = load;
	save();
}

string const& Settings::getDefaultSaveName() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->defaultSaveName;
}

void Settings::setDefaultSaveName(string name)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->defaultSaveName == name)
	{
		return;
	}

	this->defaultSaveName = name;

	save();
}

string const& Settings::getPageTemplate() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->pageTemplate;
}

void Settings::setPageTemplate(string pageTemplate)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->pageTemplate == pageTemplate)
	{
		return;
	}

	this->pageTemplate = pageTemplate;

	save();
}

string const& Settings::getAudioFolder() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->audioFolder;
}

void Settings::setAudioFolder(string audioFolder)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->audioFolder == audioFolder)
	{
		return;
	}

	this->audioFolder = audioFolder;

	save();
}

string const& Settings::getSizeUnit() const
{
	XOJ_CHECK_TYPE(Settings);

	return sizeUnit;
}

void Settings::setSizeUnit(string sizeUnit)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->sizeUnit == sizeUnit)
	{
		return;
	}

	this->sizeUnit = sizeUnit;

	save();
}

/**
 * Get size index in XOJ_UNITS
 */
int Settings::getSizeUnitIndex() const
{
	XOJ_CHECK_TYPE(Settings);

	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)
{
	XOJ_CHECK_TYPE(Settings);

	if (sizeUnitId < 0 || sizeUnitId >= XOJ_UNIT_COUNT)
	{
		sizeUnitId = 0;
	}

	setSizeUnit(XOJ_UNITS[sizeUnitId].name);
}

void Settings::setShowPairedPages(bool showPairedPages)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->showPairedPages == showPairedPages)
	{
		return;
	}

	this->showPairedPages = showPairedPages;
	save();
}

bool Settings::isShowPairedPages() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->showPairedPages;
}

void Settings::setPresentationMode(bool presentationMode)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->presentationMode == presentationMode)
	{
		return;
	}

	this->presentationMode = presentationMode;
	save();
}

bool Settings::isPresentationMode() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->presentationMode;
}

void Settings::setPressureSensitivity(gboolean presureSensitivity)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->pressureSensitivity == presureSensitivity)
	{
		return;
	}
	this->pressureSensitivity = presureSensitivity;

	save();
}

void Settings::setPairsOffset(int numOffset)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->numPairsOffset == numOffset)
	{
		return;
	}

	this->numPairsOffset = numOffset;
	save();
}

int Settings::getPairsOffset() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->numPairsOffset;
}

void Settings::setViewColumns(int numColumns)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->numColumns == numColumns)
	{
		return;
	}

	this->numColumns = numColumns;
	save();
}

int Settings::getViewColumns() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->numColumns;
}


void Settings::setViewRows(int numRows)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->numRows == numRows)
	{
		return;
	}

	this->numRows = numRows;
	save();
}

int Settings::getViewRows() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->numRows;
}

void Settings::setViewFixedRows(bool viewFixedRows)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->viewFixedRows == viewFixedRows)
	{
		return;
	}

	this->viewFixedRows = viewFixedRows;
	save();
}

bool Settings::isViewFixedRows() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->viewFixedRows;
}

void Settings::setViewLayoutVert(bool vert)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->layoutVertical == vert)
	{
		return;
	}

	this->layoutVertical = vert;
	save();
}

bool Settings::getViewLayoutVert() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->layoutVertical;
}

void Settings::setViewLayoutR2L(bool r2l)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->layoutRightToLeft == r2l)
	{
		return;
	}

	this->layoutRightToLeft = r2l;
	save();
}

bool Settings::getViewLayoutR2L() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->layoutRightToLeft;
}

void Settings::setViewLayoutB2T(bool b2t)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->layoutBottomToTop == b2t)
	{
		return;
	}

	this->layoutBottomToTop = b2t;
	save();
}

bool Settings::getViewLayoutB2T() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->layoutBottomToTop;
}

void Settings::setLastSavePath(Path p)
{
	XOJ_CHECK_TYPE(Settings);

	this->lastSavePath = p;
	save();
}

Path const& Settings::getLastSavePath() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->lastSavePath;
}

void Settings::setLastOpenPath(Path p)
{
	XOJ_CHECK_TYPE(Settings);

	this->lastOpenPath = p;
	save();
}

Path const& Settings::getLastOpenPath() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->lastOpenPath;
}

void Settings::setLastImagePath(Path path)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->lastImagePath == path)
	{
		return;
	}
	this->lastImagePath = path;
	save();
}

Path const& Settings::getLastImagePath() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->lastImagePath;
}

void Settings::setZoomStep(double zoomStep)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->zoomStep == zoomStep)
	{
		return;
	}
	this->zoomStep = zoomStep;
	save();
}

double Settings::getZoomStep() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->zoomStep;
}

void Settings::setZoomStepScroll(double zoomStepScroll)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->zoomStepScroll == zoomStepScroll)
	{
		return;
	}
	this->zoomStepScroll = zoomStepScroll;
	save();
}

double Settings::getZoomStepScroll() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->zoomStepScroll;
}

void Settings::setDisplayDpi(int dpi)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->displayDpi == dpi)
	{
		return;
	}
	this->displayDpi = dpi;
	save();
}

int Settings::getDisplayDpi() const
{
	return this->displayDpi;
}

void Settings::setDarkTheme(bool dark)
{
	XOJ_CHECK_TYPE(Settings);
	if (this->darkTheme == dark)
	{
		return;
	}
	this->darkTheme = dark;
	save();
}

bool Settings::isDarkTheme() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->darkTheme;
}

bool Settings::isSidebarVisible() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->showSidebar;
}

void Settings::setSidebarVisible(bool visible)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->showSidebar == visible)
	{
		return;
	}
	this->showSidebar = visible;
	save();
}

int Settings::getSidebarWidth() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->sidebarWidth;
}

void Settings::setSidebarWidth(int width)
{
	XOJ_CHECK_TYPE(Settings);
	width = std::max(width, 50);

	if (this->sidebarWidth == width)
	{
		return;
	}
	this->sidebarWidth = width;
	save();
}

void Settings::setMainWndSize(int width, int height)
{
	XOJ_CHECK_TYPE(Settings);

	this->mainWndWidth = width;
	this->mainWndHeight = height;

	save();
}

int Settings::getMainWndWidth() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->mainWndWidth;
}

int Settings::getMainWndHeight() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->mainWndHeight;
}

bool Settings::isMainWndMaximized() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->maximized;
}

void Settings::setMainWndMaximized(bool max)
{
	XOJ_CHECK_TYPE(Settings);

	this->maximized = max;
}

void Settings::setSelectedToolbar(string name)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->selectedToolbar == name)
	{
		return;
	}
	this->selectedToolbar = name;
	save();
}

string const& Settings::getSelectedToolbar() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->selectedToolbar;
}

SElement& Settings::getCustomElement(string name)
{
	XOJ_CHECK_TYPE(Settings);

	return this->data[name];
}

void Settings::customSettingsChanged()
{
	XOJ_CHECK_TYPE(Settings);

	save();
}

ButtonConfig* Settings::getButtonConfig(int id)
{
	XOJ_CHECK_TYPE(Settings);

	if (id < 0 || id >= BUTTON_COUNT)
	{
		g_error("Settings::getButtonConfig try to get id=%i out of range!", id);
		return NULL;
	}
	return this->buttonConfig[id];
}

ButtonConfig* Settings::getEraserButtonConfig()
{
	XOJ_CHECK_TYPE(Settings);

	return this->buttonConfig[0];
}

ButtonConfig* Settings::getMiddleButtonConfig()
{
	XOJ_CHECK_TYPE(Settings);

	return this->buttonConfig[1];
}

ButtonConfig* Settings::getRightButtonConfig()
{
	XOJ_CHECK_TYPE(Settings);

	return this->buttonConfig[2];
}

ButtonConfig* Settings::getTouchButtonConfig()
{
	XOJ_CHECK_TYPE(Settings);

	return this->buttonConfig[3];
}

ButtonConfig* Settings::getDefaultButtonConfig()
{
	XOJ_CHECK_TYPE(Settings);

	return this->buttonConfig[4];
}

ButtonConfig* Settings::getStylusButton1Config()
{
	XOJ_CHECK_TYPE(Settings);

	return this->buttonConfig[5];
}

ButtonConfig* Settings::getStylusButton2Config()
{
	XOJ_CHECK_TYPE(Settings);

	return this->buttonConfig[6];
}

string const& Settings::getFullscreenHideElements() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->fullscreenHideElements;
}

void Settings::setFullscreenHideElements(string elements)
{
	this->fullscreenHideElements = elements;
	save();
}

string const& Settings::getPresentationHideElements() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->presentationHideElements;
}

void Settings::setPresentationHideElements(string elements)
{
	XOJ_CHECK_TYPE(Settings);

	this->presentationHideElements = elements;
	save();
}

int Settings::getPdfPageCacheSize() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->pdfPageCacheSize;
}

void Settings::setPdfPageCacheSize(int size)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->pdfPageCacheSize == size)
	{
		return;
	}
	this->pdfPageCacheSize = size;
	save();
}

int Settings::getBorderColor() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->selectionBorderColor;
}

void Settings::setBorderColor(int color)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->selectionBorderColor == color)
	{
		return;
	}
	this->selectionBorderColor = color;
	save();
}

int Settings::getSelectionColor() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->selectionMarkerColor;
}

void Settings::setSelectionColor(int color)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->selectionMarkerColor == color)
	{
		return;
	}
	this->selectionMarkerColor = color;
	save();
}

int Settings::getBackgroundColor() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->backgroundColor;
}

void Settings::setBackgroundColor(int color)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->backgroundColor == color)
	{
		return;
	}
	this->backgroundColor = color;
	save();
}

XojFont& Settings::getFont()
{
	XOJ_CHECK_TYPE(Settings);

	return this->font;
}

void Settings::setFont(const XojFont& font)
{
	XOJ_CHECK_TYPE(Settings);

	this->font = font;
	save();
}


PaDeviceIndex Settings::getAudioInputDevice() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->audioInputDevice;
}

void Settings::setAudioInputDevice(PaDeviceIndex deviceIndex)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->audioInputDevice == deviceIndex)
	{
		return;
	}
	this->audioInputDevice = deviceIndex;
	save();
}

PaDeviceIndex Settings::getAudioOutputDevice() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->audioOutputDevice;
}

void Settings::setAudioOutputDevice(PaDeviceIndex deviceIndex)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->audioOutputDevice == deviceIndex)
	{
		return;
	}
	this->audioOutputDevice = deviceIndex;
	save();
}

double Settings::getAudioSampleRate() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->audioSampleRate;
}

void Settings::setAudioSampleRate(double sampleRate)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->audioSampleRate == sampleRate)
	{
		return;
	}
	this->audioSampleRate = sampleRate;
	save();
}

double Settings::getAudioGain() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->audioGain;
}

void Settings::setAudioGain(double gain)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->audioGain == gain)
	{
		return;
	}
	this->audioGain = gain;
	save();
}

string const& Settings::getPluginEnabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->pluginEnabled;
}

void Settings::setPluginEnabled(string pluginEnabled)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->pluginEnabled == pluginEnabled)
	{
		return;
	}
	this->pluginEnabled = pluginEnabled;
	save();
}

string const& Settings::getPluginDisabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->pluginDisabled;
}

void Settings::setPluginDisabled(string pluginDisabled)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->pluginDisabled == pluginDisabled)
	{
		return;
	}
	this->pluginDisabled = pluginDisabled;
	save();
}


void Settings::getStrokeFilter(int* ignoreTime, double* ignoreLength, int* successiveTime) const
{
	XOJ_CHECK_TYPE(Settings);
	*ignoreTime = this->strokeFilterIgnoreTime;
	*ignoreLength = this->strokeFilterIgnoreLength;
	*successiveTime = this->strokeFilterSuccessiveTime;

}

void Settings::setStrokeFilter( int ignoreTime, double ignoreLength, int successiveTime)
{
	XOJ_CHECK_TYPE(Settings);
	this->strokeFilterIgnoreTime = ignoreTime;
	this->strokeFilterIgnoreLength = ignoreLength;
	this->strokeFilterSuccessiveTime = successiveTime;

}

void Settings::setStrokeFilterEnabled(bool enabled)
{
	XOJ_CHECK_TYPE(Settings);
	this->strokeFilterEnabled = enabled;
}

bool Settings::getStrokeFilterEnabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->strokeFilterEnabled;
}

void Settings::setDoActionOnStrokeFiltered(bool enabled)
{
	XOJ_CHECK_TYPE(Settings);
	this->doActionOnStrokeFiltered = enabled;
}

bool Settings::getDoActionOnStrokeFiltered() const
{
	XOJ_CHECK_TYPE(Settings);
	return this->doActionOnStrokeFiltered;
}

void Settings::setTrySelectOnStrokeFiltered(bool enabled)
{
	XOJ_CHECK_TYPE(Settings);
	this->trySelectOnStrokeFiltered = enabled;
}

bool Settings::getTrySelectOnStrokeFiltered() const
{
	XOJ_CHECK_TYPE(Settings);
	return this->trySelectOnStrokeFiltered;
}


void Settings::setExperimentalInputSystemEnabled(bool systemEnabled)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->experimentalInputSystemEnabled == systemEnabled)
	{
		return;
	}
	this->experimentalInputSystemEnabled = systemEnabled;
	save();
}

bool Settings::getExperimentalInputSystemEnabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->experimentalInputSystemEnabled;
}

void Settings::setInputSystemTPCButtonEnabled(bool tpcButtonEnabled)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->inputSystemTPCButton == tpcButtonEnabled)
	{
		return;
	}
	this->inputSystemTPCButton = tpcButtonEnabled;
	save();
}

bool Settings::getInputSystemTPCButtonEnabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->inputSystemTPCButton;
}

void Settings::setInputSystemDrawOutsideWindowEnabled(bool drawOutsideWindowEnabled)
{
	XOJ_CHECK_TYPE(Settings);

	if (this->inputSystemDrawOutsideWindow == drawOutsideWindowEnabled)
	{
		return;
	}
	this->inputSystemDrawOutsideWindow = drawOutsideWindowEnabled;
	save();
}

bool Settings::getInputSystemDrawOutsideWindowEnabled() const
{
	XOJ_CHECK_TYPE(Settings);

	return this->inputSystemDrawOutsideWindow;
}

void Settings::setDeviceClassForDevice(GdkDevice* device, int deviceClass)
{
	this->setDeviceClassForDevice(gdk_device_get_name(device), gdk_device_get_source(device), deviceClass);
}

void Settings::setDeviceClassForDevice(const string& deviceName, GdkInputSource deviceSource, int deviceClass)
{
	auto it = inputDeviceClasses.find(deviceName);
	if (it != inputDeviceClasses.end())
	{
		it->second.first = deviceClass;
		it->second.second = deviceSource;
	}
	else
	{
		inputDeviceClasses.insert(std::pair<string, std::pair<int, GdkInputSource>>(
		        deviceName, std::pair<int, GdkInputSource>(deviceClass, deviceSource)));
	}
}

std::vector<InputDevice> Settings::getKnownInputDevices() const
{
	std::vector<InputDevice> inputDevices;
	for (std::pair<string, std::pair<int, GdkInputSource>> device: inputDeviceClasses)
	{
		inputDevices.emplace_back(device.first, device.second.second);
	}
	return inputDevices;
}

int Settings::getDeviceClassForDevice(GdkDevice* device) const
{
	return this->getDeviceClassForDevice(gdk_device_get_name(device), gdk_device_get_source(device));
}

int Settings::getDeviceClassForDevice(const string& deviceName, GdkInputSource deviceSource) const
{
	auto search = inputDeviceClasses.find(deviceName);
	if (search != inputDeviceClasses.end())
	{
		return search->second.first;
	}
	else
	{
		guint deviceType = 0;
		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 = 0;
				break;
			case GDK_SOURCE_MOUSE:
			case GDK_SOURCE_TOUCHPAD:
#if (GDK_MAJOR_VERSION >= 3 && GDK_MINOR_VERSION >= 22)
			case GDK_SOURCE_TRACKPOINT:
#endif
				deviceType = 1;
				break;
			case GDK_SOURCE_PEN:
				deviceType = 2;
				break;
			case GDK_SOURCE_ERASER:
				deviceType = 3;
				break;
			case GDK_SOURCE_TOUCHSCREEN:
				deviceType = 4;
				break;
		    default:
			    deviceType = 0;
		}
		return deviceType;
	}
}

bool Settings::isScrollbarFadeoutDisabled() const
{
	return disableScrollbarFadeout;
}

void Settings::setScrollbarFadeoutDisabled(bool disable)
{
	if (disableScrollbarFadeout == disable) return;
	disableScrollbarFadeout = disable;
	save();
}

//////////////////////////////////////////////////

SAttribute::SAttribute()
{
	XOJ_INIT_TYPE(SAttribute);

	this->dValue = 0;
	this->iValue = 0;
	this->type = ATTRIBUTE_TYPE_NONE;
}

SAttribute::SAttribute(const SAttribute& attrib)
{
	XOJ_INIT_TYPE(SAttribute);

	*this = attrib;
}

SAttribute::~SAttribute()
{
	XOJ_CHECK_TYPE(SAttribute);

	this->iValue = 0;
	this->type = ATTRIBUTE_TYPE_NONE;

	XOJ_RELEASE_TYPE(SAttribute);
}

//////////////////////////////////////////////////

__RefSElement::__RefSElement()
{
	XOJ_INIT_TYPE(__RefSElement);

	this->refcount = 0;
}

__RefSElement::~__RefSElement()
{
	XOJ_RELEASE_TYPE(__RefSElement);
}

void __RefSElement::ref()
{
	XOJ_CHECK_TYPE(__RefSElement);

	this->refcount++;
}

void __RefSElement::unref()
{
	XOJ_CHECK_TYPE(__RefSElement);

	this->refcount--;
	if (this->refcount == 0)
	{
		delete this;
	}
}

SElement::SElement()
{
	XOJ_INIT_TYPE(SElement);

	this->element = new __RefSElement();
	this->element->ref();
}

SElement::SElement(const SElement& elem)
{
	XOJ_INIT_TYPE(SElement);

	this->element = elem.element;
	this->element->ref();
}

SElement::~SElement()
{
	XOJ_CHECK_TYPE(SElement);

	this->element->unref();
	this->element = NULL;

	XOJ_RELEASE_TYPE(SElement);
}

void SElement::operator=(const SElement& elem)
{
	XOJ_CHECK_TYPE(SElement);

	this->element = elem.element;
	this->element->ref();
}

std::map<string, SAttribute>& SElement::attributes()
{
	XOJ_CHECK_TYPE(SElement);

	return this->element->attributes;
}

std::map<string, SElement>& SElement::children()
{
	XOJ_CHECK_TYPE(SElement);

	return this->element->children;
}

void SElement::clear()
{
	XOJ_CHECK_TYPE(SElement);

	this->element->attributes.clear();
	this->element->children.clear();
}

SElement& SElement::child(string name)
{
	XOJ_CHECK_TYPE(SElement);

	return this->element->children[name];
}

void SElement::setComment(const string name, const string comment)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	attrib.comment = comment;
}

void SElement::setIntHex(const string name, const int value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	attrib.iValue = value;
	attrib.type = ATTRIBUTE_TYPE_INT_HEX;
}

void SElement::setInt(const string name, const int value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	attrib.iValue = value;
	attrib.type = ATTRIBUTE_TYPE_INT;
}

void SElement::setBool(const string name, const bool value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	attrib.iValue = value;
	attrib.type = ATTRIBUTE_TYPE_BOOLEAN;
}

void SElement::setString(const string name, const string value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	attrib.sValue = value;
	attrib.type = ATTRIBUTE_TYPE_STRING;
}

void SElement::setDouble(const string name, const double value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	attrib.dValue = value;
	attrib.type = ATTRIBUTE_TYPE_DOUBLE;
}

bool SElement::getDouble(const string name, double& value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	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;
}

bool SElement::getInt(const string name, int& value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	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;
}

bool SElement::getBool(const string name, bool& value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	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;
}

bool SElement::getString(const string name, string& value)
{
	XOJ_CHECK_TYPE(SElement);

	SAttribute& attrib = this->element->attributes[name];

	XOJ_CHECK_TYPE_OBJ(&attrib, SAttribute);

	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;

}

