From fc013e8485ec0a739c5c9d35c469cba517e46d98 Mon Sep 17 00:00:00 2001
From: nakst <>
Date: Mon, 23 Aug 2021 19:07:44 +0100
Subject: [PATCH] cursor locator

---
 desktop/api.cpp                       |  33 ++++++++++++++++++-----
 desktop/desktop.cpp                   |  37 ++++++++++++++++++++++++++
 desktop/gui.cpp                       |  21 ++++++++++-----
 desktop/os.header                     |   1 +
 desktop/settings.cpp                  |  22 ++++++++-------
 desktop/styles.header                 |   2 ++
 kernel/windows.cpp                    |  14 ++++++++--
 res/System Configuration Template.ini |   1 -
 res/Theme Source.dat                  | Bin 49348 -> 50052 bytes
 res/Themes/Theme.dat                  | Bin 59492 -> 59876 bytes
 10 files changed, 105 insertions(+), 26 deletions(-)

diff --git a/desktop/api.cpp b/desktop/api.cpp
index bdb0a80..5815ab3 100644
--- a/desktop/api.cpp
+++ b/desktop/api.cpp
@@ -260,7 +260,7 @@ EsError NodeOpen(const char *path, size_t pathBytes, uint32_t flags, _EsNodeInfo
 	return EsSyscall(ES_SYSCALL_NODE_OPEN, (uintptr_t) path, pathBytes, flags, (uintptr_t) node);
 }
 
