diff options
author | jmarshallnz <jcmarsha@gmail.com> | 2014-08-09 13:10:16 +1200 |
---|---|---|
committer | jmarshallnz <jcmarsha@gmail.com> | 2014-08-09 13:10:16 +1200 |
commit | ba51182916400f9e999ea8f27524b958829248a9 (patch) | |
tree | 61cda5ffbca247eff5a374b85bc60ea3306136c5 | |
parent | 63b41ad9da29928edb7ee08fbdf6d952a60b8619 (diff) | |
parent | fe2428df350a426e5f7d6a80d4a82be70a58dc3d (diff) |
Merge pull request #5008 from jmarshallnz/edit_keyboard
Use an edit control in the keyboard dialog
-rw-r--r-- | system/settings/settings.xml | 5 | ||||
-rw-r--r-- | xbmc/Application.cpp | 46 | ||||
-rw-r--r-- | xbmc/dialogs/GUIDialogKeyboardGeneric.cpp | 439 | ||||
-rw-r--r-- | xbmc/dialogs/GUIDialogKeyboardGeneric.h | 31 | ||||
-rw-r--r-- | xbmc/guilib/GUIEditControl.cpp | 160 | ||||
-rw-r--r-- | xbmc/guilib/GUIEditControl.h | 9 |
6 files changed, 262 insertions, 428 deletions
diff --git a/system/settings/settings.xml b/system/settings/settings.xml index cc331b7280..32ff1d9213 100644 --- a/system/settings/settings.xml +++ b/system/settings/settings.xml @@ -2551,11 +2551,6 @@ </setting> </group> <group id="2"> - <setting id="input.remoteaskeyboard" type="boolean" label="21449" help="36376"> - <level>1</level> - <default>false</default> - <control type="toggle" /> - </setting> <setting id="input.enablemouse" type="boolean" label="21369" help="36377"> <level>0</level> <control type="toggle" /> diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index a2e4fe8317..2093886a4a 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -2461,35 +2461,31 @@ bool CApplication::OnKey(const CKey& key) } if (useKeyboard) { - action = CAction(0); // reset our action - if (CSettings::Get().GetBool("input.remoteaskeyboard")) + // use the virtualkeyboard section of the keymap, and send keyboard-specific or navigation + // actions through if that's what they are + CAction action = CButtonTranslator::GetInstance().GetAction(WINDOW_DIALOG_KEYBOARD, key); + if (!(action.GetID() == ACTION_MOVE_LEFT || + action.GetID() == ACTION_MOVE_RIGHT || + action.GetID() == ACTION_MOVE_UP || + action.GetID() == ACTION_MOVE_DOWN || + action.GetID() == ACTION_SELECT_ITEM || + action.GetID() == ACTION_ENTER || + action.GetID() == ACTION_PREVIOUS_MENU || + action.GetID() == ACTION_NAV_BACK)) { - // users remote is executing keyboard commands, so use the virtualkeyboard section of keymap.xml - // and send those rather than actual keyboard presses. Only for navigation-type commands though - action = CButtonTranslator::GetInstance().GetAction(WINDOW_DIALOG_KEYBOARD, key); - if (!(action.GetID() == ACTION_MOVE_LEFT || - action.GetID() == ACTION_MOVE_RIGHT || - action.GetID() == ACTION_MOVE_UP || - action.GetID() == ACTION_MOVE_DOWN || - action.GetID() == ACTION_SELECT_ITEM || - action.GetID() == ACTION_ENTER || - action.GetID() == ACTION_PREVIOUS_MENU || - action.GetID() == ACTION_NAV_BACK)) - { - // the action isn't plain navigation - check for a keyboard-specific keymap - action = CButtonTranslator::GetInstance().GetAction(WINDOW_DIALOG_KEYBOARD, key, false); - if (!(action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) || - action.GetID() == ACTION_BACKSPACE || - action.GetID() == ACTION_SHIFT || - action.GetID() == ACTION_SYMBOLS || - action.GetID() == ACTION_CURSOR_LEFT || - action.GetID() == ACTION_CURSOR_RIGHT) - action = CAction(0); // don't bother with this action - } + // the action isn't plain navigation - check for a keyboard-specific keymap + action = CButtonTranslator::GetInstance().GetAction(WINDOW_DIALOG_KEYBOARD, key, false); + if (!(action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) || + action.GetID() == ACTION_BACKSPACE || + action.GetID() == ACTION_SHIFT || + action.GetID() == ACTION_SYMBOLS || + action.GetID() == ACTION_CURSOR_LEFT || + action.GetID() == ACTION_CURSOR_RIGHT) + action = CAction(0); // don't bother with this action } + // else pass the keys through directly if (!action.GetID()) { - // keyboard entry - pass the keys through directly if (key.GetFromService()) action = CAction(key.GetButtonCode() != KEY_INVALID ? key.GetButtonCode() : 0, key.GetUnicode()); else diff --git a/xbmc/dialogs/GUIDialogKeyboardGeneric.cpp b/xbmc/dialogs/GUIDialogKeyboardGeneric.cpp index f8cd48ddc8..2a7902a73f 100644 --- a/xbmc/dialogs/GUIDialogKeyboardGeneric.cpp +++ b/xbmc/dialogs/GUIDialogKeyboardGeneric.cpp @@ -20,7 +20,8 @@ #include "interfaces/AnnouncementManager.h" #include "input/XBMC_vkeys.h" -#include "guilib/GUILabelControl.h" +#include "guilib/GUIEditControl.h" +#include "guilib/GUILabelControl.h" // for backward compatibility #include "guilib/GUIWindowManager.h" #include "guilib/Key.h" #include "guilib/LocalizeStrings.h" @@ -28,15 +29,9 @@ #include "GUIDialogNumeric.h" #include "GUIDialogOK.h" #include "GUIDialogKeyboardGeneric.h" -#include "utils/TimeUtils.h" #include "utils/RegExp.h" #include "ApplicationMessenger.h" -#include "windowing/WindowingFactory.h" -#include "utils/CharsetConverter.h" - -#if defined(TARGET_DARWIN) -#include "osx/CocoaInterface.h" -#endif +#include "addons/Skin.h" // for backward compatibility // Symbol mapping (based on MS virtual keyboard - may need improving) static char symbol_map[37] = ")!@#$%^&*([]{}-_=+;:\'\",.<>/?\\|`~ "; @@ -51,8 +46,9 @@ static char symbol_map[37] = ")!@#$%^&*([]{}-_=+;:\'\",.<>/?\\|`~ "; #define CTL_BUTTON_IP_ADDRESS 307 #define CTL_BUTTON_CLEAR 308 -#define CTL_LABEL_EDIT 310 +#define CTL_LABEL_EDIT 310 // backward compatibility #define CTL_LABEL_HEADING 311 +#define CTL_EDIT 312 #define CTL_BUTTON_BACKSPACE 8 @@ -60,7 +56,6 @@ static char symbolButtons[] = "._-@/\\"; #define NUM_SYMBOLS sizeof(symbolButtons) - 1 #define SEARCH_DELAY 1000 -#define REMOTE_SMS_DELAY 1000 CGUIDialogKeyboardGeneric::CGUIDialogKeyboardGeneric() : CGUIDialog(WINDOW_DIALOG_KEYBOARD, "DialogKeyboard.xml") @@ -72,12 +67,34 @@ CGUIDialogKeyboardGeneric::CGUIDialogKeyboardGeneric() m_hiddenInput = false; m_keyType = LOWER; m_strHeading = ""; - m_iCursorPos = 0; - m_iEditingOffset = 0; - m_lastRemoteClickTime = 0; m_loadType = KEEP_IN_MEMORY; } +void CGUIDialogKeyboardGeneric::OnWindowLoaded() +{ + CGUIEditControl *edit = (CGUIEditControl *)GetControl(CTL_EDIT); + if (!edit && g_SkinInfo && g_SkinInfo->APIVersion() < ADDON::AddonVersion("5.2.0")) + { + // backward compatibility: convert label to edit control + CGUILabelControl *label = (CGUILabelControl *)GetControl(CTL_LABEL_EDIT); + if (label && label->GetControlType() == CGUIControl::GUICONTROL_LABEL) + { + // create a new edit control positioned in the same spot + edit = new CGUIEditControl(label->GetParentID(), CTL_EDIT, label->GetXPosition(), label->GetYPosition(), + label->GetWidth(), label->GetHeight(), CTextureInfo(), CTextureInfo(), + label->GetLabelInfo(), ""); + AddControl(edit); + m_defaultControl = CTL_EDIT; + m_defaultAlways = true; + } + } + // show the cursor always + if (edit) + edit->SetShowCursorAlways(true); + + CGUIDialog::OnWindowLoaded(); +} + void CGUIDialogKeyboardGeneric::OnInitWindow() { CGUIDialog::OnInitWindow(); @@ -87,12 +104,6 @@ void CGUIDialogKeyboardGeneric::OnInitWindow() // set alphabetic (capitals) UpdateButtons(); - CGUILabelControl* pEdit = dynamic_cast<CGUILabelControl*>(GetControl(CTL_LABEL_EDIT)); - if (pEdit) - { - pEdit->ShowCursor(); - } - // set heading if (!m_strHeading.empty()) { @@ -103,7 +114,12 @@ void CGUIDialogKeyboardGeneric::OnInitWindow() { SET_CONTROL_HIDDEN(CTL_LABEL_HEADING); } - g_Windowing.EnableTextInput(true); + // set type + { + CGUIMessage msg(GUI_MSG_SET_TYPE, GetID(), CTL_EDIT, m_hiddenInput ? CGUIEditControl::INPUT_TYPE_PASSWORD : CGUIEditControl::INPUT_TYPE_TEXT); + OnMessage(msg); + } + SetEditText(m_text); CVariant data; data["title"] = m_strHeading; @@ -129,12 +145,7 @@ bool CGUIDialogKeyboardGeneric::OnAction(const CAction &action) } else if (action.GetID() == ACTION_CURSOR_RIGHT) { - if (m_strEditing.empty() && (unsigned int) GetCursorPos() == m_strEdit.size() && (m_strEdit.size() == 0 || m_strEdit[m_strEdit.size() - 1] != ' ')) - { // add a space - Character(L' '); - } - else - MoveCursor(1); + MoveCursor(1); } else if (action.GetID() == ACTION_SHIFT) { @@ -144,104 +155,20 @@ bool CGUIDialogKeyboardGeneric::OnAction(const CAction &action) { OnSymbols(); } - else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) - { - OnRemoteNumberClick(action.GetID()); - } - else if (action.GetID() == ACTION_PASTE) - { - OnPasteClipboard(); - } - else if (action.GetID() >= KEY_VKEY && action.GetID() < KEY_ASCII) - { // input from the keyboard (vkey, not ascii) - if (!m_strEditing.empty()) - return handled; - uint8_t b = action.GetID() & 0xFF; - if (b == XBMCVK_HOME) - { - SetCursorPos(0); - } - else if (b == XBMCVK_END) - { - SetCursorPos(m_strEdit.size()); - } - else if (b == XBMCVK_LEFT) - { - MoveCursor( -1); - } - else if (b == XBMCVK_RIGHT) - { - MoveCursor(1); - } - else if (b == XBMCVK_RETURN || b == XBMCVK_NUMPADENTER) - { - OnOK(); - } - else if (b == XBMCVK_DELETE) - { - if (GetCursorPos() < (int)m_strEdit.size()) - { - MoveCursor(1); - Backspace(); - } - } - else if (b == XBMCVK_BACK) Backspace(); - else if (b == XBMCVK_ESCAPE) Close(); - } else if (action.GetID() >= KEY_ASCII) - { // input from the keyboard - //char ch = action.GetID() & 0xFF; - int ch = action.GetUnicode(); - - // Ignore non-printing characters - if ( !((0 <= ch && ch < 0x8) || (0xE <= ch && ch < 0x1B) || (0x1C <= ch && ch < 0x20)) ) - { - switch (ch) - { - case 0x8: // backspace - Backspace(); - break; - case 0x9: // Tab (do nothing) - case 0xB: // Non-printing character, ignore - case 0xC: // Non-printing character, ignore - break; - case 0xA: // enter - case 0xD: // enter - OnOK(); - break; - case 0x1B: // escape - Close(); - break; - case 0x7F: // Delete - if (GetCursorPos() < (int)m_strEdit.size()) - { - MoveCursor(1); - Backspace(); - } - break; - default: //use character input - // When we support text input method, we only accept text by gui text message. - if (!g_Windowing.IsTextInputEnabled()) - Character(action.GetUnicode()); - break; - } - } + { // send action to the edit control + CGUIControl *edit = GetControl(CTL_EDIT); + if (edit) + edit->OnAction(action); } else // unhandled by us - let's see if the baseclass wants it handled = CGUIDialog::OnAction(action); - if (handled && m_pCharCallback) - { // we did _something_, so make sure our search message filter is reset - m_pCharCallback(this, GetText()); - } return handled; } bool CGUIDialogKeyboardGeneric::OnMessage(CGUIMessage& message) { - CGUIDialog::OnMessage(message); - - switch ( message.GetMessage() ) { case GUI_MSG_CLICKED: @@ -279,10 +206,21 @@ bool CGUIDialogKeyboardGeneric::OnMessage(CGUIMessage& message) OnIPAddress(); break; case CTL_BUTTON_CLEAR: - SetText(""); + SetEditText(""); break; + case CTL_EDIT: + { + CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), CTL_EDIT); + OnMessage(msg); + // update callback I guess? + if (m_pCharCallback) + { // we did _something_, so make sure our search message filter is reset + m_pCharCallback(this, msg.GetLabel()); + } + m_text = msg.GetLabel(); + return true; + } default: - m_lastRemoteKeyClicked = 0; OnClickButton(iControl); break; } @@ -290,145 +228,62 @@ bool CGUIDialogKeyboardGeneric::OnMessage(CGUIMessage& message) break; case GUI_MSG_SET_TEXT: - SetText(message.GetLabel()); - - // close the dialog if requested - if (message.GetParam1() > 0) - OnOK(); - break; - case GUI_MSG_INPUT_TEXT: - InputText(message.GetLabel()); - break; - case GUI_MSG_INPUT_TEXT_EDIT: - InputTextEditing(message.GetLabel(), message.GetParam1(), message.GetParam2()); - break; + { + // ensure this goes to the edit control + CGUIControl *edit = GetControl(CTL_EDIT); + if (edit) + edit->OnMessage(message); + + // close the dialog if requested + if (message.GetMessage() == GUI_MSG_SET_TEXT && message.GetParam1() > 0) + OnOK(); + return true; + } } - return true; + return CGUIDialog::OnMessage(message); } -void CGUIDialogKeyboardGeneric::SetText(const std::string& aTextString) +void CGUIDialogKeyboardGeneric::SetEditText(const std::string &text) { - m_strEdit.clear(); - m_strEditing.clear(); - m_iEditingOffset = 0; - g_charsetConverter.utf8ToW(aTextString, m_strEdit); - UpdateLabel(); - SetCursorPos(m_strEdit.size()); + CGUIMessage msg(GUI_MSG_SET_TEXT, GetID(), CTL_EDIT); + msg.SetLabel(text); + OnMessage(msg); } -void CGUIDialogKeyboardGeneric::InputText(const std::string& aTextString) +void CGUIDialogKeyboardGeneric::SetText(const std::string& text) { - std::wstring newStr; - g_charsetConverter.utf8ToW(aTextString, newStr); - if (!newStr.empty()) - { - m_strEditing.clear(); - m_iEditingOffset = 0; - m_strEdit.insert(GetCursorPos(), newStr); - UpdateLabel(); - MoveCursor(newStr.size()); - } + m_text = text; } -void CGUIDialogKeyboardGeneric::InputTextEditing(const std::string& aTextString, int start, int length) +const std::string &CGUIDialogKeyboardGeneric::GetText() const { - m_strEditing.clear(); - m_iEditingOffset = start; - m_iEditingLength = length; - g_charsetConverter.utf8ToW(aTextString, m_strEditing); -// CLog::Log(LOGDEBUG, "CGUIDialogKeyboardGeneric::InputTextEditing len %lu range(%d, %d) -> range len %d", m_strEditing.size(), m_iEditingOffset, length, m_iEditingLength); - UpdateLabel(); - SetCursorPos(GetCursorPos()); + return m_text; } -std::string CGUIDialogKeyboardGeneric::GetText() const -{ - std::string utf8String; - g_charsetConverter.wToUTF8(m_strEdit, utf8String); - return utf8String; -} - -void CGUIDialogKeyboardGeneric::Character(WCHAR ch) +void CGUIDialogKeyboardGeneric::Character(char ch) { if (!ch) return; - m_strEditing.clear(); - m_iEditingOffset = 0; - // TODO: May have to make this routine take a WCHAR for the symbols? - m_strEdit.insert(GetCursorPos(), 1, ch); - UpdateLabel(); - MoveCursor(1); -} - -void CGUIDialogKeyboardGeneric::FrameMove() -{ - // reset the hide state of the label when the remote - // sms style input times out - if (m_lastRemoteClickTime && m_lastRemoteClickTime + REMOTE_SMS_DELAY < CTimeUtils::GetFrameTime()) - { - // finished inputting a sms style character - turn off our shift and symbol states - ResetShiftAndSymbols(); - } - CGUIDialog::FrameMove(); -} -void CGUIDialogKeyboardGeneric::UpdateLabel() // FIXME seems to be called twice for one USB SDL keyboard action/character -{ - CGUILabelControl* pEdit = dynamic_cast<CGUILabelControl*>(GetControl(CTL_LABEL_EDIT)); - if (pEdit) + std::string character(1, ch); + // send text to edit control + CGUIControl *edit = GetControl(CTL_EDIT); + if (edit) { - std::wstring edit = m_strEdit; - pEdit->SetHighlight(0, 0); - pEdit->SetSelection(0, 0); - if (m_hiddenInput) - { // convert to *'s - edit.clear(); - if (m_lastRemoteClickTime + REMOTE_SMS_DELAY > CTimeUtils::GetFrameTime() && m_iCursorPos > 0) - { // using the remove to input, so display the last key input - edit.append(m_iCursorPos - 1, L'*'); - edit.append(1, m_strEdit[m_iCursorPos - 1]); - } - else - edit.append(m_strEdit.size(), L'*'); - } - else if (!m_strEditing.empty()) - { - edit.insert(m_iCursorPos, m_strEditing); - pEdit->SetHighlight(m_iCursorPos, m_iCursorPos + m_strEditing.size()); - if (m_iEditingLength > 0) - pEdit->SetSelection(m_iCursorPos + m_iEditingOffset, m_iCursorPos + m_iEditingOffset + m_iEditingLength); - } - // convert back to utf8 - std::string utf8Edit; - g_charsetConverter.wToUTF8(edit, utf8Edit); - pEdit->SetLabel(utf8Edit); - // Send off a search message - unsigned int now = CTimeUtils::GetFrameTime(); - // don't send until the REMOTE_SMS_DELAY has passed - if (m_lastRemoteClickTime && m_lastRemoteClickTime + REMOTE_SMS_DELAY >= now) - return; - - if (m_pCharCallback) - { - // do not send editing text comes from system input method - if (!m_hiddenInput && !m_strEditing.empty()) - g_charsetConverter.wToUTF8(m_strEdit, utf8Edit); - m_pCharCallback(this, utf8Edit); - } + CGUIMessage msg(GUI_MSG_INPUT_TEXT, GetID(), CTL_EDIT); + msg.SetLabel(character); + edit->OnMessage(msg); } } void CGUIDialogKeyboardGeneric::Backspace() { - int iPos = GetCursorPos(); - if (iPos > 0) - { - m_strEdit.erase(iPos - 1, 1); - MoveCursor(-1); - UpdateLabel(); - } + // send action to edit control + CGUIControl *edit = GetControl(CTL_EDIT); + if (edit) + edit->OnAction(CAction(ACTION_BACKSPACE)); } void CGUIDialogKeyboardGeneric::OnClickButton(int iButtonControl) @@ -441,48 +296,6 @@ void CGUIDialogKeyboardGeneric::OnClickButton(int iButtonControl) Character(GetCharacter(iButtonControl)); } -void CGUIDialogKeyboardGeneric::OnRemoteNumberClick(int key) -{ - unsigned int now = CTimeUtils::GetFrameTime(); - - if (m_lastRemoteClickTime) - { // a remote key has been pressed - if (key != m_lastRemoteKeyClicked || m_lastRemoteClickTime + REMOTE_SMS_DELAY < now) - { // a different key was clicked than last time, or we have timed out - m_lastRemoteKeyClicked = key; - m_indexInSeries = 0; - // reset our shift and symbol states, and update our label to ensure the search filter is sent - ResetShiftAndSymbols(); - UpdateLabel(); - } - else - { // same key as last time within the appropriate time period - m_indexInSeries++; - Backspace(); - } - } - else - { // key is pressed for the first time - m_lastRemoteKeyClicked = key; - m_indexInSeries = 0; - } - - int arrayIndex = key - REMOTE_0; - m_indexInSeries = m_indexInSeries % strlen(s_charsSeries[arrayIndex]); - m_lastRemoteClickTime = now; - - // Select the character that will be pressed - const char* characterPressed = s_charsSeries[arrayIndex]; - characterPressed += m_indexInSeries; - - // use caps where appropriate - char ch = *characterPressed; - bool caps = (m_keyType == CAPS && !m_bShift) || (m_keyType == LOWER && m_bShift); - if (!caps && *characterPressed >= 'A' && *characterPressed <= 'Z') - ch += 32; - Character(ch); -} - char CGUIDialogKeyboardGeneric::GetCharacter(int iButton) { // First the numbers @@ -579,34 +392,14 @@ void CGUIDialogKeyboardGeneric::OnDeinitWindow(int nextWindowID) // reset the heading (we don't always have this) m_strHeading = ""; - g_Windowing.EnableTextInput(false); ANNOUNCEMENT::CAnnouncementManager::Get().Announce(ANNOUNCEMENT::Input, "xbmc", "OnInputFinished"); } void CGUIDialogKeyboardGeneric::MoveCursor(int iAmount) { - if (!m_strEditing.empty()) - return; - SetCursorPos(GetCursorPos() + iAmount); -} - -void CGUIDialogKeyboardGeneric::SetCursorPos(int iPos) -{ - if (iPos < 0) - iPos = 0; - else if (iPos > (int)m_strEdit.size()) - iPos = (int)m_strEdit.size(); - m_iCursorPos = iPos; - CGUILabelControl* pEdit = dynamic_cast<CGUILabelControl*>(GetControl(CTL_LABEL_EDIT)); - if (pEdit) - { - pEdit->SetCursorPos(m_iCursorPos + (m_hiddenInput ? 0 : m_iEditingOffset)); - } -} - -int CGUIDialogKeyboardGeneric::GetCursorPos() const -{ - return m_iCursorPos; + CGUIControl *edit = GetControl(CTL_EDIT); + if (edit) + edit->OnAction(CAction(iAmount < 0 ? ACTION_CURSOR_LEFT : ACTION_CURSOR_RIGHT)); } void CGUIDialogKeyboardGeneric::OnSymbols() @@ -628,40 +421,23 @@ void CGUIDialogKeyboardGeneric::OnIPAddress() { // find any IP address in the current string if there is any // We match to #.#.#.# - std::string utf8String; - g_charsetConverter.wToUTF8(m_strEdit, utf8String); + std::string text = GetText(); std::string ip; CRegExp reg; reg.RegComp("[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+"); - int start = reg.RegFind(utf8String.c_str()); + int start = reg.RegFind(text.c_str()); int length = 0; if (start > -1) { length = reg.GetSubLength(0); - ip = utf8String.substr(start, length); + ip = text.substr(start, length); } else - start = utf8String.size(); + start = text.size(); if (CGUIDialogNumeric::ShowAndGetIPAddress(ip, g_localizeStrings.Get(14068))) - { - utf8String = utf8String.substr(0, start) + ip.c_str() + utf8String.substr(start + length); - g_charsetConverter.utf8ToW(utf8String, m_strEdit); - UpdateLabel(); - CGUILabelControl* pEdit = dynamic_cast<CGUILabelControl*>(GetControl(CTL_LABEL_EDIT)); - if (pEdit) - pEdit->SetCursorPos(m_strEdit.size()); - } + SetEditText(text.substr(0, start) + ip.c_str() + text.substr(start + length)); } -void CGUIDialogKeyboardGeneric::ResetShiftAndSymbols() -{ - if (m_bShift) OnShift(); - if (m_keyType == SYMBOLS) OnSymbols(); - m_lastRemoteClickTime = 0; -} - -const char* CGUIDialogKeyboardGeneric::s_charsSeries[10] = { " 0!@#$%^&*()[]{}<>/\\|", ".,1;:\'\"-+_=?`~", "ABC2", "DEF3", "GHI4", "JKL5", "MNO6", "PQRS7", "TUV8", "WXYZ9" }; - void CGUIDialogKeyboardGeneric::SetControlLabel(int id, const std::string &label) { // find all controls with this id, and set all their labels CGUIMessage message(GUI_MSG_LABEL_SET, GetID(), id); @@ -721,30 +497,3 @@ bool CGUIDialogKeyboardGeneric::ShowAndGetInput(char_callback_t pCallback, const } else return false; } - -void CGUIDialogKeyboardGeneric::OnPasteClipboard(void) -{ - std::wstring unicode_text; - std::string utf8_text; - -// Get text from the clipboard - utf8_text = g_Windowing.GetClipboardText(); - - // Insert the pasted text at the current cursor position. - if (utf8_text.length() > 0) - { - g_charsetConverter.utf8ToW(utf8_text, unicode_text); - - size_t i = GetCursorPos(); - if (i > m_strEdit.size()) - i = m_strEdit.size(); - std::wstring left_end = m_strEdit.substr(0, i); - std::wstring right_end = m_strEdit.substr(i); - - m_strEdit = left_end; - m_strEdit.append(unicode_text); - m_strEdit.append(right_end); - UpdateLabel(); - MoveCursor(unicode_text.length()); - } -} diff --git a/xbmc/dialogs/GUIDialogKeyboardGeneric.h b/xbmc/dialogs/GUIDialogKeyboardGeneric.h index f3236b9d7e..260eeb52b0 100644 --- a/xbmc/dialogs/GUIDialogKeyboardGeneric.h +++ b/xbmc/dialogs/GUIDialogKeyboardGeneric.h @@ -36,19 +36,14 @@ class CGUIDialogKeyboardGeneric : public CGUIDialog, public CGUIKeyboard virtual void Cancel(); virtual int GetWindowId() const; - //CGUIDialog Interface - virtual void FrameMove(); void SetHeading(const std::string& heading); - void SetText(const std::string& aTextString); - void InputText(const std::string& aTextString); - void InputTextEditing(const std::string& aTextString, int start, int length); - std::string GetText() const; + void SetText(const std::string& text); + const std::string &GetText() const; bool IsConfirmed() { return m_bIsConfirmed; }; void SetHiddenInput(bool hiddenInput) { m_hiddenInput = hiddenInput; }; - void Character(WCHAR wch); - void OnPasteClipboard(void); protected: + virtual void OnWindowLoaded(); virtual void OnInitWindow(); virtual bool OnAction(const CAction &action); virtual bool OnMessage(CGUIMessage& message); @@ -56,41 +51,27 @@ class CGUIDialogKeyboardGeneric : public CGUIDialog, public CGUIKeyboard void SetControlLabel(int id, const std::string &label); void OnShift(); void MoveCursor(int iAmount); - void SetCursorPos(int iPos); - int GetCursorPos() const; void OnSymbols(); void OnIPAddress(); void OnOK(); private: void OnClickButton(int iButtonControl); - void OnRemoteNumberClick(int key); void UpdateButtons(); char GetCharacter(int iButton); - void UpdateLabel(); - void ResetShiftAndSymbols(); + void Character(char ch); void Backspace(); + void SetEditText(const std::string& text); void SendSearchMessage(); - std::wstring m_strEdit; - int m_iCursorPos; - - // holds the spelling region of keystrokes/text generated from 'input method' - std::wstring m_strEditing; - int m_iEditingOffset; - int m_iEditingLength; - bool m_bIsConfirmed; KEYBOARD m_keyType; int m_iMode; bool m_bShift; bool m_hiddenInput; - unsigned int m_lastRemoteClickTime; - WORD m_lastRemoteKeyClicked; - int m_indexInSeries; std::string m_strHeading; - static const char* s_charsSeries[10]; + std::string m_text; ///< current text char_callback_t m_pCharCallback; diff --git a/xbmc/guilib/GUIEditControl.cpp b/xbmc/guilib/GUIEditControl.cpp index f422d71a2e..76f7f54983 100644 --- a/xbmc/guilib/GUIEditControl.cpp +++ b/xbmc/guilib/GUIEditControl.cpp @@ -29,6 +29,7 @@ #include "XBDateTime.h" #include "windowing/WindowingFactory.h" #include "utils/md5.h" +#include "GUIUserMessages.h" #if defined(TARGET_DARWIN) #include "osx/CocoaInterface.h" @@ -59,6 +60,7 @@ void CGUIEditControl::DefaultConstructor() m_textWidth = GetWidth(); m_cursorPos = 0; m_cursorBlink = 0; + m_cursorShowAlways = false; m_inputHeading = 0; m_inputType = INPUT_TYPE_TEXT; m_smsLastKey = 0; @@ -69,6 +71,8 @@ void CGUIEditControl::DefaultConstructor() m_invalidInput = false; m_inputValidator = NULL; m_inputValidatorData = NULL; + m_editLength = 0; + m_editOffset = 0; } CGUIEditControl::CGUIEditControl(const CGUIButtonControl &button) @@ -93,17 +97,31 @@ bool CGUIEditControl::OnMessage(CGUIMessage &message) message.SetLabel(GetLabel2()); return true; } - else if (message.GetMessage() == GUI_MSG_SETFOCUS || - message.GetMessage() == GUI_MSG_LOSTFOCUS) - { - m_smsTimer.Stop(); - } else if (message.GetMessage() == GUI_MSG_SET_TEXT && ((message.GetControlId() <= 0 && HasFocus()) || (message.GetControlId() == GetID()))) { SetLabel2(message.GetLabel()); UpdateText(); } + else if (message.GetMessage() == GUI_MSG_INPUT_TEXT && !message.GetLabel().empty() + && (HasFocus() || message.GetControlId() == GetID())) + { + m_edit.clear(); + std::wstring str; + g_charsetConverter.utf8ToW(message.GetLabel(), str); + m_text2.insert(m_cursorPos, str); + m_cursorPos += str.size(); + UpdateText(); + return true; + } + else if (message.GetMessage() == GUI_MSG_INPUT_TEXT_EDIT && HasFocus()) + { + g_charsetConverter.utf8ToW(message.GetLabel(), m_edit); + m_editOffset = message.GetParam1(); + m_editLength = message.GetParam2(); + UpdateText(false); + return true; + } return CGUIButtonControl::OnMessage(message); } @@ -124,7 +142,8 @@ bool CGUIEditControl::OnAction(const CAction &action) } return true; } - else if (action.GetID() == ACTION_MOVE_LEFT) + else if (action.GetID() == ACTION_MOVE_LEFT || + action.GetID() == ACTION_CURSOR_LEFT) { if (m_cursorPos > 0) { @@ -133,7 +152,8 @@ bool CGUIEditControl::OnAction(const CAction &action) return true; } } - else if (action.GetID() == ACTION_MOVE_RIGHT) + else if (action.GetID() == ACTION_MOVE_RIGHT || + action.GetID() == ACTION_CURSOR_RIGHT) { if ((unsigned int) m_cursorPos < m_text2.size()) { @@ -148,7 +168,7 @@ bool CGUIEditControl::OnAction(const CAction &action) OnPasteClipboard(); return true; } - else if (action.GetID() >= KEY_VKEY && action.GetID() < KEY_ASCII) + else if (action.GetID() >= KEY_VKEY && action.GetID() < KEY_ASCII && !m_edit.empty()) { // input from the keyboard (vkey, not ascii) BYTE b = action.GetID() & 0xFF; @@ -196,13 +216,29 @@ bool CGUIEditControl::OnAction(const CAction &action) } return true; } + else if (b == XBMCVK_RETURN || b == XBMCVK_NUMPADENTER) + { + // enter - send click message, but otherwise ignore + SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1); + return true; + } + else if (b == XBMCVK_ESCAPE) + { // escape - fallthrough to default action + return CGUIButtonControl::OnAction(action); + } } else if (action.GetID() >= KEY_ASCII) { // input from the keyboard - switch (action.GetUnicode()) + int ch = action.GetUnicode(); + // ignore non-printing characters + if ( !((0 <= ch && ch < 0x8) || (0xE <= ch && ch < 0x1B) || (0x1C <= ch && ch < 0x20)) ) + { + switch (ch) { - case '\t': + case 9: // tab, ignore + case 11: // Non-printing character, ignore + case 12: // Non-printing character, ignore break; case 10: case 13: @@ -225,19 +261,34 @@ bool CGUIEditControl::OnAction(const CAction &action) } break; } + case 127: + { // delete + if (m_cursorPos < m_text2.length()) + { + if (!ClearMD5()) + m_text2.erase(m_cursorPos, 1); + } + break; + } default: { - ClearMD5(); - m_text2.insert(m_text2.begin() + m_cursorPos++, (WCHAR)action.GetUnicode()); + if (!g_Windowing.IsTextInputEnabled()) + { + ClearMD5(); + m_edit.clear(); + m_text2.insert(m_text2.begin() + m_cursorPos++, (WCHAR)action.GetUnicode()); + } break; } } UpdateText(); return true; + } } else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) { // input from the remote ClearMD5(); + m_edit.clear(); if (m_inputType == INPUT_TYPE_FILTER) { // filtering - use single number presses m_text2.insert(m_text2.begin() + m_cursorPos++, L'0' + (action.GetID() - REMOTE_0)); @@ -326,6 +377,7 @@ void CGUIEditControl::OnClick() if (textChanged) { ClearMD5(); + m_edit.clear(); g_charsetConverter.utf8ToW(utf8, m_text2); m_cursorPos = m_text2.size(); UpdateText(); @@ -438,23 +490,20 @@ void CGUIEditControl::ProcessText(unsigned int currentTime) align |= (m_label2.GetLabelInfo().align & 3); } } + changed |= m_label2.SetMaxRect(m_clipRect.x1 + m_textOffset, m_posY, m_clipRect.Width() - m_textOffset, m_height); + CStdStringW text = GetDisplayedText(); - // add the cursor if we're focused - if (HasFocus() && m_inputType != INPUT_TYPE_READONLY) + // add the cursor and highlighting if we're focused + if ((HasFocus() || m_cursorShowAlways) && m_inputType != INPUT_TYPE_READONLY) + changed |= SetStyledText(text); + else { - CStdStringW col; - if ((m_focusCounter % 64) > 32) - col = L"|"; + if (text.empty()) + changed |= m_label2.SetText(m_hintInfo.GetLabel(GetParentID())); else - col = L"[COLOR 00FFFFFF]|[/COLOR]"; - text.insert(m_cursorPos, col); + changed |= m_label2.SetTextW(text); } - changed |= m_label2.SetMaxRect(m_clipRect.x1 + m_textOffset, m_posY, m_clipRect.Width() - m_textOffset, m_height); - if (text.empty()) - changed |= m_label2.SetText(m_hintInfo.GetLabel(GetParentID())); - else - changed |= m_label2.SetTextW(text); changed |= m_label2.SetAlign(align); changed |= m_label2.SetColor(GetTextColor()); changed |= m_label2.SetOverflow(CGUILabel::OVER_FLOW_CLIP); @@ -492,13 +541,60 @@ void CGUIEditControl::SetHint(const CGUIInfoLabel& hint) CStdStringW CGUIEditControl::GetDisplayedText() const { + CStdStringW text(m_text2); if (m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5 || m_inputType == INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW) { - CStdStringW text; - text.append(m_text2.size(), L'*'); - return text; + text.clear(); + if (m_smsTimer.IsRunning()) + { // using the remove to input, so display the last key input + text.append(m_cursorPos - 1, L'*'); + text.append(1, m_text2[m_cursorPos - 1]); + text.append(m_text2.size() - m_cursorPos, L'*'); + } + else + text.append(m_text2.size(), L'*');; + } + else if (!m_edit.empty()) + text.insert(m_editOffset, m_edit); + return text; +} + +bool CGUIEditControl::SetStyledText(const CStdStringW &text) +{ + vecText styled; + styled.reserve(text.size() + 1); + + vecColors colors; + colors.push_back(m_label.GetLabelInfo().textColor); + colors.push_back(m_label.GetLabelInfo().disabledColor); + color_t select = m_label.GetLabelInfo().selectedColor; + if (!select) + select = 0xFFFF0000; + colors.push_back(select); + colors.push_back(0x00FFFFFF); + + unsigned int startHighlight = m_cursorPos; + unsigned int endHighlight = m_cursorPos + m_edit.size(); + unsigned int startSelection = m_cursorPos + m_editOffset; + unsigned int endSelection = m_cursorPos + m_editOffset + m_editLength; + + for (unsigned int i = 0; i < text.size(); i++) + { + unsigned int ch = text[i]; + if (m_editLength > 0 && startSelection <= i && i < endSelection) + ch |= (2 << 16); // highlight the letters we're playing with + else if (!m_edit.empty() && (i < startHighlight || i >= endHighlight)) + ch |= (1 << 16); // dim the bits we're not editing + styled.push_back(ch); } - return m_text2; + + // show the cursor + unsigned int ch = L'|'; + if ((++m_cursorBlink % 64) > 32) + ch |= (3 << 16); + styled.insert(styled.begin() + m_cursorPos, ch); + + return m_label2.SetStyledText(styled, colors); } void CGUIEditControl::ValidateCursor() @@ -515,6 +611,7 @@ void CGUIEditControl::SetLabel(const std::string &text) void CGUIEditControl::SetLabel2(const std::string &text) { + m_edit.clear(); CStdStringW newText; g_charsetConverter.utf8ToW(text, newText); if (newText != m_text2) @@ -650,3 +747,10 @@ void CGUIEditControl::ValidateInput() SetInvalid(); } } + +void CGUIEditControl::SetFocus(bool focus) +{ + m_smsTimer.Stop(); + g_Windowing.EnableTextInput(focus); + CGUIControl::SetFocus(focus); +} diff --git a/xbmc/guilib/GUIEditControl.h b/xbmc/guilib/GUIEditControl.h index f32652ec4d..b10dadd4ab 100644 --- a/xbmc/guilib/GUIEditControl.h +++ b/xbmc/guilib/GUIEditControl.h @@ -72,6 +72,8 @@ public: virtual CStdString GetLabel2() const; + void SetShowCursorAlways(bool always) { m_cursorShowAlways = always; } + unsigned int GetCursorPosition() const; void SetCursorPosition(unsigned int iPosition); @@ -85,10 +87,12 @@ public: virtual void SetInputValidation(StringValidation::Validator inputValidator, void *data = NULL); protected: + virtual void SetFocus(bool focus); virtual void ProcessText(unsigned int currentTime); virtual void RenderText(); virtual CGUILabel::COLOR GetTextColor() const; CStdStringW GetDisplayedText() const; + bool SetStyledText(const CStdStringW &text); void RecalcLabelPosition(); void ValidateCursor(); void UpdateText(bool sendUpdate = true); @@ -115,6 +119,7 @@ protected: unsigned int m_cursorPos; unsigned int m_cursorBlink; + bool m_cursorShowAlways; int m_inputHeading; INPUT_TYPE m_inputType; @@ -130,6 +135,10 @@ protected: unsigned int m_smsLastKey; CStopWatch m_smsTimer; + std::wstring m_edit; + int m_editOffset; + int m_editLength; + static const char* smsLetters[10]; static const unsigned int smsDelay; }; |