| /* |
| Copyright 2013 bigbiff/Dees_Troy TeamWin |
| This file is part of TWRP/TeamWin Recovery Project. |
| |
| TWRP is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| TWRP is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with TWRP. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| // pages.cpp - Source to manage GUI base objects |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <fcntl.h> |
| #include <sys/reboot.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/mman.h> |
| #include <sys/types.h> |
| #include <sys/ioctl.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <dirent.h> |
| #include "../twrp-functions.hpp" |
| #include "../partitions.hpp" |
| |
| #include <string> |
| #include <algorithm> |
| |
| |
| #include <ziparchive/zip_archive.h> |
| #include "ZipUtil.h" |
| |
| extern "C" { |
| #include "../twcommon.h" |
| #include "gui.h" |
| } |
| #include "minuitwrp/minui.h" |
| |
| #include "rapidxml.hpp" |
| #include "objects.hpp" |
| #include "blanktimer.hpp" |
| |
| #include "../variables.h" |
| |
| #define TW_THEME_VER_ERR -2 |
| |
| extern int gGuiRunning; |
| GUITerminal* term = NULL; |
| |
| std::map<std::string, PageSet*> PageManager::mPageSets; |
| PageSet* PageManager::mCurrentSet; |
| MouseCursor *PageManager::mMouseCursor = NULL; |
| HardwareKeyboard *PageManager::mHardwareKeyboard = NULL; |
| bool PageManager::mReloadTheme = false; |
| std::string PageManager::mStartPage = "main"; |
| std::vector<language_struct> Language_List; |
| |
| int tw_x_offset = 0; |
| int tw_y_offset = 0; |
| int tw_w_offset = 0; |
| int tw_h_offset = 0; |
| |
| // Helper routine to convert a string to a color declaration |
| int ConvertStrToColor(std::string str, COLOR* color) |
| { |
| // Set the default, solid black |
| memset(color, 0, sizeof(COLOR)); |
| color->alpha = 255; |
| |
| // Translate variables |
| DataManager::GetValue(str, str); |
| |
| // Look for some defaults |
| if (str == "black") return 0; |
| else if (str == "white") { color->red = color->green = color->blue = 255; return 0; } |
| else if (str == "red") { color->red = 255; return 0; } |
| else if (str == "green") { color->green = 255; return 0; } |
| else if (str == "blue") { color->blue = 255; return 0; } |
| |
| // At this point, we require an RGB(A) color |
| if (str[0] != '#') |
| return -1; |
| |
| str.erase(0, 1); |
| |
| int result; |
| if (str.size() >= 8) { |
| // We have alpha channel |
| string alpha = str.substr(6, 2); |
| result = strtol(alpha.c_str(), NULL, 16); |
| color->alpha = result & 0x000000FF; |
| str.resize(6); |
| result = strtol(str.c_str(), NULL, 16); |
| color->red = (result >> 16) & 0x000000FF; |
| color->green = (result >> 8) & 0x000000FF; |
| color->blue = result & 0x000000FF; |
| } else { |
| result = strtol(str.c_str(), NULL, 16); |
| color->red = (result >> 16) & 0x000000FF; |
| color->green = (result >> 8) & 0x000000FF; |
| color->blue = result & 0x000000FF; |
| } |
| return 0; |
| } |
| |
| // Helper APIs |
| xml_node<>* FindNode(xml_node<>* parent, const char* nodename, int depth /* = 0 */) |
| { |
| if (!parent) |
| return NULL; |
| |
| xml_node<>* child = parent->first_node(nodename); |
| if (child) |
| return child; |
| |
| if (depth == 10) { |
| LOGERR("Too many style loops detected.\n"); |
| return NULL; |
| } |
| |
| xml_node<>* style = parent->first_node("style"); |
| if (style) { |
| while (style) { |
| if (!style->first_attribute("name")) { |
| LOGERR("No name given for style.\n"); |
| continue; |
| } else { |
| std::string name = style->first_attribute("name")->value(); |
| xml_node<>* node = PageManager::FindStyle(name); |
| |
| if (node) { |
| // We found the style that was named |
| xml_node<>* stylenode = FindNode(node, nodename, depth + 1); |
| if (stylenode) |
| return stylenode; |
| } |
| } |
| style = style->next_sibling("style"); |
| } |
| } else { |
| // Search for stylename in the parent node <object type="foo" style="foo2"> |
| xml_attribute<>* attr = parent->first_attribute("style"); |
| // If no style is found anywhere else and the node wasn't found in the object itself |
| // as a special case we will search for a style that uses the same style name as the |
| // object type, so <object type="button"> would search for a style named "button" |
| if (!attr) |
| attr = parent->first_attribute("type"); |
| // if there's no attribute type, the object type must be the element name |
| std::string stylename = attr ? attr->value() : parent->name(); |
| xml_node<>* node = PageManager::FindStyle(stylename); |
| if (node) { |
| xml_node<>* stylenode = FindNode(node, nodename, depth + 1); |
| if (stylenode) |
| return stylenode; |
| } |
| } |
| return NULL; |
| } |
| |
| std::string LoadAttrString(xml_node<>* element, const char* attrname, const char* defaultvalue) |
| { |
| if (!element) |
| return defaultvalue; |
| |
| xml_attribute<>* attr = element->first_attribute(attrname); |
| return attr ? attr->value() : defaultvalue; |
| } |
| |
| int LoadAttrInt(xml_node<>* element, const char* attrname, int defaultvalue) |
| { |
| string value = LoadAttrString(element, attrname); |
| // resolve variables |
| DataManager::GetValue(value, value); |
| return value.empty() ? defaultvalue : atoi(value.c_str()); |
| } |
| |
| int LoadAttrIntScaleX(xml_node<>* element, const char* attrname, int defaultvalue) |
| { |
| return scale_theme_x(LoadAttrInt(element, attrname, defaultvalue)); |
| } |
| |
| int LoadAttrIntScaleY(xml_node<>* element, const char* attrname, int defaultvalue) |
| { |
| return scale_theme_y(LoadAttrInt(element, attrname, defaultvalue)); |
| } |
| |
| COLOR LoadAttrColor(xml_node<>* element, const char* attrname, bool* found_color, COLOR defaultvalue) |
| { |
| string value = LoadAttrString(element, attrname); |
| *found_color = !value.empty(); |
| // resolve variables |
| DataManager::GetValue(value, value); |
| COLOR ret = defaultvalue; |
| if (ConvertStrToColor(value, &ret) == 0) |
| return ret; |
| else |
| return defaultvalue; |
| } |
| |
| COLOR LoadAttrColor(xml_node<>* element, const char* attrname, COLOR defaultvalue) |
| { |
| bool found_color = false; |
| return LoadAttrColor(element, attrname, &found_color, defaultvalue); |
| } |
| |
| FontResource* LoadAttrFont(xml_node<>* element, const char* attrname) |
| { |
| std::string name = LoadAttrString(element, attrname, ""); |
| if (name.empty()) |
| return NULL; |
| else |
| return PageManager::GetResources()->FindFont(name); |
| } |
| |
| ImageResource* LoadAttrImage(xml_node<>* element, const char* attrname) |
| { |
| std::string name = LoadAttrString(element, attrname, ""); |
| if (name.empty()) |
| return NULL; |
| else |
| return PageManager::GetResources()->FindImage(name); |
| } |
| |
| AnimationResource* LoadAttrAnimation(xml_node<>* element, const char* attrname) |
| { |
| std::string name = LoadAttrString(element, attrname, ""); |
| if (name.empty()) |
| return NULL; |
| else |
| return PageManager::GetResources()->FindAnimation(name); |
| } |
| |
| bool LoadPlacement(xml_node<>* node, int* x, int* y, int* w /* = NULL */, int* h /* = NULL */, Placement* placement /* = NULL */) |
| { |
| if (!node) |
| return false; |
| |
| if (node->first_attribute("x")) |
| *x = LoadAttrIntScaleX(node, "x") + tw_x_offset; |
| |
| if (node->first_attribute("y")) |
| *y = LoadAttrIntScaleY(node, "y") + tw_y_offset; |
| |
| if (w && node->first_attribute("w")) |
| *w = LoadAttrIntScaleX(node, "w"); |
| |
| if (h && node->first_attribute("h")) |
| *h = LoadAttrIntScaleY(node, "h"); |
| |
| if (placement && node->first_attribute("placement")) |
| *placement = (Placement) LoadAttrInt(node, "placement"); |
| |
| return true; |
| } |
| |
| int ActionObject::SetActionPos(int x, int y, int w, int h) |
| { |
| if (x < 0 || y < 0) |
| return -1; |
| |
| mActionX = x; |
| mActionY = y; |
| if (w || h) |
| { |
| mActionW = w; |
| mActionH = h; |
| } |
| return 0; |
| } |
| |
| Page::Page(xml_node<>* page, std::vector<xml_node<>*> *templates) |
| { |
| mTouchStart = NULL; |
| |
| // We can memset the whole structure, because the alpha channel is ignored |
| memset(&mBackground, 0, sizeof(COLOR)); |
| |
| // With NULL, we make a console-only display |
| if (!page) |
| { |
| mName = "console"; |
| |
| GUIConsole* element = new GUIConsole(NULL); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| return; |
| } |
| |
| if (page->first_attribute("name")) |
| mName = page->first_attribute("name")->value(); |
| else |
| { |
| LOGERR("No page name attribute found!\n"); |
| return; |
| } |
| |
| LOGINFO("Loading page %s\n", mName.c_str()); |
| |
| // This is a recursive routine for template handling |
| ProcessNode(page, templates, 0); |
| } |
| |
| Page::~Page() |
| { |
| for (std::vector<GUIObject*>::iterator itr = mObjects.begin(); itr != mObjects.end(); ++itr) |
| delete *itr; |
| } |
| |
| bool Page::ProcessNode(xml_node<>* page, std::vector<xml_node<>*> *templates, int depth) |
| { |
| if (depth == 10) |
| { |
| LOGERR("Page processing depth has exceeded 10. Failing out. This is likely a recursive template.\n"); |
| return false; |
| } |
| |
| for (xml_node<>* child = page->first_node(); child; child = child->next_sibling()) |
| { |
| std::string type = child->name(); |
| |
| if (type == "background") { |
| mBackground = LoadAttrColor(child, "color", COLOR(0,0,0,0)); |
| continue; |
| } |
| |
| if (type == "object") { |
| // legacy format : <object type="..."> |
| xml_attribute<>* attr = child->first_attribute("type"); |
| type = attr ? attr->value() : "*unspecified*"; |
| } |
| |
| if (type == "text") |
| { |
| GUIText* element = new GUIText(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "image") |
| { |
| GUIImage* element = new GUIImage(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| } |
| else if (type == "fill") |
| { |
| GUIFill* element = new GUIFill(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| } |
| else if (type == "action") |
| { |
| GUIAction* element = new GUIAction(child); |
| mObjects.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "console") |
| { |
| GUIConsole* element = new GUIConsole(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "terminal") |
| { |
| GUITerminal* element = new GUITerminal(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| mInputs.push_back(element); |
| term = element; |
| } |
| else if (type == "button") |
| { |
| GUIButton* element = new GUIButton(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "checkbox") |
| { |
| GUICheckbox* element = new GUICheckbox(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "fileselector") |
| { |
| GUIFileSelector* element = new GUIFileSelector(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "animation") |
| { |
| GUIAnimation* element = new GUIAnimation(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| } |
| else if (type == "progressbar") |
| { |
| GUIProgressBar* element = new GUIProgressBar(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "slider") |
| { |
| GUISlider* element = new GUISlider(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "slidervalue") |
| { |
| GUISliderValue *element = new GUISliderValue(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "listbox") |
| { |
| GUIListBox* element = new GUIListBox(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "keyboard") |
| { |
| GUIKeyboard* element = new GUIKeyboard(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "input") |
| { |
| GUIInput* element = new GUIInput(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| mInputs.push_back(element); |
| } |
| else if (type == "partitionlist") |
| { |
| GUIPartitionList* element = new GUIPartitionList(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "patternpassword") |
| { |
| GUIPatternPassword* element = new GUIPatternPassword(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "textbox") |
| { |
| GUITextBox* element = new GUITextBox(child); |
| mObjects.push_back(element); |
| mRenders.push_back(element); |
| mActions.push_back(element); |
| } |
| else if (type == "template") |
| { |
| if (!templates || !child->first_attribute("name")) |
| { |
| LOGERR("Invalid template request.\n"); |
| } |
| else |
| { |
| std::string name = child->first_attribute("name")->value(); |
| xml_node<>* node; |
| bool node_found = false; |
| |
| // We need to find the correct template |
| for (std::vector<xml_node<>*>::iterator itr = templates->begin(); itr != templates->end(); itr++) { |
| node = (*itr)->first_node("template"); |
| |
| while (node) |
| { |
| if (!node->first_attribute("name")) |
| continue; |
| |
| if (name == node->first_attribute("name")->value()) |
| { |
| if (!ProcessNode(node, templates, depth + 1)) |
| return false; |
| else { |
| node_found = true; |
| break; |
| } |
| } |
| if (node_found) |
| break; |
| node = node->next_sibling("template"); |
| } |
| // [check] why is there no if (node_found) here too? |
| } |
| } |
| } |
| else |
| { |
| LOGERR("Unknown object type: %s.\n", type.c_str()); |
| } |
| } |
| return true; |
| } |
| |
| int Page::Render(void) |
| { |
| // Render background |
| gr_color(mBackground.red, mBackground.green, mBackground.blue, mBackground.alpha); |
| gr_fill(0, 0, gr_fb_width(), gr_fb_height()); |
| |
| // Render remaining objects |
| std::vector<RenderObject*>::iterator iter; |
| for (iter = mRenders.begin(); iter != mRenders.end(); iter++) |
| { |
| if ((*iter)->Render()) |
| LOGERR("A render request has failed.\n"); |
| } |
| return 0; |
| } |
| |
| int Page::Update(void) |
| { |
| int retCode = 0; |
| |
| std::vector<RenderObject*>::iterator iter; |
| for (iter = mRenders.begin(); iter != mRenders.end(); iter++) |
| { |
| int ret = (*iter)->Update(); |
| if (ret < 0) |
| LOGERR("An update request has failed.\n"); |
| else if (ret > retCode) |
| retCode = ret; |
| } |
| |
| return retCode; |
| } |
| |
| int Page::NotifyTouch(TOUCH_STATE state, int x, int y) |
| { |
| // By default, return 1 to ignore further touches if nobody is listening |
| int ret = 1; |
| |
| // Don't try to handle a lack of handlers |
| if (mActions.size() == 0) |
| return ret; |
| |
| // We record mTouchStart so we can pass all the touch stream to the same handler |
| if (state == TOUCH_START) |
| { |
| std::vector<ActionObject*>::reverse_iterator iter; |
| // We work backwards, from top-most element to bottom-most element |
| for (iter = mActions.rbegin(); iter != mActions.rend(); iter++) |
| { |
| if ((*iter)->IsInRegion(x, y)) |
| { |
| mTouchStart = (*iter); |
| ret = mTouchStart->NotifyTouch(state, x, y); |
| if (ret >= 0) |
| break; |
| mTouchStart = NULL; |
| } |
| } |
| } |
| else if (state == TOUCH_RELEASE && mTouchStart != NULL) |
| { |
| ret = mTouchStart->NotifyTouch(state, x, y); |
| mTouchStart = NULL; |
| } |
| else if ((state == TOUCH_DRAG || state == TOUCH_HOLD || state == TOUCH_REPEAT) && mTouchStart != NULL) |
| { |
| ret = mTouchStart->NotifyTouch(state, x, y); |
| } |
| return ret; |
| } |
| |
| int Page::NotifyKey(int key, bool down) |
| { |
| std::vector<ActionObject*>::reverse_iterator iter; |
| |
| int ret = 1; |
| // We work backwards, from top-most element to bottom-most element |
| for (iter = mActions.rbegin(); iter != mActions.rend(); iter++) |
| { |
| ret = (*iter)->NotifyKey(key, down); |
| if (ret == 0) |
| return 0; |
| if (ret < 0) { |
| LOGERR("An action handler has returned an error\n"); |
| ret = 1; |
| } |
| } |
| return ret; |
| } |
| |
| int Page::NotifyCharInput(int ch) |
| { |
| std::vector<InputObject*>::reverse_iterator iter; |
| |
| // We work backwards, from top-most element to bottom-most element |
| for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++) |
| { |
| int ret = (*iter)->NotifyCharInput(ch); |
| if (ret == 0) |
| return 0; |
| else if (ret < 0) |
| LOGERR("A char input handler has returned an error\n"); |
| } |
| return 1; |
| } |
| |
| int Page::SetKeyBoardFocus(int inFocus) |
| { |
| std::vector<InputObject*>::reverse_iterator iter; |
| |
| // We work backwards, from top-most element to bottom-most element |
| for (iter = mInputs.rbegin(); iter != mInputs.rend(); iter++) |
| { |
| int ret = (*iter)->SetInputFocus(inFocus); |
| if (ret == 0) |
| return 0; |
| else if (ret < 0) |
| LOGERR("An input focus handler has returned an error\n"); |
| } |
| return 1; |
| } |
| |
| void Page::SetPageFocus(int inFocus) |
| { |
| // Render remaining objects |
| std::vector<RenderObject*>::iterator iter; |
| for (iter = mRenders.begin(); iter != mRenders.end(); iter++) |
| (*iter)->SetPageFocus(inFocus); |
| |
| return; |
| } |
| |
| int Page::NotifyVarChange(std::string varName, std::string value) |
| { |
| std::vector<GUIObject*>::iterator iter; |
| for (iter = mObjects.begin(); iter != mObjects.end(); ++iter) |
| { |
| if ((*iter)->NotifyVarChange(varName, value)) |
| LOGERR("An action handler errored on NotifyVarChange.\n"); |
| } |
| return 0; |
| } |
| |
| |
| // transient data for loading themes |
| struct LoadingContext |
| { |
| ZipArchiveHandle zip; // zip to load theme from, or NULL for the stock theme |
| std::set<std::string> filenames; // to detect cyclic includes |
| std::string basepath; // if zip is NULL, base path to load includes from with trailing slash, otherwise empty |
| std::vector<xml_document<>*> xmldocs; // all loaded xml docs |
| std::vector<char*> xmlbuffers; // text buffers with xml content |
| std::vector<xml_node<>*> styles; // refer to <styles> nodes inside xmldocs |
| std::vector<xml_node<>*> templates; // refer to <templates> nodes inside xmldocs |
| |
| LoadingContext() |
| { |
| zip = NULL; |
| } |
| |
| ~LoadingContext() |
| { |
| // free all xml buffers |
| for (std::vector<char*>::iterator it = xmlbuffers.begin(); it != xmlbuffers.end(); ++it) |
| free(*it); |
| } |
| |
| }; |
| |
| // for FindStyle |
| LoadingContext* PageManager::currentLoadingContext = NULL; |
| |
| |
| PageSet::PageSet() |
| { |
| mResources = new ResourceManager; |
| mCurrentPage = NULL; |
| |
| set_scale_values(1, 1); // Reset any previous scaling values |
| } |
| |
| PageSet::~PageSet() |
| { |
| mOverlays.clear(); |
| for (std::vector<Page*>::iterator itr = mPages.begin(); itr != mPages.end(); ++itr) |
| delete *itr; |
| |
| delete mResources; |
| } |
| |
| int PageSet::Load(LoadingContext& ctx, const std::string& filename) |
| { |
| bool isMain = ctx.xmlbuffers.empty(); // if we have no files yet, remember that this is the main XML file |
| |
| if (!ctx.filenames.insert(filename).second) |
| // ignore already loaded files to prevent crash with cyclic includes |
| return 0; |
| |
| // load XML into buffer |
| char* xmlbuffer = PageManager::LoadFileToBuffer(filename, ctx.zip); |
| if (!xmlbuffer) |
| return -1; // error already displayed by LoadFileToBuffer |
| ctx.xmlbuffers.push_back(xmlbuffer); |
| |
| // parse XML |
| xml_document<>* doc = new xml_document<>(); |
| doc->parse<0>(xmlbuffer); |
| ctx.xmldocs.push_back(doc); |
| |
| xml_node<>* root = doc->first_node("recovery"); |
| if (!root) |
| root = doc->first_node("install"); |
| if (!root) { |
| LOGERR("Unknown root element in %s\n", filename.c_str()); |
| return -1; |
| } |
| |
| if (isMain) { |
| int rc = LoadDetails(ctx, root); |
| if (rc != 0) |
| return rc; |
| } |
| |
| LOGINFO("Loading resources...\n"); |
| xml_node<>* child = root->first_node("resources"); |
| if (child) |
| mResources->LoadResources(child, ctx.zip, "theme"); |
| |
| LOGINFO("Loading variables...\n"); |
| child = root->first_node("variables"); |
| if (child) |
| LoadVariables(child); |
| |
| LOGINFO("Loading mouse cursor...\n"); |
| child = root->first_node("mousecursor"); |
| if (child) |
| PageManager::LoadCursorData(child); |
| |
| LOGINFO("Loading pages...\n"); |
| child = root->first_node("templates"); |
| if (child) |
| ctx.templates.push_back(child); |
| |
| child = root->first_node("styles"); |
| if (child) |
| ctx.styles.push_back(child); |
| |
| // Load pages |
| child = root->first_node("pages"); |
| if (child) { |
| if (LoadPages(ctx, child)) { |
| LOGERR("PageSet::Load returning -1\n"); |
| return -1; |
| } |
| } |
| |
| // process includes recursively |
| child = root->first_node("include"); |
| if (child) { |
| xml_node<>* include = child->first_node("xmlfile"); |
| while (include != NULL) { |
| xml_attribute<>* attr = include->first_attribute("name"); |
| if (!attr) { |
| LOGERR("Skipping include/xmlfile with no name\n"); |
| continue; |
| } |
| |
| string filename = ctx.basepath + attr->value(); |
| LOGINFO("Including file: %s...\n", filename.c_str()); |
| int rc = Load(ctx, filename); |
| if (rc != 0) |
| return rc; |
| |
| include = include->next_sibling("xmlfile"); |
| } |
| } |
| |
| return 0; |
| } |
| |
| void PageSet::MakeEmergencyConsoleIfNeeded() |
| { |
| if (mPages.empty()) { |
| mCurrentPage = new Page(NULL, NULL); // fallback console page |
| // TODO: since removal of non-TTF fonts, the emergency console doesn't work without a font, which might be missing too |
| mPages.push_back(mCurrentPage); |
| } |
| } |
| |
| int PageSet::LoadLanguage(char* languageFile, ZipArchiveHandle package) |
| { |
| xml_document<> lang; |
| xml_node<>* parent; |
| xml_node<>* child; |
| std::string resource_source; |
| int ret = 0; |
| |
| if (languageFile) { |
| printf("parsing languageFile\n"); |
| lang.parse<0>(languageFile); |
| printf("parsing languageFile done\n"); |
| } else { |
| return -1; |
| } |
| |
| parent = lang.first_node("language"); |
| if (!parent) { |
| LOGERR("Unable to locate language node in language file.\n"); |
| lang.clear(); |
| return -1; |
| } |
| |
| child = parent->first_node("display"); |
| if (child) { |
| DataManager::SetValue("tw_language_display", child->value()); |
| resource_source = child->value(); |
| } else { |
| LOGERR("language file does not have a display value set\n"); |
| DataManager::SetValue("tw_language_display", "Not Set"); |
| resource_source = languageFile; |
| } |
| |
| child = parent->first_node("resources"); |
| if (child) |
| mResources->LoadResources(child, package, resource_source); |
| else |
| ret = -1; |
| DataManager::SetValue("tw_backup_name", gui_lookup("auto_generate", "(Auto Generate)")); |
| lang.clear(); |
| return ret; |
| } |
| |
| int PageSet::LoadDetails(LoadingContext& ctx, xml_node<>* root) |
| { |
| xml_node<>* child = root->first_node("details"); |
| if (child) { |
| int theme_ver = 0; |
| xml_node<>* themeversion = child->first_node("themeversion"); |
| if (themeversion && themeversion->value()) { |
| theme_ver = atoi(themeversion->value()); |
| } else { |
| LOGINFO("No themeversion in theme.\n"); |
| } |
| if (theme_ver != TW_THEME_VERSION) { |
| LOGINFO("theme version from xml: %i, expected %i\n", theme_ver, TW_THEME_VERSION); |
| if (ctx.zip) { |
| gui_err("theme_ver_err=Custom theme version does not match TWRP version. Using stock theme."); |
| return TW_THEME_VER_ERR; |
| } else { |
| gui_print_color("warning", "Stock theme version does not match TWRP version.\n"); |
| } |
| } |
| xml_node<>* resolution = child->first_node("resolution"); |
| if (resolution) { |
| LOGINFO("Checking resolution...\n"); |
| xml_attribute<>* width_attr = resolution->first_attribute("width"); |
| xml_attribute<>* height_attr = resolution->first_attribute("height"); |
| xml_attribute<>* noscale_attr = resolution->first_attribute("noscaling"); |
| if (width_attr && height_attr && !noscale_attr) { |
| int width = atoi(width_attr->value()); |
| int height = atoi(height_attr->value()); |
| int offx = 0, offy = 0; |
| #ifdef TW_ROUND_SCREEN |
| xml_node<>* roundscreen = child->first_node("roundscreen"); |
| if (roundscreen) { |
| LOGINFO("TW_ROUND_SCREEN := true, using round screen XML settings.\n"); |
| xml_attribute<>* offx_attr = roundscreen->first_attribute("offset_x"); |
| xml_attribute<>* offy_attr = roundscreen->first_attribute("offset_y"); |
| if (offx_attr) { |
| offx = atoi(offx_attr->value()); |
| } |
| if (offy_attr) { |
| offy = atoi(offy_attr->value()); |
| } |
| } |
| #endif |
| if (width != 0 && height != 0) { |
| float scale_w = (((float)gr_fb_width() + (float)tw_w_offset) - ((float)offx * 2.0)) / (float)width; |
| float scale_h = (((float)gr_fb_height() + (float)tw_h_offset) - ((float)offy * 2.0)) / (float)height; |
| #ifdef TW_ROUND_SCREEN |
| float scale_off_w = ((float)gr_fb_width() + (float)tw_w_offset) / (float)width; |
| float scale_off_h = ((float)gr_fb_height() + (float)tw_h_offset) / (float)height; |
| tw_x_offset = offx * scale_off_w; |
| tw_y_offset = offy * scale_off_h; |
| #endif |
| if (scale_w != 1 || scale_h != 1) { |
| LOGINFO("Scaling theme width %fx and height %fx, offsets x: %i y: %i w: %i h: %i\n", |
| scale_w, scale_h, tw_x_offset, tw_y_offset, tw_w_offset, tw_h_offset); |
| set_scale_values(scale_w, scale_h); |
| } |
| } |
| } else { |
| LOGINFO("XML does not contain width and height, no scaling will be applied\n"); |
| } |
| } else { |
| LOGINFO("XML contains no resolution tag, no scaling will be applied.\n"); |
| } |
| } else { |
| LOGINFO("XML contains no details tag, no scaling will be applied.\n"); |
| } |
| |
| return 0; |
| } |
| |
| int PageSet::SetPage(std::string page) |
| { |
| Page* tmp = FindPage(page); |
| if (tmp) |
| { |
| if (mCurrentPage) mCurrentPage->SetPageFocus(0); |
| mCurrentPage = tmp; |
| mCurrentPage->SetPageFocus(1); |
| mCurrentPage->NotifyVarChange("", ""); |
| return 0; |
| } |
| else |
| { |
| LOGERR("Unable to locate page (%s)\n", page.c_str()); |
| } |
| return -1; |
| } |
| |
| int PageSet::SetOverlay(Page* page) |
| { |
| if (page) { |
| if (mOverlays.size() >= 10) { |
| LOGERR("Too many overlays requested, max is 10.\n"); |
| return -1; |
| } |
| |
| std::vector<Page*>::iterator iter; |
| for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) { |
| if ((*iter)->GetName() == page->GetName()) { |
| mOverlays.erase(iter); |
| // SetOverlay() is (and should stay) the only function which |
| // adds to mOverlays. Then, each page can appear at most once. |
| break; |
| } |
| } |
| |
| page->SetPageFocus(1); |
| page->NotifyVarChange("", ""); |
| |
| if (!mOverlays.empty()) |
| mOverlays.back()->SetPageFocus(0); |
| |
| mOverlays.push_back(page); |
| } else { |
| if (!mOverlays.empty()) { |
| mOverlays.back()->SetPageFocus(0); |
| mOverlays.pop_back(); |
| if (!mOverlays.empty()) |
| mOverlays.back()->SetPageFocus(1); |
| else if (mCurrentPage) |
| mCurrentPage->SetPageFocus(1); // Just in case somehow the regular page lost focus, we'll set it again |
| } |
| } |
| return 0; |
| } |
| |
| const ResourceManager* PageSet::GetResources() |
| { |
| return mResources; |
| } |
| |
| Page* PageSet::FindPage(std::string name) |
| { |
| std::vector<Page*>::iterator iter; |
| |
| for (iter = mPages.begin(); iter != mPages.end(); iter++) |
| { |
| if (name == (*iter)->GetName()) |
| return (*iter); |
| } |
| return NULL; |
| } |
| |
| int PageSet::LoadVariables(xml_node<>* vars) |
| { |
| xml_node<>* child; |
| xml_attribute<> *name, *value, *persist; |
| int p; |
| |
| child = vars->first_node("variable"); |
| while (child) |
| { |
| name = child->first_attribute("name"); |
| value = child->first_attribute("value"); |
| persist = child->first_attribute("persist"); |
| if (name && value) |
| { |
| if (strcmp(name->value(), "tw_x_offset") == 0) { |
| tw_x_offset = atoi(value->value()); |
| child = child->next_sibling("variable"); |
| continue; |
| } |
| if (strcmp(name->value(), "tw_y_offset") == 0) { |
| tw_y_offset = atoi(value->value()); |
| child = child->next_sibling("variable"); |
| continue; |
| } |
| if (strcmp(name->value(), "tw_w_offset") == 0) { |
| tw_w_offset = atoi(value->value()); |
| child = child->next_sibling("variable"); |
| continue; |
| } |
| if (strcmp(name->value(), "tw_h_offset") == 0) { |
| tw_h_offset = atoi(value->value()); |
| child = child->next_sibling("variable"); |
| continue; |
| } |
| p = persist ? atoi(persist->value()) : 0; |
| string temp = value->value(); |
| string valstr = gui_parse_text(temp); |
| |
| if (valstr.find("+") != string::npos) { |
| string val1str = valstr; |
| val1str = val1str.substr(0, val1str.find('+')); |
| string val2str = valstr; |
| val2str = val2str.substr(val2str.find('+') + 1, string::npos); |
| int val1 = atoi(val1str.c_str()); |
| int val2 = atoi(val2str.c_str()); |
| int val = val1 + val2; |
| |
| DataManager::SetValue(name->value(), val, p); |
| } else if (valstr.find("-") != string::npos) { |
| string val1str = valstr; |
| val1str = val1str.substr(0, val1str.find('-')); |
| string val2str = valstr; |
| val2str = val2str.substr(val2str.find('-') + 1, string::npos); |
| int val1 = atoi(val1str.c_str()); |
| int val2 = atoi(val2str.c_str()); |
| int val = val1 - val2; |
| |
| DataManager::SetValue(name->value(), val, p); |
| } else { |
| DataManager::SetValue(name->value(), valstr, p); |
| } |
| } |
| |
| child = child->next_sibling("variable"); |
| } |
| return 0; |
| } |
| |
| int PageSet::LoadPages(LoadingContext& ctx, xml_node<>* pages) |
| { |
| xml_node<>* child; |
| |
| if (!pages) |
| return -1; |
| |
| child = pages->first_node("page"); |
| while (child != NULL) |
| { |
| Page* page = new Page(child, &ctx.templates); |
| if (page->GetName().empty()) |
| { |
| LOGERR("Unable to process load page\n"); |
| delete page; |
| } |
| else |
| { |
| mPages.push_back(page); |
| } |
| child = child->next_sibling("page"); |
| } |
| if (mPages.size() > 0) |
| return 0; |
| return -1; |
| } |
| |
| int PageSet::IsCurrentPage(Page* page) |
| { |
| return ((mCurrentPage && mCurrentPage == page) ? 1 : 0); |
| } |
| |
| std::string PageSet::GetCurrentPage() const |
| { |
| return mCurrentPage ? mCurrentPage->GetName() : ""; |
| } |
| |
| int PageSet::Render(void) |
| { |
| int ret; |
| |
| ret = (mCurrentPage ? mCurrentPage->Render() : -1); |
| if (ret < 0) |
| return ret; |
| |
| std::vector<Page*>::iterator iter; |
| |
| for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) { |
| ret = ((*iter) ? (*iter)->Render() : -1); |
| if (ret < 0) |
| return ret; |
| } |
| return ret; |
| } |
| |
| int PageSet::Update(void) |
| { |
| int ret; |
| |
| ret = (mCurrentPage ? mCurrentPage->Update() : -1); |
| if (ret < 0 || ret > 1) |
| return ret; |
| |
| std::vector<Page*>::iterator iter; |
| |
| for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) { |
| ret = ((*iter) ? (*iter)->Update() : -1); |
| if (ret < 0) |
| return ret; |
| } |
| return ret; |
| } |
| |
| int PageSet::NotifyTouch(TOUCH_STATE state, int x, int y) |
| { |
| if (!mOverlays.empty()) |
| return mOverlays.back()->NotifyTouch(state, x, y); |
| |
| return (mCurrentPage ? mCurrentPage->NotifyTouch(state, x, y) : -1); |
| } |
| |
| int PageSet::NotifyKey(int key, bool down) |
| { |
| if (!mOverlays.empty()) |
| return mOverlays.back()->NotifyKey(key, down); |
| |
| return (mCurrentPage ? mCurrentPage->NotifyKey(key, down) : -1); |
| } |
| |
| int PageSet::NotifyCharInput(int ch) |
| { |
| if (!mOverlays.empty()) |
| return mOverlays.back()->NotifyCharInput(ch); |
| |
| return (mCurrentPage ? mCurrentPage->NotifyCharInput(ch) : -1); |
| } |
| |
| int PageSet::SetKeyBoardFocus(int inFocus) |
| { |
| if (!mOverlays.empty()) |
| return mOverlays.back()->SetKeyBoardFocus(inFocus); |
| |
| return (mCurrentPage ? mCurrentPage->SetKeyBoardFocus(inFocus) : -1); |
| } |
| |
| int PageSet::NotifyVarChange(std::string varName, std::string value) |
| { |
| std::vector<Page*>::iterator iter; |
| |
| for (iter = mOverlays.begin(); iter != mOverlays.end(); iter++) |
| (*iter)->NotifyVarChange(varName, value); |
| |
| return (mCurrentPage ? mCurrentPage->NotifyVarChange(varName, value) : -1); |
| } |
| |
| void PageSet::AddStringResource(std::string resource_source, std::string resource_name, std::string value) |
| { |
| mResources->AddStringResource(resource_source, resource_name, value); |
| } |
| |
| char* PageManager::LoadFileToBuffer(std::string filename, ZipArchiveHandle package) { |
| size_t len; |
| char* buffer = NULL; |
| |
| if (!package) { |
| // We can try to load the XML directly... |
| LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' directly\n", filename.c_str()); |
| struct stat st; |
| if (stat(filename.c_str(),&st) != 0) { |
| // This isn't always an error, sometimes we request files that don't exist. |
| return NULL; |
| } |
| |
| len = (size_t)st.st_size; |
| |
| buffer = (char*) malloc(len + 1); |
| if (!buffer) { |
| LOGERR("PageManager::LoadFileToBuffer failed to malloc\n"); |
| return NULL; |
| } |
| |
| int fd = open(filename.c_str(), O_RDONLY); |
| if (fd == -1) { |
| LOGERR("PageManager::LoadFileToBuffer failed to open '%s' - (%s)\n", filename.c_str(), strerror(errno)); |
| free(buffer); |
| return NULL; |
| } |
| |
| if (read(fd, buffer, len) < 0) { |
| LOGERR("PageManager::LoadFileToBuffer failed to read '%s' - (%s)\n", filename.c_str(), strerror(errno)); |
| free(buffer); |
| close(fd); |
| return NULL; |
| } |
| close(fd); |
| } else { |
| LOGINFO("PageManager::LoadFileToBuffer loading filename: '%s' from zip\n", filename.c_str()); |
| ZipEntry binary_entry; |
| if (FindEntry(package, filename, &binary_entry) != 0) { |
| LOGERR("Unable to locate '%s' in zip file\n", filename.c_str()); |
| return NULL; |
| } |
| |
| // Allocate the buffer for the file |
| len = binary_entry.uncompressed_length; |
| buffer = (char*) malloc(len + 1); |
| if (!buffer) |
| return NULL; |
| |
| int32_t err = |
| ExtractToMemory(package, &binary_entry, reinterpret_cast<uint8_t*>(buffer), len); |
| if (err != 0) { |
| LOGERR("Unable to extract '%s'\n", filename.c_str()); |
| free(buffer); |
| return NULL; |
| } |
| } |
| // NULL-terminate the string |
| buffer[len] = 0x00; |
| return buffer; |
| } |
| |
| void PageManager::LoadLanguageListDir(string dir) { |
| if (!TWFunc::Path_Exists(dir)) { |
| LOGERR("LoadLanguageListDir '%s' path not found\n", dir.c_str()); |
| return; |
| } |
| |
| DIR *d = opendir(dir.c_str()); |
| struct dirent *p; |
| |
| if (d == NULL) { |
| LOGERR("LoadLanguageListDir error opening dir: '%s', %s\n", dir.c_str(), strerror(errno)); |
| return; |
| } |
| |
| while ((p = readdir(d))) { |
| if (!strcmp(p->d_name, ".") || !strcmp(p->d_name, "..") || strlen(p->d_name) < 5) |
| continue; |
| |
| string file = p->d_name; |
| if (file.substr(strlen(p->d_name) - 4) != ".xml") |
| continue; |
| string path = dir + p->d_name; |
| string file_no_extn = file.substr(0, strlen(p->d_name) - 4); |
| struct language_struct language_entry; |
| language_entry.filename = file_no_extn; |
| char* xmlFile = PageManager::LoadFileToBuffer(dir + p->d_name, NULL); |
| if (xmlFile == NULL) { |
| LOGERR("LoadLanguageListDir unable to load '%s'\n", language_entry.filename.c_str()); |
| continue; |
| } |
| xml_document<> *doc = new xml_document<>(); |
| doc->parse<0>(xmlFile); |
| |
| xml_node<>* parent = doc->first_node("language"); |
| if (!parent) { |
| LOGERR("Invalid language XML file '%s'\n", language_entry.filename.c_str()); |
| } else { |
| xml_node<>* child = parent->first_node("display"); |
| if (child) { |
| language_entry.displayvalue = child->value(); |
| } else { |
| LOGERR("No display value for '%s'\n", language_entry.filename.c_str()); |
| language_entry.displayvalue = language_entry.filename; |
| } |
| Language_List.push_back(language_entry); |
| } |
| doc->clear(); |
| delete doc; |
| free(xmlFile); |
| } |
| closedir(d); |
| } |
| |
| void PageManager::LoadLanguageList(ZipArchiveHandle package) { |
| Language_List.clear(); |
| if (TWFunc::Path_Exists(TWRES "customlanguages")) |
| TWFunc::removeDir(TWRES "customlanguages", true); |
| if (package) { |
| TWFunc::Recursive_Mkdir(TWRES "customlanguages"); |
| ExtractPackageRecursive(package, "/", TWRES "customlanguages", nullptr, nullptr); |
| |
| // package->ExtractRecursive("languages", TWRES "customlanguages/"); |
| LoadLanguageListDir(TWRES "customlanguages/"); |
| } else { |
| LoadLanguageListDir(TWRES "languages/"); |
| } |
| |
| std::sort(Language_List.begin(), Language_List.end()); |
| } |
| |
| void PageManager::LoadLanguage(string filename) { |
| string actual_filename; |
| if (TWFunc::Path_Exists(TWRES "customlanguages/" + filename + ".xml")) |
| actual_filename = TWRES "customlanguages/" + filename + ".xml"; |
| else |
| actual_filename = TWRES "languages/" + filename + ".xml"; |
| char* xmlFile = PageManager::LoadFileToBuffer(actual_filename, NULL); |
| if (xmlFile == NULL) |
| LOGERR("Unable to load '%s'\n", actual_filename.c_str()); |
| else { |
| mCurrentSet->LoadLanguage(xmlFile, NULL); |
| free(xmlFile); |
| } |
| PartitionManager.Translate_Partition_Display_Names(); |
| } |
| |
| int PageManager::LoadPackage(std::string name, std::string package, std::string startpage) |
| { |
| std::string mainxmlfilename = package; |
| char* languageFile = NULL; |
| char* baseLanguageFile = NULL; |
| PageSet* pageSet = NULL; |
| int ret; |
| |
| mReloadTheme = false; |
| mStartPage = startpage; |
| |
| // init the loading context |
| LoadingContext ctx; |
| |
| // Open the XML file |
| LOGINFO("Loading package: %s (%s)\n", name.c_str(), package.c_str()); |
| if (package.size() > 4 && package.substr(package.size() - 4) != ".zip") |
| { |
| LOGINFO("Load XML directly\n"); |
| tw_x_offset = TW_X_OFFSET; |
| tw_y_offset = TW_Y_OFFSET; |
| tw_w_offset = TW_W_OFFSET; |
| tw_h_offset = TW_H_OFFSET; |
| if (name != "splash") { |
| LoadLanguageList(NULL); |
| languageFile = LoadFileToBuffer(TWRES "languages/en.xml", NULL); |
| } |
| ctx.basepath = TWRES; |
| } |
| else |
| { |
| LOGINFO("Loading zip theme\n"); |
| tw_x_offset = 0; |
| tw_y_offset = 0; |
| tw_w_offset = 0; |
| tw_h_offset = 0; |
| if (!TWFunc::Path_Exists(package)) { |
| return -1; |
| } |
| |
| ZipArchiveHandle Zip; |
| int err = OpenArchive(package.c_str(), &Zip); |
| |
| if (err != 0) |
| return -1; |
| |
| ctx.zip = Zip; |
| mainxmlfilename = "ui.xml"; |
| LoadLanguageList(ctx.zip); |
| languageFile = LoadFileToBuffer("languages/en.xml", ctx.zip); |
| baseLanguageFile = LoadFileToBuffer(TWRES "languages/en.xml", NULL); |
| } |
| |
| // Before loading, mCurrentSet must be the loading package so we can find resources |
| pageSet = mCurrentSet; |
| mCurrentSet = new PageSet(); |
| |
| if (baseLanguageFile) { |
| mCurrentSet->LoadLanguage(baseLanguageFile, NULL); |
| free(baseLanguageFile); |
| } |
| |
| if (languageFile) { |
| mCurrentSet->LoadLanguage(languageFile, ctx.zip); |
| free(languageFile); |
| } |
| |
| // Load and parse the XML and all includes |
| currentLoadingContext = &ctx; // required to find styles |
| ret = mCurrentSet->Load(ctx, mainxmlfilename); |
| currentLoadingContext = NULL; |
| |
| if (ret == 0) { |
| mCurrentSet->SetPage(startpage); |
| mPageSets.insert(std::pair<std::string, PageSet*>(name, mCurrentSet)); |
| } else { |
| if (ret != TW_THEME_VER_ERR) |
| LOGERR("Package %s failed to load.\n", name.c_str()); |
| } |
| |
| // reset to previous pageset |
| mCurrentSet = pageSet; |
| |
| if (ctx.zip) { |
| CloseArchive(ctx.zip); |
| } |
| return ret; |
| } |
| |
| PageSet* PageManager::FindPackage(std::string name) |
| { |
| std::map<std::string, PageSet*>::iterator iter; |
| |
| iter = mPageSets.find(name); |
| if (iter != mPageSets.end()) |
| return (*iter).second; |
| |
| LOGERR("Unable to locate package %s\n", name.c_str()); |
| return NULL; |
| } |
| |
| PageSet* PageManager::SelectPackage(std::string name) |
| { |
| LOGINFO("Switching packages (%s)\n", name.c_str()); |
| PageSet* tmp; |
| |
| tmp = FindPackage(name); |
| if (tmp) |
| { |
| mCurrentSet = tmp; |
| mCurrentSet->MakeEmergencyConsoleIfNeeded(); |
| mCurrentSet->NotifyVarChange("", ""); |
| } |
| else |
| LOGERR("Unable to find package.\n"); |
| |
| return mCurrentSet; |
| } |
| |
| int PageManager::ReloadPackage(std::string name, std::string package) |
| { |
| std::map<std::string, PageSet*>::iterator iter; |
| |
| mReloadTheme = false; |
| |
| iter = mPageSets.find(name); |
| if (iter == mPageSets.end()) |
| return -1; |
| |
| if (mMouseCursor) |
| mMouseCursor->ResetData(gr_fb_width(), gr_fb_height()); |
| |
| PageSet* set = (*iter).second; |
| mPageSets.erase(iter); |
| |
| if (LoadPackage(name, package, mStartPage) != 0) |
| { |
| LOGINFO("Failed to load package '%s'.\n", package.c_str()); |
| mPageSets.insert(std::pair<std::string, PageSet*>(name, set)); |
| return -1; |
| } |
| if (mCurrentSet == set) |
| SelectPackage(name); |
| delete set; |
| GUIConsole::Translate_Now(); |
| return 0; |
| } |
| |
| void PageManager::ReleasePackage(std::string name) |
| { |
| std::map<std::string, PageSet*>::iterator iter; |
| |
| iter = mPageSets.find(name); |
| if (iter == mPageSets.end()) |
| return; |
| |
| PageSet* set = (*iter).second; |
| mPageSets.erase(iter); |
| delete set; |
| if (set == mCurrentSet) |
| mCurrentSet = NULL; |
| return; |
| } |
| |
| int PageManager::RunReload() { |
| int ret_val = 0; |
| std::string theme_path; |
| |
| if (!mReloadTheme) |
| return 0; |
| |
| mReloadTheme = false; |
| theme_path = DataManager::GetSettingsStoragePath(); |
| if (PartitionManager.Mount_By_Path(theme_path.c_str(), 1) < 0) { |
| LOGERR("Unable to mount %s during gui_reload_theme function.\n", theme_path.c_str()); |
| ret_val = 1; |
| } |
| |
| theme_path += "/TWRP/theme/ui.zip"; |
| if (ret_val != 0 || ReloadPackage("TWRP", theme_path) != 0) |
| { |
| // Loading the custom theme failed - try loading the stock theme |
| LOGINFO("Attempting to reload stock theme...\n"); |
| if (ReloadPackage("TWRP", TWRES "ui.xml")) |
| { |
| LOGERR("Failed to load base packages.\n"); |
| ret_val = 1; |
| } |
| } |
| if (ret_val == 0) { |
| if (DataManager::GetStrValue("tw_language") != "en.xml") { |
| LOGINFO("Loading language '%s'\n", DataManager::GetStrValue("tw_language").c_str()); |
| LoadLanguage(DataManager::GetStrValue("tw_language")); |
| } |
| } |
| |
| // This makes the console re-translate |
| GUIConsole::Clear_For_Retranslation(); |
| |
| return ret_val; |
| } |
| |
| void PageManager::RequestReload() { |
| mReloadTheme = true; |
| } |
| |
| void PageManager::SetStartPage(const std::string& page_name) { |
| mStartPage = page_name; |
| } |
| |
| int PageManager::ChangePage(std::string name) |
| { |
| DataManager::SetValue("tw_operation_state", 0); |
| int ret = (mCurrentSet ? mCurrentSet->SetPage(name) : -1); |
| return ret; |
| } |
| |
| std::string PageManager::GetCurrentPage() |
| { |
| return mCurrentSet ? mCurrentSet->GetCurrentPage() : ""; |
| } |
| |
| int PageManager::ChangeOverlay(std::string name) |
| { |
| if (name.empty()) |
| return mCurrentSet->SetOverlay(NULL); |
| else |
| { |
| Page* page = mCurrentSet ? mCurrentSet->FindPage(name) : NULL; |
| return mCurrentSet->SetOverlay(page); |
| } |
| } |
| |
| const ResourceManager* PageManager::GetResources() |
| { |
| return (mCurrentSet ? mCurrentSet->GetResources() : NULL); |
| } |
| |
| int PageManager::IsCurrentPage(Page* page) |
| { |
| return (mCurrentSet ? mCurrentSet->IsCurrentPage(page) : 0); |
| } |
| |
| int PageManager::Render(void) |
| { |
| if (blankTimer.isScreenOff()) |
| return 0; |
| |
| int res = (mCurrentSet ? mCurrentSet->Render() : -1); |
| if (mMouseCursor) |
| mMouseCursor->Render(); |
| return res; |
| } |
| |
| HardwareKeyboard *PageManager::GetHardwareKeyboard() |
| { |
| if (!mHardwareKeyboard) |
| mHardwareKeyboard = new HardwareKeyboard(); |
| return mHardwareKeyboard; |
| } |
| |
| xml_node<>* PageManager::FindStyle(std::string name) |
| { |
| if (!currentLoadingContext) |
| { |
| LOGERR("FindStyle works only while loading a theme.\n"); |
| return NULL; |
| } |
| |
| for (std::vector<xml_node<>*>::iterator itr = currentLoadingContext->styles.begin(); itr != currentLoadingContext->styles.end(); itr++) { |
| xml_node<>* node = (*itr)->first_node("style"); |
| |
| while (node) { |
| if (!node->first_attribute("name")) |
| continue; |
| |
| if (name == node->first_attribute("name")->value()) |
| return node; |
| node = node->next_sibling("style"); |
| } |
| } |
| return NULL; |
| } |
| |
| MouseCursor *PageManager::GetMouseCursor() |
| { |
| if (!mMouseCursor) |
| mMouseCursor = new MouseCursor(gr_fb_width(), gr_fb_height()); |
| return mMouseCursor; |
| } |
| |
| void PageManager::LoadCursorData(xml_node<>* node) |
| { |
| if (!mMouseCursor) |
| mMouseCursor = new MouseCursor(gr_fb_width(), gr_fb_height()); |
| |
| mMouseCursor->LoadData(node); |
| } |
| |
| int PageManager::Update(void) |
| { |
| if (blankTimer.isScreenOff()) |
| return 0; |
| |
| if (RunReload()) |
| return -2; |
| |
| int res = (mCurrentSet ? mCurrentSet->Update() : -1); |
| |
| if (mMouseCursor) |
| { |
| int c_res = mMouseCursor->Update(); |
| if (c_res > res) |
| res = c_res; |
| } |
| return res; |
| } |
| |
| int PageManager::NotifyTouch(TOUCH_STATE state, int x, int y) |
| { |
| return (mCurrentSet ? mCurrentSet->NotifyTouch(state, x, y) : -1); |
| } |
| |
| int PageManager::NotifyKey(int key, bool down) |
| { |
| return (mCurrentSet ? mCurrentSet->NotifyKey(key, down) : -1); |
| } |
| |
| int PageManager::NotifyCharInput(int ch) |
| { |
| return (mCurrentSet ? mCurrentSet->NotifyCharInput(ch) : -1); |
| } |
| |
| int PageManager::SetKeyBoardFocus(int inFocus) |
| { |
| return (mCurrentSet ? mCurrentSet->SetKeyBoardFocus(inFocus) : -1); |
| } |
| |
| int PageManager::NotifyVarChange(std::string varName, std::string value) |
| { |
| return (mCurrentSet ? mCurrentSet->NotifyVarChange(varName, value) : -1); |
| } |
| |
| void PageManager::AddStringResource(std::string resource_source, std::string resource_name, std::string value) |
| { |
| if (mCurrentSet) |
| mCurrentSet->AddStringResource(resource_source, resource_name, value); |
| } |
| |
| extern "C" void gui_notifyVarChange(const char *name, const char* value) |
| { |
| if (!gGuiRunning) |
| return; |
| |
| PageManager::NotifyVarChange(name, value); |
| } |