1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "GUIEditControl.h"
10
11 #include "GUIKeyboardFactory.h"
12 #include "GUIUserMessages.h"
13 #include "GUIWindowManager.h"
14 #include "LocalizeStrings.h"
15 #include "ServiceBroker.h"
16 #include "XBDateTime.h"
17 #include "dialogs/GUIDialogNumeric.h"
18 #include "input/Key.h"
19 #include "input/XBMC_vkeys.h"
20 #include "utils/CharsetConverter.h"
21 #include "utils/Color.h"
22 #include "utils/Digest.h"
23 #include "utils/Variant.h"
24 #include "windowing/WinSystem.h"
25
26 using namespace KODI::GUILIB;
27
28 using KODI::UTILITY::CDigest;
29
30 const char* CGUIEditControl::smsLetters[10] = { " !@#$%^&*()[]{}<>/\\|0", ".,;:\'\"-+_=?`~1", "abc2ABC", "def3DEF", "ghi4GHI", "jkl5JKL", "mno6MNO", "pqrs7PQRS", "tuv8TUV", "wxyz9WXYZ" };
31 const unsigned int CGUIEditControl::smsDelay = 1000;
32
33 #ifdef TARGET_WINDOWS
34 extern HWND g_hWnd;
35 #endif
36
CGUIEditControl(int parentID,int controlID,float posX,float posY,float width,float height,const CTextureInfo & textureFocus,const CTextureInfo & textureNoFocus,const CLabelInfo & labelInfo,const std::string & text)37 CGUIEditControl::CGUIEditControl(int parentID, int controlID, float posX, float posY,
38 float width, float height, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus,
39 const CLabelInfo& labelInfo, const std::string &text)
40 : CGUIButtonControl(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo)
41 {
42 DefaultConstructor();
43 SetLabel(text);
44 }
45
DefaultConstructor()46 void CGUIEditControl::DefaultConstructor()
47 {
48 ControlType = GUICONTROL_EDIT;
49 m_textOffset = 0;
50 m_textWidth = GetWidth();
51 m_cursorPos = 0;
52 m_cursorBlink = 0;
53 m_inputHeading = g_localizeStrings.Get(16028);
54 m_inputType = INPUT_TYPE_TEXT;
55 m_smsLastKey = 0;
56 m_smsKeyIndex = 0;
57 m_label.SetAlign(m_label.GetLabelInfo().align & XBFONT_CENTER_Y); // left align
58 m_label2.GetLabelInfo().offsetX = 0;
59 m_isMD5 = false;
60 m_invalidInput = false;
61 m_inputValidator = NULL;
62 m_inputValidatorData = NULL;
63 m_editLength = 0;
64 m_editOffset = 0;
65 }
66
CGUIEditControl(const CGUIButtonControl & button)67 CGUIEditControl::CGUIEditControl(const CGUIButtonControl &button)
68 : CGUIButtonControl(button)
69 {
70 DefaultConstructor();
71 }
72
73 CGUIEditControl::~CGUIEditControl(void) = default;
74
OnMessage(CGUIMessage & message)75 bool CGUIEditControl::OnMessage(CGUIMessage &message)
76 {
77 if (message.GetMessage() == GUI_MSG_SET_TYPE)
78 {
79 SetInputType((INPUT_TYPE)message.GetParam1(), message.GetParam2());
80 return true;
81 }
82 else if (message.GetMessage() == GUI_MSG_ITEM_SELECTED)
83 {
84 message.SetLabel(GetLabel2());
85 return true;
86 }
87 else if (message.GetMessage() == GUI_MSG_SET_TEXT &&
88 ((message.GetControlId() <= 0 && HasFocus()) || (message.GetControlId() == GetID())))
89 {
90 SetLabel2(message.GetLabel());
91 UpdateText();
92 }
93 return CGUIButtonControl::OnMessage(message);
94 }
95
OnAction(const CAction & action)96 bool CGUIEditControl::OnAction(const CAction &action)
97 {
98 ValidateCursor();
99
100 if (m_inputType != INPUT_TYPE_READONLY)
101 {
102 if (action.GetID() == ACTION_BACKSPACE)
103 {
104 // backspace
105 if (m_cursorPos)
106 {
107 if (!ClearMD5())
108 m_text2.erase(--m_cursorPos, 1);
109 UpdateText();
110 }
111 return true;
112 }
113 else if (action.GetID() == ACTION_MOVE_LEFT ||
114 action.GetID() == ACTION_CURSOR_LEFT)
115 {
116 if (m_cursorPos > 0)
117 {
118 m_cursorPos--;
119 UpdateText(false);
120 return true;
121 }
122 }
123 else if (action.GetID() == ACTION_MOVE_RIGHT ||
124 action.GetID() == ACTION_CURSOR_RIGHT)
125 {
126 if (m_cursorPos < m_text2.size())
127 {
128 m_cursorPos++;
129 UpdateText(false);
130 return true;
131 }
132 }
133 else if (action.GetID() == ACTION_PASTE)
134 {
135 ClearMD5();
136 OnPasteClipboard();
137 return true;
138 }
139 else if (action.GetID() >= KEY_VKEY && action.GetID() < KEY_UNICODE && m_edit.empty())
140 {
141 // input from the keyboard (vkey, not ascii)
142 unsigned char b = action.GetID() & 0xFF;
143 if (b == XBMCVK_HOME)
144 {
145 m_cursorPos = 0;
146 UpdateText(false);
147 return true;
148 }
149 else if (b == XBMCVK_END)
150 {
151 m_cursorPos = m_text2.length();
152 UpdateText(false);
153 return true;
154 }
155 if (b == XBMCVK_LEFT && m_cursorPos > 0)
156 {
157 m_cursorPos--;
158 UpdateText(false);
159 return true;
160 }
161 if (b == XBMCVK_RIGHT && m_cursorPos < m_text2.length())
162 {
163 m_cursorPos++;
164 UpdateText(false);
165 return true;
166 }
167 if (b == XBMCVK_DELETE)
168 {
169 if (m_cursorPos < m_text2.length())
170 {
171 if (!ClearMD5())
172 m_text2.erase(m_cursorPos, 1);
173 UpdateText();
174 return true;
175 }
176 }
177 if (b == XBMCVK_BACK)
178 {
179 if (m_cursorPos > 0)
180 {
181 if (!ClearMD5())
182 m_text2.erase(--m_cursorPos, 1);
183 UpdateText();
184 }
185 return true;
186 }
187 else if (b == XBMCVK_RETURN || b == XBMCVK_NUMPADENTER)
188 {
189 // enter - send click message, but otherwise ignore
190 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1);
191 return true;
192 }
193 else if (b == XBMCVK_ESCAPE)
194 { // escape - fallthrough to default action
195 return CGUIButtonControl::OnAction(action);
196 }
197 }
198 else if (action.GetID() == KEY_UNICODE)
199 {
200 // input from the keyboard
201 int ch = action.GetUnicode();
202 // ignore non-printing characters
203 if ( !((0 <= ch && ch < 0x8) || (0xE <= ch && ch < 0x1B) || (0x1C <= ch && ch < 0x20)) )
204 {
205 switch (ch)
206 {
207 case 9: // tab, ignore
208 case 11: // Non-printing character, ignore
209 case 12: // Non-printing character, ignore
210 break;
211 case 10:
212 case 13:
213 {
214 // enter - send click message, but otherwise ignore
215 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1);
216 return true;
217 }
218 case 27:
219 { // escape - fallthrough to default action
220 return CGUIButtonControl::OnAction(action);
221 }
222 case 8:
223 {
224 // backspace
225 if (m_cursorPos)
226 {
227 if (!ClearMD5())
228 m_text2.erase(--m_cursorPos, 1);
229 }
230 break;
231 }
232 case 127:
233 { // delete
234 if (m_cursorPos < m_text2.length())
235 {
236 if (!ClearMD5())
237 m_text2.erase(m_cursorPos, 1);
238 }
239 break;
240 }
241 default:
242 {
243 ClearMD5();
244 m_edit.clear();
245 m_text2.insert(m_text2.begin() + m_cursorPos++, action.GetUnicode());
246 break;
247 }
248 }
249 UpdateText();
250 return true;
251 }
252 }
253 else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
254 { // input from the remote
255 ClearMD5();
256 m_edit.clear();
257 OnSMSCharacter(action.GetID() - REMOTE_0);
258 return true;
259 }
260 else if (action.GetID() == ACTION_INPUT_TEXT)
261 {
262 m_edit.clear();
263 std::wstring str;
264 g_charsetConverter.utf8ToW(action.GetText(), str, false);
265 m_text2.insert(m_cursorPos, str);
266 m_cursorPos += str.size();
267 UpdateText();
268 return true;
269 }
270 }
271 return CGUIButtonControl::OnAction(action);
272 }
273
OnClick()274 void CGUIEditControl::OnClick()
275 {
276 // we received a click - it's not from the keyboard, so pop up the virtual keyboard, unless
277 // that is where we reside!
278 if (GetParentID() == WINDOW_DIALOG_KEYBOARD)
279 return;
280
281 std::string utf8;
282 g_charsetConverter.wToUTF8(m_text2, utf8);
283 bool textChanged = false;
284 switch (m_inputType)
285 {
286 case INPUT_TYPE_READONLY:
287 textChanged = false;
288 break;
289 case INPUT_TYPE_NUMBER:
290 textChanged = CGUIDialogNumeric::ShowAndGetNumber(utf8, m_inputHeading);
291 break;
292 case INPUT_TYPE_SECONDS:
293 textChanged = CGUIDialogNumeric::ShowAndGetSeconds(utf8, g_localizeStrings.Get(21420));
294 break;
295 case INPUT_TYPE_TIME:
296 {
297 CDateTime dateTime;
298 dateTime.SetFromDBTime(utf8);
299 KODI::TIME::SystemTime time;
300 dateTime.GetAsSystemTime(time);
301 if (CGUIDialogNumeric::ShowAndGetTime(time, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420)))
302 {
303 dateTime = CDateTime(time);
304 utf8 = dateTime.GetAsLocalizedTime("", false);
305 textChanged = true;
306 }
307 break;
308 }
309 case INPUT_TYPE_DATE:
310 {
311 CDateTime dateTime;
312 dateTime.SetFromDBDate(utf8);
313 if (dateTime < CDateTime(2000,1, 1, 0, 0, 0))
314 dateTime = CDateTime(2000, 1, 1, 0, 0, 0);
315 KODI::TIME::SystemTime date;
316 dateTime.GetAsSystemTime(date);
317 if (CGUIDialogNumeric::ShowAndGetDate(date, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420)))
318 {
319 dateTime = CDateTime(date);
320 utf8 = dateTime.GetAsDBDate();
321 textChanged = true;
322 }
323 break;
324 }
325 case INPUT_TYPE_IPADDRESS:
326 textChanged = CGUIDialogNumeric::ShowAndGetIPAddress(utf8, m_inputHeading);
327 break;
328 case INPUT_TYPE_SEARCH:
329 textChanged = CGUIKeyboardFactory::ShowAndGetFilter(utf8, true);
330 break;
331 case INPUT_TYPE_FILTER:
332 textChanged = CGUIKeyboardFactory::ShowAndGetFilter(utf8, false);
333 break;
334 case INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW:
335 textChanged = CGUIDialogNumeric::ShowAndVerifyNewPassword(utf8);
336 break;
337 case INPUT_TYPE_PASSWORD_MD5:
338 utf8 = ""; //! @todo Ideally we'd send this to the keyboard and tell the keyboard we have this type of input
339 // fallthrough
340 case INPUT_TYPE_TEXT:
341 default:
342 textChanged = CGUIKeyboardFactory::ShowAndGetInput(utf8, m_inputHeading, true, m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5);
343 break;
344 }
345 if (textChanged)
346 {
347 ClearMD5();
348 m_edit.clear();
349 g_charsetConverter.utf8ToW(utf8, m_text2);
350 m_cursorPos = m_text2.size();
351 UpdateText();
352 m_cursorPos = m_text2.size();
353 }
354 }
355
UpdateText(bool sendUpdate)356 void CGUIEditControl::UpdateText(bool sendUpdate)
357 {
358 m_smsTimer.Stop();
359 if (sendUpdate)
360 {
361 ValidateInput();
362
363 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 0);
364
365 m_textChangeActions.ExecuteActions(GetID(), GetParentID());
366 }
367 SetInvalid();
368 }
369
SetInputType(CGUIEditControl::INPUT_TYPE type,const CVariant & heading)370 void CGUIEditControl::SetInputType(CGUIEditControl::INPUT_TYPE type, const CVariant& heading)
371 {
372 m_inputType = type;
373 if (heading.isString())
374 m_inputHeading = heading.asString();
375 else if (heading.isInteger() && heading.asInteger())
376 m_inputHeading = g_localizeStrings.Get(static_cast<uint32_t>(heading.asInteger()));
377 //! @todo Verify the current input string?
378 }
379
RecalcLabelPosition()380 void CGUIEditControl::RecalcLabelPosition()
381 {
382 // ensure that our cursor is within our width
383 ValidateCursor();
384
385 std::wstring text = GetDisplayedText();
386 m_textWidth = m_label.CalcTextWidth(text + L'|');
387 float beforeCursorWidth = m_label.CalcTextWidth(text.substr(0, m_cursorPos));
388 float afterCursorWidth = m_label.CalcTextWidth(text.substr(0, m_cursorPos) + L'|');
389 float leftTextWidth = m_label.GetRenderRect().Width();
390 float maxTextWidth = m_label.GetMaxWidth();
391 if (leftTextWidth > 0)
392 maxTextWidth -= leftTextWidth + spaceWidth;
393
394 // if skinner forgot to set height :p
395 if (m_height == 0 && m_label.GetLabelInfo().font)
396 m_height = m_label.GetLabelInfo().font->GetTextHeight(1);
397
398 if (m_textWidth > maxTextWidth)
399 { // we render taking up the full width, so make sure our cursor position is
400 // within the render window
401 if (m_textOffset + afterCursorWidth > maxTextWidth)
402 {
403 // move the position to the left (outside of the viewport)
404 m_textOffset = maxTextWidth - afterCursorWidth;
405 }
406 else if (m_textOffset + beforeCursorWidth < 0) // offscreen to the left
407 {
408 // otherwise use original position
409 m_textOffset = -beforeCursorWidth;
410 }
411 else if (m_textOffset + m_textWidth < maxTextWidth)
412 { // we have more text than we're allowed, but we aren't filling all the space
413 m_textOffset = maxTextWidth - m_textWidth;
414 }
415 }
416 else
417 m_textOffset = 0;
418 }
419
ProcessText(unsigned int currentTime)420 void CGUIEditControl::ProcessText(unsigned int currentTime)
421 {
422 if (m_smsTimer.IsRunning() && m_smsTimer.GetElapsedMilliseconds() > smsDelay)
423 UpdateText();
424
425 if (m_bInvalidated)
426 {
427 m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
428 m_label.SetText(m_info.GetLabel(GetParentID()));
429 RecalcLabelPosition();
430 }
431
432 bool changed = false;
433
434 m_clipRect.x1 = m_label.GetRenderRect().x1;
435 m_clipRect.x2 = m_clipRect.x1 + m_label.GetMaxWidth();
436 m_clipRect.y1 = m_posY;
437 m_clipRect.y2 = m_posY + m_height;
438
439 // start by rendering the normal text
440 float leftTextWidth = m_label.GetRenderRect().Width();
441 if (leftTextWidth > 0)
442 {
443 // render the text on the left
444 changed |= m_label.SetColor(GetTextColor());
445 changed |= m_label.Process(currentTime);
446
447 m_clipRect.x1 += leftTextWidth + spaceWidth;
448 }
449
450 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_clipRect.x1, m_clipRect.y1, m_clipRect.Width(), m_clipRect.Height()))
451 {
452 uint32_t align = m_label.GetLabelInfo().align & XBFONT_CENTER_Y; // start aligned left
453 if (m_label2.GetTextWidth() < m_clipRect.Width())
454 { // align text as our text fits
455 if (leftTextWidth > 0)
456 { // right align as we have 2 labels
457 align |= XBFONT_RIGHT;
458 }
459 else
460 { // align by whatever the skinner requests
461 align |= (m_label2.GetLabelInfo().align & 3);
462 }
463 }
464 changed |= m_label2.SetMaxRect(m_clipRect.x1 + m_textOffset, m_posY, m_clipRect.Width() - m_textOffset, m_height);
465
466 std::wstring text = GetDisplayedText();
467 std::string hint = m_hintInfo.GetLabel(GetParentID());
468
469 if (!HasFocus() && text.empty() && !hint.empty())
470 {
471 changed |= m_label2.SetText(hint);
472 }
473 else if ((HasFocus() || GetParentID() == WINDOW_DIALOG_KEYBOARD) &&
474 m_inputType != INPUT_TYPE_READONLY)
475 {
476 changed |= SetStyledText(text);
477 }
478 else
479 changed |= m_label2.SetTextW(text);
480
481 changed |= m_label2.SetAlign(align);
482 changed |= m_label2.SetColor(GetTextColor());
483 changed |= m_label2.SetOverflow(CGUILabel::OVER_FLOW_CLIP);
484 changed |= m_label2.Process(currentTime);
485 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
486 }
487 if (changed)
488 MarkDirtyRegion();
489 }
490
RenderText()491 void CGUIEditControl::RenderText()
492 {
493 m_label.Render();
494
495 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_clipRect.x1, m_clipRect.y1, m_clipRect.Width(), m_clipRect.Height()))
496 {
497 m_label2.Render();
498 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
499 }
500 }
501
GetTextColor() const502 CGUILabel::COLOR CGUIEditControl::GetTextColor() const
503 {
504 CGUILabel::COLOR color = CGUIButtonControl::GetTextColor();
505 if (color != CGUILabel::COLOR_DISABLED && HasInvalidInput())
506 return CGUILabel::COLOR_INVALID;
507
508 return color;
509 }
510
SetHint(const GUIINFO::CGUIInfoLabel & hint)511 void CGUIEditControl::SetHint(const GUIINFO::CGUIInfoLabel& hint)
512 {
513 m_hintInfo = hint;
514 }
515
GetDisplayedText() const516 std::wstring CGUIEditControl::GetDisplayedText() const
517 {
518 std::wstring text(m_text2);
519 if (m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW)
520 {
521 text.clear();
522 if (m_smsTimer.IsRunning())
523 { // using the remove to input, so display the last key input
524 text.append(m_cursorPos - 1, L'*');
525 text.append(1, m_text2[m_cursorPos - 1]);
526 text.append(m_text2.size() - m_cursorPos, L'*');
527 }
528 else
529 text.append(m_text2.size(), L'*');
530 }
531 else if (!m_edit.empty())
532 text.insert(m_editOffset, m_edit);
533 return text;
534 }
535
SetStyledText(const std::wstring & text)536 bool CGUIEditControl::SetStyledText(const std::wstring &text)
537 {
538 vecText styled;
539 styled.reserve(text.size() + 1);
540
541 std::vector<UTILS::Color> colors;
542 colors.push_back(m_label.GetLabelInfo().textColor);
543 colors.push_back(m_label.GetLabelInfo().disabledColor);
544 UTILS::Color select = m_label.GetLabelInfo().selectedColor;
545 if (!select)
546 select = 0xFFFF0000;
547 colors.push_back(select);
548 colors.push_back(0x00FFFFFF);
549
550 unsigned int startHighlight = m_cursorPos;
551 unsigned int endHighlight = m_cursorPos + m_edit.size();
552 unsigned int startSelection = m_cursorPos + m_editOffset;
553 unsigned int endSelection = m_cursorPos + m_editOffset + m_editLength;
554
555 for (unsigned int i = 0; i < text.size(); i++)
556 {
557 unsigned int ch = text[i];
558 if (m_editLength > 0 && startSelection <= i && i < endSelection)
559 ch |= (2 << 16); // highlight the letters we're playing with
560 else if (!m_edit.empty() && (i < startHighlight || i >= endHighlight))
561 ch |= (1 << 16); // dim the bits we're not editing
562 styled.push_back(ch);
563 }
564
565 // show the cursor
566 unsigned int ch = L'|';
567 if ((++m_cursorBlink % 64) > 32)
568 ch |= (3 << 16);
569 styled.insert(styled.begin() + m_cursorPos, ch);
570
571 return m_label2.SetStyledText(styled, colors);
572 }
573
ValidateCursor()574 void CGUIEditControl::ValidateCursor()
575 {
576 if (m_cursorPos > m_text2.size())
577 m_cursorPos = m_text2.size();
578 }
579
SetLabel(const std::string & text)580 void CGUIEditControl::SetLabel(const std::string &text)
581 {
582 CGUIButtonControl::SetLabel(text);
583 SetInvalid();
584 }
585
SetLabel2(const std::string & text)586 void CGUIEditControl::SetLabel2(const std::string &text)
587 {
588 m_edit.clear();
589 std::wstring newText;
590 g_charsetConverter.utf8ToW(text, newText, false);
591 if (newText != m_text2)
592 {
593 m_isMD5 = (m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW);
594 m_text2 = newText;
595 m_cursorPos = m_text2.size();
596 ValidateInput();
597 SetInvalid();
598 }
599 }
600
GetLabel2() const601 std::string CGUIEditControl::GetLabel2() const
602 {
603 std::string text;
604 g_charsetConverter.wToUTF8(m_text2, text);
605 if (m_inputType == INPUT_TYPE_PASSWORD_MD5 && !m_isMD5)
606 return CDigest::Calculate(CDigest::Type::MD5, text);
607 return text;
608 }
609
ClearMD5()610 bool CGUIEditControl::ClearMD5()
611 {
612 if (!(m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW) || !m_isMD5)
613 return false;
614
615 m_text2.clear();
616 m_cursorPos = 0;
617 if (m_inputType != INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW)
618 m_isMD5 = false;
619 return true;
620 }
621
GetCursorPosition() const622 unsigned int CGUIEditControl::GetCursorPosition() const
623 {
624 return m_cursorPos;
625 }
626
SetCursorPosition(unsigned int iPosition)627 void CGUIEditControl::SetCursorPosition(unsigned int iPosition)
628 {
629 m_cursorPos = iPosition;
630 }
631
OnSMSCharacter(unsigned int key)632 void CGUIEditControl::OnSMSCharacter(unsigned int key)
633 {
634 assert(key < 10);
635 if (m_smsTimer.IsRunning())
636 {
637 // we're already entering an SMS character
638 if (key != m_smsLastKey || m_smsTimer.GetElapsedMilliseconds() > smsDelay)
639 { // a different key was clicked than last time, or we have timed out
640 m_smsLastKey = key;
641 m_smsKeyIndex = 0;
642 }
643 else
644 { // same key as last time within the appropriate time period
645 m_smsKeyIndex++;
646 if (m_cursorPos)
647 m_text2.erase(--m_cursorPos, 1);
648 }
649 }
650 else
651 { // key is pressed for the first time
652 m_smsLastKey = key;
653 m_smsKeyIndex = 0;
654 }
655
656 m_smsKeyIndex = m_smsKeyIndex % strlen(smsLetters[key]);
657
658 m_text2.insert(m_text2.begin() + m_cursorPos++, smsLetters[key][m_smsKeyIndex]);
659 UpdateText();
660 m_smsTimer.StartZero();
661 }
662
OnPasteClipboard()663 void CGUIEditControl::OnPasteClipboard()
664 {
665 std::wstring unicode_text;
666 std::string utf8_text;
667
668 // Get text from the clipboard
669 utf8_text = CServiceBroker::GetWinSystem()->GetClipboardText();
670 g_charsetConverter.utf8ToW(utf8_text, unicode_text);
671
672 // Insert the pasted text at the current cursor position.
673 if (unicode_text.length() > 0)
674 {
675 std::wstring left_end = m_text2.substr(0, m_cursorPos);
676 std::wstring right_end = m_text2.substr(m_cursorPos);
677
678 m_text2 = left_end;
679 m_text2.append(unicode_text);
680 m_text2.append(right_end);
681 m_cursorPos += unicode_text.length();
682 UpdateText();
683 }
684 }
685
SetInputValidation(StringValidation::Validator inputValidator,void * data)686 void CGUIEditControl::SetInputValidation(StringValidation::Validator inputValidator, void *data /* = NULL */)
687 {
688 if (m_inputValidator == inputValidator)
689 return;
690
691 m_inputValidator = inputValidator;
692 m_inputValidatorData = data;
693 // the input validator has changed, so re-validate the current data
694 ValidateInput();
695 }
696
ValidateInput(const std::wstring & data) const697 bool CGUIEditControl::ValidateInput(const std::wstring &data) const
698 {
699 if (m_inputValidator == NULL)
700 return true;
701
702 return m_inputValidator(GetLabel2(), m_inputValidatorData != NULL ? m_inputValidatorData : const_cast<void*>((const void*)this));
703 }
704
ValidateInput()705 void CGUIEditControl::ValidateInput()
706 {
707 // validate the input
708 bool invalid = !ValidateInput(m_text2);
709 // nothing to do if still valid/invalid
710 if (invalid != m_invalidInput)
711 {
712 // the validity state has changed so we need to update the control
713 m_invalidInput = invalid;
714
715 // let the window/dialog know that the validity has changed
716 CGUIMessage msg(GUI_MSG_VALIDITY_CHANGED, GetID(), GetID(), m_invalidInput ? 0 : 1);
717 SendWindowMessage(msg);
718
719 SetInvalid();
720 }
721 }
722
SetFocus(bool focus)723 void CGUIEditControl::SetFocus(bool focus)
724 {
725 m_smsTimer.Stop();
726 CGUIControl::SetFocus(focus);
727 SetInvalid();
728 }
729
GetDescriptionByIndex(int index) const730 std::string CGUIEditControl::GetDescriptionByIndex(int index) const
731 {
732 if (index == 0)
733 return GetDescription();
734 else if(index == 1)
735 return GetLabel2();
736
737 return "";
738 }
739