[WASAPI] fix stream types and frequencies enumeration
[xbmc.git] / xbmc / guilib / GUIEditControl.cpp
blobba69f9bdecbdcfc90b44cb61b982d6af35475f82
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "GUIEditControl.h"
11 #include "GUIFont.h"
12 #include "GUIKeyboardFactory.h"
13 #include "GUIUserMessages.h"
14 #include "GUIWindowManager.h"
15 #include "LocalizeStrings.h"
16 #include "ServiceBroker.h"
17 #include "XBDateTime.h"
18 #include "dialogs/GUIDialogNumeric.h"
19 #include "input/actions/Action.h"
20 #include "input/actions/ActionIDs.h"
21 #include "input/keyboard/KeyIDs.h"
22 #include "input/keyboard/XBMC_vkeys.h"
23 #include "utils/CharsetConverter.h"
24 #include "utils/ColorUtils.h"
25 #include "utils/Digest.h"
26 #include "utils/Variant.h"
27 #include "utils/log.h"
28 #include "windowing/WinSystem.h"
30 #include <algorithm>
32 using namespace KODI::GUILIB;
34 using KODI::UTILITY::CDigest;
36 #ifdef TARGET_WINDOWS
37 extern HWND g_hWnd;
38 #endif
40 namespace
42 constexpr std::string_view smsLetters[] = {" !@#$%^&*()[]{}<>/\\|0",
43 ".,;:\'\"-+_=?`~1",
44 "abc2ABC",
45 "def3DEF",
46 "ghi4GHI",
47 "jkl5JKL",
48 "mno6MNO",
49 "pqrs7PQRS",
50 "tuv8TUV",
51 "wxyz9WXYZ"};
53 constexpr float smsDelay = 1000;
55 // Additional space between left label text and left label text in pixels
56 constexpr float TEXT_SPACE = 20.0f;
57 } // unnamed namespace
59 CGUIEditControl::CGUIEditControl(int parentID, int controlID, float posX, float posY,
60 float width, float height, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus,
61 const CLabelInfo& labelInfo, const std::string &text)
62 : CGUIButtonControl(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo)
64 DefaultConstructor();
65 SetLabel(text);
67 // if skinner forgot to set height
68 if (m_height == 0 && m_label.GetLabelInfo().font)
70 m_height = m_label.GetLabelInfo().font->GetTextHeight(1);
71 CLog::LogF(LOGWARNING,
72 "No height has been set for GUI edit control ID {}, fallback to font height",
73 controlID);
77 void CGUIEditControl::DefaultConstructor()
79 ControlType = GUICONTROL_EDIT;
80 m_textOffset = 0;
81 m_cursorPos = 0;
82 m_cursorBlink = 0;
83 m_inputHeading = g_localizeStrings.Get(16028);
84 m_inputType = INPUT_TYPE_TEXT;
85 m_smsLastKey = 0;
86 m_smsKeyIndex = 0;
87 m_label2.GetLabelInfo().offsetX = 0;
88 m_isMD5 = false;
89 m_invalidInput = false;
90 m_inputValidator = NULL;
91 m_inputValidatorData = NULL;
92 m_editLength = 0;
93 m_editOffset = 0;
96 CGUIEditControl::CGUIEditControl(const CGUIButtonControl& button) : CGUIButtonControl(button)
98 DefaultConstructor();
101 CGUIEditControl::CGUIEditControl(const CGUIEditControl& button) : CGUIButtonControl(button)
103 DefaultConstructor();
106 CGUIEditControl::~CGUIEditControl(void) = default;
108 bool CGUIEditControl::OnMessage(CGUIMessage &message)
110 if (message.GetMessage() == GUI_MSG_SET_TYPE)
112 SetInputType((INPUT_TYPE)message.GetParam1(), message.GetParam2());
113 return true;
115 else if (message.GetMessage() == GUI_MSG_ITEM_SELECTED)
117 message.SetLabel(GetLabel2());
118 return true;
120 else if (message.GetMessage() == GUI_MSG_SET_TEXT &&
121 ((message.GetControlId() <= 0 && HasFocus()) || (message.GetControlId() == GetID())))
123 SetLabel2(message.GetLabel());
124 UpdateText();
126 return CGUIButtonControl::OnMessage(message);
129 bool CGUIEditControl::OnAction(const CAction &action)
131 ValidateCursor();
133 if (m_inputType != INPUT_TYPE_READONLY)
135 if (action.GetID() == ACTION_BACKSPACE)
137 // backspace
138 if (m_cursorPos)
140 if (!ClearMD5())
141 m_text2.erase(--m_cursorPos, 1);
142 UpdateText();
144 return true;
146 else if (action.GetID() == ACTION_MOVE_LEFT ||
147 action.GetID() == ACTION_CURSOR_LEFT)
149 if (m_cursorPos > 0)
151 m_cursorPos--;
152 UpdateText(false);
153 return true;
156 else if (action.GetID() == ACTION_MOVE_RIGHT ||
157 action.GetID() == ACTION_CURSOR_RIGHT)
159 if (m_cursorPos < m_text2.size())
161 m_cursorPos++;
162 UpdateText(false);
163 return true;
166 else if (action.GetID() == ACTION_PASTE)
168 ClearMD5();
169 OnPasteClipboard();
170 return true;
172 else if (action.GetID() >= KEY_VKEY && action.GetID() < KEY_UNICODE && m_edit.empty())
174 // input from the keyboard (vkey, not ascii)
175 unsigned char b = action.GetID() & 0xFF;
176 if (b == XBMCVK_HOME)
178 m_cursorPos = 0;
179 UpdateText(false);
180 return true;
182 else if (b == XBMCVK_END)
184 m_cursorPos = m_text2.length();
185 UpdateText(false);
186 return true;
188 if (b == XBMCVK_LEFT && m_cursorPos > 0)
190 m_cursorPos--;
191 UpdateText(false);
192 return true;
194 if (b == XBMCVK_RIGHT && m_cursorPos < m_text2.length())
196 m_cursorPos++;
197 UpdateText(false);
198 return true;
200 if (b == XBMCVK_DELETE)
202 if (m_cursorPos < m_text2.length())
204 if (!ClearMD5())
205 m_text2.erase(m_cursorPos, 1);
206 UpdateText();
207 return true;
210 if (b == XBMCVK_BACK)
212 if (m_cursorPos > 0)
214 if (!ClearMD5())
215 m_text2.erase(--m_cursorPos, 1);
216 UpdateText();
218 return true;
220 else if (b == XBMCVK_RETURN || b == XBMCVK_NUMPADENTER)
222 // enter - send click message, but otherwise ignore
223 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1);
224 return true;
226 else if (b == XBMCVK_ESCAPE)
227 { // escape - fallthrough to default action
228 return CGUIButtonControl::OnAction(action);
231 else if (action.GetID() == ACTION_KEYBOARD_COMPOSING_KEY)
233 ComposingCursorAppendChar(action.GetUnicode());
235 else if (action.GetID() == ACTION_KEYBOARD_COMPOSING_KEY_CANCELLED)
237 CancelKeyComposition(action.GetUnicode());
239 else if (action.GetID() == ACTION_KEYBOARD_COMPOSING_KEY_FINISHED)
241 ResetCursor();
243 else if (action.GetID() == KEY_UNICODE)
245 // input from the keyboard
246 int ch = action.GetUnicode();
247 // ignore non-printing characters
248 if ( !((0 <= ch && ch < 0x8) || (0xE <= ch && ch < 0x1B) || (0x1C <= ch && ch < 0x20)) )
250 switch (ch)
252 case 9: // tab, ignore
253 case 11: // Non-printing character, ignore
254 case 12: // Non-printing character, ignore
255 break;
256 case 10:
257 case 13:
259 // enter - send click message, but otherwise ignore
260 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1);
261 return true;
263 case 27:
264 { // escape - fallthrough to default action
265 return CGUIButtonControl::OnAction(action);
267 case 8:
269 // backspace
270 if (m_cursorPos)
272 if (!ClearMD5())
273 m_text2.erase(--m_cursorPos, 1);
275 break;
277 case 127:
278 { // delete
279 if (m_cursorPos < m_text2.length())
281 if (!ClearMD5())
282 m_text2.erase(m_cursorPos, 1);
284 break;
286 default:
288 ClearMD5();
289 m_edit.clear();
290 m_text2.insert(m_text2.begin() + m_cursorPos++, action.GetUnicode());
291 break;
294 UpdateText();
295 return true;
298 else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
299 { // input from the remote
300 ClearMD5();
301 m_edit.clear();
302 OnSMSCharacter(action.GetID() - REMOTE_0);
303 return true;
305 else if (action.GetID() == ACTION_INPUT_TEXT)
307 m_edit.clear();
308 std::wstring str;
309 g_charsetConverter.utf8ToW(action.GetText(), str, false);
310 m_text2.insert(m_cursorPos, str);
311 m_cursorPos += str.size();
312 UpdateText();
313 return true;
316 return CGUIButtonControl::OnAction(action);
319 void CGUIEditControl::OnClick()
321 // we received a click - it's not from the keyboard, so pop up the virtual keyboard, unless
322 // that is where we reside!
323 if (GetParentID() == WINDOW_DIALOG_KEYBOARD)
324 return;
326 std::string utf8;
327 g_charsetConverter.wToUTF8(m_text2, utf8);
328 bool textChanged = false;
329 switch (m_inputType)
331 case INPUT_TYPE_READONLY:
332 textChanged = false;
333 break;
334 case INPUT_TYPE_NUMBER:
335 textChanged = CGUIDialogNumeric::ShowAndGetNumber(utf8, m_inputHeading);
336 break;
337 case INPUT_TYPE_SECONDS:
338 textChanged = CGUIDialogNumeric::ShowAndGetSeconds(utf8, g_localizeStrings.Get(21420));
339 break;
340 case INPUT_TYPE_TIME:
342 CDateTime dateTime;
343 dateTime.SetFromDBTime(utf8);
344 KODI::TIME::SystemTime time;
345 dateTime.GetAsSystemTime(time);
346 if (CGUIDialogNumeric::ShowAndGetTime(time, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420)))
348 dateTime = CDateTime(time);
349 utf8 = dateTime.GetAsLocalizedTime("", false);
350 textChanged = true;
352 break;
354 case INPUT_TYPE_DATE:
356 CDateTime dateTime;
357 dateTime.SetFromDBDate(utf8);
358 if (dateTime < CDateTime(2000,1, 1, 0, 0, 0))
359 dateTime = CDateTime(2000, 1, 1, 0, 0, 0);
360 KODI::TIME::SystemTime date;
361 dateTime.GetAsSystemTime(date);
362 if (CGUIDialogNumeric::ShowAndGetDate(date, !m_inputHeading.empty() ? m_inputHeading : g_localizeStrings.Get(21420)))
364 dateTime = CDateTime(date);
365 utf8 = dateTime.GetAsDBDate();
366 textChanged = true;
368 break;
370 case INPUT_TYPE_IPADDRESS:
371 textChanged = CGUIDialogNumeric::ShowAndGetIPAddress(utf8, m_inputHeading);
372 break;
373 case INPUT_TYPE_SEARCH:
374 textChanged = CGUIKeyboardFactory::ShowAndGetFilter(utf8, true);
375 break;
376 case INPUT_TYPE_FILTER:
377 textChanged = CGUIKeyboardFactory::ShowAndGetFilter(utf8, false);
378 break;
379 case INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW:
380 textChanged = CGUIDialogNumeric::ShowAndVerifyNewPassword(utf8);
381 break;
382 case INPUT_TYPE_PASSWORD_MD5:
383 utf8 = ""; //! @todo Ideally we'd send this to the keyboard and tell the keyboard we have this type of input
384 // fallthrough
385 [[fallthrough]];
386 case INPUT_TYPE_TEXT:
387 default:
388 textChanged = CGUIKeyboardFactory::ShowAndGetInput(utf8, m_inputHeading, true, m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5);
389 break;
391 if (textChanged)
393 ClearMD5();
394 m_edit.clear();
395 g_charsetConverter.utf8ToW(utf8, m_text2, false);
396 m_cursorPos = m_text2.size();
397 UpdateText();
398 m_cursorPos = m_text2.size();
402 void CGUIEditControl::UpdateText(bool sendUpdate)
404 m_smsTimer.Stop();
405 if (sendUpdate)
407 ValidateInput();
409 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 0);
411 m_textChangeActions.ExecuteActions(GetID(), GetParentID());
413 SetInvalid();
416 void CGUIEditControl::SetInputType(CGUIEditControl::INPUT_TYPE type, const CVariant& heading)
418 m_inputType = type;
419 if (heading.isString())
420 m_inputHeading = heading.asString();
421 else if (heading.isInteger() && heading.asInteger())
422 m_inputHeading = g_localizeStrings.Get(static_cast<uint32_t>(heading.asInteger()));
423 //! @todo Verify the current input string?
426 void CGUIEditControl::RecalcRightLabelPosition()
428 // ensure that our cursor is within our width
429 ValidateCursor();
431 const std::wstring text = GetDisplayedText();
432 const float textWidth = m_label2.CalcTextWidth(text + L'|');
433 const float beforeCursorWidth = m_label2.CalcTextWidth(text.substr(0, m_cursorPos));
434 const float afterCursorWidth = m_label2.CalcTextWidth(text.substr(0, m_cursorPos) + L'|');
435 const float leftTextWidth = std::min(m_label.GetTextWidth(), m_label.GetMaxWidth());
436 float maxTextWidth = m_width - 2 * m_label.GetLabelInfo().offsetX;
438 if (leftTextWidth > 0)
439 maxTextWidth -= leftTextWidth + TEXT_SPACE;
441 if (textWidth > maxTextWidth)
442 { // we render taking up the full width, so make sure our cursor position is
443 // within the render window
444 if (m_textOffset + afterCursorWidth > maxTextWidth)
446 // move the position to the left (outside of the viewport)
447 m_textOffset = maxTextWidth - afterCursorWidth;
449 else if (m_textOffset + beforeCursorWidth < 0) // offscreen to the left
451 // otherwise use original position
452 m_textOffset = -beforeCursorWidth;
454 else if (m_textOffset + textWidth < maxTextWidth)
455 { // we have more text than we're allowed, but we aren't filling all the space
456 m_textOffset = maxTextWidth - textWidth;
459 else
460 m_textOffset = 0;
463 void CGUIEditControl::ProcessText(unsigned int currentTime)
465 if (m_smsTimer.IsRunning() && m_smsTimer.GetElapsedMilliseconds() > smsDelay)
466 UpdateText();
468 bool changed = false;
469 changed |= m_label.SetText(m_info.GetLabel(m_parentID));
471 m_clipRect.x1 = m_posX + m_label.GetLabelInfo().offsetX;
472 m_clipRect.x2 = m_clipRect.x1 + m_width - 2 * m_label.GetLabelInfo().offsetX;
473 m_clipRect.y1 = m_posY;
474 m_clipRect.y2 = m_posY + m_height;
476 // Limit left text max width to 50% of space when focused, otherwise 70%
477 const float maxTextWidth = m_width * (HasFocus() ? 0.5f : 0.7f);
479 const float leftTextWidth =
480 std::min(m_label.GetTextWidth(), maxTextWidth - 2 * m_label.GetLabelInfo().offsetX);
482 changed |= m_label.SetMaxRect(m_posX, m_posY, maxTextWidth, m_height);
484 if (m_bInvalidated)
486 if (!HasFocus() && leftTextWidth > 0)
487 m_textOffset = 0;
488 else
489 RecalcRightLabelPosition();
492 if (leftTextWidth > 0)
494 // render the text on the left
495 changed |= m_label.SetScrolling(HasFocus());
496 changed |= m_label.SetColor(GetTextColor());
497 changed |= m_label.Process(currentTime);
499 m_clipRect.x1 += leftTextWidth + TEXT_SPACE;
502 // render the text on the right
504 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_clipRect.x1, m_clipRect.y1, m_clipRect.Width(), m_clipRect.Height()))
506 // set alignment for right label text
507 uint32_t align = m_label.GetLabelInfo().align & XBFONT_CENTER_Y; // start aligned left
508 if (leftTextWidth > 0)
509 { // right align as we have 2 labels
510 align |= XBFONT_RIGHT;
512 else
513 { // align by whatever the skinner requests
514 align |= (m_label2.GetLabelInfo().align & (XBFONT_RIGHT | XBFONT_CENTER_X));
517 changed |= m_label2.SetMaxRect(m_clipRect.x1 + m_textOffset, m_posY, m_clipRect.Width() - m_textOffset, m_height);
519 std::wstring text = GetDisplayedText();
520 std::string hint = m_hintInfo.GetLabel(GetParentID());
522 if (!HasFocus() && text.empty() && !hint.empty())
524 changed |= m_label2.SetText(hint);
526 else if ((HasFocus() || GetParentID() == WINDOW_DIALOG_KEYBOARD) &&
527 m_inputType != INPUT_TYPE_READONLY)
529 changed |= SetStyledText(text);
531 else
532 changed |= m_label2.SetTextW(text);
534 changed |= m_label2.SetAlign(align);
535 changed |= m_label2.SetColor(GetTextColor());
537 if (HasFocus() || leftTextWidth == 0)
538 changed |= m_label2.SetOverflow(CGUILabel::OVER_FLOW_CLIP);
539 else
540 changed |= m_label2.SetOverflow(CGUILabel::OVER_FLOW_TRUNCATE_LEFT);
542 changed |= m_label2.Process(currentTime);
543 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
545 if (changed)
546 MarkDirtyRegion();
549 void CGUIEditControl::RenderText()
551 if (CServiceBroker::GetWinSystem()->GetGfxContext().GetRenderOrder() ==
552 RENDER_ORDER_FRONT_TO_BACK)
553 return;
554 m_label.Render();
556 if (CServiceBroker::GetWinSystem()->GetGfxContext().SetClipRegion(m_clipRect.x1, m_clipRect.y1, m_clipRect.Width(), m_clipRect.Height()))
558 m_label2.Render();
559 CServiceBroker::GetWinSystem()->GetGfxContext().RestoreClipRegion();
563 CGUILabel::COLOR CGUIEditControl::GetTextColor() const
565 CGUILabel::COLOR color = CGUIButtonControl::GetTextColor();
566 if (color != CGUILabel::COLOR_DISABLED && HasInvalidInput())
567 return CGUILabel::COLOR_INVALID;
569 return color;
572 void CGUIEditControl::SetHint(const GUIINFO::CGUIInfoLabel& hint)
574 m_hintInfo = hint;
577 std::wstring CGUIEditControl::GetDisplayedText() const
579 std::wstring text(m_text2);
580 if (m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW)
582 text.clear();
583 if (m_smsTimer.IsRunning())
584 { // using the remove to input, so display the last key input
585 text.append(m_cursorPos - 1, L'*');
586 text.append(1, m_text2[m_cursorPos - 1]);
587 text.append(m_text2.size() - m_cursorPos, L'*');
589 else
590 text.append(m_text2.size(), L'*');
592 else if (!m_edit.empty())
593 text.insert(m_editOffset, m_edit);
594 return text;
597 bool CGUIEditControl::SetStyledText(const std::wstring &text)
599 vecText styled;
600 styled.reserve(text.size() + 1);
602 std::vector<KODI::UTILS::COLOR::Color> colors;
603 colors.push_back(m_label.GetLabelInfo().textColor);
604 colors.push_back(m_label.GetLabelInfo().disabledColor);
605 KODI::UTILS::COLOR::Color select = m_label.GetLabelInfo().selectedColor;
606 if (!select)
607 select = 0xFFFF0000;
608 colors.push_back(select);
609 colors.push_back(0x00FFFFFF);
611 unsigned int startHighlight = m_cursorPos;
612 unsigned int endHighlight = m_cursorPos + m_edit.size();
613 unsigned int startSelection = m_cursorPos + m_editOffset;
614 unsigned int endSelection = m_cursorPos + m_editOffset + m_editLength;
616 CGUIFont* font = m_label2.GetLabelInfo().font;
617 uint32_t style = (font ? font->GetStyle() : (FONT_STYLE_NORMAL & FONT_STYLE_MASK)) << 24;
619 for (unsigned int i = 0; i < text.size(); i++)
621 uint32_t ch = text[i] | style;
622 if (m_editLength > 0 && startSelection <= i && i < endSelection)
623 ch |= (2 << 16); // highlight the letters we're playing with
624 else if (!m_edit.empty() && (i < startHighlight || i >= endHighlight))
625 ch |= (1 << 16); // dim the bits we're not editing
626 styled.push_back(ch);
629 // show the cursor
630 unsigned int posChar = m_cursorPos;
631 for (const uint32_t& cursorChar : m_cursorChars)
633 uint32_t ch = cursorChar | style;
634 if (m_cursorBlinkEnabled)
636 if ((++m_cursorBlink % 64) > 32)
637 ch |= (3 << 16);
639 styled.insert(styled.begin() + posChar, ch);
640 posChar++;
642 return m_label2.SetStyledText(styled, colors);
645 void CGUIEditControl::ValidateCursor()
647 if (m_cursorPos > m_text2.size())
648 m_cursorPos = m_text2.size();
651 void CGUIEditControl::SetLabel(const std::string &text)
653 CGUIButtonControl::SetLabel(text);
654 SetInvalid();
657 void CGUIEditControl::SetLabel2(const std::string &text)
659 m_edit.clear();
660 std::wstring newText;
661 g_charsetConverter.utf8ToW(text, newText, false);
662 if (newText != m_text2)
664 m_isMD5 = (m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW);
665 m_text2 = newText;
666 m_cursorPos = m_text2.size();
667 ValidateInput();
668 SetInvalid();
672 std::string CGUIEditControl::GetLabel2() const
674 std::string text;
675 g_charsetConverter.wToUTF8(m_text2, text);
676 if (m_inputType == INPUT_TYPE_PASSWORD_MD5 && !m_isMD5)
677 return CDigest::Calculate(CDigest::Type::MD5, text);
678 return text;
681 bool CGUIEditControl::ClearMD5()
683 if (!(m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW) || !m_isMD5)
684 return false;
686 m_text2.clear();
687 m_cursorPos = 0;
688 if (m_inputType != INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW)
689 m_isMD5 = false;
690 return true;
693 unsigned int CGUIEditControl::GetCursorPosition() const
695 return m_cursorPos;
698 void CGUIEditControl::SetCursorPosition(unsigned int iPosition)
700 m_cursorPos = iPosition;
703 void CGUIEditControl::OnSMSCharacter(unsigned int key)
705 assert(key < 10);
706 if (m_smsTimer.IsRunning())
708 // we're already entering an SMS character
709 if (key != m_smsLastKey || m_smsTimer.GetElapsedMilliseconds() > smsDelay)
710 { // a different key was clicked than last time, or we have timed out
711 m_smsLastKey = key;
712 m_smsKeyIndex = 0;
714 else
715 { // same key as last time within the appropriate time period
716 m_smsKeyIndex++;
717 if (m_cursorPos)
718 m_text2.erase(--m_cursorPos, 1);
721 else
722 { // key is pressed for the first time
723 m_smsLastKey = key;
724 m_smsKeyIndex = 0;
727 m_smsKeyIndex = m_smsKeyIndex % smsLetters[key].size();
729 m_text2.insert(m_text2.begin() + m_cursorPos++, smsLetters[key][m_smsKeyIndex]);
730 UpdateText();
731 m_smsTimer.StartZero();
734 void CGUIEditControl::OnPasteClipboard()
736 std::wstring unicode_text;
737 std::string utf8_text;
739 // Get text from the clipboard
740 utf8_text = CServiceBroker::GetWinSystem()->GetClipboardText();
741 g_charsetConverter.utf8ToW(utf8_text, unicode_text, false);
743 // Insert the pasted text at the current cursor position.
744 if (unicode_text.length() > 0)
746 std::wstring left_end = m_text2.substr(0, m_cursorPos);
747 std::wstring right_end = m_text2.substr(m_cursorPos);
749 m_text2 = left_end;
750 m_text2.append(unicode_text);
751 m_text2.append(right_end);
752 m_cursorPos += unicode_text.length();
753 UpdateText();
757 void CGUIEditControl::SetInputValidation(StringValidation::Validator inputValidator, void *data /* = NULL */)
759 if (m_inputValidator == inputValidator)
760 return;
762 m_inputValidator = inputValidator;
763 m_inputValidatorData = data;
764 // the input validator has changed, so re-validate the current data
765 ValidateInput();
768 bool CGUIEditControl::ValidateInput(const std::wstring &data) const
770 if (m_inputValidator == NULL)
771 return true;
773 return m_inputValidator(GetLabel2(), m_inputValidatorData != NULL ? m_inputValidatorData : const_cast<void*>((const void*)this));
776 void CGUIEditControl::ValidateInput()
778 // validate the input
779 bool invalid = !ValidateInput(m_text2);
780 // nothing to do if still valid/invalid
781 if (invalid != m_invalidInput)
783 // the validity state has changed so we need to update the control
784 m_invalidInput = invalid;
786 // let the window/dialog know that the validity has changed
787 CGUIMessage msg(GUI_MSG_VALIDITY_CHANGED, GetID(), GetID(), m_invalidInput ? 0 : 1);
788 SendWindowMessage(msg);
790 SetInvalid();
794 void CGUIEditControl::SetFocus(bool focus)
796 m_smsTimer.Stop();
797 CGUIControl::SetFocus(focus);
798 SetInvalid();
801 std::string CGUIEditControl::GetDescriptionByIndex(int index) const
803 if (index == 0)
804 return GetDescription();
805 else if(index == 1)
806 return GetLabel2();
808 return "";
811 void CGUIEditControl::ComposingCursorAppendChar(std::uint32_t deadUnicodeKey)
813 std::uint32_t ch;
814 if (m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5 ||
815 m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW)
817 ch = '*';
819 else
821 ch = deadUnicodeKey;
824 if (IsComposingKey())
826 m_cursorChars.emplace_back(ch);
827 m_cursorCharsBuffer.emplace_back(deadUnicodeKey);
829 else
831 m_cursorChars = {ch};
832 m_cursorCharsBuffer.emplace_back(deadUnicodeKey);
834 m_cursorBlinkEnabled = false;
837 void CGUIEditControl::CancelKeyComposition(std::uint32_t deadUnicodeKey)
839 // sequence cancelled and reverted...
840 if (deadUnicodeKey == XBMCK_BACKSPACE)
842 ResetCursor();
844 // sequence cancelled and replay...
845 else
847 ClearMD5();
848 m_edit.clear();
849 for (const uint32_t& cursorChar : m_cursorCharsBuffer)
851 m_text2.insert(m_text2.begin() + m_cursorPos++, cursorChar);
853 UpdateText();
854 ResetCursor();
858 void CGUIEditControl::ResetCursor()
860 m_cursorChars = {'|'};
861 m_cursorCharsBuffer.clear();
862 m_cursorBlinkEnabled = true;
865 bool CGUIEditControl::IsComposingKey() const
867 return !m_cursorBlinkEnabled;