From b1e58c9c35ae3eb6167a0745086cdb0ddb1dd9d7 Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Thu, 19 Apr 2018 01:55:42 +0700 Subject: [PATCH] Android: Modify touch screen GUI's buttons (#7240) * Android: Add zoom, minimap, and toggle chat button Zoom button is put above jump button. Minimap and toggle chat button are put in settings bar. * Jump button is rotated down button * Move three buttons on the right screen higher --- LICENSE.txt | 8 ++ src/gui/touchscreengui.cpp | 168 +++++++++++++++++---------- src/gui/touchscreengui.h | 13 ++- textures/base/pack/chat_hide_btn.png | Bin 0 -> 289 bytes textures/base/pack/chat_show_btn.png | Bin 0 -> 188 bytes textures/base/pack/jump_btn.png | Bin 434 -> 396 bytes textures/base/pack/minimap_btn.png | Bin 0 -> 218 bytes textures/base/pack/zoom.png | Bin 0 -> 1290 bytes 8 files changed, 127 insertions(+), 62 deletions(-) create mode 100755 textures/base/pack/chat_hide_btn.png create mode 100755 textures/base/pack/chat_show_btn.png mode change 100644 => 100755 textures/base/pack/jump_btn.png create mode 100755 textures/base/pack/minimap_btn.png create mode 100755 textures/base/pack/zoom.png diff --git a/LICENSE.txt b/LICENSE.txt index 92954e7eb..994e024c9 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -31,6 +31,14 @@ erlehmann: JRottm textures/base/pack/player_marker.png +srifqi + textures/base/pack/chat_hide_btn.png + textures/base/pack/chat_show_btn.png + textures/base/pack/joystick_bg.png + textures/base/pack/joystick_center.png + textures/base/pack/joystick_off.png + textures/base/pack/minimap_btn.png + License of Minetest source code ------------------------------- diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 9b5731652..dfd06c21b 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -39,9 +39,10 @@ with this program; if not, write to the Free Software Foundation, Inc., using namespace irr::core; -const char **touchgui_button_imagenames = (const char*[]) { +const char **touchgui_button_imagenames = (const char *[]) { "jump_btn.png", - "down.png" + "down.png", + "zoom.png" }; const char **touchgui_joystick_imagenames = (const char *[]) { @@ -78,6 +79,9 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id) case crunch_id: key = "sneak"; break; + case zoom_id: + key = "zoom"; + break; case fly_id: key = "freemove"; break; @@ -90,6 +94,12 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id) case debug_id: key = "toggle_debug"; break; + case toggle_chat_id: + key = "toggle_chat"; + break; + case minimap_id: + key = "minimap"; + break; case chat_id: key = "chat"; break; @@ -106,8 +116,8 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id) TouchScreenGUI *g_touchscreengui; -static void load_button_texture(button_info* btn, const char* path, - rect button_rect, ISimpleTextureSource* tsrc, video::IVideoDriver *driver) +static void load_button_texture(button_info *btn, const char *path, + rect button_rect, ISimpleTextureSource *tsrc, video::IVideoDriver *driver) { unsigned int tid; video::ITexture *texture = guiScalingImageButton(driver, @@ -131,15 +141,15 @@ static void load_button_texture(button_info* btn, const char* path, } AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device, - IEventReceiver* receiver) : + IEventReceiver *receiver) : m_driver(device->getVideoDriver()), m_guienv(device->getGUIEnvironment()), m_receiver(receiver) { } -void AutoHideButtonBar::init(ISimpleTextureSource* tsrc, - const char* starter_img, int button_id, v2s32 UpperLeft, +void AutoHideButtonBar::init(ISimpleTextureSource *tsrc, + const char *starter_img, int button_id, v2s32 UpperLeft, v2s32 LowerRight, autohide_button_bar_dir dir, float timeout) { m_texturesource = tsrc; @@ -177,7 +187,7 @@ AutoHideButtonBar::~AutoHideButtonBar() } void AutoHideButtonBar::addButton(touch_gui_button_id button_id, - const wchar_t* caption, const char* btn_image) + const wchar_t *caption, const char *btn_image) { if (!m_initialized) { @@ -230,7 +240,7 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, y_end); } - button_info* btn = new button_info(); + button_info *btn = new button_info(); btn->guibutton = m_guienv->addButton(current_button, 0, button_id, caption, 0); btn->guibutton->grab(); btn->guibutton->setVisible(false); @@ -246,9 +256,20 @@ void AutoHideButtonBar::addButton(touch_gui_button_id button_id, m_buttons.push_back(btn); } +void AutoHideButtonBar::addToggleButton(touch_gui_button_id button_id, + const wchar_t *caption, const char *btn_image_1, + const char *btn_image_2) +{ + addButton(button_id, caption, btn_image_1); + button_info *btn = m_buttons.back(); + btn->togglable = 1; + btn->textures.push_back(btn_image_1); + btn->textures.push_back(btn_image_2); +} + bool AutoHideButtonBar::isButton(const SEvent &event) { - IGUIElement* rootguielement = m_guienv->getRootGUIElement(); + IGUIElement *rootguielement = m_guienv->getRootGUIElement(); if (rootguielement == NULL) { return false; @@ -264,12 +285,12 @@ bool AutoHideButtonBar::isButton(const SEvent &event) if (m_active) { // check for all buttons in vector - std::vector::iterator iter = m_buttons.begin(); + std::vector::iterator iter = m_buttons.begin(); while (iter != m_buttons.end()) { if ((*iter)->guibutton == element) { - SEvent* translated = new SEvent(); + SEvent *translated = new SEvent(); memset(translated, 0, sizeof(SEvent)); translated->EventType = irr::EET_KEY_INPUT_EVENT; translated->KeyInput.Key = (*iter)->keycode; @@ -291,6 +312,18 @@ bool AutoHideButtonBar::isButton(const SEvent &event) m_timeout = 0; + if ((*iter)->togglable == 1) { + (*iter)->togglable = 2; + load_button_texture(*iter, (*iter)->textures[1], + (*iter)->guibutton->getRelativePosition(), + m_texturesource, m_driver); + } else if ((*iter)->togglable == 2) { + (*iter)->togglable = 1; + load_button_texture(*iter, (*iter)->textures[0], + (*iter)->guibutton->getRelativePosition(), + m_texturesource, m_driver); + } + return true; } ++iter; @@ -328,7 +361,7 @@ bool AutoHideButtonBar::isReleaseButton(int eventID) return true; } - std::vector::iterator iter = m_buttons.begin(); + std::vector::iterator iter = m_buttons.begin(); while (iter != m_buttons.end()) { std::vector::iterator id = std::find((*iter)->ids.begin(), @@ -364,7 +397,7 @@ void AutoHideButtonBar::deactivate() } m_active = false; - std::vector::iterator iter = m_buttons.begin(); + std::vector::iterator iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(false); @@ -379,7 +412,7 @@ void AutoHideButtonBar::hide() m_starter.guibutton->setVisible(false); m_starter.guibutton->setEnabled(false); - std::vector::iterator iter = m_buttons.begin(); + std::vector::iterator iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(false); @@ -393,7 +426,7 @@ void AutoHideButtonBar::show() m_visible = true; if (m_active) { - std::vector::iterator iter = m_buttons.begin(); + std::vector::iterator iter = m_buttons.begin(); while (iter != m_buttons.end()) { (*iter)->guibutton->setVisible(true); @@ -406,7 +439,7 @@ void AutoHideButtonBar::show() } } -TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver): +TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver): m_device(device), m_guienv(device->getGUIEnvironment()), m_receiver(receiver), @@ -428,7 +461,7 @@ void TouchScreenGUI::initButton(touch_gui_button_id id, rect button_rect, std::wstring caption, bool immediate_release, float repeat_delay) { - button_info* btn = &m_buttons[id]; + button_info *btn = &m_buttons[id]; btn->guibutton = m_guienv->addButton(button_rect, 0, id, caption.c_str()); btn->guibutton->grab(); btn->repeatcounter = -1; @@ -437,7 +470,7 @@ void TouchScreenGUI::initButton(touch_gui_button_id id, rect button_rect, btn->immediate_release = immediate_release; btn->ids.clear(); - load_button_texture(btn,touchgui_button_imagenames[id],button_rect, + load_button_texture(btn, touchgui_button_imagenames[id], button_rect, m_texturesource, m_device->getVideoDriver()); } @@ -468,7 +501,7 @@ int TouchScreenGUI::getGuiButtonSize() return control_pad_size / 3; } -void TouchScreenGUI::init(ISimpleTextureSource* tsrc) +void TouchScreenGUI::init(ISimpleTextureSource *tsrc) { assert(tsrc); @@ -505,19 +538,27 @@ void TouchScreenGUI::init(ISimpleTextureSource* tsrc) // init jump button initButton(jump_id, - rect(m_screensize.X-(1.75*button_size), - m_screensize.Y - (0.5*button_size), - m_screensize.X-(0.25*button_size), - m_screensize.Y), - L"x",false); + rect(m_screensize.X - (1.75 * button_size), + m_screensize.Y - button_size, + m_screensize.X - (0.25 * button_size), + m_screensize.Y - (0.5 * button_size)), + L"x", false); // init crunch button initButton(crunch_id, - rect(m_screensize.X-(3.25*button_size), - m_screensize.Y - (0.5*button_size), - m_screensize.X-(1.75*button_size), - m_screensize.Y), - L"H",false); + rect(m_screensize.X - (3.25 * button_size), + m_screensize.Y - button_size, + m_screensize.X - (1.75 * button_size), + m_screensize.Y - (0.5 * button_size)), + L"H", false); + + // init zoom button + initButton(zoom_id, + rect(m_screensize.X - (1.25 * button_size), + m_screensize.Y - (3 * button_size), + m_screensize.X - (0.25 * button_size), + m_screensize.Y - (2 * button_size)), + L"z", false); m_settingsbar.init(m_texturesource, "gear_icon.png", settings_starter_id, v2s32(m_screensize.X - (button_size / 2), @@ -528,12 +569,17 @@ void TouchScreenGUI::init(ISimpleTextureSource* tsrc) + (button_size * 0.5)), AHBB_Dir_Right_Left, 3.0); - m_settingsbar.addButton(fly_id, L"fly", "fly_btn.png"); - m_settingsbar.addButton(noclip_id, L"noclip", "noclip_btn.png"); - m_settingsbar.addButton(fast_id, L"fast", "fast_btn.png"); - m_settingsbar.addButton(debug_id, L"debug", "debug_btn.png"); - m_settingsbar.addButton(camera_id, L"camera", "camera_btn.png"); - m_settingsbar.addButton(range_id, L"rangeview", "rangeview_btn.png"); + m_settingsbar.addButton(fly_id, L"fly", "fly_btn.png"); + m_settingsbar.addButton(noclip_id, L"noclip", "noclip_btn.png"); + m_settingsbar.addButton(fast_id, L"fast", "fast_btn.png"); + m_settingsbar.addButton(debug_id, L"debug", "debug_btn.png"); + m_settingsbar.addButton(camera_id, L"camera", "camera_btn.png"); + m_settingsbar.addButton(range_id, L"rangeview", "rangeview_btn.png"); + m_settingsbar.addButton(minimap_id, L"minimap", "minimap_btn.png"); + + // Chat is shown by default, so chat_hide_btn.png is shown first. + m_settingsbar.addToggleButton(toggle_chat_id, L"togglechat", + "chat_hide_btn.png", "chat_show_btn.png"); m_rarecontrolsbar.init(m_texturesource, "rare_controls.png", rare_controls_starter_id, @@ -554,11 +600,11 @@ void TouchScreenGUI::init(ISimpleTextureSource* tsrc) touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y) { - IGUIElement* rootguielement = m_guienv->getRootGUIElement(); + IGUIElement *rootguielement = m_guienv->getRootGUIElement(); if (rootguielement != NULL) { gui::IGUIElement *element = - rootguielement->getElementFromPoint(core::position2d(x,y)); + rootguielement->getElementFromPoint(core::position2d(x, y)); if (element) { for (unsigned int i=0; i < after_last_element_id; i++) { @@ -574,10 +620,10 @@ touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y) touch_gui_button_id TouchScreenGUI::getButtonID(int eventID) { for (unsigned int i=0; i < after_last_element_id; i++) { - button_info* btn = &m_buttons[i]; + button_info *btn = &m_buttons[i]; std::vector::iterator id = - std::find(btn->ids.begin(),btn->ids.end(), eventID); + std::find(btn->ids.begin(), btn->ids.end(), eventID); if (id != btn->ids.end()) return (touch_gui_button_id) i; @@ -589,15 +635,15 @@ touch_gui_button_id TouchScreenGUI::getButtonID(int eventID) bool TouchScreenGUI::isHUDButton(const SEvent &event) { // check if hud item is pressed - for (std::map >::iterator iter = m_hud_rects.begin(); + for (std::map >::iterator iter = m_hud_rects.begin(); iter != m_hud_rects.end(); ++iter) { if (iter->second.isPointInside( v2s32(event.TouchInput.X, event.TouchInput.Y) )) { if ( iter->first < 8) { - SEvent* translated = new SEvent(); - memset(translated,0,sizeof(SEvent)); + SEvent *translated = new SEvent(); + memset(translated, 0, sizeof(SEvent)); translated->EventType = irr::EET_KEY_INPUT_EVENT; translated->KeyInput.Key = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first); translated->KeyInput.Control = false; @@ -615,11 +661,11 @@ bool TouchScreenGUI::isHUDButton(const SEvent &event) bool TouchScreenGUI::isReleaseHUDButton(int eventID) { - std::map::iterator iter = m_hud_ids.find(eventID); + std::map::iterator iter = m_hud_ids.find(eventID); if (iter != m_hud_ids.end()) { - SEvent* translated = new SEvent(); - memset(translated,0,sizeof(SEvent)); + SEvent *translated = new SEvent(); + memset(translated, 0, sizeof(SEvent)); translated->EventType = irr::EET_KEY_INPUT_EVENT; translated->KeyInput.Key = iter->second; translated->KeyInput.PressedDown = false; @@ -636,9 +682,9 @@ bool TouchScreenGUI::isReleaseHUDButton(int eventID) void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, int eventID, bool action) { - button_info* btn = &m_buttons[button]; - SEvent* translated = new SEvent(); - memset(translated,0,sizeof(SEvent)); + button_info *btn = &m_buttons[button]; + SEvent *translated = new SEvent(); + memset(translated, 0, sizeof(SEvent)); translated->EventType = irr::EET_KEY_INPUT_EVENT; translated->KeyInput.Key = btn->keycode; translated->KeyInput.Control = false; @@ -647,7 +693,7 @@ void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, // add this event if (action) { - assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end()); + assert(std::find(btn->ids.begin(), btn->ids.end(), eventID) == btn->ids.end()); btn->ids.push_back(eventID); @@ -662,7 +708,7 @@ void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button, if ((!action) || (btn->immediate_release)) { std::vector::iterator pos = - std::find(btn->ids.begin(),btn->ids.end(), eventID); + std::find(btn->ids.begin(), btn->ids.end(), eventID); // has to be in touch list assert(pos != btn->ids.end()); btn->ids.erase(pos); @@ -699,8 +745,8 @@ void TouchScreenGUI::handleReleaseEvent(int evt_id) // if this pointer issued a mouse event issue symmetric release here if (m_move_sent_as_mouse_event) { - SEvent* translated = new SEvent; - memset(translated,0,sizeof(SEvent)); + SEvent *translated = new SEvent; + memset(translated, 0, sizeof(SEvent)); translated->EventType = EET_MOUSE_INPUT_EVENT; translated->MouseInput.X = m_move_downlocation.X; translated->MouseInput.Y = m_move_downlocation.Y; @@ -1000,12 +1046,12 @@ void TouchScreenGUI::handleChangedButton(const SEvent &event) } // remove old button - handleButtonEvent((touch_gui_button_id) i,*iter,false); + handleButtonEvent((touch_gui_button_id) i, *iter, false); if (current_button_id == after_last_element_id) { return; } - handleButtonEvent((touch_gui_button_id) current_button_id,*iter,true); + handleButtonEvent((touch_gui_button_id) current_button_id, *iter, true); return; } @@ -1018,8 +1064,8 @@ void TouchScreenGUI::handleChangedButton(const SEvent &event) return; } - button_info* btn = &m_buttons[current_button_id]; - if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID) + button_info *btn = &m_buttons[current_button_id]; + if (std::find(btn->ids.begin(), btn->ids.end(), event.TouchInput.ID) == btn->ids.end()) { handleButtonEvent((touch_gui_button_id) current_button_id, @@ -1049,7 +1095,7 @@ bool TouchScreenGUI::doubleTapDetection() if (distance > (20 + m_touchscreen_threshold)) return false; - SEvent* translated = new SEvent(); + SEvent *translated = new SEvent(); memset(translated, 0, sizeof(SEvent)); translated->EventType = EET_MOUSE_INPUT_EVENT; translated->MouseInput.X = m_key_events[0].x; @@ -1096,7 +1142,7 @@ void TouchScreenGUI::applyJoystickStatus() TouchScreenGUI::~TouchScreenGUI() { for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info* btn = &m_buttons[i]; + button_info *btn = &m_buttons[i]; if (btn->guibutton) { btn->guibutton->drop(); btn->guibutton = NULL; @@ -1123,7 +1169,7 @@ void TouchScreenGUI::step(float dtime) { // simulate keyboard repeats for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info* btn = &m_buttons[i]; + button_info *btn = &m_buttons[i]; if (btn->ids.size() > 0) { btn->repeatcounter += dtime; @@ -1197,7 +1243,7 @@ void TouchScreenGUI::Toggle(bool visible) { m_visible = visible; for (unsigned int i = 0; i < after_last_element_id; i++) { - button_info* btn = &m_buttons[i]; + button_info *btn = &m_buttons[i]; if (btn->guibutton) { btn->guibutton->setVisible(visible); } diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index 21c52f756..d3ce84929 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -36,6 +36,7 @@ using namespace irr::gui; typedef enum { jump_id = 0, crunch_id, + zoom_id, after_last_element_id, settings_starter_id, rare_controls_starter_id, @@ -45,6 +46,8 @@ typedef enum { debug_id, camera_id, range_id, + minimap_id, + toggle_chat_id, chat_id, inventory_id, drop_id, @@ -70,7 +73,7 @@ typedef enum { #define MAX_TOUCH_COUNT 64 #define BUTTON_REPEAT_DELAY 0.2f -#define SETTINGS_BAR_Y_OFFSET 6.5 +#define SETTINGS_BAR_Y_OFFSET 5 #define RARE_CONTROLS_BAR_Y_OFFSET 4 extern const char **touchgui_button_imagenames; @@ -84,6 +87,10 @@ struct button_info std::vector ids; IGUIButton *guibutton = nullptr; bool immediate_release; + + // 0: false, 1: (true) first texture, 2: (true) second texture + int togglable = 0; + std::vector textures; }; class AutoHideButtonBar @@ -101,6 +108,10 @@ public: void addButton(touch_gui_button_id id, const wchar_t *caption, const char *btn_image); + // add toggle button to be shown + void addToggleButton(touch_gui_button_id id, const wchar_t *caption, + const char *btn_image_1, const char *btn_image_2); + // detect settings bar button events bool isButton(const SEvent &event); diff --git a/textures/base/pack/chat_hide_btn.png b/textures/base/pack/chat_hide_btn.png new file mode 100755 index 0000000000000000000000000000000000000000..42e9fa6b9945506af2672dba5609e3150d1a8b48 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxB}__|Nk$&IsYz@#aI&L z7tG-B>_!@pbJEkrF{C1H?S+G!hYfgG1K7oX2Tfw-ddp!e#_`Qd@_@kos<&)$N2Nzopr026F;@jRu?6^qxB}__|Nk$&IsYz@#aI&L z7tG-B>_!@plj7;(7*Y|p_rgZ51_J@+3mymcn&#)&zg=K|$^XR*g=?n;cFFpk{nGYW z;oz;9U6fJ3$a#2T(hCbW)}V&_^*k#KOV(IzUZ8be@R^QJT}$)} iCt)9rCboMU&E!|?t?EiL7dZ~JkHOQ`&t;ucLK6UBnnrK{ literal 0 HcmV?d00001 diff --git a/textures/base/pack/jump_btn.png b/textures/base/pack/jump_btn.png old mode 100644 new mode 100755 index e9ce9d0c60f42ae500ad2914f409f0c9cfbb2a55..663055020c761c1552300edfc2cd8ebcdfd8d49f GIT binary patch literal 396 zcmV;70dxL|P)0e$MNuOP$}GW4nWd4Xv6L)f zN*AMi9E0Xbx#3wtg37<5xcNRZlGBG~%xUff=UXS3emg-fo#485j1e7UgjY@xUYR3` zH%6@9Sf^w3%4rK0Z>&mh^vZ~}Hr7~jRoZ*Bce#DNZE?o=R2|OPDr5Xh>8C1q2~RANuyC-v{7d+FhU6ox~`oLp!*Sh5eG9bLr>q#GJ1`?jHE}wQlvYZ9b1C?Cv}3vq*UI&A!uz zEi`X)GE}{O{D=Jt!-VQtQu^#G@;Mx~G)OCbGhZYx^iT8gbk6)78&qol`;+0Gu28asU7T diff --git a/textures/base/pack/minimap_btn.png b/textures/base/pack/minimap_btn.png new file mode 100755 index 0000000000000000000000000000000000000000..71d79f6bc99e9897a69eac585e68c066ee6a6fea GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0P3?wHke>@jRu?6^qxMn5!|NsAA$N2topa^40 zkY6x^!?PP{Ku(jVi(^Pd+|!F2d6^7&SR9naHI1FzPG&4j*7cTb5abXR=w$MCsr+NT z^(LurbiJjNApXGX3`@^}?5p1oGy5<2E-S&CrO>rHREc-1&x(~x z{~zfw?pPW&C&71i#$M*_4nOaO|7D5Db89HoR-CFVaP#rR2Oge&PuZ;&wW#RsS55}H OhQZU-&t;ucLK6VnWKMSg literal 0 HcmV?d00001 diff --git a/textures/base/pack/zoom.png b/textures/base/pack/zoom.png new file mode 100755 index 0000000000000000000000000000000000000000..2ba479eedf11b40a6b942e71d5c62c8503dc4dc0 GIT binary patch literal 1290 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD~={fl(*GC&U%V-M)SMjT<-qj{-s= zU>NdfHZZ)BOM?7@85kKE**Libgha(8l+@HUwRH83Oss93+}s1gBa%|mvhs>5sv5ic zCrq3&b^6SC3l=X~wqoU~)oa&n*tB`e*6llX?mlqv$k7uQE?>WK^VaP<_a8id^8Cfi zH*ep)|M2ni*Ka?5{`os6$mA{q1GAQ=i(^Pd+}oL%;kN<=+9IPTY+4bKdn8JWQ72@Z zP?=O&Q|fB&B;Jmn|Nk$PH?rL5&b$5jos@ldzH1vluia5@RKEG|@)=0rPWAEh&hD6n z6Lx#dk$t~&ZR!0^mCawzCG3jGHCuhO>3|f{mD;@0Ras)!R>r-TgKxZu6h= z;z?_Ars_N`^VRzI>5u34%Ba3uTPFqRa4n6h-cs}L^q!i8uKKTD<~ye!;W^)GqqqF> zQ_jK~w<+&RTUM{)WZ9j1x873cL*ntz4DK`Zd8S)A&itiQQC!TrWmZVizUhMdB2qU6 zIeeCR>*eEhOGp3L%LlIw50=y~yxKTr$)CU9xBS|l>H0R`_2%NW(j1ekLw+~LU)cBm zHCwvK3Db33>l7}$>FnJ2?Z349v5D@yxje@>yI%Ke9R9O?NAAWL?pF?CFW364l)O@J zzsuI*R(pQpv_;pNW^MTAd+FQTi{HMmTsx+=woIg*DA3oLaP+~GXGC)|9jlP zZJL1eAwE_A^yy+Ro&M|pI3ZNS8X>+*(8^c9E8vl*aRuk830~GE>8V?HcO_n$vSG!y zTubld?fc@}h0a)b1j|Tj9R6j;+Az6~%iz(;;vW-dUAA1vP+%9pcuS^|?|N7<+j9|y zBP+QAxFS zv&wR|sTIR7kp<$4ztTJIbzF!x{FT(QPbN9v<< z4`x`w9?<{%)Z$6}39$hU4GfRF+|&>3TvFxNQ0%*WbyrcKrybju^;su5U&Mz=3q+K} z=KDDOKiCu#y2{^-BiZ3k#@FB1uO4eS@F#uu`}v>Qk`qoIPCd|Yplf-wa?>i){_Dq* z6ZU=JlweF$-4vO?vPS45r^LbS-%qf_=dPG)(Z6r=L%{}nDaId*ft1=oagGBD4Ihsy tGt`?feQ<|TDnMTCkCZ3pk3hf|aRF}Gr)Sf0W&$%QgQu&X%Q~loCIDQ;3w{6q literal 0 HcmV?d00001 -- 2.25.1