-EsSystemConfigurationItem *SystemConfigurationGetItem(EsSystemConfigurationGroup *group, const char *key, ptrdiff_t keyBytes) {
+EsSystemConfigurationItem *SystemConfigurationGetItem(EsSystemConfigurationGroup *group, const char *key, ptrdiff_t keyBytes, bool createIfNeeded = false) {
 	if (keyBytes == -1) keyBytes = EsCStringLength(key);
 
 	for (uintptr_t i = 0; i < group->itemCount; i++) {
@@ -269,10 +269,23 @@ EsSystemConfigurationItem *SystemConfigurationGetItem(EsSystemConfigurationGroup
 		}
 	}
 
+	if (createIfNeeded) {
+		EsSystemConfigurationItem item = {};
+		item.key = (char *) EsHeapAllocate(keyBytes, false);
+		item.keyBytes = keyBytes;
+		EsMemoryCopy(item.key, key, keyBytes);
+
+		Array<EsSystemConfigurationItem> items = { group->items };
+		EsSystemConfigurationItem *_item = items.Add(item);
+		group->items = items.array;
+		group->itemCount++;
+		return _item;
+	}
+
 	return nullptr;
 }
 
-EsSystemConfigurationGroup *SystemConfigurationGetGroup(const char *section, ptrdiff_t sectionBytes) {
+EsSystemConfigurationGroup *SystemConfigurationGetGroup(const char *section, ptrdiff_t sectionBytes, bool createIfNeeded = false) {
 	if (sectionBytes == -1) sectionBytes = EsCStringLength(section);
 
 	for (uintptr_t i = 0; i < api.systemConfigurationGroups.Length(); i++) {
@@ -281,6 +294,14 @@ EsSystemConfigurationGroup *SystemConfigurationGetGroup(const char *section, ptr
 		}
 	}
 
+	if (createIfNeeded) {
+		EsSystemConfigurationGroup group = {};
+		group.section = (char *) EsHeapAllocate(sectionBytes, false);
+		group.sectionBytes = sectionBytes;
+		EsMemoryCopy(group.section, section, sectionBytes);
+		return api.systemConfigurationGroups.Add(group);
+	}
+
 	return nullptr;
 }
 
@@ -791,7 +812,7 @@ EsMessage *EsMessageReceive() {
 			EsMessageSend((EsElement *) message.object, &message.message);
 		} else if (type == ES_MSG_TIMER) {
 			((EsTimerCallbackFunction) message.message.user.context1.p)(message.message.user.context2);
-		} else if (type >= ES_MSG_WM_START && type <= ES_MSG_WM_END) {
+		} else if (type >= ES_MSG_WM_START && type <= ES_MSG_WM_END && message.object) {
 #if 0
 			ProcessMessageTiming timing = {};
 			double start = EsTimeStampMs();
@@ -802,11 +823,9 @@ EsMessage *EsMessageReceive() {
 					timing.endLayout - timing.startLayout,
 					timing.endPaint - timing.startPaint,
 					timing.endUpdate - timing.startUpdate);
+#else
+			UIProcessWindowManagerMessage((EsWindow *) message.object, &message.message, nullptr);
 #endif
-			
-			if (message.object) {
-				UIProcessWindowManagerMessage((EsWindow *) message.object, &message.message, nullptr);
-			}
 		} else if (type == ES_MSG_TAB_INSPECT_UI) {
 			EsInstance *_instance = InstanceFromWindowID(message.message.tabOperation.id);
 
diff --git a/desktop/desktop.cpp b/desktop/desktop.cpp
index 984a68d..35083dd 100644
--- a/desktop/desktop.cpp
+++ b/desktop/desktop.cpp
@@ -2019,6 +2019,30 @@ void EmbeddedWindowDestroyed(EsObjectID id) {
 	EsHeapFree(instance);
 }
 
+int CursorLocatorMessage(EsElement *element, EsMessage *message) {
+	EsWindow *window = element->window;
+
+	if (message->type == ES_MSG_ANIMATE) {
+		window->announcementTimeMs += message->animate.deltaMs;
+		double progress = window->announcementTimeMs / GetConstantNumber("cursorLocatorDuration");
+
+		if (progress > 1) {
+			EsElementDestroy(window);
+		} else {
+			EsElementRelayout(element);
+			message->animate.complete = false;
+			return ES_HANDLED;
+		}
+	} else if (message->type == ES_MSG_LAYOUT) {
+		EsElement *child = element->GetChild(0);
+		double progress = 1.0 - window->announcementTimeMs / GetConstantNumber("cursorLocatorDuration");
+		int width = progress * child->GetWidth(0), height = progress * child->GetHeight(0);
+		child->InternalMove(width, height, (element->width - width) / 2, (element->height - height) / 2);
+	}
+
+	return 0;
+}
+
 void DesktopMessage(EsMessage *message) {
 	if (message->type == ES_MSG_POWER_BUTTON_PRESSED) {
 		ShutdownModalCreate();
@@ -2089,6 +2113,19 @@ void DesktopMessage(EsMessage *message) {
 		} else {
 			// The screen resolution will be correctly queried in DesktopSetup.
 		}
+	} else if (message->type == ES_MSG_SINGLE_CTRL_PRESS) {
+		if (EsSystemConfigurationReadInteger(EsLiteral("general"), EsLiteral("locate_cursor_on_ctrl"))) {
+			EsPoint position = EsMouseGetPosition();
+			EsWindow *window = EsWindowCreate(nullptr, ES_WINDOW_TIP);
+			EsElement *wrapper = EsCustomElementCreate(window, ES_CELL_FILL, ES_STYLE_CLEAR_BACKGROUND);
+			wrapper->messageUser = CursorLocatorMessage;
+			window->announcementBase = position;
+			EsElement *element = EsCustomElementCreate(wrapper, ES_CELL_FILL, ES_STYLE_CURSOR_LOCATOR);
+			int width = element->GetWidth(0), height = element->GetHeight(0);
+			EsRectangle bounds = ES_RECT_4PD(position.x - width / 2, position.y - height / 2, width, height);
+			EsSyscall(ES_SYSCALL_WINDOW_MOVE, window->handle, (uintptr_t) &bounds, 0, ES_WINDOW_MOVE_ALWAYS_ON_TOP);
+			wrapper->StartAnimating();
+		}
 	} else if (message->type == MSG_SETUP_DESKTOP_UI) {
 		DesktopSetup();
 	}
diff --git a/desktop/gui.cpp b/desktop/gui.cpp
index 012f475..07f5d24 100644
--- a/desktop/gui.cpp
+++ b/desktop/gui.cpp
@@ -449,7 +449,7 @@ struct EsWindow : EsElement {
 	EsElement *source; // Menu source.
 	EsWindow *targetMenu; // The menu that keyboard events should be sent to.
 
-	int32_t announcementBaseY;
+	EsPoint announcementBase;
 	double announcementTimeMs;
 };
 
@@ -3582,7 +3582,6 @@ int AnnouncementMessage(EsElement *element, EsMessage *message) {
 		double progress = window->announcementTimeMs / GetConstantNumber("announcementDuration");
 
 		if (progress > 1) {
-			progress = 1;
 			EsElementDestroy(window);
 			return 0;
 		}
@@ -3595,7 +3594,7 @@ int AnnouncementMessage(EsElement *element, EsMessage *message) {
 
 		EsRectangle bounds = EsWindowGetBounds(window);
 		int32_t height = Height(bounds);
-		bounds.t = window->announcementBaseY - inOnly * GetConstantNumber("announcementMovement");
+		bounds.t = window->announcementBase.y - inOnly * GetConstantNumber("announcementMovement");
 		bounds.b = bounds.t + height;
 
 		EsSyscall(ES_SYSCALL_WINDOW_SET_PROPERTY, window->handle, 0xFF * inOut, 0, ES_WINDOW_PROPERTY_ALPHA);
@@ -3619,11 +3618,12 @@ void EsAnnouncementShow(EsWindow *parent, uint64_t flags, int32_t x, int32_t y,
 	int32_t width = display->GetWidth(0);
 	int32_t height = display->GetHeight(width);
 
-	EsRectangle parentBounds = EsWindowGetBounds(parent);
+	EsRectangle parentBounds = {};
+       	if (parent) parentBounds = EsWindowGetBounds(parent);
 	EsRectangle bounds = ES_RECT_4PD(x - width / 2 + parentBounds.l, y - height + parentBounds.t, width, height);
 	EsSyscall(ES_SYSCALL_WINDOW_SET_PROPERTY, window->handle, 0x00, 0, ES_WINDOW_PROPERTY_ALPHA);
 	EsSyscall(ES_SYSCALL_WINDOW_MOVE, window->handle, (uintptr_t) &bounds, 0, ES_WINDOW_MOVE_ADJUST_TO_FIT_SCREEN | ES_WINDOW_MOVE_ALWAYS_ON_TOP);
-	window->announcementBaseY = EsWindowGetBounds(window).t;
+	window->announcementBase.y = EsWindowGetBounds(window).t;
 	window->StartAnimating();
 }
 
@@ -6003,11 +6003,14 @@ void AccessKeyModeHandleKeyPress(EsMessage *message) {
 		return;
 	}
 
+	EsWindow *window = gui.accessKeys.window;
+
 	int ic, isc;
 	ConvertScancodeToCharacter(message->keyboard.scancode, &ic, &isc, false, false);
 	ic = EsCRTtoupper(ic);
 
 	bool keepAccessKeyModeActive = false;
+	bool regatherKeys = false;
 
 	if (ic >= 'A' && ic <= 'Z' && !gui.accessKeys.typedCharacter) {
 		if (gui.accessKeys.numbers[ic - 'A'] > 1) {
@@ -6023,6 +6026,7 @@ void AccessKeyModeHandleKeyPress(EsMessage *message) {
 					EsElementFocus(entry->element, ES_ELEMENT_FOCUS_ENSURE_VISIBLE | ES_ELEMENT_FOCUS_FROM_KEYBOARD);
 
 					keepAccessKeyModeActive = entry->element->flags & ES_ELEMENT_STICKY_ACCESS_KEY;
+					regatherKeys = true;
 				}
 			}
 		}
@@ -6036,14 +6040,19 @@ void AccessKeyModeHandleKeyPress(EsMessage *message) {
 				EsElementFocus(entry->element, ES_ELEMENT_FOCUS_ENSURE_VISIBLE | ES_ELEMENT_FOCUS_FROM_KEYBOARD);
 
 				keepAccessKeyModeActive = entry->element->flags & ES_ELEMENT_STICKY_ACCESS_KEY;
+				regatherKeys = true;
 			}
 		}
 	}
 
 	if (!keepAccessKeyModeActive) {
 		AccessKeyModeExit();
+	} else if (regatherKeys) {
+		AccessKeyModeExit();
+		window->InternalMove(window->width, window->height, 0, 0);
+		AccessKeyModeEnter(window);
 	} else {
-		gui.accessKeys.window->Repaint(true);
+		window->Repaint(true);
 	}
 }
 
diff --git a/desktop/os.header b/desktop/os.header
index 99dd605..91ee8d6 100644
--- a/desktop/os.header
+++ b/desktop/os.header
@@ -954,6 +954,7 @@ enum EsMessageType {
 	ES_MSG_DESKTOP	                        = 0x4806
 	ES_MSG_DEVICE_CONNECTED			= 0x4807
 	ES_MSG_DEVICE_DISCONNECTED		= 0x4808
+	ES_MSG_SINGLE_CTRL_PRESS		= 0x4809
 
 	// Messages sent from Desktop to application instances:
 	ES_MSG_TAB_INSPECT_UI			= 0x4A01
diff --git a/desktop/settings.cpp b/desktop/settings.cpp
index 2bd635d..443d954 100644
--- a/desktop/settings.cpp
+++ b/desktop/settings.cpp
@@ -121,8 +121,8 @@ void SettingsNumberBoxSetValue(EsElement *element, int32_t newValue) {
 	EsTextboxInsert(textbox, buffer, bytes);
 
 	EsMutexAcquire(&api.systemConfigurationMutex);
-	EsSystemConfigurationGroup *group = SystemConfigurationGetGroup(control->cConfigurationSection, -1); // TODO Create if needed.
-	EsSystemConfigurationItem *item = SystemConfigurationGetItem(group, control->cConfigurationKey, -1); // TODO Create if needed.
+	EsSystemConfigurationGroup *group = SystemConfigurationGetGroup(control->cConfigurationSection, -1, true);
+	EsSystemConfigurationItem *item = SystemConfigurationGetItem(group, control->cConfigurationKey, -1, true);
 	int32_t oldValue = EsIntegerParse(item->value, item->valueBytes);
 	EsHeapFree(item->value);
 	item->value = (char *) EsHeapAllocate(65, true);
@@ -162,7 +162,7 @@ void SettingsAddTitle(EsElement *container, SettingsPage *page) {
 
 void SettingsAddUndoButton(EsElement *stack) {
 	EsPanel *overlay = EsPanelCreate(stack, ES_CELL_H_RIGHT | ES_CELL_V_TOP, &styleSettingsOverlayPanel);
-	EsButton *undoButton = EsButtonCreate(overlay, ES_BUTTON_TOOLBAR, 0, INTERFACE_STRING(DesktopSettingsUndoButton));
+	EsButton *undoButton = EsButtonCreate(overlay, ES_BUTTON_TOOLBAR | ES_ELEMENT_STICKY_ACCESS_KEY, 0, INTERFACE_STRING(DesktopSettingsUndoButton));
 	undoButton->accessKey = 'U';
 	((SettingsInstance *) stack->instance)->undoButton = undoButton;
 	EsButtonSetIcon(undoButton, ES_ICON_EDIT_UNDO_SYMBOLIC);
@@ -184,8 +184,8 @@ void SettingsCheckboxCommand(EsInstance *_instance, EsElement *element, EsComman
 	bool newValue = EsButtonGetCheck(button) == ES_CHECK_CHECKED;
 
 	EsMutexAcquire(&api.systemConfigurationMutex);
-	EsSystemConfigurationGroup *group = SystemConfigurationGetGroup(control->cConfigurationSection, -1); // TODO Create if needed.
-	EsSystemConfigurationItem *item = SystemConfigurationGetItem(group, control->cConfigurationKey, -1); // TODO Create if needed.
+	EsSystemConfigurationGroup *group = SystemConfigurationGetGroup(control->cConfigurationSection, -1, true);
+	EsSystemConfigurationItem *item = SystemConfigurationGetItem(group, control->cConfigurationKey, -1, true);
 	bool oldValue = EsIntegerParse(item->value, item->valueBytes);
 	EsHeapFree(item->value);
 	item->value = (char *) EsHeapAllocate(2, true);
@@ -209,7 +209,7 @@ void SettingsAddCheckbox(EsElement *table, const char *string, ptrdiff_t stringB
 	control->globalPointerBool = globalPointerBool;
 	control->originalValueBool = EsSystemConfigurationReadInteger(control->cConfigurationSection, -1, control->cConfigurationKey, -1);
 
-	EsButton *button = EsButtonCreate(table, ES_CELL_H_FILL | ES_BUTTON_CHECKBOX | ES_ELEMENT_FREE_USER_DATA, 0, string, stringBytes);
+	EsButton *button = EsButtonCreate(table, ES_CELL_H_FILL | ES_BUTTON_CHECKBOX | ES_ELEMENT_FREE_USER_DATA | ES_ELEMENT_STICKY_ACCESS_KEY, 0, string, stringBytes);
 	button->userData = control;
 	button->accessKey = accessKey;
 	if (control->originalValueBool) EsButtonSetCheck(button, ES_CHECK_CHECKED, false);
@@ -327,8 +327,10 @@ void SettingsPageMouse(EsElement *element, SettingsPage *page) {
 	table = EsPanelCreate(container, ES_CELL_H_FILL, &styleSettingsCheckboxGroup);
 	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseSwapLeftAndRightButtons), 'B', 
 			"general", "swap_left_and_right_buttons", &api.global->swapLeftAndRightButtons);
-	EsButtonCreate(table, ES_CELL_H_FILL | ES_BUTTON_CHECKBOX, 0, INTERFACE_STRING(DesktopSettingsMouseShowShadow))->accessKey = 'W'; // TODO.
-	EsButtonCreate(table, ES_CELL_H_FILL | ES_BUTTON_CHECKBOX, 0, INTERFACE_STRING(DesktopSettingsMouseLocateCursorOnCtrl))->accessKey = 'L'; // TODO.
+	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseShowShadow), 'W', 
+			"general", "show_cursor_shadow", nullptr); // TODO.
+	SettingsAddCheckbox(table, INTERFACE_STRING(DesktopSettingsMouseLocateCursorOnCtrl), 'L', 
+			"general", "locate_cursor_on_ctrl", nullptr);
 
 	EsSpacerCreate(container, ES_CELL_H_FILL, ES_STYLE_BUTTON_GROUP_SEPARATOR);
 
@@ -407,7 +409,7 @@ void SettingsButtonPressed(EsInstance *_instance, EsElement *element, EsCommand
 
 	{
 		EsPanel *overlay = EsPanelCreate(stack, ES_CELL_H_LEFT | ES_CELL_V_TOP, &styleSettingsOverlayPanel);
-		EsButton *backButton = EsButtonCreate(overlay, ES_CELL_H_LEFT | ES_BUTTON_TOOLBAR, 0, INTERFACE_STRING(DesktopSettingsBackButton));
+		EsButton *backButton = EsButtonCreate(overlay, ES_CELL_H_LEFT | ES_BUTTON_TOOLBAR | ES_ELEMENT_STICKY_ACCESS_KEY, 0, INTERFACE_STRING(DesktopSettingsBackButton));
 		backButton->accessKey = 'A';
 		EsButtonSetIcon(backButton, ES_ICON_GO_HOME_SYMBOLIC);
 		EsButtonOnCommand(backButton, SettingsBackButton);
@@ -443,7 +445,7 @@ void InstanceSettingsCreate(EsMessage *message) {
 		}, nullptr);
 
 		for (uintptr_t i = 0; i < sizeof(settingsPages) / sizeof(settingsPages[0]); i++) {
-			EsButton *button = EsButtonCreate(container, ES_ELEMENT_NO_FOCUS_ON_CLICK | ES_CELL_H_FILL, 
+			EsButton *button = EsButtonCreate(container, ES_ELEMENT_NO_FOCUS_ON_CLICK | ES_CELL_H_FILL | ES_ELEMENT_STICKY_ACCESS_KEY, 
 					&styleSettingsButton, settingsPages[i].string, settingsPages[i].stringBytes);
 			button->userData = &settingsPages[i];
 			button->accessKey = settingsPages[i].accessKey;
diff --git a/desktop/styles.header b/desktop/styles.header
index 7445a6f..98d21d2 100644
--- a/desktop/styles.header
+++ b/desktop/styles.header
@@ -10,6 +10,7 @@ define ES_STYLE_BUTTON_GROUP_SEPARATOR (ES_STYLE_CAST(1233))
 define_private ES_STYLE_CANVAS_SHADOW (ES_STYLE_CAST(1451))
 define_private ES_STYLE_CHECKBOX_NORMAL (ES_STYLE_CAST(1559))
 define_private ES_STYLE_CHECKBOX_RADIOBOX (ES_STYLE_CAST(1567))
+define ES_STYLE_CLEAR_BACKGROUND (ES_STYLE_CAST(1597))
 define_private ES_STYLE_COLOR_CHOSEN_POINT (ES_STYLE_CAST(1241))
 define_private ES_STYLE_COLOR_CIRCLE (ES_STYLE_CAST(1243))
 define_private ES_STYLE_COLOR_HEX_TEXTBOX (ES_STYLE_CAST(1245))
@@ -17,6 +18,7 @@ define_private ES_STYLE_COLOR_PICKER_MAIN_PANEL (ES_STYLE_CAST(1247))
 define_private ES_STYLE_COLOR_SLIDER (ES_STYLE_CAST(1249))
 define_private ES_STYLE_CONTAINER_WINDOW_ACTIVE (ES_STYLE_CAST(1251))
 define_private ES_STYLE_CONTAINER_WINDOW_INACTIVE (ES_STYLE_CAST(1255))
+define_private ES_STYLE_CURSOR_LOCATOR (ES_STYLE_CAST(1591))
 define ES_STYLE_DIALOG_BUTTON_AREA (ES_STYLE_CAST(1259))
 define ES_STYLE_DIALOG_CONTENT (ES_STYLE_CAST(1261))
 define ES_STYLE_DIALOG_HEADING (ES_STYLE_CAST(1263))
diff --git a/kernel/windows.cpp b/kernel/windows.cpp
index 2c944be..14db25a 100644
--- a/kernel/windows.cpp
+++ b/kernel/windows.cpp
@@ -123,6 +123,7 @@ struct WindowManager {
 	// Miscellaneous:
 
 	EsRectangle workArea;
+	bool inSingleCtrlPress;
 
 	// Game controllers:
 
@@ -181,8 +182,6 @@ void SendMessageToWindow(Window *window, EsMessage *message) {
 		return;
 	}
 
-	window->lastEmbedKeyboardMessage.type = ES_MSG_INVALID;
-
 	if (message->type == ES_MSG_WINDOW_RESIZED) {
 		message->windowResized.content = ES_RECT_4(0, window->width, 0, window->height);
 		window->owner->messageQueue.SendMessage(window->apiWindow, message);
@@ -309,6 +308,17 @@ void WindowManager::PressKey(unsigned scancode) {
 		KernelPanic("WindowManager::PressKey - Panic key pressed.\n");
 	}
 
+	if (scancode == ES_SCANCODE_LEFT_CTRL) {
+		inSingleCtrlPress = true;
+	} else if (scancode == (ES_SCANCODE_LEFT_CTRL | K_SCANCODE_KEY_RELEASED) && inSingleCtrlPress) {
+		EsMessage m;
+		EsMemoryZero(&m, sizeof(EsMessage));
+		m.type = ES_MSG_SINGLE_CTRL_PRESS;
+		desktopProcess->messageQueue.SendMessage(nullptr, &m); 
+	} else {
+		inSingleCtrlPress = false;
+	}
+
 	if (eyedropping) {
 		if (scancode == (ES_SCANCODE_ESCAPE | K_SCANCODE_KEY_RELEASED)) {
 			EndEyedrop(true);
diff --git a/res/System Configuration Template.ini b/res/System Configuration Template.ini
index c037a3c..5569e13 100644
--- a/res/System Configuration Template.ini	
+++ b/res/System Configuration Template.ini	
@@ -8,7 +8,6 @@ settings_path=0:/Settings
 default_user_documents_path=0:/
 installation_state=0
 click_chain_timeout_ms=500
-swap_left_and_right_buttons=0
 
 [ui]
 ; User interface settings that are accessible by all applications.
diff --git a/res/Theme Source.dat b/res/Theme Source.dat
index 1e845e5caa2ad61b2d7f96b07251f9bb8bdb81ed..8a5759d03cba2e3bc5bb019509c5ac611fbea3ab 100644
GIT binary patch
delta 250
zcmX@o$lTJ-%r45nz#!jNwvj!QQKAXRXzh2-Nlh$rN=(j9FUl{?OOb11oE*(~UPPu%
zwyoE>w5T}0$R|HJu_V7px^1&MQ#Th2ZxdtVWJew8$p;)*H(%qiSj@~Q(*_h!n4A$T
zGdX&yu&!)dLz{DEQF2Zy&^91QVMu9U1k!9kkOBlv)*w*`sW5q?iOA&ix$=`4L&YZ_
z;ANdOTVeA2nRA%r+a?#rnM@9-WCNKA6q?OAYcl@^Kk2E-$gXlJElMoO%+G5!GB9A+
a9J66AFJs>3Uwiu*nJaQ%Z|>P|!3Y387gRI=

delta 72
zcmV-O0Jr~yhXcfc0|ylV000_>bg>6j0h4tB&a+wqhzbM<f&qb(aV#5?@IVE#1`Rl)
e0hyDtvQD!dvz!e9aI?C=j{yUAZQHX<!Z-m^BN@g3

diff --git a/res/Themes/Theme.dat b/res/Themes/Theme.dat
index 42b250fcc749437cf1ef63bf5f97a7cece1be43c..09dcfb62735a54fda620fa1ef512073e14c3959f 100644
GIT binary patch
delta 4713
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

delta 4561
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
KtDTk6ul^sag2aIU