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