2 * Copyright (C) 2005-2008 Team XBMC
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)
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, write to
17 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18 * http://www.gnu.org/copyleft/gpl.html
22 #include "GUIEditControl.h"
23 #include "GUIWindowManager.h"
24 #include "utils/CharsetConverter.h"
25 #include "dialogs/GUIDialogKeyboard.h"
26 #include "dialogs/GUIDialogNumeric.h"
27 #include "LocalizeStrings.h"
29 #include "utils/md5.h"
32 #include "CocoaInterface.h"
35 const char* CGUIEditControl::smsLetters[10] = { " !@#$%^&*()[]{}<>/\\|0", ".,;:\'\"-+_=?`~1", "abc2", "def3", "ghi4", "jkl5", "mno6", "pqrs7", "tuv8", "wxyz9" };
36 const unsigned int CGUIEditControl::smsDelay = 1000;
44 CGUIEditControl::CGUIEditControl(int parentID, int controlID, float posX, float posY,
45 float width, float height, const CTextureInfo &textureFocus, const CTextureInfo &textureNoFocus,
46 const CLabelInfo& labelInfo, const std::string &text)
47 : CGUIButtonControl(parentID, controlID, posX, posY, width, height, textureFocus, textureNoFocus, labelInfo)
53 void CGUIEditControl::DefaultConstructor()
55 ControlType = GUICONTROL_EDIT;
57 m_textWidth = GetWidth();
61 m_inputType = INPUT_TYPE_TEXT;
64 m_label.SetAlign(m_label.GetLabelInfo().align & XBFONT_CENTER_Y); // left align
65 m_label2.GetLabelInfo().offsetX = 0;
69 CGUIEditControl::CGUIEditControl(const CGUIButtonControl &button)
70 : CGUIButtonControl(button)
75 CGUIEditControl::~CGUIEditControl(void)
79 bool CGUIEditControl::OnMessage(CGUIMessage &message)
81 if (message.GetMessage() == GUI_MSG_SET_TYPE)
83 SetInputType((INPUT_TYPE)message.GetParam1(), (int)message.GetParam2());
86 else if (message.GetMessage() == GUI_MSG_ITEM_SELECTED)
88 message.SetLabel(GetLabel2());
91 else if (message.GetMessage() == GUI_MSG_SETFOCUS ||
92 message.GetMessage() == GUI_MSG_LOSTFOCUS)
96 return CGUIButtonControl::OnMessage(message);
99 bool CGUIEditControl::OnAction(const CAction &action)
103 if (action.GetID() == ACTION_BACKSPACE)
109 m_text2.erase(--m_cursorPos, 1);
114 else if (action.GetID() == ACTION_MOVE_LEFT)
123 else if (action.GetID() == ACTION_MOVE_RIGHT)
125 if ((unsigned int) m_cursorPos < m_text2.size())
132 else if (action.GetID() == ACTION_PASTE)
137 else if (action.GetID() >= KEY_VKEY && action.GetID() < KEY_ASCII)
139 // input from the keyboard (vkey, not ascii)
140 BYTE b = action.GetID() & 0xFF;
141 if (b == 0x24) // home
147 else if (b == 0x23) // end
149 m_cursorPos = m_text2.length();
153 if (b == 0x25 && m_cursorPos > 0)
159 if (b == 0x27 && m_cursorPos < m_text2.length())
167 if (m_cursorPos < m_text2.length())
170 m_text2.erase(m_cursorPos, 1);
180 m_text2.erase(--m_cursorPos, 1);
186 else if (action.GetID() >= KEY_ASCII)
188 // input from the keyboard
189 switch (action.GetUnicode())
196 // enter - send click message, but otherwise ignore
197 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 1);
201 { // escape - fallthrough to default action
202 return CGUIButtonControl::OnAction(action);
210 m_text2.erase(--m_cursorPos, 1);
217 m_text2.insert(m_text2.begin() + m_cursorPos++, (WCHAR)action.GetUnicode());
224 else if (action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9)
225 { // input from the remote
227 if (m_inputType == INPUT_TYPE_FILTER)
228 { // filtering - use single number presses
229 m_text2.insert(m_text2.begin() + m_cursorPos++, L'0' + (action.GetID() - REMOTE_0));
233 OnSMSCharacter(action.GetID() - REMOTE_0);
236 return CGUIButtonControl::OnAction(action);
239 void CGUIEditControl::OnClick()
241 // we received a click - it's not from the keyboard, so pop up the virtual keyboard, unless
242 // that is where we reside!
243 if (GetParentID() == WINDOW_DIALOG_KEYBOARD)
247 g_charsetConverter.wToUTF8(m_text2, utf8);
248 bool textChanged = false;
249 CStdString heading = g_localizeStrings.Get(m_inputHeading ? m_inputHeading : 16028);
252 case INPUT_TYPE_NUMBER:
253 textChanged = CGUIDialogNumeric::ShowAndGetNumber(utf8, heading);
255 case INPUT_TYPE_SECONDS:
256 textChanged = CGUIDialogNumeric::ShowAndGetSeconds(utf8, g_localizeStrings.Get(21420));
258 case INPUT_TYPE_DATE:
261 dateTime.SetFromDBDate(utf8);
262 if (dateTime < CDateTime(2000,1, 1, 0, 0, 0))
263 dateTime = CDateTime(2000, 1, 1, 0, 0, 0);
265 dateTime.GetAsSystemTime(date);
266 if (CGUIDialogNumeric::ShowAndGetDate(date, g_localizeStrings.Get(21420)))
268 dateTime = CDateTime(date);
269 utf8 = dateTime.GetAsDBDate();
274 case INPUT_TYPE_IPADDRESS:
275 textChanged = CGUIDialogNumeric::ShowAndGetIPAddress(utf8, heading);
277 case INPUT_TYPE_SEARCH:
278 textChanged = CGUIDialogKeyboard::ShowAndGetFilter(utf8, true);
280 case INPUT_TYPE_FILTER:
281 textChanged = CGUIDialogKeyboard::ShowAndGetFilter(utf8, false);
283 case INPUT_TYPE_PASSWORD_MD5:
284 utf8 = ""; // TODO: Ideally we'd send this to the keyboard and tell the keyboard we have this type of input
286 case INPUT_TYPE_TEXT:
288 textChanged = CGUIDialogKeyboard::ShowAndGetInput(utf8, heading, true, m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5);
294 g_charsetConverter.utf8ToW(utf8, m_text2);
295 m_cursorPos = m_text2.size();
297 m_cursorPos = m_text2.size();
301 void CGUIEditControl::UpdateText(bool sendUpdate)
306 SEND_CLICK_MESSAGE(GetID(), GetParentID(), 0);
308 vector<CGUIActionDescriptor> textChangeActions = m_textChangeActions;
309 for (unsigned int i = 0; i < textChangeActions.size(); i++)
311 CGUIMessage message(GUI_MSG_EXECUTE, GetID(), GetParentID());
312 message.SetAction(textChangeActions[i]);
313 g_windowManager.SendMessage(message);
319 void CGUIEditControl::SetInputType(CGUIEditControl::INPUT_TYPE type, int heading)
322 m_inputHeading = heading;
323 // TODO: Verify the current input string?
326 void CGUIEditControl::RecalcLabelPosition()
328 // ensure that our cursor is within our width
331 CStdStringW text = GetDisplayedText();
332 m_textWidth = m_label.CalcTextWidth(text + L'|');
333 float beforeCursorWidth = m_label.CalcTextWidth(text.Left(m_cursorPos));
334 float afterCursorWidth = m_label.CalcTextWidth(text.Left(m_cursorPos) + L'|');
335 float leftTextWidth = m_label.GetRenderRect().Width();
336 float maxTextWidth = m_label.GetMaxWidth();
337 if (leftTextWidth > 0)
338 maxTextWidth -= leftTextWidth + spaceWidth;
340 // if skinner forgot to set height :p
341 if (m_height == 0 && m_label.GetLabelInfo().font)
342 m_height = m_label.GetLabelInfo().font->GetTextHeight(1);
344 if (m_textWidth > maxTextWidth)
345 { // we render taking up the full width, so make sure our cursor position is
346 // within the render window
347 if (m_textOffset + afterCursorWidth > maxTextWidth)
349 // move the position to the left (outside of the viewport)
350 m_textOffset = maxTextWidth - afterCursorWidth;
352 else if (m_textOffset + beforeCursorWidth < 0) // offscreen to the left
354 // otherwise use original position
355 m_textOffset = -beforeCursorWidth;
357 else if (m_textOffset + m_textWidth < maxTextWidth)
358 { // we have more text than we're allowed, but we aren't filling all the space
359 m_textOffset = maxTextWidth - m_textWidth;
366 void CGUIEditControl::RenderText()
368 if (m_smsTimer.GetElapsedMilliseconds() > smsDelay)
373 m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
374 m_label.SetText(m_info.GetLabel(GetParentID()));
375 RecalcLabelPosition();
379 float posX = m_label.GetRenderRect().x1;
380 float maxTextWidth = m_label.GetMaxWidth();
382 // start by rendering the normal text
383 float leftTextWidth = m_label.GetRenderRect().Width();
384 if (leftTextWidth > 0)
386 // render the text on the left
387 m_label.SetColor(GetTextColor());
390 posX += leftTextWidth + spaceWidth;
391 maxTextWidth -= leftTextWidth + spaceWidth;
394 if (g_graphicsContext.SetClipRegion(posX, m_posY, maxTextWidth, m_height))
396 uint32_t align = m_label.GetLabelInfo().align & XBFONT_CENTER_Y; // start aligned left
397 if (m_label2.GetTextWidth() < maxTextWidth)
398 { // align text as our text fits
399 if (leftTextWidth > 0)
400 { // right align as we have 2 labels
401 align |= XBFONT_RIGHT;
404 { // align by whatever the skinner requests
405 align |= (m_label2.GetLabelInfo().align & 3);
408 CStdStringW text = GetDisplayedText();
409 // add the cursor if we're focused
413 if ((m_focusCounter % 64) > 32)
416 col = L"[COLOR 00FFFFFF]|[/COLOR]";
417 text.Insert(m_cursorPos, col);
420 m_label2.SetMaxRect(posX + m_textOffset, m_posY, maxTextWidth - m_textOffset, m_height);
421 m_label2.SetTextW(text);
422 m_label2.SetAlign(align);
423 m_label2.SetColor(GetTextColor());
425 g_graphicsContext.RestoreClipRegion();
429 CStdStringW CGUIEditControl::GetDisplayedText() const
431 if (m_inputType == INPUT_TYPE_PASSWORD || m_inputType == INPUT_TYPE_PASSWORD_MD5)
434 text.append(m_text2.size(), L'*');
440 void CGUIEditControl::ValidateCursor()
442 if (m_cursorPos > m_text2.size())
443 m_cursorPos = m_text2.size();
446 void CGUIEditControl::SetLabel(const std::string &text)
448 CGUIButtonControl::SetLabel(text);
452 void CGUIEditControl::SetLabel2(const std::string &text)
455 g_charsetConverter.utf8ToW(text, newText);
456 if (newText != m_text2)
458 m_isMD5 = m_inputType == INPUT_TYPE_PASSWORD_MD5;
460 m_cursorPos = m_text2.size();
465 CStdString CGUIEditControl::GetLabel2() const
468 g_charsetConverter.wToUTF8(m_text2, text);
469 if (m_inputType == INPUT_TYPE_PASSWORD_MD5 && !m_isMD5)
470 return XBMC::XBMC_MD5::GetMD5(text);
474 bool CGUIEditControl::ClearMD5()
476 if (m_inputType != INPUT_TYPE_PASSWORD_MD5 || !m_isMD5)
485 unsigned int CGUIEditControl::GetCursorPosition() const
490 void CGUIEditControl::SetCursorPosition(unsigned int iPosition)
492 m_cursorPos = iPosition;
495 void CGUIEditControl::OnSMSCharacter(unsigned int key)
498 bool sendUpdate = false;
499 if (m_smsTimer.IsRunning())
501 // we're already entering an SMS character
502 if (key != m_smsLastKey || m_smsTimer.GetElapsedMilliseconds() > smsDelay)
503 { // a different key was clicked than last time, or we have timed out
509 { // same key as last time within the appropriate time period
512 m_text2.erase(--m_cursorPos, 1);
516 { // key is pressed for the first time
521 m_smsKeyIndex = m_smsKeyIndex % strlen(smsLetters[key]);
523 m_text2.insert(m_text2.begin() + m_cursorPos++, smsLetters[key][m_smsKeyIndex]);
524 UpdateText(sendUpdate);
525 m_smsTimer.StartZero();
528 void CGUIEditControl::OnPasteClipboard()
530 #if defined(__APPLE__) && !defined(__arm__)
531 const char *szStr = Cocoa_Paste();
535 m_cursorPos+=strlen(szStr);
539 if (OpenClipboard(g_hWnd))
541 HGLOBAL hglb = GetClipboardData(CF_TEXT);
544 LPTSTR lptstr = (LPTSTR)GlobalLock(hglb);
547 m_text2 = (char*)lptstr;