/* Copyright 2012 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 . */ // input.cpp - GUIInput object #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern "C" { #include "../twcommon.h" #include "../minuitwrp/minui.h" } #include "rapidxml.hpp" #include "objects.hpp" #include "../data.hpp" GUIInput::GUIInput(xml_node<>* node) : GUIObject(node) { xml_attribute<>* attr; xml_node<>* child; mInputText = NULL; mAction = NULL; mBackground = NULL; mCursor = NULL; mFont = NULL; mRendered = false; HasMask = false; DrawCursor = false; isLocalChange = true; HasAllowed = false; HasDisabled = false; skipChars = scrollingX = mFontHeight = mFontY = lastX = 0; mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = MinLen = MaxLen = 0; mCursorLocation = -1; // -1 is always the end of the string CursorWidth = 3; ConvertStrToColor("black", &mBackgroundColor); ConvertStrToColor("white", &mCursorColor); if (!node) return; // Load text directly from the node mInputText = new GUIText(node); // Load action directly from the node mAction = new GUIAction(node); if (mInputText->Render() < 0) { delete mInputText; mInputText = NULL; } // Load the background child = node->first_node("background"); if (child) { attr = child->first_attribute("resource"); if (attr) mBackground = PageManager::FindResource(attr->value()); attr = child->first_attribute("color"); if (attr) { std::string color = attr->value(); ConvertStrToColor(color, &mBackgroundColor); } } if (mBackground && mBackground->GetResource()) { mBackgroundW = gr_get_width(mBackground->GetResource()); mBackgroundH = gr_get_height(mBackground->GetResource()); } // Load the cursor color child = node->first_node("cursor"); if (child) { attr = child->first_attribute("resource"); if (attr) mCursor = PageManager::FindResource(attr->value()); attr = child->first_attribute("color"); if (attr) { std::string color = attr->value(); ConvertStrToColor(color, &mCursorColor); } attr = child->first_attribute("hasfocus"); if (attr) { std::string color = attr->value(); SetInputFocus(atoi(color.c_str())); } attr = child->first_attribute("width"); if (attr) { std::string cwidth = gui_parse_text(attr->value()); CursorWidth = atoi(cwidth.c_str()); } } DrawCursor = HasInputFocus; // Load the font, and possibly override the color child = node->first_node("font"); if (child) { attr = child->first_attribute("resource"); if (attr) { mFont = PageManager::FindResource(attr->value()); mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL); } } child = node->first_node("text"); if (child) mText = child->value(); mLastValue = gui_parse_text(mText); child = node->first_node("data"); if (child) { attr = child->first_attribute("name"); if (attr) mVariable = attr->value(); attr = child->first_attribute("default"); if (attr) DataManager::SetValue(mVariable, attr->value()); attr = child->first_attribute("mask"); if (attr) { mMask = attr->value(); HasMask = true; } attr = child->first_attribute("maskvariable"); if (attr) mMaskVariable = attr->value(); else mMaskVariable = mVariable; } // Load input restrictions child = node->first_node("restrict"); if (child) { attr = child->first_attribute("minlen"); if (attr) { std::string attrib = attr->value(); MinLen = atoi(attrib.c_str()); } attr = child->first_attribute("maxlen"); if (attr) { std::string attrib = attr->value(); MaxLen = atoi(attrib.c_str()); } attr = child->first_attribute("allow"); if (attr) { HasAllowed = true; AllowedList = attr->value(); } attr = child->first_attribute("disable"); if (attr) { HasDisabled = true; DisabledList = attr->value(); } } // Load the placement LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH); SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH); if (mInputText && mFontHeight && mFontHeight < (unsigned)mRenderH) { mFontY = ((mRenderH - mFontHeight) / 2) + mRenderY; mInputText->SetRenderPos(mRenderX, mFontY); } else mFontY = mRenderY; if (mInputText) mInputText->SetMaxWidth(mRenderW); isLocalChange = false; HandleTextLocation(-3); } GUIInput::~GUIInput() { if (mInputText) delete mInputText; if (mBackground) delete mBackground; if (mCursor) delete mCursor; if (mAction) delete mAction; } int GUIInput::HandleTextLocation(int x) { int textWidth; string displayValue, originalValue, insertChar; void* fontResource = NULL; if (mFont) fontResource = mFont->GetResource(); DataManager::GetValue(mVariable, originalValue); displayValue = originalValue; if (HasMask) { int index, string_size = displayValue.size(); string maskedValue; for (index=0; indexSkipCharCount(skipChars); mRendered = false; return 0; } if (skipChars && skipChars < displayValue.size()) displayValue.erase(0, skipChars); textWidth = gr_measureEx(displayValue.c_str(), fontResource); mRendered = false; int deltaX, deltaText, newWidth; if (x < -1000) { // No change in scrolling if (x == -1003) mCursorLocation = -1; if (mCursorLocation == -1) { displayValue = originalValue; skipChars = 0; textWidth = gr_measureEx(displayValue.c_str(), fontResource); while (textWidth > mRenderW) { displayValue.erase(0, 1); skipChars++; textWidth = gr_measureEx(displayValue.c_str(), fontResource); } scrollingX = mRenderW - textWidth; mInputText->SkipCharCount(skipChars); } else if (x == -1001) { // Added a new character int adjust_scrollingX = 0; string cursorLocate; cursorLocate = displayValue; cursorLocate.resize(mCursorLocation); textWidth = gr_measureEx(cursorLocate.c_str(), fontResource); while (textWidth > mRenderW) { skipChars++; mCursorLocation--; cursorLocate.erase(0, 1); textWidth = gr_measureEx(cursorLocate.c_str(), fontResource); adjust_scrollingX = -1; } if (adjust_scrollingX) { scrollingX = mRenderW - textWidth; if (scrollingX < 0) scrollingX = 0; } mInputText->SkipCharCount(skipChars); } else if (x == -1002) { // Deleted a character while (-1) { if (skipChars == 0) { scrollingX = 0; mInputText->SkipCharCount(skipChars); return 0; } insertChar = originalValue.substr(skipChars - 1, 1); displayValue.insert(0, insertChar); newWidth = gr_measureEx(displayValue.c_str(), fontResource); deltaText = newWidth - textWidth; if (newWidth > mRenderW) { scrollingX = mRenderW - textWidth; if (scrollingX < 0) scrollingX = 0; mInputText->SkipCharCount(skipChars); return 0; } else { textWidth = newWidth; skipChars--; mCursorLocation++; } } } else LOGINFO("GUIInput::HandleTextLocation -> We really shouldn't ever get here...\n"); } else if (x > lastX) { // Dragging to right, scrolling left while (-1) { deltaX = x - lastX + scrollingX; if (skipChars == 0 || deltaX == 0) { scrollingX = 0; lastX = x; mInputText->SkipCharCount(skipChars); return 0; } insertChar = originalValue.substr(skipChars - 1, 1); displayValue.insert(0, insertChar); newWidth = gr_measureEx(displayValue.c_str(), fontResource); deltaText = newWidth - textWidth; if (deltaText < deltaX) { lastX += deltaText; textWidth = newWidth; skipChars--; } else { scrollingX = deltaX; lastX = x; mInputText->SkipCharCount(skipChars); return 0; } } } else if (x < lastX) { // Dragging to left, scrolling right if (textWidth <= mRenderW) { lastX = x; scrollingX = mRenderW - textWidth; return 0; } if (scrollingX) { deltaX = lastX - x; if (scrollingX > deltaX) { scrollingX -= deltaX; lastX = x; return 0; } else { lastX -= deltaX; scrollingX = 0; } } while (-1) { deltaX = lastX - x; displayValue.erase(0, 1); skipChars++; newWidth = gr_measureEx(displayValue.c_str(), fontResource); deltaText = textWidth - newWidth; if (newWidth <= mRenderW) { scrollingX = mRenderW - newWidth; lastX = x; mInputText->SkipCharCount(skipChars); return 0; } if (deltaText < deltaX) { lastX -= deltaText; textWidth = newWidth; } else { scrollingX = deltaText - deltaX; lastX = x; mInputText->SkipCharCount(skipChars); return 0; } } } return 0; } int GUIInput::Render(void) { if (!isConditionTrue()) { mRendered = false; return 0; } void* fontResource = NULL; if (mFont) fontResource = mFont->GetResource(); // First step, fill background gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255); gr_fill(mRenderX, mRenderY, mRenderW, mRenderH); // Next, render the background resource (if it exists) if (mBackground && mBackground->GetResource()) { mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2); mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2); gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY); } int ret = 0; // Render the text mInputText->SetRenderPos(mRenderX + scrollingX, mFontY); mInputText->SetMaxWidth(mRenderW - scrollingX); if (mInputText) ret = mInputText->Render(); if (ret < 0) return ret; if (HasInputFocus && DrawCursor) { // Render the cursor string displayValue; int cursorX; DataManager::GetValue(mVariable, displayValue); if (HasMask) { int index, string_size = displayValue.size(); string maskedValue; for (index=0; index 0) { // Cursor is in the middle if (displayValue.size() > (unsigned)mCursorLocation) { string cursorDisplay; cursorDisplay = displayValue; cursorDisplay.resize(mCursorLocation); cursorX = gr_measureEx(cursorDisplay.c_str(), fontResource) + mRenderX; } else { // Cursor location is after the end of the text - reset to -1 mCursorLocation = -1; cursorX = gr_measureEx(displayValue.c_str(), fontResource) + mRenderX; } } else { // Cursor is at the end (-1) cursorX = gr_measureEx(displayValue.c_str(), fontResource) + mRenderX; } } cursorX += scrollingX; // Make sure that the cursor doesn't go past the boundaries of the box if (cursorX + (int)CursorWidth > mRenderX + mRenderW) cursorX = mRenderX + mRenderW - CursorWidth; // Set the color for the cursor gr_color(mCursorColor.red, mCursorColor.green, mCursorColor.blue, 255); gr_fill(cursorX, mFontY, CursorWidth, mFontHeight); } mRendered = true; return ret; } int GUIInput::Update(void) { if (!isConditionTrue()) return (mRendered ? 2 : 0); if (!mRendered) return 2; int ret = 0; if (mInputText) ret = mInputText->Update(); if (ret < 0) return ret; return ret; } int GUIInput::GetSelection(int x, int y) { if (x < mRenderX || x - mRenderX > mRenderW || y < mRenderY || y - mRenderY > mRenderH) return -1; return (x - mRenderX); } int GUIInput::NotifyTouch(TOUCH_STATE state, int x, int y) { static int startSelection = -1; int textWidth; string displayValue, originalValue; void* fontResource = NULL; if (mFont) fontResource = mFont->GetResource(); if (!isConditionTrue()) return -1; if (!HasInputFocus) { if (state != TOUCH_RELEASE) return 0; // Only change focus if touch releases within the input box if (GetSelection(x, y) >= 0) { // When changing focus, we don't scroll or change the cursor location PageManager::SetKeyBoardFocus(0); PageManager::NotifyKeyboard(0); SetInputFocus(1); DrawCursor = true; mRendered = false; } } else { switch (state) { case TOUCH_HOLD: case TOUCH_REPEAT: break; case TOUCH_START: startSelection = GetSelection(x,y); lastX = x; DrawCursor = false; mRendered = false; break; case TOUCH_DRAG: // Check if we dragged out of the selection window if (GetSelection(x, y) == -1) { lastX = 0; break; } DrawCursor = false; // Provide some debounce on initial touches if (startSelection != -1 && abs(x - lastX) < 6) { break; } startSelection = -1; if (lastX != x) HandleTextLocation(x); break; case TOUCH_RELEASE: // We've moved the cursor location int relativeX = x - mRenderX; mRendered = false; DrawCursor = true; DataManager::GetValue(mVariable, displayValue); if (HasMask) { int index, string_size = displayValue.size(); string maskedValue; for (index=0; index x) { if (index > 0) mCursorLocation = index - 1; else mCursorLocation = index; return 0; } } mCursorLocation = -1; break; } } return 0; } int GUIInput::NotifyVarChange(const std::string& varName, const std::string& value) { GUIObject::NotifyVarChange(varName, value); if (varName == mVariable && !isLocalChange) { HandleTextLocation(-1003); return 0; } return 0; } int GUIInput::NotifyKeyboard(int key) { string variableValue; if (HasInputFocus) { if (key == KEYBOARD_BACKSPACE) { //Backspace DataManager::GetValue(mVariable, variableValue); if (variableValue.size() > 0 && (mCursorLocation + skipChars != 0 || mCursorLocation == -1)) { if (mCursorLocation == -1) { variableValue.resize(variableValue.size() - 1); } else { variableValue.erase(mCursorLocation + skipChars - 1, 1); if (mCursorLocation > 0) mCursorLocation--; else if (skipChars > 0) skipChars--; } isLocalChange = true; DataManager::SetValue(mVariable, variableValue); isLocalChange = false; if (HasMask) { int index, string_size = variableValue.size(); string maskedValue; for (index=0; indexSkipCharCount(skipChars); isLocalChange = false; mRendered = false; return 0; } else if (key == KEYBOARD_ARROW_LEFT) { if (mCursorLocation == 0 && skipChars == 0) return 0; // we're already at the beginning if (mCursorLocation == -1) { DataManager::GetValue(mVariable, variableValue); if (variableValue.size() == 0) return 0; mCursorLocation = variableValue.size() - skipChars - 1; } else if (mCursorLocation == 0) { skipChars--; HandleTextLocation(-1002); } else { mCursorLocation--; HandleTextLocation(-1002); } mRendered = false; return 0; } else if (key == KEYBOARD_ARROW_RIGHT) { if (mCursorLocation == -1) return 0; // we're already at the end mCursorLocation++; DataManager::GetValue(mVariable, variableValue); if (variableValue.size() <= mCursorLocation + skipChars) mCursorLocation = -1; HandleTextLocation(-1001); mRendered = false; return 0; } else if (key == KEYBOARD_HOME || key == KEYBOARD_ARROW_UP) { DataManager::GetValue(mVariable, variableValue); if (variableValue.size() == 0) return 0; mCursorLocation = 0; skipChars = 0; mRendered = false; HandleTextLocation(-1002); return 0; } else if (key == KEYBOARD_END || key == KEYBOARD_ARROW_DOWN) { mCursorLocation = -1; mRendered = false; HandleTextLocation(-1003); return 0; } else if (key < KEYBOARD_SPECIAL_KEYS && key > 0) { // Regular key if (HasAllowed && AllowedList.find((char)key) == string::npos) { return 0; } if (HasDisabled && DisabledList.find((char)key) != string::npos) { return 0; } DataManager::GetValue(mVariable, variableValue); if (MaxLen != 0 && variableValue.size() >= MaxLen) { return 0; } if (mCursorLocation == -1) { variableValue += key; } else { variableValue.insert(mCursorLocation + skipChars, 1, key); mCursorLocation++; } isLocalChange = true; DataManager::SetValue(mVariable, variableValue); HandleTextLocation(-1001); isLocalChange = false; if (HasMask) { int index, string_size = variableValue.size(); string maskedValue; for (index=0; index MaxLen) return 0; else return (mAction ? mAction->NotifyTouch(TOUCH_RELEASE, mRenderX, mRenderY) : 1); } } return 0; } else { if (key == 0) { // Somewhat ugly hack-ish way to tell the box to redraw after losing focus to remove the cursor mRendered = false; return 1; } } return 1; }