blob: 989da9a5ec8eb7f25b4635bb32384a0b528485db [file] [log] [blame]
Dees_Troya13d74f2013-03-24 08:54:55 -05001/*
2 Copyright 2013 bigbiff/Dees_Troy TeamWin
3 This file is part of TWRP/TeamWin Recovery Project.
4
5 TWRP is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 TWRP is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TWRP. If not, see <http://www.gnu.org/licenses/>.
17*/
Dees Troy3be70a82013-10-22 14:25:12 +000018
Dees_Troya13d74f2013-03-24 08:54:55 -050019// pages.cpp - Source to manage GUI base objects
Dees_Troy51a0e822012-09-05 15:24:24 -040020
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <fcntl.h>
26#include <sys/reboot.h>
27#include <sys/stat.h>
28#include <sys/time.h>
29#include <sys/mman.h>
30#include <sys/types.h>
31#include <sys/ioctl.h>
32#include <time.h>
33#include <unistd.h>
34#include <stdlib.h>
Ethan Yonker3fdcda42016-11-30 12:29:37 -060035#include <dirent.h>
Ethan Yonkera2dc2f22014-11-08 08:13:40 -060036#include "../twrp-functions.hpp"
Ethan Yonker74db1572015-10-28 12:44:49 -050037#include "../partitions.hpp"
Dees_Troy51a0e822012-09-05 15:24:24 -040038
39#include <string>
Xuefercac6ace2016-02-01 02:28:55 +080040#include <algorithm>
Dees_Troy51a0e822012-09-05 15:24:24 -040041
Ethan Yonker8373cfe2017-09-08 06:50:54 -050042#ifdef USE_MINZIP
43#include "../minzip/SysUtil.h"
44#else
Ethan Yonker58f21322018-08-24 11:17:36 -050045#include <otautil/SysUtil.h>
Ethan Yonker8373cfe2017-09-08 06:50:54 -050046#endif
47
Dees_Troy51a0e822012-09-05 15:24:24 -040048extern "C" {
Dees_Troy2673cec2013-04-02 20:22:16 +000049#include "../twcommon.h"
Ethan Yonker63e414f2015-02-06 15:44:39 -060050#include "gui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040051}
Ethan Yonker8373cfe2017-09-08 06:50:54 -050052#include "../zipwrap.hpp"
Ethan Yonkerfbb43532015-12-28 21:54:50 +010053#include "../minuitwrp/minui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040054
55#include "rapidxml.hpp"
56#include "objects.hpp"
gordon13370d9133d2013-06-08 14:17:07 +020057#include "blanktimer.hpp"
Dees_Troy51a0e822012-09-05 15:24:24 -040058
that74bff7f2017-01-18 22:32:36 +010059// version 2 requires theme to handle power button as action togglebacklight
Captain Throwback268091d2020-10-22 09:33:29 -040060// version 4 adds listbox support to reboot page
61#define TW_THEME_VERSION 4
that74bff7f2017-01-18 22:32:36 +010062
Ethan Yonker8e5692f2016-01-21 11:21:06 -060063#define TW_THEME_VER_ERR -2
Ethan Yonker1308d532016-01-14 22:21:49 -060064
Dees_Troy51a0e822012-09-05 15:24:24 -040065extern int gGuiRunning;
Mohd Faraz3c25ab32020-08-12 12:29:42 +000066GUITerminal* term = NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -040067
68std::map<std::string, PageSet*> PageManager::mPageSets;
69PageSet* PageManager::mCurrentSet;
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +010070MouseCursor *PageManager::mMouseCursor = NULL;
Vojtech Bocek0b7fe502014-03-13 17:36:52 +010071HardwareKeyboard *PageManager::mHardwareKeyboard = NULL;
Ethan Yonkere0f1f3b2015-10-27 09:49:01 -050072bool PageManager::mReloadTheme = false;
73std::string PageManager::mStartPage = "main";
Ethan Yonker74db1572015-10-28 12:44:49 -050074std::vector<language_struct> Language_List;
Dees_Troy51a0e822012-09-05 15:24:24 -040075
Ethan Yonker751a85e2014-12-12 16:59:10 -060076int tw_x_offset = 0;
77int tw_y_offset = 0;
James Christopher Adduonodcd1e442016-11-06 13:17:34 -050078int tw_w_offset = 0;
79int tw_h_offset = 0;
Ethan Yonker751a85e2014-12-12 16:59:10 -060080
Dees_Troy51a0e822012-09-05 15:24:24 -040081// Helper routine to convert a string to a color declaration
82int ConvertStrToColor(std::string str, COLOR* color)
83{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020084 // Set the default, solid black
85 memset(color, 0, sizeof(COLOR));
86 color->alpha = 255;
Dees_Troy51a0e822012-09-05 15:24:24 -040087
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020088 // Translate variables
89 DataManager::GetValue(str, str);
Matt Mowerfb1c4ff2014-04-16 13:43:36 -050090
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020091 // Look for some defaults
thatf6ed8fc2015-02-14 20:23:16 +010092 if (str == "black") return 0;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020093 else if (str == "white") { color->red = color->green = color->blue = 255; return 0; }
94 else if (str == "red") { color->red = 255; return 0; }
95 else if (str == "green") { color->green = 255; return 0; }
96 else if (str == "blue") { color->blue = 255; return 0; }
Dees_Troy51a0e822012-09-05 15:24:24 -040097
Vojtech Bocekfafb0c52013-07-25 22:53:02 +020098 // At this point, we require an RGB(A) color
99 if (str[0] != '#')
100 return -1;
101
102 str.erase(0, 1);
Dees_Troy51a0e822012-09-05 15:24:24 -0400103
Dees_Troy30b962e2012-10-19 20:48:59 -0400104 int result;
105 if (str.size() >= 8) {
106 // We have alpha channel
107 string alpha = str.substr(6, 2);
108 result = strtol(alpha.c_str(), NULL, 16);
109 color->alpha = result & 0x000000FF;
110 str.resize(6);
111 result = strtol(str.c_str(), NULL, 16);
112 color->red = (result >> 16) & 0x000000FF;
113 color->green = (result >> 8) & 0x000000FF;
114 color->blue = result & 0x000000FF;
115 } else {
116 result = strtol(str.c_str(), NULL, 16);
117 color->red = (result >> 16) & 0x000000FF;
118 color->green = (result >> 8) & 0x000000FF;
119 color->blue = result & 0x000000FF;
120 }
121 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400122}
123
124// Helper APIs
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600125xml_node<>* FindNode(xml_node<>* parent, const char* nodename, int depth /* = 0 */)
126{
that8d46c092015-02-26 01:30:04 +0100127 if (!parent)
128 return NULL;
129
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600130 xml_node<>* child = parent->first_node(nodename);
131 if (child)
132 return child;
133
134 if (depth == 10) {
135 LOGERR("Too many style loops detected.\n");
136 return NULL;
137 }
138
139 xml_node<>* style = parent->first_node("style");
140 if (style) {
141 while (style) {
142 if (!style->first_attribute("name")) {
143 LOGERR("No name given for style.\n");
144 continue;
145 } else {
146 std::string name = style->first_attribute("name")->value();
147 xml_node<>* node = PageManager::FindStyle(name);
148
149 if (node) {
150 // We found the style that was named
151 xml_node<>* stylenode = FindNode(node, nodename, depth + 1);
152 if (stylenode)
153 return stylenode;
154 }
155 }
156 style = style->next_sibling("style");
157 }
158 } else {
that54e9c832015-11-04 21:46:01 +0100159 // Search for stylename in the parent node <object type="foo" style="foo2">
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600160 xml_attribute<>* attr = parent->first_attribute("style");
161 // If no style is found anywhere else and the node wasn't found in the object itself
162 // as a special case we will search for a style that uses the same style name as the
163 // object type, so <object type="button"> would search for a style named "button"
164 if (!attr)
165 attr = parent->first_attribute("type");
that54e9c832015-11-04 21:46:01 +0100166 // if there's no attribute type, the object type must be the element name
167 std::string stylename = attr ? attr->value() : parent->name();
168 xml_node<>* node = PageManager::FindStyle(stylename);
169 if (node) {
170 xml_node<>* stylenode = FindNode(node, nodename, depth + 1);
171 if (stylenode)
172 return stylenode;
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600173 }
174 }
175 return NULL;
176}
177
thatf6ed8fc2015-02-14 20:23:16 +0100178std::string LoadAttrString(xml_node<>* element, const char* attrname, const char* defaultvalue)
179{
180 if (!element)
181 return defaultvalue;
182
183 xml_attribute<>* attr = element->first_attribute(attrname);
184 return attr ? attr->value() : defaultvalue;
185}
186
187int LoadAttrInt(xml_node<>* element, const char* attrname, int defaultvalue)
188{
189 string value = LoadAttrString(element, attrname);
190 // resolve variables
191 DataManager::GetValue(value, value);
192 return value.empty() ? defaultvalue : atoi(value.c_str());
193}
194
195int LoadAttrIntScaleX(xml_node<>* element, const char* attrname, int defaultvalue)
196{
197 return scale_theme_x(LoadAttrInt(element, attrname, defaultvalue));
198}
199
200int LoadAttrIntScaleY(xml_node<>* element, const char* attrname, int defaultvalue)
201{
202 return scale_theme_y(LoadAttrInt(element, attrname, defaultvalue));
203}
204
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600205COLOR LoadAttrColor(xml_node<>* element, const char* attrname, bool* found_color, COLOR defaultvalue)
thatf6ed8fc2015-02-14 20:23:16 +0100206{
207 string value = LoadAttrString(element, attrname);
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600208 *found_color = !value.empty();
thatf6ed8fc2015-02-14 20:23:16 +0100209 // resolve variables
210 DataManager::GetValue(value, value);
211 COLOR ret = defaultvalue;
212 if (ConvertStrToColor(value, &ret) == 0)
213 return ret;
214 else
215 return defaultvalue;
216}
217
Ethan Yonker21ff02a2015-02-18 14:35:00 -0600218COLOR LoadAttrColor(xml_node<>* element, const char* attrname, COLOR defaultvalue)
219{
220 bool found_color = false;
221 return LoadAttrColor(element, attrname, &found_color, defaultvalue);
222}
223
thatf6ed8fc2015-02-14 20:23:16 +0100224FontResource* LoadAttrFont(xml_node<>* element, const char* attrname)
225{
226 std::string name = LoadAttrString(element, attrname, "");
227 if (name.empty())
228 return NULL;
229 else
that74ac6062015-03-04 22:39:34 +0100230 return PageManager::GetResources()->FindFont(name);
thatf6ed8fc2015-02-14 20:23:16 +0100231}
232
233ImageResource* LoadAttrImage(xml_node<>* element, const char* attrname)
234{
235 std::string name = LoadAttrString(element, attrname, "");
236 if (name.empty())
237 return NULL;
238 else
that74ac6062015-03-04 22:39:34 +0100239 return PageManager::GetResources()->FindImage(name);
thatf6ed8fc2015-02-14 20:23:16 +0100240}
241
242AnimationResource* LoadAttrAnimation(xml_node<>* element, const char* attrname)
243{
244 std::string name = LoadAttrString(element, attrname, "");
245 if (name.empty())
246 return NULL;
247 else
that74ac6062015-03-04 22:39:34 +0100248 return PageManager::GetResources()->FindAnimation(name);
thatf6ed8fc2015-02-14 20:23:16 +0100249}
250
Ethan Yonkerb7a54a32015-10-05 10:16:27 -0500251bool LoadPlacement(xml_node<>* node, int* x, int* y, int* w /* = NULL */, int* h /* = NULL */, Placement* placement /* = NULL */)
Dees_Troy51a0e822012-09-05 15:24:24 -0400252{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200253 if (!node)
254 return false;
Dees_Troy51a0e822012-09-05 15:24:24 -0400255
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200256 if (node->first_attribute("x"))
thatf6ed8fc2015-02-14 20:23:16 +0100257 *x = LoadAttrIntScaleX(node, "x") + tw_x_offset;
Dees_Troy51a0e822012-09-05 15:24:24 -0400258
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200259 if (node->first_attribute("y"))
thatf6ed8fc2015-02-14 20:23:16 +0100260 *y = LoadAttrIntScaleY(node, "y") + tw_y_offset;
Dees_Troy51a0e822012-09-05 15:24:24 -0400261
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200262 if (w && node->first_attribute("w"))
thatf6ed8fc2015-02-14 20:23:16 +0100263 *w = LoadAttrIntScaleX(node, "w");
Dees_Troy51a0e822012-09-05 15:24:24 -0400264
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200265 if (h && node->first_attribute("h"))
thatf6ed8fc2015-02-14 20:23:16 +0100266 *h = LoadAttrIntScaleY(node, "h");
Dees_Troy51a0e822012-09-05 15:24:24 -0400267
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200268 if (placement && node->first_attribute("placement"))
Ethan Yonkerb7a54a32015-10-05 10:16:27 -0500269 *placement = (Placement) LoadAttrInt(node, "placement");
Dees_Troy51a0e822012-09-05 15:24:24 -0400270
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200271 return true;
Dees_Troy51a0e822012-09-05 15:24:24 -0400272}
273
274int ActionObject::SetActionPos(int x, int y, int w, int h)
275{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200276 if (x < 0 || y < 0)
277 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400278
Matt Mowerfb1c4ff2014-04-16 13:43:36 -0500279 mActionX = x;
280 mActionY = y;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200281 if (w || h)
282 {
283 mActionW = w;
284 mActionH = h;
285 }
286 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400287}
288
thatb63e2f92015-06-27 21:35:11 +0200289Page::Page(xml_node<>* page, std::vector<xml_node<>*> *templates)
Dees_Troy51a0e822012-09-05 15:24:24 -0400290{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200291 mTouchStart = NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -0400292
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200293 // We can memset the whole structure, because the alpha channel is ignored
294 memset(&mBackground, 0, sizeof(COLOR));
Dees_Troy51a0e822012-09-05 15:24:24 -0400295
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200296 // With NULL, we make a console-only display
297 if (!page)
298 {
299 mName = "console";
Dees_Troy51a0e822012-09-05 15:24:24 -0400300
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200301 GUIConsole* element = new GUIConsole(NULL);
302 mRenders.push_back(element);
303 mActions.push_back(element);
304 return;
305 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400306
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200307 if (page->first_attribute("name"))
308 mName = page->first_attribute("name")->value();
309 else
310 {
311 LOGERR("No page name attribute found!\n");
312 return;
313 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400314
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200315 LOGINFO("Loading page %s\n", mName.c_str());
Dees_Troy51a0e822012-09-05 15:24:24 -0400316
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200317 // This is a recursive routine for template handling
thatb63e2f92015-06-27 21:35:11 +0200318 ProcessNode(page, templates, 0);
Dees_Troy51a0e822012-09-05 15:24:24 -0400319}
320
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100321Page::~Page()
322{
323 for (std::vector<GUIObject*>::iterator itr = mObjects.begin(); itr != mObjects.end(); ++itr)
324 delete *itr;
325}
326
thatb63e2f92015-06-27 21:35:11 +0200327bool Page::ProcessNode(xml_node<>* page, std::vector<xml_node<>*> *templates, int depth)
Dees_Troy51a0e822012-09-05 15:24:24 -0400328{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200329 if (depth == 10)
330 {
331 LOGERR("Page processing depth has exceeded 10. Failing out. This is likely a recursive template.\n");
332 return false;
333 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400334
thatb63e2f92015-06-27 21:35:11 +0200335 for (xml_node<>* child = page->first_node(); child; child = child->next_sibling())
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200336 {
thatb63e2f92015-06-27 21:35:11 +0200337 std::string type = child->name();
338
339 if (type == "background") {
340 mBackground = LoadAttrColor(child, "color", COLOR(0,0,0,0));
341 continue;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200342 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400343
thatb63e2f92015-06-27 21:35:11 +0200344 if (type == "object") {
345 // legacy format : <object type="...">
346 xml_attribute<>* attr = child->first_attribute("type");
347 type = attr ? attr->value() : "*unspecified*";
348 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400349
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200350 if (type == "text")
351 {
352 GUIText* element = new GUIText(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100353 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200354 mRenders.push_back(element);
355 mActions.push_back(element);
356 }
357 else if (type == "image")
358 {
359 GUIImage* element = new GUIImage(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100360 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200361 mRenders.push_back(element);
362 }
363 else if (type == "fill")
364 {
365 GUIFill* element = new GUIFill(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100366 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200367 mRenders.push_back(element);
368 }
369 else if (type == "action")
370 {
371 GUIAction* element = new GUIAction(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100372 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200373 mActions.push_back(element);
374 }
375 else if (type == "console")
376 {
377 GUIConsole* element = new GUIConsole(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100378 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200379 mRenders.push_back(element);
380 mActions.push_back(element);
381 }
that1964d192016-01-07 00:41:03 +0100382 else if (type == "terminal")
383 {
384 GUITerminal* element = new GUITerminal(child);
385 mObjects.push_back(element);
386 mRenders.push_back(element);
387 mActions.push_back(element);
388 mInputs.push_back(element);
Mohd Faraz3c25ab32020-08-12 12:29:42 +0000389 term = element;
that1964d192016-01-07 00:41:03 +0100390 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200391 else if (type == "button")
392 {
393 GUIButton* element = new GUIButton(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100394 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200395 mRenders.push_back(element);
396 mActions.push_back(element);
397 }
398 else if (type == "checkbox")
399 {
400 GUICheckbox* element = new GUICheckbox(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100401 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200402 mRenders.push_back(element);
403 mActions.push_back(element);
404 }
405 else if (type == "fileselector")
406 {
407 GUIFileSelector* element = new GUIFileSelector(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100408 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200409 mRenders.push_back(element);
410 mActions.push_back(element);
411 }
412 else if (type == "animation")
413 {
414 GUIAnimation* element = new GUIAnimation(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100415 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200416 mRenders.push_back(element);
417 }
418 else if (type == "progressbar")
419 {
420 GUIProgressBar* element = new GUIProgressBar(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100421 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200422 mRenders.push_back(element);
423 mActions.push_back(element);
424 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400425 else if (type == "slider")
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200426 {
427 GUISlider* element = new GUISlider(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100428 mObjects.push_back(element);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200429 mRenders.push_back(element);
430 mActions.push_back(element);
431 }
Vojtech Bocek85932342013-04-01 22:11:33 +0200432 else if (type == "slidervalue")
433 {
434 GUISliderValue *element = new GUISliderValue(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100435 mObjects.push_back(element);
Vojtech Bocek85932342013-04-01 22:11:33 +0200436 mRenders.push_back(element);
437 mActions.push_back(element);
438 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400439 else if (type == "listbox")
440 {
441 GUIListBox* element = new GUIListBox(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100442 mObjects.push_back(element);
Dees_Troy51a0e822012-09-05 15:24:24 -0400443 mRenders.push_back(element);
444 mActions.push_back(element);
445 }
446 else if (type == "keyboard")
447 {
448 GUIKeyboard* element = new GUIKeyboard(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100449 mObjects.push_back(element);
Dees_Troy51a0e822012-09-05 15:24:24 -0400450 mRenders.push_back(element);
451 mActions.push_back(element);
452 }
453 else if (type == "input")
454 {
455 GUIInput* element = new GUIInput(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100456 mObjects.push_back(element);
Dees_Troy51a0e822012-09-05 15:24:24 -0400457 mRenders.push_back(element);
458 mActions.push_back(element);
459 mInputs.push_back(element);
460 }
Dees_Troya13d74f2013-03-24 08:54:55 -0500461 else if (type == "partitionlist")
462 {
463 GUIPartitionList* element = new GUIPartitionList(child);
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100464 mObjects.push_back(element);
Dees_Troya13d74f2013-03-24 08:54:55 -0500465 mRenders.push_back(element);
466 mActions.push_back(element);
467 }
Vojtech Bocek7e11ac52015-03-05 23:21:49 +0100468 else if (type == "patternpassword")
469 {
470 GUIPatternPassword* element = new GUIPatternPassword(child);
471 mObjects.push_back(element);
472 mRenders.push_back(element);
473 mActions.push_back(element);
474 }
Ethan Yonker44925ad2015-07-22 12:33:59 -0500475 else if (type == "textbox")
476 {
477 GUITextBox* element = new GUITextBox(child);
478 mObjects.push_back(element);
479 mRenders.push_back(element);
480 mActions.push_back(element);
481 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200482 else if (type == "template")
483 {
484 if (!templates || !child->first_attribute("name"))
485 {
486 LOGERR("Invalid template request.\n");
487 }
488 else
489 {
490 std::string name = child->first_attribute("name")->value();
Ethan Yonker780cd392014-07-21 15:24:39 -0500491 xml_node<>* node;
492 bool node_found = false;
Dees_Troy51a0e822012-09-05 15:24:24 -0400493
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200494 // We need to find the correct template
Ethan Yonker780cd392014-07-21 15:24:39 -0500495 for (std::vector<xml_node<>*>::iterator itr = templates->begin(); itr != templates->end(); itr++) {
496 node = (*itr)->first_node("template");
Dees_Troy51a0e822012-09-05 15:24:24 -0400497
Ethan Yonker780cd392014-07-21 15:24:39 -0500498 while (node)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200499 {
Ethan Yonker780cd392014-07-21 15:24:39 -0500500 if (!node->first_attribute("name"))
501 continue;
502
503 if (name == node->first_attribute("name")->value())
504 {
505 if (!ProcessNode(node, templates, depth + 1))
506 return false;
507 else {
508 node_found = true;
509 break;
510 }
511 }
512 if (node_found)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200513 break;
Ethan Yonker780cd392014-07-21 15:24:39 -0500514 node = node->next_sibling("template");
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200515 }
thatb63e2f92015-06-27 21:35:11 +0200516 // [check] why is there no if (node_found) here too?
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200517 }
518 }
519 }
520 else
521 {
thatb63e2f92015-06-27 21:35:11 +0200522 LOGERR("Unknown object type: %s.\n", type.c_str());
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200523 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200524 }
525 return true;
Dees_Troy51a0e822012-09-05 15:24:24 -0400526}
527
528int Page::Render(void)
529{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200530 // Render background
531 gr_color(mBackground.red, mBackground.green, mBackground.blue, mBackground.alpha);
532 gr_fill(0, 0, gr_fb_width(), gr_fb_height());
Dees_Troy51a0e822012-09-05 15:24:24 -0400533
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200534 // Render remaining objects
535 std::vector<RenderObject*>::iterator iter;
536 for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
537 {
538 if ((*iter)->Render())
539 LOGERR("A render request has failed.\n");
540 }
541 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400542}
543
544int Page::Update(void)
545{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200546 int retCode = 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400547
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200548 std::vector<RenderObject*>::iterator iter;
549 for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
550 {
551 int ret = (*iter)->Update();
552 if (ret < 0)
553 LOGERR("An update request has failed.\n");
554 else if (ret > retCode)
555 retCode = ret;
556 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400557
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200558 return retCode;
Dees_Troy51a0e822012-09-05 15:24:24 -0400559}
560
561int Page::NotifyTouch(TOUCH_STATE state, int x, int y)
562{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200563 // By default, return 1 to ignore further touches if nobody is listening
564 int ret = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400565
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200566 // Don't try to handle a lack of handlers
567 if (mActions.size() == 0)
568 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -0400569
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200570 // We record mTouchStart so we can pass all the touch stream to the same handler
571 if (state == TOUCH_START)
572 {
573 std::vector<ActionObject*>::reverse_iterator iter;
574 // We work backwards, from top-most element to bottom-most element
575 for (iter = mActions.rbegin(); iter != mActions.rend(); iter++)
576 {
577 if ((*iter)->IsInRegion(x, y))
578 {
579 mTouchStart = (*iter);
580 ret = mTouchStart->NotifyTouch(state, x, y);
581 if (ret >= 0)
582 break;
583 mTouchStart = NULL;
584 }
585 }
586 }
587 else if (state == TOUCH_RELEASE && mTouchStart != NULL)
588 {
589 ret = mTouchStart->NotifyTouch(state, x, y);
590 mTouchStart = NULL;
591 }
592 else if ((state == TOUCH_DRAG || state == TOUCH_HOLD || state == TOUCH_REPEAT) && mTouchStart != NULL)
593 {
594 ret = mTouchStart->NotifyTouch(state, x, y);
595 }
596 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -0400597}
598
Vojtech Bocek0b7fe502014-03-13 17:36:52 +0100599int Page::NotifyKey(int key, bool down)
Dees_Troy51a0e822012-09-05 15:24:24 -0400600{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200601 std::vector<ActionObject*>::reverse_iterator iter;
Dees_Troy51a0e822012-09-05 15:24:24 -0400602
Vojtech Bocek0b7fe502014-03-13 17:36:52 +0100603 int ret = 1;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200604 // We work backwards, from top-most element to bottom-most element
605 for (iter = mActions.rbegin(); iter != mActions.rend(); iter++)
606 {
Vojtech Bocek0b7fe502014-03-13 17:36:52 +0100607 ret = (*iter)->NotifyKey(key, down);
that8834a0f2016-01-05 23:29:30 +0100608 if (ret == 0)
609 return 0;
Vojtech Bocek0b7fe502014-03-13 17:36:52 +0100610 if (ret < 0) {
611 LOGERR("An action handler has returned an error\n");
612 ret = 1;
613 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200614 }
Vojtech Bocek0b7fe502014-03-13 17:36:52 +0100615 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -0400616}
617
that8834a0f2016-01-05 23:29:30 +0100618int Page::NotifyCharInput(int ch)
Dees_Troy51a0e822012-09-05 15:24:24 -0400619{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200620 std::vector<InputObject*>::reverse_iterator iter;
Dees_Troy51a0e822012-09-05 15:24:24 -0400621
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200622 // We work backwards, from top-most element to bottom-most element
623 for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++)
624 {
that8834a0f2016-01-05 23:29:30 +0100625 int ret = (*iter)->NotifyCharInput(ch);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200626 if (ret == 0)
627 return 0;
628 else if (ret < 0)
that6a894592016-03-13 17:51:28 +0100629 LOGERR("A char input handler has returned an error\n");
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200630 }
631 return 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400632}
633
634int Page::SetKeyBoardFocus(int inFocus)
635{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200636 std::vector<InputObject*>::reverse_iterator iter;
Dees_Troy51a0e822012-09-05 15:24:24 -0400637
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200638 // We work backwards, from top-most element to bottom-most element
639 for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++)
640 {
641 int ret = (*iter)->SetInputFocus(inFocus);
642 if (ret == 0)
643 return 0;
644 else if (ret < 0)
that6a894592016-03-13 17:51:28 +0100645 LOGERR("An input focus handler has returned an error\n");
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200646 }
647 return 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400648}
649
650void Page::SetPageFocus(int inFocus)
651{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200652 // Render remaining objects
653 std::vector<RenderObject*>::iterator iter;
654 for (iter = mRenders.begin(); iter != mRenders.end(); iter++)
655 (*iter)->SetPageFocus(inFocus);
656
657 return;
Dees_Troy51a0e822012-09-05 15:24:24 -0400658}
659
660int Page::NotifyVarChange(std::string varName, std::string value)
661{
Vojtech Bocek07220562014-02-08 02:05:33 +0100662 std::vector<GUIObject*>::iterator iter;
663 for (iter = mObjects.begin(); iter != mObjects.end(); ++iter)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200664 {
665 if ((*iter)->NotifyVarChange(varName, value))
666 LOGERR("An action handler errored on NotifyVarChange.\n");
667 }
668 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400669}
670
that6a894592016-03-13 17:51:28 +0100671
672// transient data for loading themes
673struct LoadingContext
674{
Ethan Yonker8373cfe2017-09-08 06:50:54 -0500675 ZipWrap* zip; // zip to load theme from, or NULL for the stock theme
that6a894592016-03-13 17:51:28 +0100676 std::set<std::string> filenames; // to detect cyclic includes
677 std::string basepath; // if zip is NULL, base path to load includes from with trailing slash, otherwise empty
678 std::vector<xml_document<>*> xmldocs; // all loaded xml docs
679 std::vector<char*> xmlbuffers; // text buffers with xml content
680 std::vector<xml_node<>*> styles; // refer to <styles> nodes inside xmldocs
681 std::vector<xml_node<>*> templates; // refer to <templates> nodes inside xmldocs
682
683 LoadingContext()
684 {
685 zip = NULL;
686 }
687
688 ~LoadingContext()
689 {
690 // free all xml buffers
691 for (std::vector<char*>::iterator it = xmlbuffers.begin(); it != xmlbuffers.end(); ++it)
692 free(*it);
693 }
694
695};
696
697// for FindStyle
698LoadingContext* PageManager::currentLoadingContext = NULL;
699
700
701PageSet::PageSet()
Dees_Troy51a0e822012-09-05 15:24:24 -0400702{
that74ac6062015-03-04 22:39:34 +0100703 mResources = new ResourceManager;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200704 mCurrentPage = NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -0400705
that6a894592016-03-13 17:51:28 +0100706 set_scale_values(1, 1); // Reset any previous scaling values
Dees_Troy51a0e822012-09-05 15:24:24 -0400707}
708
709PageSet::~PageSet()
710{
Ethan Yonker1c273312015-03-16 12:18:56 -0500711 mOverlays.clear();
Vojtech Bocekbfb63342014-02-08 00:32:31 +0100712 for (std::vector<Page*>::iterator itr = mPages.begin(); itr != mPages.end(); ++itr)
713 delete *itr;
714
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200715 delete mResources;
Dees_Troy51a0e822012-09-05 15:24:24 -0400716}
717
that6a894592016-03-13 17:51:28 +0100718int PageSet::Load(LoadingContext& ctx, const std::string& filename)
719{
720 bool isMain = ctx.xmlbuffers.empty(); // if we have no files yet, remember that this is the main XML file
721
722 if (!ctx.filenames.insert(filename).second)
723 // ignore already loaded files to prevent crash with cyclic includes
724 return 0;
725
726 // load XML into buffer
727 char* xmlbuffer = PageManager::LoadFileToBuffer(filename, ctx.zip);
728 if (!xmlbuffer)
729 return -1; // error already displayed by LoadFileToBuffer
730 ctx.xmlbuffers.push_back(xmlbuffer);
731
732 // parse XML
733 xml_document<>* doc = new xml_document<>();
734 doc->parse<0>(xmlbuffer);
735 ctx.xmldocs.push_back(doc);
736
737 xml_node<>* root = doc->first_node("recovery");
738 if (!root)
739 root = doc->first_node("install");
740 if (!root) {
741 LOGERR("Unknown root element in %s\n", filename.c_str());
742 return -1;
743 }
744
745 if (isMain) {
746 int rc = LoadDetails(ctx, root);
747 if (rc != 0)
748 return rc;
749 }
750
751 LOGINFO("Loading resources...\n");
752 xml_node<>* child = root->first_node("resources");
753 if (child)
754 mResources->LoadResources(child, ctx.zip, "theme");
755
756 LOGINFO("Loading variables...\n");
757 child = root->first_node("variables");
758 if (child)
759 LoadVariables(child);
760
761 LOGINFO("Loading mouse cursor...\n");
762 child = root->first_node("mousecursor");
763 if (child)
764 PageManager::LoadCursorData(child);
765
766 LOGINFO("Loading pages...\n");
767 child = root->first_node("templates");
768 if (child)
769 ctx.templates.push_back(child);
770
771 child = root->first_node("styles");
772 if (child)
773 ctx.styles.push_back(child);
774
775 // Load pages
776 child = root->first_node("pages");
777 if (child) {
778 if (LoadPages(ctx, child)) {
779 LOGERR("PageSet::Load returning -1\n");
780 return -1;
781 }
782 }
783
784 // process includes recursively
785 child = root->first_node("include");
786 if (child) {
787 xml_node<>* include = child->first_node("xmlfile");
788 while (include != NULL) {
789 xml_attribute<>* attr = include->first_attribute("name");
790 if (!attr) {
791 LOGERR("Skipping include/xmlfile with no name\n");
792 continue;
793 }
794
795 string filename = ctx.basepath + attr->value();
796 LOGINFO("Including file: %s...\n", filename.c_str());
797 int rc = Load(ctx, filename);
798 if (rc != 0)
799 return rc;
800
801 include = include->next_sibling("xmlfile");
802 }
803 }
804
805 return 0;
806}
807
808void PageSet::MakeEmergencyConsoleIfNeeded()
809{
810 if (mPages.empty()) {
811 mCurrentPage = new Page(NULL, NULL); // fallback console page
812 // TODO: since removal of non-TTF fonts, the emergency console doesn't work without a font, which might be missing too
813 mPages.push_back(mCurrentPage);
814 }
815}
816
Ethan Yonker8373cfe2017-09-08 06:50:54 -0500817int PageSet::LoadLanguage(char* languageFile, ZipWrap* package)
Ethan Yonker74db1572015-10-28 12:44:49 -0500818{
819 xml_document<> lang;
820 xml_node<>* parent;
821 xml_node<>* child;
822 std::string resource_source;
Ethan Yonker4adc33e2016-01-22 16:07:11 -0600823 int ret = 0;
Ethan Yonker74db1572015-10-28 12:44:49 -0500824
825 if (languageFile) {
826 printf("parsing languageFile\n");
827 lang.parse<0>(languageFile);
828 printf("parsing languageFile done\n");
829 } else {
830 return -1;
831 }
832
833 parent = lang.first_node("language");
834 if (!parent) {
835 LOGERR("Unable to locate language node in language file.\n");
836 lang.clear();
837 return -1;
838 }
839
840 child = parent->first_node("display");
841 if (child) {
842 DataManager::SetValue("tw_language_display", child->value());
843 resource_source = child->value();
844 } else {
845 LOGERR("language file does not have a display value set\n");
846 DataManager::SetValue("tw_language_display", "Not Set");
847 resource_source = languageFile;
848 }
849
850 child = parent->first_node("resources");
851 if (child)
852 mResources->LoadResources(child, package, resource_source);
853 else
Ethan Yonker4adc33e2016-01-22 16:07:11 -0600854 ret = -1;
855 DataManager::SetValue("tw_backup_name", gui_lookup("auto_generate", "(Auto Generate)"));
Ethan Yonker74db1572015-10-28 12:44:49 -0500856 lang.clear();
Ethan Yonker4adc33e2016-01-22 16:07:11 -0600857 return ret;
Ethan Yonker74db1572015-10-28 12:44:49 -0500858}
859
that6a894592016-03-13 17:51:28 +0100860int PageSet::LoadDetails(LoadingContext& ctx, xml_node<>* root)
Dees_Troy51a0e822012-09-05 15:24:24 -0400861{
that6a894592016-03-13 17:51:28 +0100862 xml_node<>* child = root->first_node("details");
Ethan Yonker63e414f2015-02-06 15:44:39 -0600863 if (child) {
Ethan Yonker1308d532016-01-14 22:21:49 -0600864 int theme_ver = 0;
865 xml_node<>* themeversion = child->first_node("themeversion");
866 if (themeversion && themeversion->value()) {
867 theme_ver = atoi(themeversion->value());
868 } else {
869 LOGINFO("No themeversion in theme.\n");
870 }
871 if (theme_ver != TW_THEME_VERSION) {
872 LOGINFO("theme version from xml: %i, expected %i\n", theme_ver, TW_THEME_VERSION);
that6a894592016-03-13 17:51:28 +0100873 if (ctx.zip) {
Ethan Yonker1308d532016-01-14 22:21:49 -0600874 gui_err("theme_ver_err=Custom theme version does not match TWRP version. Using stock theme.");
Ethan Yonker8e5692f2016-01-21 11:21:06 -0600875 return TW_THEME_VER_ERR;
Ethan Yonker1308d532016-01-14 22:21:49 -0600876 } else {
877 gui_print_color("warning", "Stock theme version does not match TWRP version.\n");
878 }
879 }
Ethan Yonker63e414f2015-02-06 15:44:39 -0600880 xml_node<>* resolution = child->first_node("resolution");
881 if (resolution) {
Ethan Yonker1308d532016-01-14 22:21:49 -0600882 LOGINFO("Checking resolution...\n");
Ethan Yonker63e414f2015-02-06 15:44:39 -0600883 xml_attribute<>* width_attr = resolution->first_attribute("width");
884 xml_attribute<>* height_attr = resolution->first_attribute("height");
885 xml_attribute<>* noscale_attr = resolution->first_attribute("noscaling");
886 if (width_attr && height_attr && !noscale_attr) {
887 int width = atoi(width_attr->value());
888 int height = atoi(height_attr->value());
889 int offx = 0, offy = 0;
890#ifdef TW_ROUND_SCREEN
891 xml_node<>* roundscreen = child->first_node("roundscreen");
892 if (roundscreen) {
893 LOGINFO("TW_ROUND_SCREEN := true, using round screen XML settings.\n");
894 xml_attribute<>* offx_attr = roundscreen->first_attribute("offset_x");
895 xml_attribute<>* offy_attr = roundscreen->first_attribute("offset_y");
896 if (offx_attr) {
897 offx = atoi(offx_attr->value());
898 }
899 if (offy_attr) {
900 offy = atoi(offy_attr->value());
901 }
902 }
903#endif
904 if (width != 0 && height != 0) {
James Christopher Adduonodcd1e442016-11-06 13:17:34 -0500905 float scale_w = (((float)gr_fb_width() + (float)tw_w_offset) - ((float)offx * 2.0)) / (float)width;
906 float scale_h = (((float)gr_fb_height() + (float)tw_h_offset) - ((float)offy * 2.0)) / (float)height;
Ethan Yonker63e414f2015-02-06 15:44:39 -0600907#ifdef TW_ROUND_SCREEN
James Christopher Adduonodcd1e442016-11-06 13:17:34 -0500908 float scale_off_w = ((float)gr_fb_width() + (float)tw_w_offset) / (float)width;
909 float scale_off_h = ((float)gr_fb_height() + (float)tw_h_offset) / (float)height;
Ethan Yonker63e414f2015-02-06 15:44:39 -0600910 tw_x_offset = offx * scale_off_w;
911 tw_y_offset = offy * scale_off_h;
912#endif
913 if (scale_w != 1 || scale_h != 1) {
James Christopher Adduonodcd1e442016-11-06 13:17:34 -0500914 LOGINFO("Scaling theme width %fx and height %fx, offsets x: %i y: %i w: %i h: %i\n",
915 scale_w, scale_h, tw_x_offset, tw_y_offset, tw_w_offset, tw_h_offset);
Ethan Yonker63e414f2015-02-06 15:44:39 -0600916 set_scale_values(scale_w, scale_h);
917 }
918 }
919 } else {
920 LOGINFO("XML does not contain width and height, no scaling will be applied\n");
921 }
922 } else {
923 LOGINFO("XML contains no resolution tag, no scaling will be applied.\n");
924 }
925 } else {
926 LOGINFO("XML contains no details tag, no scaling will be applied.\n");
927 }
Ethan Yonker74db1572015-10-28 12:44:49 -0500928
Ethan Yonker780cd392014-07-21 15:24:39 -0500929 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400930}
931
932int PageSet::SetPage(std::string page)
933{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200934 Page* tmp = FindPage(page);
935 if (tmp)
936 {
937 if (mCurrentPage) mCurrentPage->SetPageFocus(0);
938 mCurrentPage = tmp;
939 mCurrentPage->SetPageFocus(1);
940 mCurrentPage->NotifyVarChange("", "");
941 return 0;
942 }
943 else
944 {
945 LOGERR("Unable to locate page (%s)\n", page.c_str());
946 }
947 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400948}
949
950int PageSet::SetOverlay(Page* page)
951{
Ethan Yonker1c273312015-03-16 12:18:56 -0500952 if (page) {
953 if (mOverlays.size() >= 10) {
954 LOGERR("Too many overlays requested, max is 10.\n");
955 return -1;
956 }
Matt Mowerd411f8d2015-04-09 16:04:12 -0500957
958 std::vector<Page*>::iterator iter;
959 for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
960 if ((*iter)->GetName() == page->GetName()) {
961 mOverlays.erase(iter);
962 // SetOverlay() is (and should stay) the only function which
963 // adds to mOverlays. Then, each page can appear at most once.
964 break;
965 }
966 }
967
Ethan Yonker1c273312015-03-16 12:18:56 -0500968 page->SetPageFocus(1);
969 page->NotifyVarChange("", "");
970
971 if (!mOverlays.empty())
972 mOverlays.back()->SetPageFocus(0);
973
974 mOverlays.push_back(page);
975 } else {
976 if (!mOverlays.empty()) {
977 mOverlays.back()->SetPageFocus(0);
978 mOverlays.pop_back();
979 if (!mOverlays.empty())
980 mOverlays.back()->SetPageFocus(1);
981 else if (mCurrentPage)
982 mCurrentPage->SetPageFocus(1); // Just in case somehow the regular page lost focus, we'll set it again
983 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200984 }
985 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400986}
987
that74ac6062015-03-04 22:39:34 +0100988const ResourceManager* PageSet::GetResources()
Dees_Troy51a0e822012-09-05 15:24:24 -0400989{
that74ac6062015-03-04 22:39:34 +0100990 return mResources;
Dees_Troy51a0e822012-09-05 15:24:24 -0400991}
992
993Page* PageSet::FindPage(std::string name)
994{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200995 std::vector<Page*>::iterator iter;
Dees_Troy51a0e822012-09-05 15:24:24 -0400996
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200997 for (iter = mPages.begin(); iter != mPages.end(); iter++)
998 {
999 if (name == (*iter)->GetName())
1000 return (*iter);
1001 }
1002 return NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -04001003}
1004
1005int PageSet::LoadVariables(xml_node<>* vars)
1006{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001007 xml_node<>* child;
Vojtech Bocek81c29dc2013-12-07 23:02:09 +01001008 xml_attribute<> *name, *value, *persist;
1009 int p;
Dees_Troy51a0e822012-09-05 15:24:24 -04001010
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001011 child = vars->first_node("variable");
1012 while (child)
1013 {
Vojtech Bocek81c29dc2013-12-07 23:02:09 +01001014 name = child->first_attribute("name");
1015 value = child->first_attribute("value");
1016 persist = child->first_attribute("persist");
Matt Mowera8a89d12016-12-30 18:10:37 -06001017 if (name && value)
Vojtech Bocek81c29dc2013-12-07 23:02:09 +01001018 {
Ethan Yonker751a85e2014-12-12 16:59:10 -06001019 if (strcmp(name->value(), "tw_x_offset") == 0) {
1020 tw_x_offset = atoi(value->value());
1021 child = child->next_sibling("variable");
1022 continue;
1023 }
1024 if (strcmp(name->value(), "tw_y_offset") == 0) {
1025 tw_y_offset = atoi(value->value());
1026 child = child->next_sibling("variable");
1027 continue;
1028 }
James Christopher Adduonodcd1e442016-11-06 13:17:34 -05001029 if (strcmp(name->value(), "tw_w_offset") == 0) {
1030 tw_w_offset = atoi(value->value());
1031 child = child->next_sibling("variable");
1032 continue;
1033 }
1034 if (strcmp(name->value(), "tw_h_offset") == 0) {
1035 tw_h_offset = atoi(value->value());
1036 child = child->next_sibling("variable");
1037 continue;
1038 }
Vojtech Bocek81c29dc2013-12-07 23:02:09 +01001039 p = persist ? atoi(persist->value()) : 0;
Ethan Yonker96acb3d2014-08-05 09:20:30 -05001040 string temp = value->value();
1041 string valstr = gui_parse_text(temp);
1042
1043 if (valstr.find("+") != string::npos) {
1044 string val1str = valstr;
1045 val1str = val1str.substr(0, val1str.find('+'));
1046 string val2str = valstr;
1047 val2str = val2str.substr(val2str.find('+') + 1, string::npos);
1048 int val1 = atoi(val1str.c_str());
1049 int val2 = atoi(val2str.c_str());
1050 int val = val1 + val2;
1051
1052 DataManager::SetValue(name->value(), val, p);
1053 } else if (valstr.find("-") != string::npos) {
1054 string val1str = valstr;
1055 val1str = val1str.substr(0, val1str.find('-'));
1056 string val2str = valstr;
1057 val2str = val2str.substr(val2str.find('-') + 1, string::npos);
1058 int val1 = atoi(val1str.c_str());
1059 int val2 = atoi(val2str.c_str());
1060 int val = val1 - val2;
1061
1062 DataManager::SetValue(name->value(), val, p);
1063 } else {
1064 DataManager::SetValue(name->value(), valstr, p);
1065 }
Vojtech Bocek81c29dc2013-12-07 23:02:09 +01001066 }
Dees_Troy51a0e822012-09-05 15:24:24 -04001067
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001068 child = child->next_sibling("variable");
1069 }
1070 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -04001071}
1072
that6a894592016-03-13 17:51:28 +01001073int PageSet::LoadPages(LoadingContext& ctx, xml_node<>* pages)
Dees_Troy51a0e822012-09-05 15:24:24 -04001074{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001075 xml_node<>* child;
Dees_Troy51a0e822012-09-05 15:24:24 -04001076
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001077 if (!pages)
1078 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -04001079
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001080 child = pages->first_node("page");
1081 while (child != NULL)
1082 {
that6a894592016-03-13 17:51:28 +01001083 Page* page = new Page(child, &ctx.templates);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001084 if (page->GetName().empty())
1085 {
1086 LOGERR("Unable to process load page\n");
1087 delete page;
1088 }
1089 else
1090 {
1091 mPages.push_back(page);
1092 }
1093 child = child->next_sibling("page");
1094 }
1095 if (mPages.size() > 0)
1096 return 0;
1097 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -04001098}
1099
1100int PageSet::IsCurrentPage(Page* page)
1101{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001102 return ((mCurrentPage && mCurrentPage == page) ? 1 : 0);
Dees_Troy51a0e822012-09-05 15:24:24 -04001103}
1104
that10ae24f2015-12-26 20:53:51 +01001105std::string PageSet::GetCurrentPage() const
1106{
1107 return mCurrentPage ? mCurrentPage->GetName() : "";
1108}
1109
Dees_Troy51a0e822012-09-05 15:24:24 -04001110int PageSet::Render(void)
1111{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001112 int ret;
Dees_Troy51a0e822012-09-05 15:24:24 -04001113
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001114 ret = (mCurrentPage ? mCurrentPage->Render() : -1);
1115 if (ret < 0)
1116 return ret;
Ethan Yonker1c273312015-03-16 12:18:56 -05001117
1118 std::vector<Page*>::iterator iter;
1119
1120 for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
1121 ret = ((*iter) ? (*iter)->Render() : -1);
1122 if (ret < 0)
1123 return ret;
1124 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001125 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -04001126}
1127
1128int PageSet::Update(void)
1129{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001130 int ret;
Dees_Troy51a0e822012-09-05 15:24:24 -04001131
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001132 ret = (mCurrentPage ? mCurrentPage->Update() : -1);
1133 if (ret < 0 || ret > 1)
1134 return ret;
Ethan Yonker1c273312015-03-16 12:18:56 -05001135
1136 std::vector<Page*>::iterator iter;
1137
1138 for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) {
1139 ret = ((*iter) ? (*iter)->Update() : -1);
1140 if (ret < 0)
1141 return ret;
1142 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001143 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -04001144}
1145
1146int PageSet::NotifyTouch(TOUCH_STATE state, int x, int y)
1147{
Ethan Yonker1c273312015-03-16 12:18:56 -05001148 if (!mOverlays.empty())
1149 return mOverlays.back()->NotifyTouch(state, x, y);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001150
1151 return (mCurrentPage ? mCurrentPage->NotifyTouch(state, x, y) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001152}
1153
Vojtech Bocek0b7fe502014-03-13 17:36:52 +01001154int PageSet::NotifyKey(int key, bool down)
Dees_Troy51a0e822012-09-05 15:24:24 -04001155{
Ethan Yonker1c273312015-03-16 12:18:56 -05001156 if (!mOverlays.empty())
1157 return mOverlays.back()->NotifyKey(key, down);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001158
Vojtech Bocek0b7fe502014-03-13 17:36:52 +01001159 return (mCurrentPage ? mCurrentPage->NotifyKey(key, down) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001160}
1161
that8834a0f2016-01-05 23:29:30 +01001162int PageSet::NotifyCharInput(int ch)
Dees_Troy51a0e822012-09-05 15:24:24 -04001163{
Ethan Yonker1c273312015-03-16 12:18:56 -05001164 if (!mOverlays.empty())
that8834a0f2016-01-05 23:29:30 +01001165 return mOverlays.back()->NotifyCharInput(ch);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001166
that8834a0f2016-01-05 23:29:30 +01001167 return (mCurrentPage ? mCurrentPage->NotifyCharInput(ch) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001168}
1169
1170int PageSet::SetKeyBoardFocus(int inFocus)
1171{
Ethan Yonker1c273312015-03-16 12:18:56 -05001172 if (!mOverlays.empty())
1173 return mOverlays.back()->SetKeyBoardFocus(inFocus);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001174
1175 return (mCurrentPage ? mCurrentPage->SetKeyBoardFocus(inFocus) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001176}
1177
1178int PageSet::NotifyVarChange(std::string varName, std::string value)
1179{
Ethan Yonker1c273312015-03-16 12:18:56 -05001180 std::vector<Page*>::iterator iter;
1181
1182 for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++)
1183 (*iter)->NotifyVarChange(varName, value);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001184
1185 return (mCurrentPage ? mCurrentPage->NotifyVarChange(varName, value) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001186}
1187
Ethan Yonker74db1572015-10-28 12:44:49 -05001188void PageSet::AddStringResource(std::string resource_source, std::string resource_name, std::string value)
1189{
1190 mResources->AddStringResource(resource_source, resource_name, value);
1191}
1192
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001193char* PageManager::LoadFileToBuffer(std::string filename, ZipWrap* package) {
Ethan Yonker561c58d2015-10-05 08:48:22 -05001194 size_t len;
1195 char* buffer = NULL;
1196
1197 if (!package) {
1198 // We can try to load the XML directly...
1199 LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' directly\n", filename.c_str());
1200 struct stat st;
Matt Mowera8a89d12016-12-30 18:10:37 -06001201 if (stat(filename.c_str(),&st) != 0) {
Ethan Yonker561c58d2015-10-05 08:48:22 -05001202 // This isn't always an error, sometimes we request files that don't exist.
1203 return NULL;
1204 }
1205
1206 len = (size_t)st.st_size;
1207
1208 buffer = (char*) malloc(len + 1);
1209 if (!buffer) {
1210 LOGERR("PageManager::LoadFileToBuffer failed to malloc\n");
1211 return NULL;
1212 }
1213
1214 int fd = open(filename.c_str(), O_RDONLY);
1215 if (fd == -1) {
1216 LOGERR("PageManager::LoadFileToBuffer failed to open '%s' - (%s)\n", filename.c_str(), strerror(errno));
1217 free(buffer);
1218 return NULL;
1219 }
1220
1221 if (read(fd, buffer, len) < 0) {
1222 LOGERR("PageManager::LoadFileToBuffer failed to read '%s' - (%s)\n", filename.c_str(), strerror(errno));
1223 free(buffer);
1224 close(fd);
1225 return NULL;
1226 }
1227 close(fd);
1228 } else {
1229 LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' from zip\n", filename.c_str());
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001230 if (!package->EntryExists(filename)) {
Ethan Yonker561c58d2015-10-05 08:48:22 -05001231 LOGERR("Unable to locate '%s' in zip file\n", filename.c_str());
1232 return NULL;
1233 }
1234
1235 // Allocate the buffer for the file
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001236 len = package->GetUncompressedSize(filename);
Ethan Yonker561c58d2015-10-05 08:48:22 -05001237 buffer = (char*) malloc(len + 1);
1238 if (!buffer)
1239 return NULL;
1240
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001241 if (!package->ExtractToBuffer(filename, (unsigned char*) buffer)) {
Ethan Yonker561c58d2015-10-05 08:48:22 -05001242 LOGERR("Unable to extract '%s'\n", filename.c_str());
1243 free(buffer);
1244 return NULL;
1245 }
1246 }
1247 // NULL-terminate the string
1248 buffer[len] = 0x00;
1249 return buffer;
1250}
1251
Ethan Yonker74db1572015-10-28 12:44:49 -05001252void PageManager::LoadLanguageListDir(string dir) {
1253 if (!TWFunc::Path_Exists(dir)) {
1254 LOGERR("LoadLanguageListDir '%s' path not found\n", dir.c_str());
1255 return;
1256 }
1257
1258 DIR *d = opendir(dir.c_str());
1259 struct dirent *p;
1260
1261 if (d == NULL) {
1262 LOGERR("LoadLanguageListDir error opening dir: '%s', %s\n", dir.c_str(), strerror(errno));
1263 return;
1264 }
1265
1266 while ((p = readdir(d))) {
1267 if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..") || strlen(p->d_name) < 5)
1268 continue;
1269
1270 string file = p->d_name;
1271 if (file.substr(strlen(p->d_name) - 4) != ".xml")
1272 continue;
1273 string path = dir + p->d_name;
1274 string file_no_extn = file.substr(0, strlen(p->d_name) - 4);
1275 struct language_struct language_entry;
1276 language_entry.filename = file_no_extn;
1277 char* xmlFile = PageManager::LoadFileToBuffer(dir + p->d_name, NULL);
1278 if (xmlFile == NULL) {
1279 LOGERR("LoadLanguageListDir unable to load '%s'\n", language_entry.filename.c_str());
1280 continue;
1281 }
1282 xml_document<> *doc = new xml_document<>();
1283 doc->parse<0>(xmlFile);
1284
1285 xml_node<>* parent = doc->first_node("language");
1286 if (!parent) {
1287 LOGERR("Invalid language XML file '%s'\n", language_entry.filename.c_str());
1288 } else {
1289 xml_node<>* child = parent->first_node("display");
1290 if (child) {
1291 language_entry.displayvalue = child->value();
1292 } else {
1293 LOGERR("No display value for '%s'\n", language_entry.filename.c_str());
1294 language_entry.displayvalue = language_entry.filename;
1295 }
1296 Language_List.push_back(language_entry);
1297 }
1298 doc->clear();
1299 delete doc;
1300 free(xmlFile);
1301 }
1302 closedir(d);
1303}
1304
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001305void PageManager::LoadLanguageList(ZipWrap* package) {
Ethan Yonker74db1572015-10-28 12:44:49 -05001306 Language_List.clear();
1307 if (TWFunc::Path_Exists(TWRES "customlanguages"))
1308 TWFunc::removeDir(TWRES "customlanguages", true);
1309 if (package) {
1310 TWFunc::Recursive_Mkdir(TWRES "customlanguages");
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001311 package->ExtractRecursive("languages", TWRES "customlanguages/");
Ethan Yonker74db1572015-10-28 12:44:49 -05001312 LoadLanguageListDir(TWRES "customlanguages/");
1313 } else {
1314 LoadLanguageListDir(TWRES "languages/");
1315 }
Xuefercac6ace2016-02-01 02:28:55 +08001316
1317 std::sort(Language_List.begin(), Language_List.end());
Ethan Yonker74db1572015-10-28 12:44:49 -05001318}
1319
1320void PageManager::LoadLanguage(string filename) {
1321 string actual_filename;
1322 if (TWFunc::Path_Exists(TWRES "customlanguages/" + filename + ".xml"))
1323 actual_filename = TWRES "customlanguages/" + filename + ".xml";
1324 else
1325 actual_filename = TWRES "languages/" + filename + ".xml";
1326 char* xmlFile = PageManager::LoadFileToBuffer(actual_filename, NULL);
1327 if (xmlFile == NULL)
1328 LOGERR("Unable to load '%s'\n", actual_filename.c_str());
1329 else {
1330 mCurrentSet->LoadLanguage(xmlFile, NULL);
1331 free(xmlFile);
1332 }
1333 PartitionManager.Translate_Partition_Display_Names();
1334}
1335
Dees_Troy51a0e822012-09-05 15:24:24 -04001336int PageManager::LoadPackage(std::string name, std::string package, std::string startpage)
1337{
that6a894592016-03-13 17:51:28 +01001338 std::string mainxmlfilename = package;
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001339 ZipWrap zip;
Ethan Yonker74db1572015-10-28 12:44:49 -05001340 char* languageFile = NULL;
Ethan Yonker8e5692f2016-01-21 11:21:06 -06001341 char* baseLanguageFile = NULL;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001342 PageSet* pageSet = NULL;
1343 int ret;
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001344 MemMapping map;
Dees_Troy51a0e822012-09-05 15:24:24 -04001345
Ethan Yonkere0f1f3b2015-10-27 09:49:01 -05001346 mReloadTheme = false;
1347 mStartPage = startpage;
1348
that6a894592016-03-13 17:51:28 +01001349 // init the loading context
1350 LoadingContext ctx;
1351
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001352 // Open the XML file
1353 LOGINFO("Loading package: %s (%s)\n", name.c_str(), package.c_str());
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001354 if (package.size() > 4 && package.substr(package.size() - 4) != ".zip")
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001355 {
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001356 LOGINFO("Load XML directly\n");
Ethan Yonker751a85e2014-12-12 16:59:10 -06001357 tw_x_offset = TW_X_OFFSET;
1358 tw_y_offset = TW_Y_OFFSET;
James Christopher Adduonodcd1e442016-11-06 13:17:34 -05001359 tw_w_offset = TW_W_OFFSET;
1360 tw_h_offset = TW_H_OFFSET;
Ethan Yonker4f74d142016-03-31 08:10:37 -05001361 if (name != "splash") {
1362 LoadLanguageList(NULL);
1363 languageFile = LoadFileToBuffer(TWRES "languages/en.xml", NULL);
1364 }
that6a894592016-03-13 17:51:28 +01001365 ctx.basepath = TWRES;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001366 }
1367 else
1368 {
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001369 LOGINFO("Loading zip theme\n");
Ethan Yonker751a85e2014-12-12 16:59:10 -06001370 tw_x_offset = 0;
1371 tw_y_offset = 0;
James Christopher Adduonodcd1e442016-11-06 13:17:34 -05001372 tw_w_offset = 0;
1373 tw_h_offset = 0;
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001374 if (!TWFunc::Path_Exists(package))
1375 return -1;
Ethan Yonkerecbd3e82017-12-14 14:43:59 -06001376#ifdef USE_MINZIP
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001377 if (sysMapFile(package.c_str(), &map) != 0) {
Ethan Yonkerecbd3e82017-12-14 14:43:59 -06001378#else
1379 if (!map.MapFile(package)) {
1380#endif
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001381 LOGERR("Failed to map '%s'\n", package.c_str());
Ethan Yonker561c58d2015-10-05 08:48:22 -05001382 goto error;
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001383 }
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001384 if (!zip.Open(package.c_str(), &map)) {
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001385 LOGERR("Unable to open zip archive '%s'\n", package.c_str());
Ethan Yonkerecbd3e82017-12-14 14:43:59 -06001386#ifdef USE_MINZIP
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001387 sysReleaseMap(&map);
Ethan Yonkerecbd3e82017-12-14 14:43:59 -06001388#endif
Ethan Yonker561c58d2015-10-05 08:48:22 -05001389 goto error;
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001390 }
that6a894592016-03-13 17:51:28 +01001391 ctx.zip = &zip;
1392 mainxmlfilename = "ui.xml";
1393 LoadLanguageList(ctx.zip);
1394 languageFile = LoadFileToBuffer("languages/en.xml", ctx.zip);
Ethan Yonker8e5692f2016-01-21 11:21:06 -06001395 baseLanguageFile = LoadFileToBuffer(TWRES "languages/en.xml", NULL);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001396 }
Dees_Troy51a0e822012-09-05 15:24:24 -04001397
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001398 // Before loading, mCurrentSet must be the loading package so we can find resources
1399 pageSet = mCurrentSet;
that6a894592016-03-13 17:51:28 +01001400 mCurrentSet = new PageSet();
1401
1402 if (baseLanguageFile) {
1403 mCurrentSet->LoadLanguage(baseLanguageFile, NULL);
1404 free(baseLanguageFile);
Ethan Yonker74db1572015-10-28 12:44:49 -05001405 }
that6a894592016-03-13 17:51:28 +01001406
1407 if (languageFile) {
1408 mCurrentSet->LoadLanguage(languageFile, ctx.zip);
1409 free(languageFile);
1410 }
1411
1412 // Load and parse the XML and all includes
1413 currentLoadingContext = &ctx; // required to find styles
1414 ret = mCurrentSet->Load(ctx, mainxmlfilename);
1415 currentLoadingContext = NULL;
1416
Ethan Yonker8e5692f2016-01-21 11:21:06 -06001417 if (ret == 0) {
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001418 mCurrentSet->SetPage(startpage);
1419 mPageSets.insert(std::pair<std::string, PageSet*>(name, mCurrentSet));
Ethan Yonker8e5692f2016-01-21 11:21:06 -06001420 } else {
1421 if (ret != TW_THEME_VER_ERR)
1422 LOGERR("Package %s failed to load.\n", name.c_str());
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001423 }
Matt Mowerfb1c4ff2014-04-16 13:43:36 -05001424
that6a894592016-03-13 17:51:28 +01001425 // reset to previous pageset
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001426 mCurrentSet = pageSet;
1427
that6a894592016-03-13 17:51:28 +01001428 if (ctx.zip) {
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001429 ctx.zip->Close();
Ethan Yonkerecbd3e82017-12-14 14:43:59 -06001430#ifdef USE_MINZIP
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001431 sysReleaseMap(&map);
Ethan Yonkerecbd3e82017-12-14 14:43:59 -06001432#endif
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001433 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001434 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -04001435
1436error:
Ethan Yonker561c58d2015-10-05 08:48:22 -05001437 // Sometimes we get here without a real error
that6a894592016-03-13 17:51:28 +01001438 if (ctx.zip) {
Ethan Yonker8373cfe2017-09-08 06:50:54 -05001439 ctx.zip->Close();
Ethan Yonkerecbd3e82017-12-14 14:43:59 -06001440#ifdef USE_MINZIP
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001441 sysReleaseMap(&map);
Ethan Yonkerecbd3e82017-12-14 14:43:59 -06001442#endif
Ethan Yonkera2dc2f22014-11-08 08:13:40 -06001443 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001444 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -04001445}
1446
1447PageSet* PageManager::FindPackage(std::string name)
1448{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001449 std::map<std::string, PageSet*>::iterator iter;
Dees_Troy51a0e822012-09-05 15:24:24 -04001450
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001451 iter = mPageSets.find(name);
1452 if (iter != mPageSets.end())
1453 return (*iter).second;
1454
1455 LOGERR("Unable to locate package %s\n", name.c_str());
1456 return NULL;
Dees_Troy51a0e822012-09-05 15:24:24 -04001457}
1458
1459PageSet* PageManager::SelectPackage(std::string name)
1460{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001461 LOGINFO("Switching packages (%s)\n", name.c_str());
1462 PageSet* tmp;
Dees_Troy51a0e822012-09-05 15:24:24 -04001463
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001464 tmp = FindPackage(name);
1465 if (tmp)
Vojtech Bocek07220562014-02-08 02:05:33 +01001466 {
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001467 mCurrentSet = tmp;
that6a894592016-03-13 17:51:28 +01001468 mCurrentSet->MakeEmergencyConsoleIfNeeded();
Vojtech Bocek07220562014-02-08 02:05:33 +01001469 mCurrentSet->NotifyVarChange("", "");
1470 }
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001471 else
1472 LOGERR("Unable to find package.\n");
Dees_Troy51a0e822012-09-05 15:24:24 -04001473
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001474 return mCurrentSet;
Dees_Troy51a0e822012-09-05 15:24:24 -04001475}
1476
1477int PageManager::ReloadPackage(std::string name, std::string package)
1478{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001479 std::map<std::string, PageSet*>::iterator iter;
Dees_Troy51a0e822012-09-05 15:24:24 -04001480
Ethan Yonkere0f1f3b2015-10-27 09:49:01 -05001481 mReloadTheme = false;
1482
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001483 iter = mPageSets.find(name);
1484 if (iter == mPageSets.end())
1485 return -1;
Dees_Troy51a0e822012-09-05 15:24:24 -04001486
Matt Mowera8a89d12016-12-30 18:10:37 -06001487 if (mMouseCursor)
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001488 mMouseCursor->ResetData(gr_fb_width(), gr_fb_height());
1489
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001490 PageSet* set = (*iter).second;
1491 mPageSets.erase(iter);
Dees_Troy51a0e822012-09-05 15:24:24 -04001492
Ethan Yonkere0f1f3b2015-10-27 09:49:01 -05001493 if (LoadPackage(name, package, mStartPage) != 0)
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001494 {
Ethan Yonker74db1572015-10-28 12:44:49 -05001495 LOGINFO("Failed to load package '%s'.\n", package.c_str());
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001496 mPageSets.insert(std::pair<std::string, PageSet*>(name, set));
1497 return -1;
1498 }
1499 if (mCurrentSet == set)
1500 SelectPackage(name);
1501 delete set;
Ethan Yonker74db1572015-10-28 12:44:49 -05001502 GUIConsole::Translate_Now();
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001503 return 0;
Dees_Troy51a0e822012-09-05 15:24:24 -04001504}
1505
1506void PageManager::ReleasePackage(std::string name)
1507{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001508 std::map<std::string, PageSet*>::iterator iter;
Dees_Troy51a0e822012-09-05 15:24:24 -04001509
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001510 iter = mPageSets.find(name);
1511 if (iter == mPageSets.end())
1512 return;
Dees_Troy51a0e822012-09-05 15:24:24 -04001513
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001514 PageSet* set = (*iter).second;
1515 mPageSets.erase(iter);
1516 delete set;
that235c6482016-01-24 21:59:00 +01001517 if (set == mCurrentSet)
1518 mCurrentSet = NULL;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001519 return;
Dees_Troy51a0e822012-09-05 15:24:24 -04001520}
1521
Ethan Yonkere0f1f3b2015-10-27 09:49:01 -05001522int PageManager::RunReload() {
1523 int ret_val = 0;
1524 std::string theme_path;
1525
1526 if (!mReloadTheme)
1527 return 0;
1528
1529 mReloadTheme = false;
1530 theme_path = DataManager::GetSettingsStoragePath();
1531 if (PartitionManager.Mount_By_Path(theme_path.c_str(), 1) < 0) {
1532 LOGERR("Unable to mount %s during gui_reload_theme function.\n", theme_path.c_str());
1533 ret_val = 1;
1534 }
1535
1536 theme_path += "/TWRP/theme/ui.zip";
1537 if (ret_val != 0 || ReloadPackage("TWRP", theme_path) != 0)
1538 {
1539 // Loading the custom theme failed - try loading the stock theme
1540 LOGINFO("Attempting to reload stock theme...\n");
1541 if (ReloadPackage("TWRP", TWRES "ui.xml"))
1542 {
1543 LOGERR("Failed to load base packages.\n");
1544 ret_val = 1;
1545 }
1546 }
Ethan Yonker74db1572015-10-28 12:44:49 -05001547 if (ret_val == 0) {
1548 if (DataManager::GetStrValue("tw_language") != "en.xml") {
1549 LOGINFO("Loading language '%s'\n", DataManager::GetStrValue("tw_language").c_str());
1550 LoadLanguage(DataManager::GetStrValue("tw_language"));
1551 }
1552 }
1553
1554 // This makes the console re-translate
thata9dd9f02017-02-23 23:08:56 +01001555 GUIConsole::Clear_For_Retranslation();
Ethan Yonker74db1572015-10-28 12:44:49 -05001556
Ethan Yonkere0f1f3b2015-10-27 09:49:01 -05001557 return ret_val;
1558}
1559
1560void PageManager::RequestReload() {
1561 mReloadTheme = true;
1562}
1563
Ethan Yonkerafde0982016-01-23 08:55:35 -06001564void PageManager::SetStartPage(const std::string& page_name) {
1565 mStartPage = page_name;
1566}
1567
Dees_Troy51a0e822012-09-05 15:24:24 -04001568int PageManager::ChangePage(std::string name)
1569{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001570 DataManager::SetValue("tw_operation_state", 0);
1571 int ret = (mCurrentSet ? mCurrentSet->SetPage(name) : -1);
1572 return ret;
Dees_Troy51a0e822012-09-05 15:24:24 -04001573}
1574
that10ae24f2015-12-26 20:53:51 +01001575std::string PageManager::GetCurrentPage()
1576{
1577 return mCurrentSet ? mCurrentSet->GetCurrentPage() : "";
1578}
1579
Dees_Troy51a0e822012-09-05 15:24:24 -04001580int PageManager::ChangeOverlay(std::string name)
1581{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001582 if (name.empty())
1583 return mCurrentSet->SetOverlay(NULL);
1584 else
1585 {
Ethan Yonker1308d532016-01-14 22:21:49 -06001586 Page* page = mCurrentSet ? mCurrentSet->FindPage(name) : NULL;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001587 return mCurrentSet->SetOverlay(page);
1588 }
Dees_Troy51a0e822012-09-05 15:24:24 -04001589}
1590
that74ac6062015-03-04 22:39:34 +01001591const ResourceManager* PageManager::GetResources()
Dees_Troy51a0e822012-09-05 15:24:24 -04001592{
that74ac6062015-03-04 22:39:34 +01001593 return (mCurrentSet ? mCurrentSet->GetResources() : NULL);
Dees_Troy51a0e822012-09-05 15:24:24 -04001594}
1595
Dees_Troy51a0e822012-09-05 15:24:24 -04001596int PageManager::IsCurrentPage(Page* page)
1597{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001598 return (mCurrentSet ? mCurrentSet->IsCurrentPage(page) : 0);
Dees_Troy51a0e822012-09-05 15:24:24 -04001599}
1600
1601int PageManager::Render(void)
1602{
Matt Mowera8a89d12016-12-30 18:10:37 -06001603 if (blankTimer.isScreenOff())
Ethan Yonkere0f1f3b2015-10-27 09:49:01 -05001604 return 0;
1605
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001606 int res = (mCurrentSet ? mCurrentSet->Render() : -1);
Matt Mowera8a89d12016-12-30 18:10:37 -06001607 if (mMouseCursor)
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001608 mMouseCursor->Render();
1609 return res;
1610}
1611
Vojtech Bocek0b7fe502014-03-13 17:36:52 +01001612HardwareKeyboard *PageManager::GetHardwareKeyboard()
1613{
Matt Mowera8a89d12016-12-30 18:10:37 -06001614 if (!mHardwareKeyboard)
Vojtech Bocek0b7fe502014-03-13 17:36:52 +01001615 mHardwareKeyboard = new HardwareKeyboard();
1616 return mHardwareKeyboard;
1617}
1618
Ethan Yonker21ff02a2015-02-18 14:35:00 -06001619xml_node<>* PageManager::FindStyle(std::string name)
1620{
that6a894592016-03-13 17:51:28 +01001621 if (!currentLoadingContext)
1622 {
1623 LOGERR("FindStyle works only while loading a theme.\n");
1624 return NULL;
1625 }
1626
1627 for (std::vector<xml_node<>*>::iterator itr = currentLoadingContext->styles.begin(); itr != currentLoadingContext->styles.end(); itr++) {
Ethan Yonker21ff02a2015-02-18 14:35:00 -06001628 xml_node<>* node = (*itr)->first_node("style");
1629
1630 while (node) {
1631 if (!node->first_attribute("name"))
1632 continue;
1633
1634 if (name == node->first_attribute("name")->value())
1635 return node;
1636 node = node->next_sibling("style");
1637 }
1638 }
1639 return NULL;
1640}
1641
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001642MouseCursor *PageManager::GetMouseCursor()
1643{
Matt Mowera8a89d12016-12-30 18:10:37 -06001644 if (!mMouseCursor)
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001645 mMouseCursor = new MouseCursor(gr_fb_width(), gr_fb_height());
1646 return mMouseCursor;
1647}
1648
1649void PageManager::LoadCursorData(xml_node<>* node)
1650{
Matt Mowera8a89d12016-12-30 18:10:37 -06001651 if (!mMouseCursor)
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001652 mMouseCursor = new MouseCursor(gr_fb_width(), gr_fb_height());
1653
1654 mMouseCursor->LoadData(node);
Dees_Troy51a0e822012-09-05 15:24:24 -04001655}
1656
1657int PageManager::Update(void)
1658{
Matt Mowera8a89d12016-12-30 18:10:37 -06001659 if (blankTimer.isScreenOff())
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001660 return 0;
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001661
Ethan Yonkere0f1f3b2015-10-27 09:49:01 -05001662 if (RunReload())
1663 return -2;
1664
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001665 int res = (mCurrentSet ? mCurrentSet->Update() : -1);
1666
Matt Mowera8a89d12016-12-30 18:10:37 -06001667 if (mMouseCursor)
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001668 {
1669 int c_res = mMouseCursor->Update();
Matt Mowera8a89d12016-12-30 18:10:37 -06001670 if (c_res > res)
Vojtech Bocek1fc30fc2014-01-29 18:37:19 +01001671 res = c_res;
1672 }
1673 return res;
Dees_Troy51a0e822012-09-05 15:24:24 -04001674}
1675
1676int PageManager::NotifyTouch(TOUCH_STATE state, int x, int y)
1677{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001678 return (mCurrentSet ? mCurrentSet->NotifyTouch(state, x, y) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001679}
1680
Vojtech Bocek0b7fe502014-03-13 17:36:52 +01001681int PageManager::NotifyKey(int key, bool down)
Dees_Troy51a0e822012-09-05 15:24:24 -04001682{
Vojtech Bocek0b7fe502014-03-13 17:36:52 +01001683 return (mCurrentSet ? mCurrentSet->NotifyKey(key, down) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001684}
1685
that8834a0f2016-01-05 23:29:30 +01001686int PageManager::NotifyCharInput(int ch)
Dees_Troy51a0e822012-09-05 15:24:24 -04001687{
that8834a0f2016-01-05 23:29:30 +01001688 return (mCurrentSet ? mCurrentSet->NotifyCharInput(ch) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001689}
1690
1691int PageManager::SetKeyBoardFocus(int inFocus)
1692{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001693 return (mCurrentSet ? mCurrentSet->SetKeyBoardFocus(inFocus) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001694}
1695
1696int PageManager::NotifyVarChange(std::string varName, std::string value)
1697{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001698 return (mCurrentSet ? mCurrentSet->NotifyVarChange(varName, value) : -1);
Dees_Troy51a0e822012-09-05 15:24:24 -04001699}
1700
Ethan Yonker74db1572015-10-28 12:44:49 -05001701void PageManager::AddStringResource(std::string resource_source, std::string resource_name, std::string value)
1702{
1703 if (mCurrentSet)
1704 mCurrentSet->AddStringResource(resource_source, resource_name, value);
1705}
1706
Dees_Troy51a0e822012-09-05 15:24:24 -04001707extern "C" void gui_notifyVarChange(const char *name, const char* value)
1708{
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001709 if (!gGuiRunning)
1710 return;
Dees_Troy51a0e822012-09-05 15:24:24 -04001711
Vojtech Bocekfafb0c52013-07-25 22:53:02 +02001712 PageManager::NotifyVarChange(name, value);
Dees_Troy51a0e822012-09-05 15:24:24 -04001713}