summaryrefslogtreecommitdiffstats
path: root/gui/input.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/input.cpp')
-rw-r--r--gui/input.cpp574
1 files changed, 574 insertions, 0 deletions
diff --git a/gui/input.cpp b/gui/input.cpp
new file mode 100644
index 000000000..ec370abe5
--- /dev/null
+++ b/gui/input.cpp
@@ -0,0 +1,574 @@
+/*
+ Copyright 2012 to 2016 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"
+
+#define TW_INPUT_NO_UPDATE -1000 // Magic value for HandleTextLocation when no change in scrolling has occurred
+
+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;
+ cursorX = textWidth = 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);
+ mValue = "";
+ displayValue = "";
+
+ 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, "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();
+ }
+
+ // 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(0);
+}
+
+GUIInput::~GUIInput()
+{
+ delete mInputText;
+ delete mAction;
+}
+
+void GUIInput::HandleTextLocation(int x) {
+ mRendered = false;
+ if (textWidth <= mRenderW) {
+ if (x != TW_INPUT_NO_UPDATE)
+ lastX = x;
+ scrollingX = 0;
+ return;
+ }
+ if (scrollingX + textWidth < mRenderW) {
+ scrollingX = mRenderW - textWidth;
+ }
+
+ if (x == TW_INPUT_NO_UPDATE)
+ return;
+
+ scrollingX += x - lastX;
+ if (scrollingX > 0)
+ scrollingX = 0;
+ else if (scrollingX + textWidth < mRenderW)
+ scrollingX = mRenderW - textWidth;
+ lastX = x;
+}
+
+void GUIInput::UpdateDisplayText() {
+ void* fontResource = NULL;
+
+ if (mFont) {
+ fontResource = mFont->GetResource();
+ } else {
+ textWidth = 0;
+ return;
+ }
+
+ DataManager::GetValue(mVariable, mValue);
+ if (HasMask) {
+ int index, string_size = mValue.size();
+ string maskedValue;
+ for (index=0; index<string_size; index++)
+ maskedValue += mMask;
+ displayValue = maskedValue;
+ } else {
+ displayValue = mValue;
+ }
+
+ textWidth = gr_ttf_measureEx(displayValue.c_str(), fontResource);
+}
+
+void GUIInput::HandleCursorByTouch(int x) {
+// Uses x to find mCursorLocation and cursorX
+ if (displayValue.size() == 0) {
+ mCursorLocation = -1;
+ cursorX = mRenderX;
+ return;
+ }
+
+ void* fontResource = NULL;
+ if (mFont) {
+ fontResource = mFont->GetResource();
+ } else {
+ return;
+ }
+
+ string cursorString;
+ unsigned index = 0, displaySize = displayValue.size();
+ int prevX = mRenderX + scrollingX;
+
+ for(index = 0; index <= displaySize; index++) {
+ cursorString = displayValue.substr(0, index);
+ cursorX = gr_ttf_measureEx(cursorString.c_str(), fontResource) + mRenderX + scrollingX;
+ if (cursorX > x) {
+ if (index > 0 && x <= cursorX - ((x - prevX) / 2) && prevX >= mRenderX) {
+ // This helps make sure that we can place the cursor before the very first char if the first char is
+ // is fully visible while also still letting us place the cursor after the last char if fully visible
+ mCursorLocation = index - 1;
+ cursorX = prevX;
+ return;
+ }
+ mCursorLocation = index;
+ if (cursorX > mRenderX + mRenderW) {
+ cursorX = prevX; // This makes sure that the cursor doesn't get placed after the end of the input box
+ mCursorLocation--;
+ return;
+ }
+ if (cursorX >= mRenderX) {
+ return; // This makes sure that the cursor doesn't get placed before the beginning of the input box
+ }
+ }
+ prevX = cursorX;
+ }
+ mCursorLocation = -1; // x is at or past the end of the string
+}
+
+void GUIInput::HandleCursorByText() {
+// Uses mCursorLocation to find cursorX
+ if (!DrawCursor)
+ return;
+
+ void* fontResource = NULL;
+ if (mFont) {
+ fontResource = mFont->GetResource();
+ } else {
+ return;
+ }
+
+ int cursorTextWidth = textWidth; // width of text to the left of the cursor
+
+ if (mCursorLocation != -1) {
+ string cursorDisplay = displayValue;
+ cursorDisplay.resize(mCursorLocation);
+ cursorTextWidth = gr_ttf_measureEx(cursorDisplay.c_str(), fontResource);
+ }
+ cursorX = mRenderX + cursorTextWidth + scrollingX;
+ if (cursorX >= mRenderX + mRenderW) {
+ scrollingX = mRenderW - cursorTextWidth;
+ cursorX = mRenderX + mRenderW - CursorWidth;
+ } else if (cursorX < mRenderX) {
+ scrollingX = cursorTextWidth * -1;
+ cursorX = mRenderX;
+ }
+}
+
+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
+ if (mInputText) {
+ mInputText->SetRenderPos(mRenderX + scrollingX, mFontY);
+ mInputText->SetText(displayValue);
+ gr_clip(mRenderX, mRenderY, mRenderW, mRenderH);
+ ret = mInputText->Render();
+ gr_noclip();
+ }
+ if (ret < 0)
+ return ret;
+
+ if (HasInputFocus && DrawCursor) {
+ // Render 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;
+ 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::NotifyCharInput(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
+ mRendered = false;
+ DrawCursor = true;
+ HandleCursorByTouch(x);
+ break;
+ }
+ }
+ return 0;
+}
+
+int GUIInput::NotifyVarChange(const std::string& varName, const std::string& value)
+{
+ GUIObject::NotifyVarChange(varName, value);
+
+ if (varName == mVariable) {
+ if (!isLocalChange) {
+ UpdateDisplayText();
+ HandleTextLocation(TW_INPUT_NO_UPDATE);
+ } else
+ isLocalChange = false;
+ return 0;
+ }
+ if (varName.empty()) {
+ UpdateDisplayText();
+ HandleTextLocation(TW_INPUT_NO_UPDATE);
+ HandleCursorByText();
+ }
+ return 0;
+}
+
+int GUIInput::NotifyKey(int key, bool down)
+{
+ if (!HasInputFocus || !down)
+ return 1;
+
+ switch (key)
+ {
+ case KEY_LEFT:
+ if (mCursorLocation == 0)
+ return 0; // we're already at the beginning
+ if (mCursorLocation == -1) {
+ if (displayValue.size() == 0) {
+ cursorX = mRenderX;
+ return 0;
+ }
+ mCursorLocation = displayValue.size() - 1;
+ } else {
+ mCursorLocation--;
+ }
+ mRendered = false;
+ HandleCursorByText();
+ return 0;
+
+ case KEY_RIGHT:
+ if (mCursorLocation == -1)
+ return 0; // we're already at the end
+ mCursorLocation++;
+ if ((int)displayValue.size() <= mCursorLocation)
+ mCursorLocation = -1;
+ HandleCursorByText();
+ mRendered = false;
+ return 0;
+
+ case KEY_HOME:
+ case KEY_UP:
+ if (displayValue.size() == 0)
+ return 0;
+ mCursorLocation = 0;
+ mRendered = false;
+ cursorX = mRenderX;
+ return 0;
+
+ case KEY_END:
+ case KEY_DOWN:
+ mCursorLocation = -1;
+ mRendered = false;
+ HandleCursorByText();
+ return 0;
+ }
+
+ return 1;
+}
+
+int GUIInput::NotifyCharInput(int key)
+{
+ if (HasInputFocus) {
+ if (key == KEYBOARD_BACKSPACE) {
+ //Backspace
+ if (mValue.size() > 0 && mCursorLocation != 0) {
+ if (mCursorLocation == -1) {
+ mValue.resize(mValue.size() - 1);
+ } else {
+ mValue.erase(mCursorLocation - 1, 1);
+ mCursorLocation--;
+ }
+ isLocalChange = true;
+ DataManager::SetValue(mVariable, mValue);
+ UpdateDisplayText();
+ HandleTextLocation(TW_INPUT_NO_UPDATE);
+ HandleCursorByText();
+ }
+ } else if (key == KEYBOARD_SWIPE_LEFT) {
+ // Delete all
+ isLocalChange = true;
+ if (mCursorLocation == -1) {
+ DataManager::SetValue (mVariable, "");
+ mValue = "";
+ textWidth = 0;
+ mCursorLocation = -1;
+ } else {
+ mValue.erase(0, mCursorLocation);
+ DataManager::SetValue(mVariable, mValue);
+ mCursorLocation = 0;
+ }
+ UpdateDisplayText();
+ cursorX = mRenderX;
+ scrollingX = 0;
+ mRendered = false;
+ return 0;
+ } else if (key >= 32) {
+ // Regular key
+ if (HasAllowed && AllowedList.find((char)key) == string::npos) {
+ return 0;
+ }
+ if (HasDisabled && DisabledList.find((char)key) != string::npos) {
+ return 0;
+ }
+ if (MaxLen != 0 && mValue.size() >= MaxLen) {
+ return 0;
+ }
+ if (mCursorLocation == -1) {
+ mValue += key;
+ } else {
+ mValue.insert(mCursorLocation, 1, key);
+ mCursorLocation++;
+ }
+ isLocalChange = true;
+ DataManager::SetValue(mVariable, mValue);
+ UpdateDisplayText();
+ HandleTextLocation(TW_INPUT_NO_UPDATE);
+ HandleCursorByText();
+ } else if (key == KEYBOARD_ACTION) {
+ // Action
+ if (mAction) {
+ unsigned inputLen = mValue.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;
+}