blob: 2418c4c410c17b8856201b198879baefd464fe9f [file] [log] [blame]
Dees_Troy51a0e822012-09-05 15:24:24 -04001// input.cpp - GUIInput object
2
3#include <linux/input.h>
4#include <pthread.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <fcntl.h>
10#include <sys/reboot.h>
11#include <sys/stat.h>
12#include <sys/time.h>
13#include <sys/mman.h>
14#include <sys/types.h>
15#include <sys/ioctl.h>
16#include <time.h>
17#include <unistd.h>
18#include <stdlib.h>
19
20#include <string>
21
22extern "C" {
23#include "../common.h"
24#include "../minuitwrp/minui.h"
25#include "../recovery_ui.h"
26}
27
28#include "rapidxml.hpp"
29#include "objects.hpp"
30#include "../data.hpp"
31
32GUIInput::GUIInput(xml_node<>* node)
33 : Conditional(node)
34{
35 xml_attribute<>* attr;
36 xml_node<>* child;
37
38 mInputText = NULL;
39 mAction = NULL;
40 mBackground = NULL;
41 mCursor = NULL;
42 mFont = NULL;
43 mRendered = false;
44 HasMask = false;
45 DrawCursor = false;
46 isLocalChange = true;
47 HasAllowed = false;
48 HasDisabled = false;
49 skipChars = scrollingX = mFontHeight = mFontY = lastX = 0;
50 mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = MinLen = MaxLen = 0;
51 mCursorLocation = -1; // -1 is always the end of the string
52 CursorWidth = 3;
53 ConvertStrToColor("black", &mBackgroundColor);
54 ConvertStrToColor("white", &mCursorColor);
55
56 if (!node) return;
57
58 // Load text directly from the node
59 mInputText = new GUIText(node);
60 // Load action directly from the node
61 mAction = new GUIAction(node);
62
63 if (mInputText->Render() < 0)
64 {
65 delete mInputText;
66 mInputText = NULL;
67 }
68
69 // Load the background
70 child = node->first_node("background");
71 if (child)
72 {
73 attr = child->first_attribute("resource");
74 if (attr)
75 mBackground = PageManager::FindResource(attr->value());
76 attr = child->first_attribute("color");
77 if (attr)
78 {
79 std::string color = attr->value();
80 ConvertStrToColor(color, &mBackgroundColor);
81 }
82 }
83 if (mBackground && mBackground->GetResource())
84 {
85 mBackgroundW = gr_get_width(mBackground->GetResource());
86 mBackgroundH = gr_get_height(mBackground->GetResource());
87 }
88
89 // Load the cursor color
90 child = node->first_node("cursor");
91 if (child)
92 {
93 attr = child->first_attribute("resource");
94 if (attr)
95 mCursor = PageManager::FindResource(attr->value());
96 attr = child->first_attribute("color");
97 if (attr)
98 {
99 std::string color = attr->value();
100 ConvertStrToColor(color, &mCursorColor);
101 }
102 attr = child->first_attribute("hasfocus");
103 if (attr)
104 {
105 std::string color = attr->value();
106 SetInputFocus(atoi(color.c_str()));
107 }
108 attr = child->first_attribute("width");
109 if (attr)
110 {
111 std::string cwidth = gui_parse_text(attr->value());
112 CursorWidth = atoi(cwidth.c_str());
113 }
114 }
115 DrawCursor = HasInputFocus;
116
117 // Load the font, and possibly override the color
118 child = node->first_node("font");
119 if (child)
120 {
121 attr = child->first_attribute("resource");
122 if (attr) {
123 mFont = PageManager::FindResource(attr->value());
124 gr_getFontDetails(mFont ? mFont->GetResource() : NULL, &mFontHeight, NULL);
125 }
126 }
127
128 child = node->first_node("text");
129 if (child) mText = child->value();
130 mLastValue = gui_parse_text(mText);
131
132 child = node->first_node("data");
133 if (child)
134 {
135 attr = child->first_attribute("name");
136 if (attr)
137 mVariable = attr->value();
138 attr = child->first_attribute("default");
139 if (attr)
140 DataManager::SetValue(mVariable, attr->value());
141 attr = child->first_attribute("mask");
142 if (attr) {
143 mMask = attr->value();
144 HasMask = true;
145 }
146 attr = child->first_attribute("maskvariable");
147 if (attr)
148 mMaskVariable = attr->value();
149 else
150 mMaskVariable = mVariable;
151 }
152
153 // Load input restrictions
154 child = node->first_node("restrict");
155 if (child)
156 {
157 attr = child->first_attribute("minlen");
158 if (attr) {
159 std::string attrib = attr->value();
160 MinLen = atoi(attrib.c_str());
161 }
162 attr = child->first_attribute("maxlen");
163 if (attr) {
164 std::string attrib = attr->value();
165 MaxLen = atoi(attrib.c_str());
166 }
167 attr = child->first_attribute("allow");
168 if (attr) {
169 HasAllowed = true;
170 AllowedList = attr->value();
171 }
172 attr = child->first_attribute("disable");
173 if (attr) {
174 HasDisabled = true;
175 DisabledList = attr->value();
176 }
177 }
178
179 // Load the placement
180 LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
181 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
182
183 if (mInputText && mFontHeight && mFontHeight < (unsigned)mRenderH) {
184 mFontY = ((mRenderH - mFontHeight) / 2) + mRenderY;
185 mInputText->SetRenderPos(mRenderX, mFontY);
186 } else
187 mFontY = mRenderY;
188
189 if (mInputText)
190 mInputText->SetMaxWidth(mRenderW);
191
192 isLocalChange = false;
193 HandleTextLocation(-3);
194
195 return;
196}
197
198GUIInput::~GUIInput()
199{
200 if (mInputText) delete mInputText;
201 if (mBackground) delete mBackground;
202 if (mCursor) delete mCursor;
203 if (mFont) delete mFont;
204 if (mAction) delete mAction;
205}
206
207int GUIInput::HandleTextLocation(int x)
208{
209 int textWidth;
210 string displayValue, originalValue, insertChar;
211 void* fontResource = NULL;
212
213 if (mFont) fontResource = mFont->GetResource();
214
215 DataManager::GetValue(mVariable, originalValue);
216 displayValue = originalValue;
217 if (HasMask) {
218 int index, string_size = displayValue.size();
219 string maskedValue;
220 for (index=0; index<string_size; index++)
221 maskedValue += mMask;
222 displayValue = maskedValue;
223 }
224 textWidth = gr_measureEx(displayValue.c_str(), fontResource);
225 if (textWidth <= mRenderW) {
226 lastX = x;
227 scrollingX = 0;
228 skipChars = 0;
229 mInputText->SkipCharCount(skipChars);
230 mRendered = false;
231 return 0;
232 }
233 if (skipChars && skipChars < displayValue.size()) {
234 displayValue.erase(0, skipChars);
235 }
236 textWidth = gr_measureEx(displayValue.c_str(), fontResource);
237 mRendered = false;
238
239 int deltaX, deltaText, newWidth;
240
241 if (x < -1000) {
242 // No change in scrolling
243 if (x == -1003) {
244 mCursorLocation = -1;
245 }
246 if (mCursorLocation == -1) {
247 displayValue = originalValue;
248 skipChars = 0;
249 textWidth = gr_measureEx(displayValue.c_str(), fontResource);
250 while (textWidth > mRenderW) {
251 displayValue.erase(0, 1);
252 skipChars++;
253 textWidth = gr_measureEx(displayValue.c_str(), fontResource);
254 }
255 scrollingX = mRenderW - textWidth;
256 mInputText->SkipCharCount(skipChars);
257 } else if (x == -1001) {
258 // Added a new character
259 int adjust_scrollingX = 0;
260 string cursorLocate;
261
262 cursorLocate = displayValue;
263 cursorLocate.resize(mCursorLocation);
264 textWidth = gr_measureEx(cursorLocate.c_str(), fontResource);
265 while (textWidth > mRenderW) {
266 skipChars++;
267 mCursorLocation--;
268 cursorLocate.erase(0, 1);
269 textWidth = gr_measureEx(cursorLocate.c_str(), fontResource);
270 adjust_scrollingX = -1;
271 }
272 if (adjust_scrollingX) {
273 scrollingX = mRenderW - textWidth;
274 if (scrollingX < 0)
275 scrollingX = 0;
276 }
277 mInputText->SkipCharCount(skipChars);
278 } else if (x == -1002) {
279 // Deleted a character
280 while (-1) {
281 if (skipChars == 0) {
282 scrollingX = 0;
283 mInputText->SkipCharCount(skipChars);
284 return 0;
285 }
286 insertChar = originalValue.substr(skipChars - 1, 1);
287 displayValue.insert(0, insertChar);
288 newWidth = gr_measureEx(displayValue.c_str(), fontResource);
289 deltaText = newWidth - textWidth;
290 if (newWidth > mRenderW) {
291 scrollingX = mRenderW - textWidth;
292 if (scrollingX < 0)
293 scrollingX = 0;
294 mInputText->SkipCharCount(skipChars);
295 return 0;
296 } else {
297 textWidth = newWidth;
298 skipChars--;
299 mCursorLocation++;
300 }
301 }
302 } else
303 LOGI("GUIInput::HandleTextLocation -> We really shouldn't ever get here...\n");
304 } else if (x > lastX) {
305 // Dragging to right, scrolling left
306 while (-1) {
307 deltaX = x - lastX + scrollingX;
308 if (skipChars == 0 || deltaX == 0) {
309 scrollingX = 0;
310 lastX = x;
311 mInputText->SkipCharCount(skipChars);
312 return 0;
313 }
314 insertChar = originalValue.substr(skipChars - 1, 1);
315 displayValue.insert(0, insertChar);
316 newWidth = gr_measureEx(displayValue.c_str(), fontResource);
317 deltaText = newWidth - textWidth;
318 if (deltaText < deltaX) {
319 lastX += deltaText;
320 textWidth = newWidth;
321 skipChars--;
322 } else {
323 scrollingX = deltaX;
324 lastX = x;
325 mInputText->SkipCharCount(skipChars);
326 return 0;
327 }
328 }
329 } else if (x < lastX) {
330 // Dragging to left, scrolling right
331 if (textWidth <= mRenderW) {
332 lastX = x;
333 scrollingX = mRenderW - textWidth;
334 return 0;
335 }
336 if (scrollingX) {
337 deltaX = lastX - x;
338 if (scrollingX > deltaX) {
339 scrollingX -= deltaX;
340 lastX = x;
341 return 0;
342 } else {
343 lastX -= deltaX;
344 scrollingX = 0;
345 }
346 }
347 while (-1) {
348 deltaX = lastX - x;
349 displayValue.erase(0, 1);
350 skipChars++;
351 newWidth = gr_measureEx(displayValue.c_str(), fontResource);
352 deltaText = textWidth - newWidth;
353 if (newWidth <= mRenderW) {
354 scrollingX = mRenderW - newWidth;
355 lastX = x;
356 mInputText->SkipCharCount(skipChars);
357 return 0;
358 }
359 if (deltaText < deltaX) {
360 lastX -= deltaText;
361 textWidth = newWidth;
362 } else {
363 scrollingX = deltaText - deltaX;
364 lastX = x;
365 mInputText->SkipCharCount(skipChars);
366 return 0;
367 }
368 }
369 }
370 return 0;
371}
372
373int GUIInput::Render(void)
374{
375 if (!isConditionTrue())
376 {
377 mRendered = false;
378 return 0;
379 }
380
381 void* fontResource = NULL;
382 if (mFont) fontResource = mFont->GetResource();
383
384 // First step, fill background
385 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
386 gr_fill(mRenderX, mRenderY, mRenderW, mRenderH);
387
388 // Next, render the background resource (if it exists)
389 if (mBackground && mBackground->GetResource())
390 {
391 mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
392 mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
393 gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
394 }
395
396 int ret = 0;
397
398 // Render the text
399 mInputText->SetRenderPos(mRenderX + scrollingX, mFontY);
400 mInputText->SetMaxWidth(mRenderW - scrollingX);
401 if (mInputText) ret = mInputText->Render();
402 if (ret < 0) return ret;
403
404 if (HasInputFocus && DrawCursor) {
405 // Render the cursor
406 string displayValue;
407 int cursorX;
408 DataManager::GetValue(mVariable, displayValue);
409 if (HasMask) {
410 int index, string_size = displayValue.size();
411 string maskedValue;
412 for (index=0; index<string_size; index++)
413 maskedValue += mMask;
414 displayValue = maskedValue;
415 }
416 if (displayValue.size() == 0) {
417 skipChars = 0;
418 mCursorLocation = -1;
419 cursorX = mRenderX;
420 } else {
421 if (skipChars && skipChars < displayValue.size()) {
422 displayValue.erase(0, skipChars);
423 }
424 if (mCursorLocation == 0) {
425 // Cursor is at the beginning
426 cursorX = mRenderX;
427 } else if (mCursorLocation > 0) {
428 // Cursor is in the middle
429 if (displayValue.size() > (unsigned)mCursorLocation) {
430 string cursorDisplay;
431
432 cursorDisplay = displayValue;
433 cursorDisplay.resize(mCursorLocation);
434 cursorX = gr_measureEx(cursorDisplay.c_str(), fontResource) + mRenderX;
435 } else {
436 // Cursor location is after the end of the text - reset to -1
437 mCursorLocation = -1;
438 cursorX = gr_measureEx(displayValue.c_str(), fontResource) + mRenderX;
439 }
440 } else {
441 // Cursor is at the end (-1)
442 cursorX = gr_measureEx(displayValue.c_str(), fontResource) + mRenderX;
443 }
444 }
445 cursorX += scrollingX;
446 // Make sure that the cursor doesn't go past the boundaries of the box
447 if (cursorX + (int)CursorWidth > mRenderX + mRenderW)
448 cursorX = mRenderX + mRenderW - CursorWidth;
449
450 // Set the color for the cursor
451 gr_color(mCursorColor.red, mCursorColor.green, mCursorColor.blue, 255);
452 gr_fill(cursorX, mFontY, CursorWidth, mFontHeight);
453 }
454
455 mRendered = true;
456 return ret;
457}
458
459int GUIInput::Update(void)
460{
461 if (!isConditionTrue()) return (mRendered ? 2 : 0);
462 if (!mRendered) return 2;
463
464 int ret = 0;
465
466 if (mInputText) ret = mInputText->Update();
467 if (ret < 0) return ret;
468
469 return ret;
470}
471
472int GUIInput::GetSelection(int x, int y)
473{
474 if (x < mRenderX || x - mRenderX > mRenderW || y < mRenderY || y - mRenderY > mRenderH) return -1;
475 return (x - mRenderX);
476}
477
478int GUIInput::NotifyTouch(TOUCH_STATE state, int x, int y)
479{
480 static int startSelection = -1;
481 int textWidth;
482 string displayValue, originalValue;
483 void* fontResource = NULL;
484
485 if (mFont) fontResource = mFont->GetResource();
486
487 if (!isConditionTrue()) return -1;
488
489 if (!HasInputFocus) {
490 if (state != TOUCH_RELEASE)
491 return 0; // Only change focus if touch releases within the input box
492 if (GetSelection(x, y) >= 0) {
493 // When changing focus, we don't scroll or change the cursor location
494 PageManager::SetKeyBoardFocus(0);
495 PageManager::NotifyKeyboard(0);
496 SetInputFocus(1);
497 DrawCursor = true;
498 mRendered = false;
499 }
500 } else {
501 switch (state) {
502 case TOUCH_HOLD:
503 case TOUCH_REPEAT:
504 break;
505 case TOUCH_START:
506 startSelection = GetSelection(x,y);
507 lastX = x;
508 DrawCursor = false;
509 mRendered = false;
510 break;
511
512 case TOUCH_DRAG:
513 // Check if we dragged out of the selection window
514 if (GetSelection(x, y) == -1) {
515 lastX = 0;
516 break;
517 }
518
519 DrawCursor = false;
520
521 // Provide some debounce on initial touches
522 if (startSelection != -1 && abs(x - lastX) < 6) {
523 break;
524 }
525
526 startSelection = -1;
527 if (lastX != x)
528 HandleTextLocation(x);
529 break;
530
531 case TOUCH_RELEASE:
532 // We've moved the cursor location
533 int relativeX = x - mRenderX;
534
535 mRendered = false;
536 DrawCursor = true;
537 DataManager::GetValue(mVariable, displayValue);
538 if (HasMask) {
539 int index, string_size = displayValue.size();
540 string maskedValue;
541 for (index=0; index<string_size; index++)
542 maskedValue += mMask;
543 displayValue = maskedValue;
544 }
545 if (displayValue.size() == 0) {
546 skipChars = 0;
547 mCursorLocation = -1;
548 return 0;
549 } else if (skipChars && skipChars < displayValue.size()) {
550 displayValue.erase(0, skipChars);
551 }
552
553 string cursorString;
554 int cursorX = 0;
555 unsigned index = 0;
556
557 for(index=0; index<displayValue.size(); index++)
558 {
559 cursorString = displayValue.substr(0, index);
560 cursorX = gr_measureEx(cursorString.c_str(), fontResource) + mRenderX;
561 if (cursorX > x) {
562 if (index > 0)
563 mCursorLocation = index - 1;
564 else
565 mCursorLocation = index;
566 return 0;
567 }
568 }
569 mCursorLocation = -1;
570 break;
571 }
572 }
573 return 0;
574}
575
576int GUIInput::NotifyVarChange(std::string varName, std::string value)
577{
578 if (varName == mVariable && !isLocalChange) {
579 HandleTextLocation(-1003);
580 return 0;
581 }
582 return 0;
583}
584
585int GUIInput::NotifyKeyboard(int key)
586{
587 string variableValue;
588
589 if (HasInputFocus) {
590 if (key == KEYBOARD_BACKSPACE) {
591 //Backspace
592 DataManager::GetValue(mVariable, variableValue);
593 if (variableValue.size() > 0 && (mCursorLocation + skipChars != 0 || mCursorLocation == -1)) {
594 if (mCursorLocation == -1) {
595 variableValue.resize(variableValue.size() - 1);
596 } else {
597 variableValue.erase(mCursorLocation + skipChars - 1, 1);
598 if (mCursorLocation > 0)
599 mCursorLocation--;
600 else if (skipChars > 0)
601 skipChars--;
602 }
603 isLocalChange = true;
604 DataManager::SetValue(mVariable, variableValue);
605 isLocalChange = false;
606
607 if (HasMask) {
608 int index, string_size = variableValue.size();
609 string maskedValue;
610 for (index=0; index<string_size; index++)
611 maskedValue += mMask;
612 DataManager::SetValue(mMaskVariable, maskedValue);
613 }
614 HandleTextLocation(-1002);
615 }
616 } else if (key == KEYBOARD_SWIPE_LEFT) {
617 // Delete all
618 isLocalChange = true;
619 if (mCursorLocation == -1) {
620 DataManager::SetValue (mVariable, "");
621 if (HasMask)
622 DataManager::SetValue(mMaskVariable, "");
623 mCursorLocation = -1;
624 } else {
625 DataManager::GetValue(mVariable, variableValue);
626 variableValue.erase(0, mCursorLocation + skipChars);
627 DataManager::SetValue(mVariable, variableValue);
628 if (HasMask) {
629 DataManager::GetValue(mMaskVariable, variableValue);
630 variableValue.erase(0, mCursorLocation + skipChars);
631 DataManager::SetValue(mMaskVariable, variableValue);
632 }
633 mCursorLocation = 0;
634 }
635 skipChars = 0;
636 scrollingX = 0;
637 mInputText->SkipCharCount(skipChars);
638 isLocalChange = false;
639 mRendered = false;
640 return 0;
641 } else if (key == KEYBOARD_ARROW_LEFT) {
642 if (mCursorLocation == 0 && skipChars == 0)
643 return 0; // we're already at the beginning
644 if (mCursorLocation == -1) {
645 DataManager::GetValue(mVariable, variableValue);
646 if (variableValue.size() == 0)
647 return 0;
648 mCursorLocation = variableValue.size() - skipChars - 1;
649 } else if (mCursorLocation == 0) {
650 skipChars--;
651 HandleTextLocation(-1002);
652 } else {
653 mCursorLocation--;
654 HandleTextLocation(-1002);
655 }
656 mRendered = false;
657 return 0;
658 } else if (key == KEYBOARD_ARROW_RIGHT) {
659 if (mCursorLocation == -1)
660 return 0; // we're already at the end
661 mCursorLocation++;
662 DataManager::GetValue(mVariable, variableValue);
663 if (variableValue.size() <= mCursorLocation + skipChars)
664 mCursorLocation = -1;
665 HandleTextLocation(-1001);
666 mRendered = false;
667 return 0;
668 } else if (key == KEYBOARD_HOME || key == KEYBOARD_ARROW_UP) {
669 DataManager::GetValue(mVariable, variableValue);
670 if (variableValue.size() == 0)
671 return 0;
672 mCursorLocation = 0;
673 skipChars = 0;
674 mRendered = false;
675 HandleTextLocation(-1002);
676 return 0;
677 } else if (key == KEYBOARD_END || key == KEYBOARD_ARROW_DOWN) {
678 mCursorLocation = -1;
679 mRendered = false;
680 HandleTextLocation(-1003);
681 return 0;
682 } else if (key < KEYBOARD_SPECIAL_KEYS && key > 0) {
683 // Regular key
684 if (HasAllowed && AllowedList.find((char)key) == string::npos) {
685 return 0;
686 }
687 if (HasDisabled && DisabledList.find((char)key) != string::npos) {
688 return 0;
689 }
690 DataManager::GetValue(mVariable, variableValue);
691 if (MaxLen != 0 && variableValue.size() >= MaxLen) {
692 return 0;
693 }
694 if (mCursorLocation == -1) {
695 variableValue += key;
696 } else {
697 const char newchar = (char)key;
698 const char* a = &newchar;
699 string newstring = a;
700 newstring.resize(1);
701 variableValue.insert(mCursorLocation + skipChars, newstring);
702 mCursorLocation++;
703 }
704 isLocalChange = true;
705 DataManager::SetValue(mVariable, variableValue);
706 HandleTextLocation(-1001);
707 isLocalChange = false;
708
709 if (HasMask) {
710 int index, string_size = variableValue.size();
711 string maskedValue;
712 for (index=0; index<string_size; index++)
713 maskedValue += mMask;
714 DataManager::SetValue(mMaskVariable, maskedValue);
715 }
716 } else if (key == KEYBOARD_ACTION) {
717 // Action
718 DataManager::GetValue(mVariable, variableValue);
719 if (mAction) {
720 unsigned inputLen = variableValue.length();
721 if (inputLen < MinLen)
722 return 0;
723 else if (MaxLen != 0 && inputLen > MaxLen)
724 return 0;
725 else
726 return (mAction ? mAction->NotifyTouch(TOUCH_RELEASE, mRenderX, mRenderY) : 1);
727 }
728 }
729 return 0;
730 } else {
731 if (key == 0) {
732 // Somewhat ugly hack-ish way to tell the box to redraw after losing focus to remove the cursor
733 mRendered = false;
734 return 1;
735 }
736 }
737 return 1;
738}