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