2 * Copyright (C) 2005-2013 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, see
17 * <http://www.gnu.org/licenses/>.
21 #include "GUIDialogNumeric.h"
22 #include "guilib/GUILabelControl.h"
23 #include "utils/md5.h"
24 #include "guilib/GUIWindowManager.h"
25 #include "GUIDialogOK.h"
26 #include "input/XBMC_vkeys.h"
27 #include "utils/StringUtils.h"
28 #include "guilib/Key.h"
29 #include "guilib/LocalizeStrings.h"
30 #include "interfaces/AnnouncementManager.h"
32 #define CONTROL_HEADING_LABEL 1
33 #define CONTROL_INPUT_LABEL 4
34 #define CONTROL_NUM0 10
35 #define CONTROL_NUM9 19
36 #define CONTROL_PREVIOUS 20
37 #define CONTROL_ENTER 21
38 #define CONTROL_NEXT 22
39 #define CONTROL_BACKSPACE 23
42 CGUIDialogNumeric::CGUIDialogNumeric(void)
43 : CGUIDialog(WINDOW_DIALOG_NUMERIC
, "DialogNumeric.xml")
48 m_mode
= INPUT_PASSWORD
;
50 memset(&m_datetime
, 0, sizeof(SYSTEMTIME
));
52 m_loadType
= KEEP_IN_MEMORY
;
55 CGUIDialogNumeric::~CGUIDialogNumeric(void)
59 void CGUIDialogNumeric::OnInitWindow()
61 CGUIDialog::OnInitWindow();
67 data
["type"] = "time";
70 data
["type"] = "date";
72 case INPUT_IP_ADDRESS
:
76 data
["type"] = "numericpassword";
79 data
["type"] = "number";
81 case INPUT_TIME_SECONDS
:
82 data
["type"] = "seconds";
85 data
["type"] = "keyboard";
89 const CGUILabelControl
*control
= (const CGUILabelControl
*)GetControl(CONTROL_HEADING_LABEL
);
91 data
["title"] = control
->GetDescription();
93 data
["value"] = GetOutput();
94 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Input
, "xbmc", "OnInputRequested", data
);
97 void CGUIDialogNumeric::OnDeinitWindow(int nextWindowID
)
100 CGUIDialog::OnDeinitWindow(nextWindowID
);
102 ANNOUNCEMENT::CAnnouncementManager::Announce(ANNOUNCEMENT::Input
, "xbmc", "OnInputFinished");
105 bool CGUIDialogNumeric::OnAction(const CAction
&action
)
107 if (action
.GetID() == ACTION_NEXT_ITEM
)
109 else if (action
.GetID() == ACTION_PREV_ITEM
)
111 else if (action
.GetID() == ACTION_BACKSPACE
)
113 else if (action
.GetID() == ACTION_ENTER
)
115 else if (action
.GetID() >= REMOTE_0
&& action
.GetID() <= REMOTE_9
)
116 OnNumber(action
.GetID() - REMOTE_0
);
117 else if (action
.GetID() >= KEY_VKEY
&& action
.GetID() < KEY_ASCII
)
118 { // input from the keyboard (vkey, not ascii)
119 BYTE b
= action
.GetID() & 0xFF;
120 if (b
== XBMCVK_LEFT
) OnPrevious();
121 else if (b
== XBMCVK_RIGHT
) OnNext();
122 else if (b
== XBMCVK_RETURN
|| b
== XBMCVK_NUMPADENTER
) OnOK();
123 else if (b
== XBMCVK_BACK
) OnBackSpace();
124 else if (b
== XBMCVK_ESCAPE
) OnCancel();
126 else if (action
.GetID() >= KEY_ASCII
) // FIXME make it KEY_UNICODE
127 { // input from the keyboard
128 if (action
.GetUnicode() == 10 || action
.GetUnicode() == 13) OnOK(); // enter
129 else if (action
.GetUnicode() == 8) OnBackSpace(); // backspace
130 else if (action
.GetUnicode() == 27) OnCancel(); // escape
131 else if (action
.GetUnicode() >= 48 && action
.GetUnicode() < 58) // number
132 OnNumber(action
.GetUnicode() - 48);
135 return CGUIDialog::OnAction(action
);
139 bool CGUIDialogNumeric::OnBack(int actionID
)
145 bool CGUIDialogNumeric::OnMessage(CGUIMessage
& message
)
147 switch ( message
.GetMessage() )
149 case GUI_MSG_WINDOW_INIT
:
151 m_bConfirmed
= false;
154 return CGUIDialog::OnMessage(message
);
158 case GUI_MSG_CLICKED
:
160 int iControl
= message
.GetSenderId();
161 m_bConfirmed
= false;
163 if (CONTROL_NUM0
<= iControl
&& iControl
<= CONTROL_NUM9
) // User numeric entry via dialog button UI
165 OnNumber(iControl
- 10);
168 else if (iControl
== CONTROL_PREVIOUS
)
173 else if (iControl
== CONTROL_NEXT
)
178 else if (iControl
== CONTROL_BACKSPACE
)
183 else if (iControl
== CONTROL_ENTER
)
191 case GUI_MSG_SET_TEXT
:
192 SetMode(m_mode
, message
.GetLabel());
194 // close the dialog if requested
195 if (message
.GetParam1() > 0)
199 return CGUIDialog::OnMessage(message
);
202 void CGUIDialogNumeric::OnBackSpace()
204 if (!m_dirty
&& m_block
)
209 if (m_mode
== INPUT_NUMBER
|| m_mode
== INPUT_PASSWORD
)
210 { // just go back one character
211 if (!m_number
.empty())
212 m_number
.erase(m_number
.size() - 1);
214 else if (m_mode
== INPUT_IP_ADDRESS
)
224 else if (m_mode
== INPUT_TIME
)
227 m_datetime
.wHour
/= 10;
228 else if (m_datetime
.wMinute
)
229 m_datetime
.wMinute
/= 10;
236 else if (m_mode
== INPUT_TIME_SECONDS
)
238 if (m_block
== 0) // minutes
239 m_datetime
.wMinute
/= 10;
240 else if (m_datetime
.wSecond
)
241 m_datetime
.wSecond
/= 10;
248 else if (m_mode
== INPUT_DATE
)
251 m_datetime
.wDay
/= 10;
252 else if (m_block
== 1)
254 if (m_datetime
.wMonth
)
255 m_datetime
.wMonth
/= 10;
262 else if (m_datetime
.wYear
) // m_block == 2
263 m_datetime
.wYear
/= 10;
272 void CGUIDialogNumeric::OnPrevious()
279 void CGUIDialogNumeric::OnNext()
281 if (m_mode
== INPUT_IP_ADDRESS
&& m_block
==0 && m_ip
[0]==0)
284 if (m_block
< m_lastblock
)
287 if (m_mode
== INPUT_DATE
)
288 VerifyDate(m_block
== 2);
291 void CGUIDialogNumeric::FrameMove()
294 unsigned int start
= 0;
295 unsigned int end
= 0;
296 if (m_mode
== INPUT_PASSWORD
)
298 for (unsigned int i
=0; i
< m_number
.size(); i
++)
301 else if (m_mode
== INPUT_NUMBER
)
302 { // simple - just render text directly
305 else if (m_mode
== INPUT_TIME
)
306 { // format up the time
307 strLabel
= StringUtils::Format("%2d:%02d", m_datetime
.wHour
, m_datetime
.wMinute
);
309 end
= m_block
* 3 + 2;
311 else if (m_mode
== INPUT_TIME_SECONDS
)
312 { // format up the time
313 strLabel
= StringUtils::Format("%2d:%02d", m_datetime
.wMinute
, m_datetime
.wSecond
);
315 end
= m_block
* 3 + 2;
317 else if (m_mode
== INPUT_DATE
)
318 { // format up the date
319 strLabel
= StringUtils::Format("%2d/%2d/%4d", m_datetime
.wDay
, m_datetime
.wMonth
, m_datetime
.wYear
);
321 end
= m_block
* 3 + 2;
323 end
= m_block
* 3 + 4;
325 else if (m_mode
== INPUT_IP_ADDRESS
)
326 { // format up the date
327 strLabel
= StringUtils::Format("%3d.%3d.%3d.%3d", m_ip
[0], m_ip
[1], m_ip
[2], m_ip
[3]);
329 end
= m_block
* 4 + 3;
331 CGUILabelControl
*pLabel
= (CGUILabelControl
*)GetControl(CONTROL_INPUT_LABEL
);
334 pLabel
->SetLabel(strLabel
);
335 pLabel
->SetHighlight(start
, end
);
337 CGUIDialog::FrameMove();
340 void CGUIDialogNumeric::OnNumber(unsigned int num
)
344 if (m_mode
== INPUT_NUMBER
|| m_mode
== INPUT_PASSWORD
)
346 m_number
+= num
+ '0';
348 else if (m_mode
== INPUT_TIME
)
350 if (m_block
== 0) // hour
352 if (m_dirty
) // have input the first digit
354 if (m_datetime
.wHour
< 2 || num
< 4)
356 m_datetime
.wHour
*= 10;
357 m_datetime
.wHour
+= num
;
360 m_datetime
.wHour
= num
;
361 m_block
= 1; // move to minutes
364 else // this is the first digit
366 m_datetime
.wHour
= num
;
369 m_block
= 1; // move to minutes
378 if (m_dirty
) // have input the first digit
380 m_datetime
.wMinute
*= 10;
381 m_datetime
.wMinute
+= num
;
382 m_block
= 0; // move to hours
385 else // this is the first digit
387 m_datetime
.wMinute
= num
;
390 m_block
= 0; // move to hours
398 else if (m_mode
== INPUT_TIME_SECONDS
)
400 if (m_block
== 0) // minute
402 if (m_dirty
) // have input the first digit
404 m_datetime
.wMinute
*= 10;
405 m_datetime
.wMinute
+= num
;
406 m_block
= 1; // move to seconds - allows up to 99 minutes
409 else // this is the first digit
411 m_datetime
.wMinute
= num
;
417 if (m_dirty
) // have input the first digit
419 m_datetime
.wSecond
*= 10;
420 m_datetime
.wSecond
+= num
;
421 m_block
= 0; // move to minutes
424 else // this is the first digit
426 m_datetime
.wSecond
= num
;
429 m_block
= 0; // move to minutes
437 else if (m_mode
== INPUT_DATE
)
439 if (m_block
== 0) // day of month
441 if (m_dirty
&& (m_datetime
.wDay
< 3 || num
< 2))
443 m_datetime
.wDay
*= 10;
444 m_datetime
.wDay
+= num
;
447 m_datetime
.wDay
= num
;
448 if (m_datetime
.wDay
> 3)
450 m_block
= 1; // move to months
456 else if (m_block
== 1) // months
458 if (m_dirty
&& num
< 3)
460 m_datetime
.wMonth
*= 10;
461 m_datetime
.wMonth
+= num
;
464 m_datetime
.wMonth
= num
;
465 if (m_datetime
.wMonth
> 1)
468 m_block
= 2; // move to year
476 if (m_dirty
&& m_datetime
.wYear
< 1000) // have taken input
478 m_datetime
.wYear
*= 10;
479 m_datetime
.wYear
+= num
;
482 m_datetime
.wYear
= num
;
483 if (m_datetime
.wYear
> 1000)
486 m_block
= 0; // move to day of month
493 else if (m_mode
== INPUT_IP_ADDRESS
)
495 if (m_dirty
&& ((m_ip
[m_block
] < 25) || (m_ip
[m_block
] == 25 && num
< 6) || !(m_block
==0 && num
==0)))
498 m_ip
[m_block
] += num
;
502 if (m_ip
[m_block
] > 25 || (m_ip
[m_block
] == 0 && num
== 0))
505 if (m_block
> 3) m_block
= 0;
513 void CGUIDialogNumeric::SetMode(INPUT_MODE mode
, void *initial
)
518 if (m_mode
== INPUT_TIME
|| m_mode
== INPUT_TIME_SECONDS
|| m_mode
== INPUT_DATE
)
520 m_datetime
= *(SYSTEMTIME
*)initial
;
521 m_lastblock
= (m_mode
== INPUT_DATE
) ? 2 : 1;
523 else if (m_mode
== INPUT_IP_ADDRESS
)
526 m_ip
[0] = m_ip
[1] = m_ip
[2] = m_ip
[3] = 0;
527 // copy ip string into numeric form
528 CStdString ip
= *(CStdString
*)initial
;
529 unsigned int block
= 0;
530 for (unsigned int i
=0; i
< ip
.size(); i
++)
535 if (block
> m_lastblock
)
538 else if (isdigit(ip
[i
]))
541 m_ip
[block
] += ip
[i
] - '0';
545 else if (m_mode
== INPUT_NUMBER
|| m_mode
== INPUT_PASSWORD
)
546 m_number
= *(CStdString
*)initial
;
549 void CGUIDialogNumeric::SetMode(INPUT_MODE mode
, const CStdString
&initial
)
554 if (m_mode
== INPUT_TIME
|| m_mode
== INPUT_TIME_SECONDS
|| m_mode
== INPUT_DATE
)
557 if (m_mode
== INPUT_TIME
|| m_mode
== INPUT_TIME_SECONDS
)
559 // check if we have a pure number
560 if (initial
.find_first_not_of("0123456789") == std::string::npos
)
562 long seconds
= strtol(initial
.c_str(), NULL
, 10);
567 CStdString tmp
= initial
;
568 // if we are handling seconds and if the string only contains
569 // "mm:ss" we need to add dummy "hh:" to get "hh:mm:ss"
570 if (m_mode
== INPUT_TIME_SECONDS
&& tmp
.size() <= 5)
572 dateTime
.SetFromDBTime(tmp
);
575 else if (m_mode
== INPUT_DATE
)
577 CStdString tmp
= initial
;
578 StringUtils::Replace(tmp
, '/', '.');
579 dateTime
.SetFromDBDate(tmp
);
582 if (!dateTime
.IsValid())
585 dateTime
.GetAsSystemTime(m_datetime
);
586 m_lastblock
= (m_mode
== INPUT_DATE
) ? 2 : 1;
589 SetMode(mode
, (void*)&initial
);
592 void CGUIDialogNumeric::GetOutput(void *output
) const
595 if (m_mode
== INPUT_TIME
|| m_mode
== INPUT_TIME_SECONDS
|| m_mode
== INPUT_DATE
)
596 memcpy(output
, &m_datetime
, sizeof(m_datetime
));
597 else if (m_mode
== INPUT_IP_ADDRESS
)
598 *(CStdString
*)output
= StringUtils::Format("%d.%d.%d.%d", m_ip
[0], m_ip
[1], m_ip
[2], m_ip
[3]);
599 else if (m_mode
== INPUT_NUMBER
|| m_mode
== INPUT_PASSWORD
)
600 *(CStdString
*)output
= m_number
;
603 CStdString
CGUIDialogNumeric::GetOutput() const
606 if (m_mode
== INPUT_DATE
)
607 output
= StringUtils::Format("%02i/%02i/%04i", m_datetime
.wDay
, m_datetime
.wMonth
, m_datetime
.wYear
);
608 else if (m_mode
== INPUT_TIME
)
609 output
= StringUtils::Format("%i:%02i", m_datetime
.wHour
, m_datetime
.wMinute
);
610 else if (m_mode
== INPUT_TIME_SECONDS
)
611 output
= StringUtils::Format("%i:%02i", m_datetime
.wMinute
, m_datetime
.wSecond
);
617 bool CGUIDialogNumeric::ShowAndGetSeconds(CStdString
&timeString
, const CStdString
&heading
)
619 CGUIDialogNumeric
*pDialog
= (CGUIDialogNumeric
*)g_windowManager
.GetWindow(WINDOW_DIALOG_NUMERIC
);
620 if (!pDialog
) return false;
621 int seconds
= StringUtils::TimeStringToSeconds(timeString
);
622 SYSTEMTIME time
= {0};
623 time
.wHour
= seconds
/ 3600;
624 time
.wMinute
= (seconds
- time
.wHour
* 3600) / 60;
625 time
.wSecond
= seconds
- time
.wHour
* 3600 - time
.wMinute
* 60;
626 pDialog
->SetMode(INPUT_TIME_SECONDS
, (void *)&time
);
627 pDialog
->SetHeading(heading
);
629 if (!pDialog
->IsConfirmed() || pDialog
->IsCanceled())
631 pDialog
->GetOutput(&time
);
632 seconds
= time
.wHour
* 3600 + time
.wMinute
* 60 + time
.wSecond
;
633 timeString
= StringUtils::SecondsToTimeString(seconds
);
637 bool CGUIDialogNumeric::ShowAndGetTime(SYSTEMTIME
&time
, const CStdString
&heading
)
639 CGUIDialogNumeric
*pDialog
= (CGUIDialogNumeric
*)g_windowManager
.GetWindow(WINDOW_DIALOG_NUMERIC
);
640 if (!pDialog
) return false;
641 pDialog
->SetMode(INPUT_TIME
, (void *)&time
);
642 pDialog
->SetHeading(heading
);
644 if (!pDialog
->IsConfirmed() || pDialog
->IsCanceled())
646 pDialog
->GetOutput(&time
);
650 bool CGUIDialogNumeric::ShowAndGetDate(SYSTEMTIME
&date
, const CStdString
&heading
)
652 CGUIDialogNumeric
*pDialog
= (CGUIDialogNumeric
*)g_windowManager
.GetWindow(WINDOW_DIALOG_NUMERIC
);
653 if (!pDialog
) return false;
654 pDialog
->SetMode(INPUT_DATE
, (void *)&date
);
655 pDialog
->SetHeading(heading
);
657 if (!pDialog
->IsConfirmed() || pDialog
->IsCanceled())
659 pDialog
->GetOutput(&date
);
663 bool CGUIDialogNumeric::ShowAndGetIPAddress(CStdString
&IPAddress
, const CStdString
&heading
)
665 CGUIDialogNumeric
*pDialog
= (CGUIDialogNumeric
*)g_windowManager
.GetWindow(WINDOW_DIALOG_NUMERIC
);
666 if (!pDialog
|| !IPAddress
) return false;
667 pDialog
->SetMode(INPUT_IP_ADDRESS
, (void *)&IPAddress
);
668 pDialog
->SetHeading(heading
);
670 if (!pDialog
->IsConfirmed() || pDialog
->IsCanceled())
672 pDialog
->GetOutput(&IPAddress
);
676 bool CGUIDialogNumeric::ShowAndGetNumber(CStdString
& strInput
, const CStdString
&strHeading
, unsigned int iAutoCloseTimeoutMs
/* = 0 */)
678 // Prompt user for password input
679 CGUIDialogNumeric
*pDialog
= (CGUIDialogNumeric
*)g_windowManager
.GetWindow(WINDOW_DIALOG_NUMERIC
);
680 pDialog
->SetHeading( strHeading
);
682 pDialog
->SetMode(INPUT_NUMBER
, (void *)&strInput
);
683 if (iAutoCloseTimeoutMs
)
684 pDialog
->SetAutoClose(iAutoCloseTimeoutMs
);
688 if (!pDialog
->IsAutoClosed() && (!pDialog
->IsConfirmed() || pDialog
->IsCanceled()))
690 pDialog
->GetOutput(&strInput
);
694 // \brief Show numeric keypad twice to get and confirm a user-entered password string.
695 // \param strNewPassword String to preload into the keyboard accumulator. Overwritten with user input if return=true.
696 // \return true if successful display and user input entry/re-entry. false if unsuccessful display, no user input, or canceled editing.
697 bool CGUIDialogNumeric::ShowAndVerifyNewPassword(CStdString
& strNewPassword
)
699 // Prompt user for password input
700 CStdString strUserInput
= "";
701 if (!ShowAndVerifyInput(strUserInput
, g_localizeStrings
.Get(12340), false))
703 // Show error to user saying the password entry was blank
704 CGUIDialogOK::ShowAndGetInput(12357, 12358, 0, 0); // Password is empty/blank
708 if (strUserInput
.empty())
712 // Prompt again for password input, this time sending previous input as the password to verify
713 if (!ShowAndVerifyInput(strUserInput
, g_localizeStrings
.Get(12341), true))
715 // Show error to user saying the password re-entry failed
716 CGUIDialogOK::ShowAndGetInput(12357, 12344, 0, 0); // Password do not match
720 // password entry and re-entry succeeded
721 strNewPassword
= strUserInput
;
725 // \brief Show numeric keypad and verify user input against strPassword.
726 // \param strPassword Value to compare against user input.
727 // \param strHeading String shown on dialog title. Converts to localized string if contains a positive integer.
728 // \param iRetries If greater than 0, shows "Incorrect password, %d retries left" on dialog line 2, else line 2 is blank.
729 // \return 0 if successful display and user input. 1 if unsuccessful input. -1 if no user input or canceled editing.
730 int CGUIDialogNumeric::ShowAndVerifyPassword(CStdString
& strPassword
, const CStdString
& strHeading
, int iRetries
)
732 CStdString strTempHeading
= strHeading
;
735 // Show a string telling user they have iRetries retries left
736 strTempHeading
= StringUtils::Format("%s. %s %i %s", strHeading
.c_str(), g_localizeStrings
.Get(12342).c_str(), iRetries
, g_localizeStrings
.Get(12343).c_str());
738 // make a copy of strPassword to prevent from overwriting it later
739 CStdString strPassTemp
= strPassword
;
740 if (ShowAndVerifyInput(strPassTemp
, strTempHeading
, true))
741 return 0; // user entered correct password
742 if (strPassTemp
.empty()) return -1; // user canceled out
743 return 1; // user must have entered an incorrect password
746 // \brief Show numeric keypad and verify user input against strToVerify.
747 // \param strToVerify Value to compare against user input.
748 // \param dlgHeading String shown on dialog title.
749 // \param bVerifyInput If set as true we verify the users input versus strToVerify.
750 // \return true if successful display and user input. false if unsuccessful display, no user input, or canceled editing.
751 bool CGUIDialogNumeric::ShowAndVerifyInput(CStdString
& strToVerify
, const CStdString
& dlgHeading
, bool bVerifyInput
)
753 // Prompt user for password input
754 CGUIDialogNumeric
*pDialog
= (CGUIDialogNumeric
*)g_windowManager
.GetWindow(WINDOW_DIALOG_NUMERIC
);
755 pDialog
->SetHeading( dlgHeading
);
757 CStdString strInput
= "";
759 strInput
= strToVerify
;
760 pDialog
->SetMode(INPUT_PASSWORD
, (void *)&strInput
);
763 pDialog
->GetOutput(&strInput
);
765 if (!pDialog
->IsConfirmed() || pDialog
->IsCanceled())
772 CStdString md5pword2
;
773 XBMC::XBMC_MD5 md5state
;
774 md5state
.append(strInput
);
775 md5state
.getDigest(md5pword2
);
779 strToVerify
= md5pword2
;
780 StringUtils::ToLower(strToVerify
);
784 if (strToVerify
.Equals(md5pword2
))
785 return true; // entered correct password
787 // incorrect password
791 bool CGUIDialogNumeric::IsConfirmed() const
796 bool CGUIDialogNumeric::IsCanceled() const
801 void CGUIDialogNumeric::SetHeading(const CStdString
& strHeading
)
804 CGUIMessage
msg(GUI_MSG_LABEL_SET
, GetID(), CONTROL_HEADING_LABEL
);
805 msg
.SetLabel(strHeading
);
809 void CGUIDialogNumeric::VerifyDate(bool checkYear
)
811 if (m_datetime
.wDay
== 0)
813 if (m_datetime
.wMonth
== 0)
814 m_datetime
.wMonth
= 1;
815 // check for number of days in the month
816 if (m_datetime
.wDay
== 31)
818 if (m_datetime
.wMonth
== 4 || m_datetime
.wMonth
== 6 || m_datetime
.wMonth
== 9 || m_datetime
.wMonth
== 11)
819 m_datetime
.wDay
= 30;
821 if (m_datetime
.wMonth
== 2 && m_datetime
.wDay
> 28)
823 m_datetime
.wDay
= 29; // max in february.
826 // leap years occur when the year is divisible by 4 but not by 100, or the year is divisible by 400
827 // thus they don't occur, if the year has a remainder when divided by 4, or when the year is divisible by 100 but not by 400
828 if ( (m_datetime
.wYear
% 4) || ( !(m_datetime
.wYear
% 100) && (m_datetime
.wYear
% 400) ) )
829 m_datetime
.wDay
= 28;
834 void CGUIDialogNumeric::OnOK()
841 void CGUIDialogNumeric::OnCancel()
843 m_bConfirmed
= false;