xref: /reactos/dll/cpl/timedate/dateandtime.c (revision 4cd92f0d)
1 /*
2  * PROJECT:     ReactOS Timedate Control Panel
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/cpl/timedate/dateandtime.c
5  * PURPOSE:     Date & Time property page
6  * COPYRIGHT:   Copyright 2004-2007 Eric Kohl
7  *              Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
8  *              Copyright 2006 Thomas Weidenmueller <w3seek@reactos.com>
9  *
10  */
11 
12 #include "timedate.h"
13 
14 static WNDPROC pOldWndProc = NULL;
15 
16 BOOL
SystemSetLocalTime(LPSYSTEMTIME lpSystemTime)17 SystemSetLocalTime(LPSYSTEMTIME lpSystemTime)
18 {
19     HANDLE hToken;
20     DWORD PrevSize;
21     TOKEN_PRIVILEGES priv, previouspriv;
22     BOOL Ret = FALSE;
23 
24     /*
25      * Enable the SeSystemtimePrivilege privilege
26      */
27 
28     if (OpenProcessToken(GetCurrentProcess(),
29                          TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
30                          &hToken))
31     {
32         priv.PrivilegeCount = 1;
33         priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
34 
35         if (LookupPrivilegeValueW(NULL,
36                                   SE_SYSTEMTIME_NAME,
37                                   &priv.Privileges[0].Luid))
38         {
39             if (AdjustTokenPrivileges(hToken,
40                                       FALSE,
41                                       &priv,
42                                       sizeof(previouspriv),
43                                       &previouspriv,
44                                       &PrevSize) &&
45                 GetLastError() == ERROR_SUCCESS)
46             {
47                 /*
48                  * We successfully enabled it, we're permitted to change the time.
49                  * Call SetLocalTime twice to ensure correct results.
50                  */
51                 Ret = SetLocalTime(lpSystemTime) &&
52                       SetLocalTime(lpSystemTime);
53 
54                 /*
55                  * For the sake of security, restore the previous status again
56                  */
57                 if (previouspriv.PrivilegeCount > 0)
58                 {
59                     AdjustTokenPrivileges(hToken,
60                                           FALSE,
61                                           &previouspriv,
62                                           0,
63                                           NULL,
64                                           0);
65                 }
66             }
67         }
68         CloseHandle(hToken);
69     }
70 
71     return Ret;
72 }
73 
74 
75 static VOID
SetLocalSystemTime(HWND hwnd)76 SetLocalSystemTime(HWND hwnd)
77 {
78     SYSTEMTIME Time;
79 
80     if (DateTime_GetSystemtime(GetDlgItem(hwnd,
81                                           IDC_TIMEPICKER),
82                                &Time) == GDT_VALID &&
83         SendMessageW(GetDlgItem(hwnd,
84                                 IDC_MONTHCALENDAR),
85                      MCCM_GETDATE,
86                      (WPARAM)&Time,
87                      0))
88     {
89         SystemSetLocalTime(&Time);
90 
91         SetWindowLongPtrW(hwnd,
92                           DWLP_MSGRESULT,
93                           PSNRET_NOERROR);
94 
95         SendMessageW(GetDlgItem(hwnd,
96                                 IDC_MONTHCALENDAR),
97                      MCCM_RESET,
98                      (WPARAM)&Time,
99                      0);
100 
101         /* Broadcast the time change message */
102         SendMessageW(HWND_BROADCAST,
103                      WM_TIMECHANGE,
104                      0,
105                      0);
106     }
107 }
108 
109 
110 static VOID
SetTimeZoneName(HWND hwnd)111 SetTimeZoneName(HWND hwnd)
112 {
113     TIME_ZONE_INFORMATION TimeZoneInfo;
114     WCHAR TimeZoneString[128];
115     WCHAR TimeZoneText[128];
116     WCHAR TimeZoneName[128];
117     DWORD TimeZoneId;
118 
119     TimeZoneId = GetTimeZoneInformation(&TimeZoneInfo);
120 
121     LoadStringW(hApplet, IDS_TIMEZONETEXT, TimeZoneText, 128);
122 
123     switch (TimeZoneId)
124     {
125         case TIME_ZONE_ID_STANDARD:
126         case TIME_ZONE_ID_UNKNOWN:
127             wcscpy(TimeZoneName, TimeZoneInfo.StandardName);
128             break;
129 
130         case TIME_ZONE_ID_DAYLIGHT:
131             wcscpy(TimeZoneName, TimeZoneInfo.DaylightName);
132             break;
133 
134         case TIME_ZONE_ID_INVALID:
135         default:
136             LoadStringW(hApplet, IDS_TIMEZONEINVALID, TimeZoneName, 128);
137             break;
138     }
139 
140     wsprintfW(TimeZoneString, TimeZoneText, TimeZoneName);
141     SendDlgItemMessageW(hwnd, IDC_TIMEZONE, WM_SETTEXT, 0, (LPARAM)TimeZoneString);
142 }
143 
144 
145 static VOID
FillMonthsComboBox(HWND hCombo)146 FillMonthsComboBox(HWND hCombo)
147 {
148     SYSTEMTIME LocalDate = {0};
149     WCHAR szBuf[64];
150     INT i;
151     UINT Month;
152 
153     GetLocalTime(&LocalDate);
154 
155     SendMessageW(hCombo,
156                  CB_RESETCONTENT,
157                  0,
158                  0);
159 
160     for (Month = 1;
161          Month <= 13;
162          Month++)
163     {
164         i = GetLocaleInfoW(LOCALE_USER_DEFAULT,
165                            ((Month < 13) ? LOCALE_SMONTHNAME1 + Month - 1 : LOCALE_SMONTHNAME13),
166                            szBuf,
167                            sizeof(szBuf) / sizeof(szBuf[0]));
168         if (i > 1)
169         {
170             i = (INT)SendMessageW(hCombo,
171                                   CB_ADDSTRING,
172                                   0,
173                                   (LPARAM)szBuf);
174             if (i != CB_ERR)
175             {
176                 SendMessageW(hCombo,
177                              CB_SETITEMDATA,
178                              (WPARAM)i,
179                              Month);
180 
181                 if (Month == (UINT)LocalDate.wMonth)
182                 {
183                     SendMessageW(hCombo,
184                                  CB_SETCURSEL,
185                                  (WPARAM)i,
186                                  0);
187                 }
188             }
189         }
190     }
191 }
192 
193 
194 static WORD
GetCBSelectedMonth(HWND hCombo)195 GetCBSelectedMonth(HWND hCombo)
196 {
197     INT i;
198     WORD Ret = (WORD)-1;
199 
200     i = (INT)SendMessageW(hCombo,
201                           CB_GETCURSEL,
202                           0,
203                           0);
204     if (i != CB_ERR)
205     {
206         i = (INT)SendMessageW(hCombo,
207                               CB_GETITEMDATA,
208                               (WPARAM)i,
209                               0);
210 
211         if (i >= 1 && i <= 13)
212             Ret = (WORD)i;
213     }
214 
215     return Ret;
216 }
217 
218 
219 static VOID
ChangeMonthCalDate(HWND hMonthCal,WORD Day,WORD Month,WORD Year)220 ChangeMonthCalDate(HWND hMonthCal,
221                    WORD Day,
222                    WORD Month,
223                    WORD Year)
224 {
225     SendMessageW(hMonthCal,
226                  MCCM_SETDATE,
227                  MAKEWPARAM(Day,
228                             Month),
229                  MAKELPARAM(Year,
230                             0));
231 }
232 
233 static VOID
AutoUpdateMonthCal(HWND hwndDlg,PNMMCCAUTOUPDATE lpAutoUpdate)234 AutoUpdateMonthCal(HWND hwndDlg,
235                    PNMMCCAUTOUPDATE lpAutoUpdate)
236 {
237     UNREFERENCED_PARAMETER(lpAutoUpdate);
238 
239     /* Update the controls */
240     FillMonthsComboBox(GetDlgItem(hwndDlg,
241                                   IDC_MONTHCB));
242 }
243 
244 
245 static INT_PTR CALLBACK
DTPProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)246 DTPProc(HWND hwnd,
247         UINT uMsg,
248         WPARAM wParam,
249         LPARAM lParam)
250 {
251     switch (uMsg)
252     {
253         case WM_KEYDOWN:
254             /* Stop the timer when the user is about to change the time */
255             if ((wParam != VK_LEFT) & (wParam != VK_RIGHT))
256                 KillTimer(GetParent(hwnd), ID_TIMER);
257             break;
258     }
259 
260     return CallWindowProcW(pOldWndProc, hwnd, uMsg, wParam, lParam);
261 }
262 
263 /* Property page dialog callback */
264 INT_PTR CALLBACK
DateTimePageProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)265 DateTimePageProc(HWND hwndDlg,
266          UINT uMsg,
267          WPARAM wParam,
268          LPARAM lParam)
269 {
270     SYSTEMTIME st;
271     GetLocalTime(&st);
272 
273     switch (uMsg)
274     {
275         case WM_INITDIALOG:
276             FillMonthsComboBox(GetDlgItem(hwndDlg,
277                                           IDC_MONTHCB));
278 
279             /* Set range and current year */
280             SendMessageW(GetDlgItem(hwndDlg, IDC_YEAR), UDM_SETRANGE, 0, MAKELONG ((short) 9999, (short) 1900));
281             SendMessageW(GetDlgItem(hwndDlg, IDC_YEAR), UDM_SETPOS, 0, MAKELONG( (short) st.wYear, 0));
282 
283             pOldWndProc = (WNDPROC)SetWindowLongPtrW(GetDlgItem(hwndDlg, IDC_TIMEPICKER), GWLP_WNDPROC, (LONG_PTR)DTPProc);
284 
285             SetTimer(hwndDlg, ID_TIMER, 1000 - st.wMilliseconds, NULL);
286             break;
287 
288         case WM_TIMER:
289             SendMessageW(GetDlgItem(hwndDlg, IDC_TIMEPICKER), DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM) &st);
290 
291             // Reset timeout.
292             SetTimer(hwndDlg, ID_TIMER, 1000 - st.wMilliseconds, NULL);
293             break;
294 
295         case WM_COMMAND:
296             switch (LOWORD(wParam))
297             {
298                 case IDC_MONTHCB:
299                     if (HIWORD(wParam) == CBN_SELCHANGE)
300                     {
301                         ChangeMonthCalDate(GetDlgItem(hwndDlg,
302                                                       IDC_MONTHCALENDAR),
303                                                       (WORD) -1,
304                                                       GetCBSelectedMonth((HWND)lParam),
305                                                       (WORD) -1);
306                     }
307                     break;
308             }
309             break;
310 
311         case WM_CTLCOLORSTATIC:
312             if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_YEARTEXT))
313                 return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
314             break;
315 
316         case WM_NOTIFY:
317         {
318             LPNMHDR lpnm = (LPNMHDR)lParam;
319 
320             switch (lpnm->idFrom)
321             {
322                 case IDC_YEAR:
323                     switch (lpnm->code)
324                     {
325                         case UDN_DELTAPOS:
326                         {
327                             SHORT wYear;
328                             LPNMUPDOWN updown = (LPNMUPDOWN)lpnm;
329                             wYear = (SHORT)SendMessageW(GetDlgItem(hwndDlg, IDC_YEAR), UDM_GETPOS, 0, 0);
330                             /* Enable the 'Apply' button */
331                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
332                             ChangeMonthCalDate(GetDlgItem(hwndDlg,
333                                                IDC_MONTHCALENDAR),
334                                                (WORD) -1,
335                                                (WORD) -1,
336                                                (WORD) (wYear + updown->iDelta));
337                         }
338                         break;
339                     }
340                     break;
341 
342                 case IDC_TIMEPICKER:
343                     switch (lpnm->code)
344                     {
345                         case DTN_DATETIMECHANGE:
346                             /* Stop the timer */
347                             KillTimer(hwndDlg, ID_TIMER);
348 
349                             /* Tell the clock to stop ticking */
350                             SendDlgItemMessageW(hwndDlg, IDC_CLOCKWND, CLM_STOPCLOCK,
351                                                 0, 0);
352 
353                             // TODO: Set the clock to the input time.
354 
355                             /* Enable the 'Apply' button */
356                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
357                             break;
358                     }
359                     break;
360 
361                 case IDC_MONTHCALENDAR:
362                     switch (lpnm->code)
363                     {
364                         case MCCN_SELCHANGE:
365                             /* Enable the 'Apply' button */
366                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
367                             break;
368 
369                         case MCCN_AUTOUPDATE:
370                             AutoUpdateMonthCal(hwndDlg,
371                                                  (PNMMCCAUTOUPDATE)lpnm);
372                             break;
373                     }
374                     break;
375 
376                 default:
377                     switch (lpnm->code)
378                     {
379                         case PSN_SETACTIVE:
380                             SetTimeZoneName(hwndDlg);
381                             break;
382 
383                         case PSN_APPLY:
384                             SetLocalSystemTime(hwndDlg);
385 
386                             /* Tell the clock to start ticking */
387                             SendDlgItemMessageW(hwndDlg, IDC_CLOCKWND, CLM_STARTCLOCK,
388                                                 0, 0);
389 
390                             SetTimer(hwndDlg, ID_TIMER, 1000 - st.wMilliseconds, NULL);
391                             return TRUE;
392                     }
393                     break;
394             }
395         }
396         break;
397 
398         case WM_TIMECHANGE:
399             /* FIXME: We don't get this message as we're not a top-level window... */
400             SendMessageW(GetDlgItem(hwndDlg,
401                                     IDC_MONTHCALENDAR),
402                          MCCM_RESET,
403                          0,
404                          0);
405             break;
406 
407         case WM_DESTROY:
408             KillTimer(hwndDlg, ID_TIMER);
409             break;
410     }
411 
412     return FALSE;
413 }
414