summaryrefslogtreecommitdiffstats
path: root/gui/input.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/input.cpp')
-rw-r--r--gui/input.cpp713
1 files changed, 713 insertions, 0 deletions
diff --git a/gui/input.cpp b/gui/input.cpp
new file mode 100644
index 000000000..ca27ea812
--- /dev/null
+++ b/gui/input.cpp
@@ -0,0 +1,713 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+// input.cpp - GUIInput object
+
+#include <linux/input.h>
+#include <pthread.h>
+#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 <string>
+
+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 = FindNode(node, "background");
+ if (child)
+ {
+ mBackground = LoadAttrImage(child, "resource");
+ mBackgroundColor = LoadAttrColor(child, "color", mBackgroundColor);
+ }
+ if (mBackground && mBackground->GetResource())
+ {
+ mBackgroundW = mBackground->GetWidth();
+ mBackgroundH = mBackground->GetHeight();
+ }
+
+ // Load the cursor color
+ child = FindNode(node, "cursor");
+ if (child)
+ {
+ mCursor = LoadAttrImage(child, "resource");
+ mCursorColor = LoadAttrColor(child, "color", mCursorColor);
+ attr = child->first_attribute("hasfocus");
+ if (attr)
+ {
+ std::string focus = attr->value();
+ SetInputFocus(atoi(focus.c_str()));
+ }
+ CursorWidth = LoadAttrIntScaleX(child, "width", CursorWidth);
+ }
+ DrawCursor = HasInputFocus;
+
+ // Load the font
+ child = FindNode(node, "font");
+ if (child)
+ {
+ mFont = LoadAttrFont(child, "resource");
+ mFontHeight = mFont->GetHeight();
+ }
+
+ child = FindNode(node, "text");
+ if (child) mText = child->value();
+ mLastValue = gui_parse_text(mText);
+
+ child = FindNode(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());
+ mMask = LoadAttrString(child, "mask");
+ HasMask = !mMask.empty();
+ attr = child->first_attribute("maskvariable");
+ if (attr)
+ mMaskVariable = attr->value();
+ else
+ mMaskVariable = mVariable;
+ }
+
+ // Load input restrictions
+ child = FindNode(node, "restrict");
+ if (child)
+ {
+ MinLen = LoadAttrInt(child, "minlen", MinLen);
+ MaxLen = LoadAttrInt(child, "maxlen", MaxLen);
+ AllowedList = LoadAttrString(child, "allow");
+ HasAllowed = !AllowedList.empty();
+ DisabledList = LoadAttrString(child, "disable");
+ HasDisabled = !DisabledList.empty();
+ }
+
+ // Load the placement
+ LoadPlacement(FindNode(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()
+{
+ delete mInputText;
+ 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; index<string_size; index++)
+ maskedValue += mMask;
+ displayValue = maskedValue;
+ }
+ textWidth = gr_measureEx(displayValue.c_str(), fontResource);
+ if (textWidth <= mRenderW) {
+ lastX = x;
+ scrollingX = 0;
+ skipChars = 0;
+ mInputText->SkipCharCount(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<string_size; index++)
+ maskedValue += mMask;
+ displayValue = maskedValue;
+ }
+ if (displayValue.size() == 0) {
+ skipChars = 0;
+ mCursorLocation = -1;
+ cursorX = mRenderX;
+ } else {
+ if (skipChars && skipChars < displayValue.size()) {
+ displayValue.erase(0, skipChars);
+ }
+ if (mCursorLocation == 0) {
+ // Cursor is at the beginning
+ cursorX = mRenderX;
+ } else if (mCursorLocation > 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<string_size; index++)
+ maskedValue += mMask;
+ displayValue = maskedValue;
+ }
+ if (displayValue.size() == 0) {
+ skipChars = 0;
+ mCursorLocation = -1;
+ return 0;
+ } else if (skipChars && skipChars < displayValue.size()) {
+ displayValue.erase(0, skipChars);
+ }
+
+ string cursorString;
+ int cursorX = 0;
+ unsigned index = 0;
+
+ for(index=0; index<displayValue.size(); index++)
+ {
+ cursorString = displayValue.substr(0, index);
+ cursorX = gr_measureEx(cursorString.c_str(), fontResource) + mRenderX;
+ if (cursorX > 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; index<string_size; index++)
+ maskedValue += mMask;
+ DataManager::SetValue(mMaskVariable, maskedValue);
+ }
+ HandleTextLocation(-1002);
+ }
+ } else if (key == KEYBOARD_SWIPE_LEFT) {
+ // Delete all
+ isLocalChange = true;
+ if (mCursorLocation == -1) {
+ DataManager::SetValue (mVariable, "");
+ if (HasMask)
+ DataManager::SetValue(mMaskVariable, "");
+ mCursorLocation = -1;
+ } else {
+ DataManager::GetValue(mVariable, variableValue);
+ variableValue.erase(0, mCursorLocation + skipChars);
+ DataManager::SetValue(mVariable, variableValue);
+ if (HasMask) {
+ DataManager::GetValue(mMaskVariable, variableValue);
+ variableValue.erase(0, mCursorLocation + skipChars);
+ DataManager::SetValue(mMaskVariable, variableValue);
+ }
+ mCursorLocation = 0;
+ }
+ skipChars = 0;
+ scrollingX = 0;
+ mInputText->SkipCharCount(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<string_size; index++)
+ maskedValue += mMask;
+ DataManager::SetValue(mMaskVariable, maskedValue);
+ }
+ } else if (key == KEYBOARD_ACTION) {
+ // Action
+ DataManager::GetValue(mVariable, variableValue);
+ if (mAction) {
+ unsigned inputLen = variableValue.length();
+ if (inputLen < MinLen)
+ return 0;
+ else if (MaxLen != 0 && inputLen > 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;
+}