diff options
Diffstat (limited to 'gui/input.cpp')
-rw-r--r-- | gui/input.cpp | 713 |
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; +} |