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