xref: /reactos/dll/cpl/timedate/dateandtime.c (revision 139a3d66)
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 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
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
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
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
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
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
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
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
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             SetTimer(hwndDlg, ID_TIMER, 1000, NULL);
280 
281             /* Set range and current year */
282             SendMessageW(GetDlgItem(hwndDlg, IDC_YEAR), UDM_SETRANGE, 0, MAKELONG ((short) 9999, (short) 1900));
283             SendMessageW(GetDlgItem(hwndDlg, IDC_YEAR), UDM_SETPOS, 0, MAKELONG( (short) st.wYear, 0));
284 
285             pOldWndProc = (WNDPROC)SetWindowLongPtrW(GetDlgItem(hwndDlg, IDC_TIMEPICKER), GWLP_WNDPROC, (LONG_PTR)DTPProc);
286             break;
287 
288         case WM_TIMER:
289             SendMessageW(GetDlgItem(hwndDlg, IDC_TIMEPICKER), DTM_SETSYSTEMTIME, GDT_VALID, (LPARAM) &st);
290             break;
291 
292         case WM_COMMAND:
293             switch (LOWORD(wParam))
294             {
295                 case IDC_MONTHCB:
296                     if (HIWORD(wParam) == CBN_SELCHANGE)
297                     {
298                         ChangeMonthCalDate(GetDlgItem(hwndDlg,
299                                                       IDC_MONTHCALENDAR),
300                                                       (WORD) -1,
301                                                       GetCBSelectedMonth((HWND)lParam),
302                                                       (WORD) -1);
303                     }
304                     break;
305             }
306             break;
307 
308         case WM_CTLCOLORSTATIC:
309             if ((HWND)lParam == GetDlgItem(hwndDlg, IDC_YEARTEXT))
310                 return (INT_PTR)GetSysColorBrush(COLOR_WINDOW);
311             break;
312 
313         case WM_NOTIFY:
314         {
315             LPNMHDR lpnm = (LPNMHDR)lParam;
316 
317             switch (lpnm->idFrom)
318             {
319                 case IDC_YEAR:
320                     switch (lpnm->code)
321                     {
322                         case UDN_DELTAPOS:
323                         {
324                             SHORT wYear;
325                             LPNMUPDOWN updown = (LPNMUPDOWN)lpnm;
326                             wYear = (SHORT)SendMessageW(GetDlgItem(hwndDlg, IDC_YEAR), UDM_GETPOS, 0, 0);
327                             /* Enable the 'Apply' button */
328                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
329                             ChangeMonthCalDate(GetDlgItem(hwndDlg,
330                                                IDC_MONTHCALENDAR),
331                                                (WORD) -1,
332                                                (WORD) -1,
333                                                (WORD) (wYear + updown->iDelta));
334                         }
335                         break;
336                     }
337                     break;
338 
339                 case IDC_TIMEPICKER:
340                     switch (lpnm->code)
341                     {
342                         case DTN_DATETIMECHANGE:
343                             /* Stop the timer */
344                             KillTimer(hwndDlg, ID_TIMER);
345 
346                             /* Tell the clock to stop ticking */
347                             SendDlgItemMessageW(hwndDlg, IDC_CLOCKWND, CLM_STOPCLOCK,
348                                                 0, 0);
349 
350                             /* Enable the 'Apply' button */
351                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
352                             break;
353                     }
354                     break;
355 
356                 case IDC_MONTHCALENDAR:
357                     switch (lpnm->code)
358                     {
359                         case MCCN_SELCHANGE:
360                             /* Enable the 'Apply' button */
361                             PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
362                             break;
363 
364                         case MCCN_AUTOUPDATE:
365                             AutoUpdateMonthCal(hwndDlg,
366                                                  (PNMMCCAUTOUPDATE)lpnm);
367                             break;
368                     }
369                     break;
370 
371                 default:
372                     switch (lpnm->code)
373                     {
374                         case PSN_SETACTIVE:
375                             SetTimeZoneName(hwndDlg);
376                             break;
377 
378                         case PSN_APPLY:
379                             SetLocalSystemTime(hwndDlg);
380                             SetTimer(hwndDlg, ID_TIMER, 1000, NULL);
381 
382                             /* Tell the clock to start ticking */
383                             SendDlgItemMessageW(hwndDlg, IDC_CLOCKWND, CLM_STARTCLOCK,
384                                                 0, 0);
385                             return TRUE;
386                     }
387                     break;
388             }
389         }
390         break;
391 
392         case WM_TIMECHANGE:
393             /* FIXME: We don't get this message as we're not a top-level window... */
394             SendMessageW(GetDlgItem(hwndDlg,
395                                     IDC_MONTHCALENDAR),
396                          MCCM_RESET,
397                          0,
398                          0);
399             break;
400 
401         case WM_DESTROY:
402             KillTimer(hwndDlg, ID_TIMER);
403             break;
404     }
405 
406     return FALSE;
407 }
408