Merge pull request #4594 from FernetMenta/paplayer
[xbmc.git] / xbmc / dialogs / GUIDialogKeyboardGeneric.cpp
blob9d898de6650508ad9d805dc1d1d50295128d502d
1 /*
2 * Copyright (C) 2012-2013 Team XBMC
3 * http://xbmc.org
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "interfaces/AnnouncementManager.h"
22 #include "input/XBMC_vkeys.h"
23 #include "guilib/GUILabelControl.h"
24 #include "guilib/GUIWindowManager.h"
25 #include "guilib/Key.h"
26 #include "guilib/LocalizeStrings.h"
27 #include "GUIUserMessages.h"
28 #include "GUIDialogNumeric.h"
29 #include "GUIDialogOK.h"
30 #include "GUIDialogKeyboardGeneric.h"
31 #include "utils/TimeUtils.h"
32 #include "utils/RegExp.h"
33 #include "ApplicationMessenger.h"
34 #include "windowing/WindowingFactory.h"
35 #include "utils/CharsetConverter.h"
37 #if defined(TARGET_DARWIN)
38 #include "osx/CocoaInterface.h"
39 #endif
41 // Symbol mapping (based on MS virtual keyboard - may need improving)
42 static char symbol_map[37] = ")!@#$%^&*([]{}-_=+;:\'\",.<>/?\\|`~ ";
44 #define CTL_BUTTON_DONE 300
45 #define CTL_BUTTON_CANCEL 301
46 #define CTL_BUTTON_SHIFT 302
47 #define CTL_BUTTON_CAPS 303
48 #define CTL_BUTTON_SYMBOLS 304
49 #define CTL_BUTTON_LEFT 305
50 #define CTL_BUTTON_RIGHT 306
51 #define CTL_BUTTON_IP_ADDRESS 307
52 #define CTL_BUTTON_CLEAR 308
54 #define CTL_LABEL_EDIT 310
55 #define CTL_LABEL_HEADING 311
57 #define CTL_BUTTON_BACKSPACE 8
59 static char symbolButtons[] = "._-@/\\";
60 #define NUM_SYMBOLS sizeof(symbolButtons) - 1
62 #define SEARCH_DELAY 1000
63 #define REMOTE_SMS_DELAY 1000
65 CGUIDialogKeyboardGeneric::CGUIDialogKeyboardGeneric()
66 : CGUIDialog(WINDOW_DIALOG_KEYBOARD, "DialogKeyboard.xml")
67 , CGUIKeyboard()
68 , m_pCharCallback(NULL)
70 m_bIsConfirmed = false;
71 m_bShift = false;
72 m_hiddenInput = false;
73 m_keyType = LOWER;
74 m_strHeading = "";
75 m_iCursorPos = 0;
76 m_iEditingOffset = 0;
77 m_lastRemoteClickTime = 0;
78 m_loadType = KEEP_IN_MEMORY;
81 void CGUIDialogKeyboardGeneric::OnInitWindow()
83 CGUIDialog::OnInitWindow();
85 m_bIsConfirmed = false;
87 // set alphabetic (capitals)
88 UpdateButtons();
90 CGUILabelControl* pEdit = ((CGUILabelControl*)GetControl(CTL_LABEL_EDIT));
91 if (pEdit)
93 pEdit->ShowCursor();
96 // set heading
97 if (!m_strHeading.empty())
99 SET_CONTROL_LABEL(CTL_LABEL_HEADING, m_strHeading);
100 SET_CONTROL_VISIBLE(CTL_LABEL_HEADING);
102 else
104 SET_CONTROL_HIDDEN(CTL_LABEL_HEADING);
106 g_Windowing.EnableTextInput(true);
108 CVariant data;
109 data["title"] = m_strHeading;
110 data["type"] = !m_hiddenInput ? "keyboard" : "password";
111 data["value"] = GetText();
112 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Input, "xbmc", "OnInputRequested", data);
115 bool CGUIDialogKeyboardGeneric::OnAction(const CAction &action)
117 bool handled(true);
118 if (action.GetID() == ACTION_BACKSPACE)
120 Backspace();
122 else if (action.GetID() == ACTION_ENTER)
124 OnOK();
126 else if (action.GetID() == ACTION_CURSOR_LEFT)
128 MoveCursor( -1);
130 else if (action.GetID() == ACTION_CURSOR_RIGHT)
132 if (m_strEditing.empty() && (unsigned int) GetCursorPos() == m_strEdit.size() && (m_strEdit.size() == 0 || m_strEdit[m_strEdit.size() - 1] != ' '))
133 { // add a space
134 Character(L' ');
136 else
137 MoveCursor(1);
139 else if (action.GetID() == ACTION_SHIFT)
141 OnShift();
143 else if (action.GetID() == ACTION_SYMBOLS)
145 OnSymbols();
147 else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
149 OnRemoteNumberClick(action.GetID());
151 else if (action.GetID() == ACTION_PASTE)
153 OnPasteClipboard();
155 else if (action.GetID() >= KEY_VKEY && action.GetID() < KEY_ASCII)
156 { // input from the keyboard (vkey, not ascii)
157 if (!m_strEditing.empty())
158 return handled;
159 uint8_t b = action.GetID() & 0xFF;
160 if (b == XBMCVK_HOME)
162 SetCursorPos(0);
164 else if (b == XBMCVK_END)
166 SetCursorPos(m_strEdit.size());
168 else if (b == XBMCVK_LEFT)
170 MoveCursor( -1);
172 else if (b == XBMCVK_RIGHT)
174 MoveCursor(1);
176 else if (b == XBMCVK_RETURN || b == XBMCVK_NUMPADENTER)
178 OnOK();
180 else if (b == XBMCVK_DELETE)
182 if (GetCursorPos() < (int)m_strEdit.size())
184 MoveCursor(1);
185 Backspace();
188 else if (b == XBMCVK_BACK) Backspace();
189 else if (b == XBMCVK_ESCAPE) Close();
191 else if (action.GetID() >= KEY_ASCII)
192 { // input from the keyboard
193 //char ch = action.GetID() & 0xFF;
194 int ch = action.GetUnicode();
196 // Ignore non-printing characters
197 if ( !((0 <= ch && ch < 0x8) || (0xE <= ch && ch < 0x1B) || (0x1C <= ch && ch < 0x20)) )
199 switch (ch)
201 case 0x8: // backspace
202 Backspace();
203 break;
204 case 0x9: // Tab (do nothing)
205 case 0xB: // Non-printing character, ignore
206 case 0xC: // Non-printing character, ignore
207 break;
208 case 0xA: // enter
209 case 0xD: // enter
210 OnOK();
211 break;
212 case 0x1B: // escape
213 Close();
214 break;
215 case 0x7F: // Delete
216 if (GetCursorPos() < (int)m_strEdit.size())
218 MoveCursor(1);
219 Backspace();
221 break;
222 default: //use character input
223 // When we support text input method, we only accept text by gui text message.
224 if (!g_Windowing.IsTextInputEnabled())
225 Character(action.GetUnicode());
226 break;
230 else // unhandled by us - let's see if the baseclass wants it
231 handled = CGUIDialog::OnAction(action);
233 if (handled && m_pCharCallback)
234 { // we did _something_, so make sure our search message filter is reset
235 m_pCharCallback(this, GetText());
237 return handled;
240 bool CGUIDialogKeyboardGeneric::OnMessage(CGUIMessage& message)
242 CGUIDialog::OnMessage(message);
245 switch ( message.GetMessage() )
247 case GUI_MSG_CLICKED:
249 int iControl = message.GetSenderId();
251 switch (iControl)
253 case CTL_BUTTON_DONE:
254 OnOK();
255 break;
256 case CTL_BUTTON_CANCEL:
257 Close();
258 break;
259 case CTL_BUTTON_SHIFT:
260 OnShift();
261 break;
262 case CTL_BUTTON_CAPS:
263 if (m_keyType == LOWER)
264 m_keyType = CAPS;
265 else if (m_keyType == CAPS)
266 m_keyType = LOWER;
267 UpdateButtons();
268 break;
269 case CTL_BUTTON_SYMBOLS:
270 OnSymbols();
271 break;
272 case CTL_BUTTON_LEFT:
273 MoveCursor( -1);
274 break;
275 case CTL_BUTTON_RIGHT:
276 MoveCursor(1);
277 break;
278 case CTL_BUTTON_IP_ADDRESS:
279 OnIPAddress();
280 break;
281 case CTL_BUTTON_CLEAR:
282 SetText("");
283 break;
284 default:
285 m_lastRemoteKeyClicked = 0;
286 OnClickButton(iControl);
287 break;
290 break;
292 case GUI_MSG_SET_TEXT:
293 SetText(message.GetLabel());
295 // close the dialog if requested
296 if (message.GetParam1() > 0)
297 OnOK();
298 break;
300 case GUI_MSG_INPUT_TEXT:
301 InputText(message.GetLabel());
302 break;
304 case GUI_MSG_INPUT_TEXT_EDIT:
305 InputTextEditing(message.GetLabel(), message.GetParam1(), message.GetParam2());
306 break;
309 return true;
312 void CGUIDialogKeyboardGeneric::SetText(const CStdString& aTextString)
314 m_strEdit.clear();
315 m_strEditing.clear();
316 m_iEditingOffset = 0;
317 g_charsetConverter.utf8ToW(aTextString, m_strEdit);
318 UpdateLabel();
319 SetCursorPos(m_strEdit.size());
322 void CGUIDialogKeyboardGeneric::InputText(const CStdString& aTextString)
324 CStdStringW newStr;
325 g_charsetConverter.utf8ToW(aTextString, newStr);
326 if (!newStr.empty())
328 m_strEditing.clear();
329 m_iEditingOffset = 0;
330 m_strEdit.insert(GetCursorPos(), newStr);
331 UpdateLabel();
332 MoveCursor(newStr.size());
336 void CGUIDialogKeyboardGeneric::InputTextEditing(const CStdString& aTextString, int start, int length)
338 m_strEditing.clear();
339 m_iEditingOffset = start;
340 m_iEditingLength = length;
341 g_charsetConverter.utf8ToW(aTextString, m_strEditing);
342 // CLog::Log(LOGDEBUG, "CGUIDialogKeyboardGeneric::InputTextEditing len %lu range(%d, %d) -> range len %d", m_strEditing.size(), m_iEditingOffset, length, m_iEditingLength);
343 UpdateLabel();
344 SetCursorPos(GetCursorPos());
347 CStdString CGUIDialogKeyboardGeneric::GetText() const
349 CStdString utf8String;
350 g_charsetConverter.wToUTF8(m_strEdit, utf8String);
351 return utf8String;
354 void CGUIDialogKeyboardGeneric::Character(WCHAR ch)
356 if (!ch) return;
357 m_strEditing.clear();
358 m_iEditingOffset = 0;
359 // TODO: May have to make this routine take a WCHAR for the symbols?
360 m_strEdit.insert(GetCursorPos(), 1, ch);
361 UpdateLabel();
362 MoveCursor(1);
365 void CGUIDialogKeyboardGeneric::FrameMove()
367 // reset the hide state of the label when the remote
368 // sms style input times out
369 if (m_lastRemoteClickTime && m_lastRemoteClickTime + REMOTE_SMS_DELAY < CTimeUtils::GetFrameTime())
371 // finished inputting a sms style character - turn off our shift and symbol states
372 ResetShiftAndSymbols();
374 CGUIDialog::FrameMove();
377 void CGUIDialogKeyboardGeneric::UpdateLabel() // FIXME seems to be called twice for one USB SDL keyboard action/character
379 CGUILabelControl* pEdit = ((CGUILabelControl*)GetControl(CTL_LABEL_EDIT));
380 if (pEdit)
382 CStdStringW edit = m_strEdit;
383 pEdit->SetHighlight(0, 0);
384 pEdit->SetSelection(0, 0);
385 if (m_hiddenInput)
386 { // convert to *'s
387 edit.clear();
388 if (m_lastRemoteClickTime + REMOTE_SMS_DELAY > CTimeUtils::GetFrameTime() && m_iCursorPos > 0)
389 { // using the remove to input, so display the last key input
390 edit.append(m_iCursorPos - 1, L'*');
391 edit.append(1, m_strEdit[m_iCursorPos - 1]);
393 else
394 edit.append(m_strEdit.size(), L'*');
396 else if (!m_strEditing.empty())
398 edit.insert(m_iCursorPos, m_strEditing);
399 pEdit->SetHighlight(m_iCursorPos, m_iCursorPos + m_strEditing.size());
400 if (m_iEditingLength > 0)
401 pEdit->SetSelection(m_iCursorPos + m_iEditingOffset, m_iCursorPos + m_iEditingOffset + m_iEditingLength);
403 // convert back to utf8
404 CStdString utf8Edit;
405 g_charsetConverter.wToUTF8(edit, utf8Edit);
406 pEdit->SetLabel(utf8Edit);
407 // Send off a search message
408 unsigned int now = CTimeUtils::GetFrameTime();
409 // don't send until the REMOTE_SMS_DELAY has passed
410 if (m_lastRemoteClickTime && m_lastRemoteClickTime + REMOTE_SMS_DELAY >= now)
411 return;
413 if (m_pCharCallback)
415 // do not send editing text comes from system input method
416 if (!m_hiddenInput && !m_strEditing.empty())
417 g_charsetConverter.wToUTF8(m_strEdit, utf8Edit);
418 m_pCharCallback(this, utf8Edit);
423 void CGUIDialogKeyboardGeneric::Backspace()
425 int iPos = GetCursorPos();
426 if (iPos > 0)
428 m_strEdit.erase(iPos - 1, 1);
429 MoveCursor(-1);
430 UpdateLabel();
434 void CGUIDialogKeyboardGeneric::OnClickButton(int iButtonControl)
436 if (iButtonControl == CTL_BUTTON_BACKSPACE)
438 Backspace();
440 else
441 Character(GetCharacter(iButtonControl));
444 void CGUIDialogKeyboardGeneric::OnRemoteNumberClick(int key)
446 unsigned int now = CTimeUtils::GetFrameTime();
448 if (m_lastRemoteClickTime)
449 { // a remote key has been pressed
450 if (key != m_lastRemoteKeyClicked || m_lastRemoteClickTime + REMOTE_SMS_DELAY < now)
451 { // a different key was clicked than last time, or we have timed out
452 m_lastRemoteKeyClicked = key;
453 m_indexInSeries = 0;
454 // reset our shift and symbol states, and update our label to ensure the search filter is sent
455 ResetShiftAndSymbols();
456 UpdateLabel();
458 else
459 { // same key as last time within the appropriate time period
460 m_indexInSeries++;
461 Backspace();
464 else
465 { // key is pressed for the first time
466 m_lastRemoteKeyClicked = key;
467 m_indexInSeries = 0;
470 int arrayIndex = key - REMOTE_0;
471 m_indexInSeries = m_indexInSeries % strlen(s_charsSeries[arrayIndex]);
472 m_lastRemoteClickTime = now;
474 // Select the character that will be pressed
475 const char* characterPressed = s_charsSeries[arrayIndex];
476 characterPressed += m_indexInSeries;
478 // use caps where appropriate
479 char ch = *characterPressed;
480 bool caps = (m_keyType == CAPS && !m_bShift) || (m_keyType == LOWER && m_bShift);
481 if (!caps && *characterPressed >= 'A' && *characterPressed <= 'Z')
482 ch += 32;
483 Character(ch);
486 char CGUIDialogKeyboardGeneric::GetCharacter(int iButton)
488 // First the numbers
489 if (iButton >= 48 && iButton <= 57)
491 if (m_keyType == SYMBOLS)
493 OnSymbols();
494 return symbol_map[iButton -48];
496 else
497 return (char)iButton;
499 else if (iButton == 32) // space
500 return (char)iButton;
501 else if (iButton >= 65 && iButton < 91)
503 if (m_keyType == SYMBOLS)
504 { // symbol
505 OnSymbols();
506 return symbol_map[iButton - 65 + 10];
508 if ((m_keyType == CAPS && m_bShift) || (m_keyType == LOWER && !m_bShift))
509 { // make lower case
510 iButton += 32;
512 if (m_bShift)
513 { // turn off the shift key
514 OnShift();
516 return (char) iButton;
518 else
519 { // check for symbols
520 for (unsigned int i = 0; i < NUM_SYMBOLS; i++)
521 if (iButton == symbolButtons[i])
522 return (char)iButton;
524 return 0;
527 void CGUIDialogKeyboardGeneric::UpdateButtons()
529 if (m_bShift)
530 { // show the button depressed
531 CGUIMessage msg(GUI_MSG_SELECTED, GetID(), CTL_BUTTON_SHIFT);
532 OnMessage(msg);
534 else
536 CGUIMessage msg(GUI_MSG_DESELECTED, GetID(), CTL_BUTTON_SHIFT);
537 OnMessage(msg);
539 if (m_keyType == CAPS)
541 CGUIMessage msg(GUI_MSG_SELECTED, GetID(), CTL_BUTTON_CAPS);
542 OnMessage(msg);
544 else
546 CGUIMessage msg(GUI_MSG_DESELECTED, GetID(), CTL_BUTTON_CAPS);
547 OnMessage(msg);
549 if (m_keyType == SYMBOLS)
551 CGUIMessage msg(GUI_MSG_SELECTED, GetID(), CTL_BUTTON_SYMBOLS);
552 OnMessage(msg);
554 else
556 CGUIMessage msg(GUI_MSG_DESELECTED, GetID(), CTL_BUTTON_SYMBOLS);
557 OnMessage(msg);
559 char szLabel[2];
560 szLabel[0] = 32;
561 szLabel[1] = 0;
562 CStdString aLabel = szLabel;
564 // set numerals
565 for (int iButton = 48; iButton <= 57; iButton++)
567 if (m_keyType == SYMBOLS)
568 aLabel[0] = symbol_map[iButton - 48];
569 else
570 aLabel[0] = iButton;
571 SetControlLabel(iButton, aLabel);
574 // set correct alphabet characters...
576 for (int iButton = 65; iButton <= 90; iButton++)
578 // set the correct case...
579 if ((m_keyType == CAPS && m_bShift) || (m_keyType == LOWER && !m_bShift))
580 { // make lower case
581 aLabel[0] = iButton + 32;
583 else if (m_keyType == SYMBOLS)
585 aLabel[0] = symbol_map[iButton - 65 + 10];
587 else
589 aLabel[0] = iButton;
591 SetControlLabel(iButton, aLabel);
593 for (unsigned int i = 0; i < NUM_SYMBOLS; i++)
595 aLabel[0] = symbolButtons[i];
596 SetControlLabel(symbolButtons[i], aLabel);
601 void CGUIDialogKeyboardGeneric::OnDeinitWindow(int nextWindowID)
603 // call base class
604 CGUIDialog::OnDeinitWindow(nextWindowID);
605 // reset the heading (we don't always have this)
606 m_strHeading = "";
608 g_Windowing.EnableTextInput(false);
609 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Input, "xbmc", "OnInputFinished");
612 void CGUIDialogKeyboardGeneric::MoveCursor(int iAmount)
614 if (!m_strEditing.empty())
615 return;
616 SetCursorPos(GetCursorPos() + iAmount);
619 void CGUIDialogKeyboardGeneric::SetCursorPos(int iPos)
621 if (iPos < 0)
622 iPos = 0;
623 else if (iPos > (int)m_strEdit.size())
624 iPos = (int)m_strEdit.size();
625 m_iCursorPos = iPos;
626 CGUILabelControl* pEdit = ((CGUILabelControl*)GetControl(CTL_LABEL_EDIT));
627 if (pEdit)
629 pEdit->SetCursorPos(m_iCursorPos + (m_hiddenInput ? 0 : m_iEditingOffset));
633 int CGUIDialogKeyboardGeneric::GetCursorPos() const
635 return m_iCursorPos;
638 void CGUIDialogKeyboardGeneric::OnSymbols()
640 if (m_keyType == SYMBOLS)
641 m_keyType = LOWER;
642 else
643 m_keyType = SYMBOLS;
644 UpdateButtons();
647 void CGUIDialogKeyboardGeneric::OnShift()
649 m_bShift = !m_bShift;
650 UpdateButtons();
653 void CGUIDialogKeyboardGeneric::OnIPAddress()
655 // find any IP address in the current string if there is any
656 // We match to #.#.#.#
657 CStdString utf8String;
658 g_charsetConverter.wToUTF8(m_strEdit, utf8String);
659 CStdString ip;
660 CRegExp reg;
661 reg.RegComp("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+");
662 int start = reg.RegFind(utf8String.c_str());
663 int length = 0;
664 if (start > -1)
666 length = reg.GetSubLength(0);
667 ip = utf8String.substr(start, length);
669 else
670 start = utf8String.size();
671 if (CGUIDialogNumeric::ShowAndGetIPAddress(ip, g_localizeStrings.Get(14068)))
673 utf8String = utf8String.substr(0, start) + ip.c_str() + utf8String.substr(start + length);
674 g_charsetConverter.utf8ToW(utf8String, m_strEdit);
675 UpdateLabel();
676 CGUILabelControl* pEdit = ((CGUILabelControl*)GetControl(CTL_LABEL_EDIT));
677 if (pEdit)
678 pEdit->SetCursorPos(m_strEdit.size());
682 void CGUIDialogKeyboardGeneric::ResetShiftAndSymbols()
684 if (m_bShift) OnShift();
685 if (m_keyType == SYMBOLS) OnSymbols();
686 m_lastRemoteClickTime = 0;
689 const char* CGUIDialogKeyboardGeneric::s_charsSeries[10] = { " 0!@#$%^&*()[]{}<>/\\|", ".,1;:\'\"-+_=?`~", "ABC2", "DEF3", "GHI4", "JKL5", "MNO6", "PQRS7", "TUV8", "WXYZ9" };
691 void CGUIDialogKeyboardGeneric::SetControlLabel(int id, const CStdString &label)
692 { // find all controls with this id, and set all their labels
693 CGUIMessage message(GUI_MSG_LABEL_SET, GetID(), id);
694 message.SetLabel(label);
695 for (unsigned int i = 0; i < m_children.size(); i++)
697 if (m_children[i]->GetID() == id || m_children[i]->IsGroup())
698 m_children[i]->OnMessage(message);
702 void CGUIDialogKeyboardGeneric::OnOK()
704 m_bIsConfirmed = true;
705 Close();
708 void CGUIDialogKeyboardGeneric::SetHeading(const std::string &heading)
710 m_strHeading = heading;
713 int CGUIDialogKeyboardGeneric::GetWindowId() const
715 return GetID();
718 void CGUIDialogKeyboardGeneric::Cancel()
720 m_bIsConfirmed = false;
721 Close();
724 bool CGUIDialogKeyboardGeneric::ShowAndGetInput(char_callback_t pCallback, const std::string &initialString, std::string &typedString, const std::string &heading, bool bHiddenInput)
726 CGUIDialogKeyboardGeneric *pKeyboard = (CGUIDialogKeyboardGeneric*)g_windowManager.GetWindow(WINDOW_DIALOG_KEYBOARD);
728 if (!pKeyboard)
729 return false;
731 m_pCharCallback = pCallback;
732 // setup keyboard
733 pKeyboard->Initialize();
734 pKeyboard->SetHeading(heading);
735 pKeyboard->SetHiddenInput(bHiddenInput);
736 pKeyboard->SetText(initialString);
737 // do this using a thread message to avoid render() conflicts
738 ThreadMessage tMsg = {TMSG_DIALOG_DOMODAL, WINDOW_DIALOG_KEYBOARD, (unsigned int)g_windowManager.GetActiveWindow()};
739 CApplicationMessenger::Get().SendMessage(tMsg, true);
740 pKeyboard->Close();
742 // If have text - update this.
743 if (pKeyboard->IsConfirmed())
745 typedString = pKeyboard->GetText();
746 return true;
748 else return false;
751 void CGUIDialogKeyboardGeneric::OnPasteClipboard(void)
753 CStdStringW unicode_text;
754 CStdStringA utf8_text;
756 // Get text from the clipboard
757 utf8_text = g_Windowing.GetClipboardText();
759 // Insert the pasted text at the current cursor position.
760 if (utf8_text.length() > 0)
762 g_charsetConverter.utf8ToW(utf8_text, unicode_text);
764 size_t i = GetCursorPos();
765 if (i > m_strEdit.size())
766 i = m_strEdit.size();
767 CStdStringW left_end = m_strEdit.substr(0, i);
768 CStdStringW right_end = m_strEdit.substr(i);
770 m_strEdit = left_end;
771 m_strEdit.append(unicode_text);
772 m_strEdit.append(right_end);
773 UpdateLabel();
774 MoveCursor(unicode_text.length());