/xbmc/guilib/GUIEditControl.cpp

http://github.com/xbmc/xbmc · C++ · 738 lines · 634 code · 70 blank · 34 comment · 179 complexity · 87020e53afcc8adf498c16d8115c86a4 MD5 · raw file

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