a7c4d7880f8dd6333027eef57697a03766d05930
[reactos.git] / reactos / lib / cpl / timedate / timedate.c
1 /*
2 * ReactOS
3 * Copyright (C) 2004 ReactOS Team
4 *
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 of the License, or
8 * (at your option) any later version.
9 *
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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * PROJECT: ReactOS Timedate Control Panel
22 * FILE: lib/cpl/timedate/timedate.c
23 * PURPOSE: ReactOS Timedate Control Panel
24 * PROGRAMMER: Eric Kohl
25 */
26
27 #include <windows.h>
28 #include <commctrl.h>
29 #include <cpl.h>
30
31 #include "resource.h"
32 #include "timedate.h"
33
34
35 typedef struct _TZ_INFO
36 {
37 LONG Bias;
38 LONG StandardBias;
39 LONG DaylightBias;
40 SYSTEMTIME StandardDate;
41 SYSTEMTIME DaylightDate;
42 } TZ_INFO, *PTZ_INFO;
43
44 typedef struct _TIMEZONE_ENTRY
45 {
46 struct _TIMEZONE_ENTRY *Prev;
47 struct _TIMEZONE_ENTRY *Next;
48 WCHAR Description[64]; /* 'Display' */
49 WCHAR StandardName[32]; /* 'Std' */
50 WCHAR DaylightName[32]; /* 'Dlt' */
51 TZ_INFO TimezoneInfo; /* 'TZI' */
52 ULONG Index; /* 'Index ' */
53 } TIMEZONE_ENTRY, *PTIMEZONE_ENTRY;
54
55
56 #define NUM_APPLETS (1)
57
58 LONG APIENTRY
59 Applet(HWND hwnd, UINT uMsg, LONG wParam, LONG lParam);
60
61
62 HINSTANCE hApplet = 0;
63
64 PTIMEZONE_ENTRY TimeZoneListHead = NULL;
65 PTIMEZONE_ENTRY TimeZoneListTail = NULL;
66
67
68 /* Applets */
69 APPLET Applets[NUM_APPLETS] =
70 {
71 {IDC_CPLICON, IDS_CPLNAME, IDS_CPLDESCRIPTION, Applet}
72 };
73
74
75 static VOID
76 SetLocalSystemTime(HWND hwnd)
77 {
78 SYSTEMTIME Date;
79 SYSTEMTIME Time;
80
81 if (DateTime_GetSystemTime(GetDlgItem(hwnd, IDC_DATEPICKER), &Date) != GDT_VALID)
82 {
83 return;
84 }
85
86 if (DateTime_GetSystemTime(GetDlgItem(hwnd, IDC_TIMEPICKER), &Time) != GDT_VALID)
87 {
88 return;
89 }
90
91 Time.wYear = Date.wYear;
92 Time.wMonth = Date.wMonth;
93 Time.wDayOfWeek = Date.wDayOfWeek;
94 Time.wDay = Date.wDay;
95
96 SetLocalTime(&Time);
97 }
98
99
100 static VOID
101 SetTimeZoneName(HWND hwnd)
102 {
103 TIME_ZONE_INFORMATION TimeZoneInfo;
104 WCHAR TimeZoneString[128];
105 DWORD TimeZoneId;
106
107 TimeZoneId = GetTimeZoneInformation(&TimeZoneInfo);
108
109 wsprintf(TimeZoneString,
110 L"Current time zone: %s\n",
111 (TimeZoneId == TIME_ZONE_ID_DAYLIGHT) ? TimeZoneInfo.DaylightName : TimeZoneInfo.StandardName);
112
113 SendDlgItemMessageW(hwnd, IDC_TIMEZONE, WM_SETTEXT, 0, (LPARAM)TimeZoneString);
114 }
115
116
117 /* Property page dialog callback */
118 INT_PTR CALLBACK
119 DateTimePageProc(HWND hwndDlg,
120 UINT uMsg,
121 WPARAM wParam,
122 LPARAM lParam)
123 {
124 switch (uMsg)
125 {
126 case WM_NOTIFY:
127 {
128 LPNMHDR lpnm = (LPNMHDR)lParam;
129
130 switch (lpnm->code)
131 {
132 case DTN_DATETIMECHANGE:
133 case MCN_SELECT:
134 /* Enable the 'Apply' button */
135 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
136 break;
137
138 case PSN_SETACTIVE:
139 SetTimeZoneName(hwndDlg);
140 return 0;
141
142 case PSN_APPLY:
143 SetLocalSystemTime(hwndDlg);
144 SetWindowLong(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
145 return TRUE;
146
147 default:
148 break;
149 }
150 }
151 break;
152 }
153
154 return FALSE;
155 }
156
157
158 static PTIMEZONE_ENTRY
159 GetLargerTimeZoneEntry(DWORD Index)
160 {
161 PTIMEZONE_ENTRY Entry;
162
163 Entry = TimeZoneListHead;
164 while (Entry != NULL)
165 {
166 if (Entry->Index >= Index)
167 return Entry;
168
169 Entry = Entry->Next;
170 }
171
172 return NULL;
173 }
174
175
176 static VOID
177 CreateTimeZoneList(VOID)
178 {
179 WCHAR szKeyName[256];
180 DWORD dwIndex;
181 DWORD dwNameSize;
182 DWORD dwValueSize;
183 LONG lError;
184 HKEY hZonesKey;
185 HKEY hZoneKey;
186
187 PTIMEZONE_ENTRY Entry;
188 PTIMEZONE_ENTRY Current;
189
190 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
191 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
192 0,
193 KEY_ALL_ACCESS,
194 &hZonesKey))
195 return;
196
197 dwIndex = 0;
198 while (TRUE)
199 {
200 dwNameSize = 256;
201 lError = RegEnumKeyExW(hZonesKey,
202 dwIndex,
203 szKeyName,
204 &dwNameSize,
205 NULL,
206 NULL,
207 NULL,
208 NULL);
209 if (lError != ERROR_SUCCESS && lError != ERROR_MORE_DATA)
210 break;
211
212 if (RegOpenKeyExW(hZonesKey,
213 szKeyName,
214 0,
215 KEY_ALL_ACCESS,
216 &hZoneKey))
217 break;
218
219 Entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TIMEZONE_ENTRY));
220 if (Entry == NULL)
221 {
222 RegCloseKey(hZoneKey);
223 break;
224 }
225
226 dwValueSize = 64 * sizeof(WCHAR);
227 if (RegQueryValueExW(hZoneKey,
228 L"Display",
229 NULL,
230 NULL,
231 (LPBYTE)&Entry->Description,
232 &dwValueSize))
233 {
234 RegCloseKey(hZoneKey);
235 break;
236 }
237
238 dwValueSize = 32 * sizeof(WCHAR);
239 if (RegQueryValueExW(hZoneKey,
240 L"Std",
241 NULL,
242 NULL,
243 (LPBYTE)&Entry->StandardName,
244 &dwValueSize))
245 {
246 RegCloseKey(hZoneKey);
247 break;
248 }
249
250 dwValueSize = 32 * sizeof(WCHAR);
251 if (RegQueryValueExW(hZoneKey,
252 L"Dlt",
253 NULL,
254 NULL,
255 (LPBYTE)&Entry->DaylightName,
256 &dwValueSize))
257 {
258 RegCloseKey(hZoneKey);
259 break;
260 }
261
262 dwValueSize = sizeof(DWORD);
263 if (RegQueryValueExW(hZoneKey,
264 L"Index",
265 NULL,
266 NULL,
267 (LPBYTE)&Entry->Index,
268 &dwValueSize))
269 {
270 RegCloseKey(hZoneKey);
271 break;
272 }
273
274 dwValueSize = sizeof(TZ_INFO);
275 if (RegQueryValueExW(hZoneKey,
276 L"TZI",
277 NULL,
278 NULL,
279 (LPBYTE)&Entry->TimezoneInfo,
280 &dwValueSize))
281 {
282 RegCloseKey(hZoneKey);
283 break;
284 }
285
286 RegCloseKey(hZoneKey);
287
288 if (TimeZoneListHead == NULL &&
289 TimeZoneListTail == NULL)
290 {
291 Entry->Prev = NULL;
292 Entry->Next = NULL;
293 TimeZoneListHead = Entry;
294 TimeZoneListTail = Entry;
295 }
296 else
297 {
298 Current = GetLargerTimeZoneEntry(Entry->Index);
299 if (Current != NULL)
300 {
301 if (Current == TimeZoneListHead)
302 {
303 /* Prepend to head */
304 Entry->Prev = NULL;
305 Entry->Next = TimeZoneListHead;
306 TimeZoneListHead->Prev = Entry;
307 TimeZoneListHead = Entry;
308 }
309 else
310 {
311 /* Insert before current */
312 Entry->Prev = Current->Prev;
313 Entry->Next = Current;
314 Current->Prev->Next = Entry;
315 Current->Prev = Entry;
316 }
317 }
318 else
319 {
320 /* Append to tail */
321 Entry->Prev = TimeZoneListTail;
322 Entry->Next = NULL;
323 TimeZoneListTail->Next = Entry;
324 TimeZoneListTail = Entry;
325 }
326 }
327
328 dwIndex++;
329 }
330
331 RegCloseKey(hZonesKey);
332 }
333
334
335 static VOID
336 DestroyTimeZoneList(VOID)
337 {
338 PTIMEZONE_ENTRY Entry;
339
340 while (TimeZoneListHead != NULL)
341 {
342 Entry = TimeZoneListHead;
343
344 TimeZoneListHead = Entry->Next;
345 if (TimeZoneListHead != NULL)
346 {
347 TimeZoneListHead->Prev = NULL;
348 }
349
350 HeapFree(GetProcessHeap(), 0, Entry);
351 }
352
353 TimeZoneListTail = NULL;
354 }
355
356
357 static VOID
358 ShowTimeZoneList(HWND hwnd)
359 {
360 TIME_ZONE_INFORMATION TimeZoneInfo;
361 PTIMEZONE_ENTRY Entry;
362 DWORD dwIndex;
363 DWORD i;
364
365 GetTimeZoneInformation(&TimeZoneInfo);
366
367 dwIndex = 0;
368 i = 0;
369 Entry = TimeZoneListHead;
370 while (Entry != NULL)
371 {
372 SendMessageW(hwnd,
373 CB_ADDSTRING,
374 0,
375 (LPARAM)Entry->Description);
376
377 if (!wcscmp(Entry->StandardName, TimeZoneInfo.StandardName))
378 dwIndex = i;
379
380 i++;
381 Entry = Entry->Next;
382 }
383
384 SendMessageW(hwnd,
385 CB_SETCURSEL,
386 (WPARAM)dwIndex,
387 0);
388 }
389
390
391 static VOID
392 SetLocalTimeZone(HWND hwnd)
393 {
394 TIME_ZONE_INFORMATION TimeZoneInformation;
395 PTIMEZONE_ENTRY Entry;
396 DWORD dwIndex;
397 DWORD i;
398
399 dwIndex = SendMessage(hwnd,
400 CB_GETCURSEL,
401 0,
402 0);
403
404 i = 0;
405 Entry = TimeZoneListHead;
406 while (i < dwIndex)
407 {
408 if (Entry == NULL)
409 return;
410
411 i++;
412 Entry = Entry->Next;
413 }
414
415 wcscpy(TimeZoneInformation.StandardName,
416 Entry->StandardName);
417 wcscpy(TimeZoneInformation.DaylightName,
418 Entry->DaylightName);
419
420 TimeZoneInformation.Bias = Entry->TimezoneInfo.Bias;
421 TimeZoneInformation.StandardBias = Entry->TimezoneInfo.StandardBias;
422 TimeZoneInformation.DaylightBias = Entry->TimezoneInfo.DaylightBias;
423
424 memcpy(&TimeZoneInformation.StandardDate,
425 &Entry->TimezoneInfo.StandardDate,
426 sizeof(SYSTEMTIME));
427 memcpy(&TimeZoneInformation.DaylightDate,
428 &Entry->TimezoneInfo.DaylightDate,
429 sizeof(SYSTEMTIME));
430
431 /* Set time zone information */
432 SetTimeZoneInformation(&TimeZoneInformation);
433 }
434
435
436 static VOID
437 GetAutoDaylightInfo(HWND hwnd)
438 {
439 HKEY hKey;
440
441 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
442 L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
443 0,
444 KEY_SET_VALUE,
445 &hKey))
446 return;
447
448 if (RegQueryValueExW(hKey,
449 L"DisableAutoDaylightTimeSet",
450 NULL,
451 NULL,
452 NULL,
453 NULL))
454 {
455 SendMessage(hwnd, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
456 }
457 else
458 {
459 SendMessage(hwnd, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
460 }
461
462 RegCloseKey(hKey);
463 }
464
465
466 static VOID
467 SetAutoDaylightInfo(HWND hwnd)
468 {
469 HKEY hKey;
470 DWORD dwValue = 1;
471
472 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
473 L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
474 0,
475 KEY_SET_VALUE,
476 &hKey))
477 return;
478
479 if (SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED)
480 {
481 RegSetValueExW(hKey,
482 L"DisableAutoDaylightTimeSet",
483 0,
484 REG_DWORD,
485 (LPBYTE)&dwValue,
486 sizeof(DWORD));
487 }
488 else
489 {
490 RegDeleteValueW(hKey,
491 L"DisableAutoDaylightTimeSet");
492 }
493
494 RegCloseKey(hKey);
495 }
496
497
498 /* Property page dialog callback */
499 INT_PTR CALLBACK
500 TimeZonePageProc(HWND hwndDlg,
501 UINT uMsg,
502 WPARAM wParam,
503 LPARAM lParam)
504 {
505 switch (uMsg)
506 {
507 case WM_INITDIALOG:
508 CreateTimeZoneList();
509 ShowTimeZoneList(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
510 GetAutoDaylightInfo(GetDlgItem(hwndDlg, IDC_AUTODAYLIGHT));
511 break;
512
513 case WM_COMMAND:
514 if ((LOWORD(wParam) == IDC_TIMEZONELIST && HIWORD(wParam) == CBN_SELCHANGE) ||
515 (LOWORD(wParam) == IDC_AUTODAYLIGHT && HIWORD(wParam) == BN_CLICKED))
516 {
517 /* Enable the 'Apply' button */
518 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
519 }
520 break;
521
522 case WM_DESTROY:
523 DestroyTimeZoneList();
524 break;
525
526 case WM_NOTIFY:
527 {
528 LPNMHDR lpnm = (LPNMHDR)lParam;
529
530 switch (lpnm->code)
531 {
532 case PSN_APPLY:
533 SetAutoDaylightInfo(GetDlgItem(hwndDlg, IDC_AUTODAYLIGHT));
534 SetLocalTimeZone(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
535 SetWindowLong(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
536 return TRUE;
537
538 default:
539 break;
540 }
541
542 }
543 break;
544 }
545
546 return FALSE;
547 }
548
549
550 static VOID
551 InitPropSheetPage(PROPSHEETPAGE *psp, WORD idDlg, DLGPROC DlgProc)
552 {
553 ZeroMemory(psp, sizeof(PROPSHEETPAGE));
554 psp->dwSize = sizeof(PROPSHEETPAGE);
555 psp->dwFlags = PSP_DEFAULT;
556 psp->hInstance = hApplet;
557 psp->pszTemplate = MAKEINTRESOURCE(idDlg);
558 psp->pfnDlgProc = DlgProc;
559 }
560
561
562 LONG APIENTRY
563 Applet(HWND hwnd, UINT uMsg, LONG wParam, LONG lParam)
564 {
565 PROPSHEETHEADER psh;
566 PROPSHEETPAGE psp[3];
567 TCHAR Caption[256];
568
569 LoadString(hApplet, IDS_CPLNAME, Caption, sizeof(Caption) / sizeof(TCHAR));
570
571 ZeroMemory(&psh, sizeof(PROPSHEETHEADER));
572 psh.dwSize = sizeof(PROPSHEETHEADER);
573 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_PROPTITLE;
574 psh.hwndParent = NULL;
575 psh.hInstance = hApplet;
576 psh.hIcon = LoadIcon(hApplet, MAKEINTRESOURCE(IDC_CPLICON));
577 psh.pszCaption = Caption;
578 psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
579 psh.nStartPage = 0;
580 psh.ppsp = psp;
581
582 InitPropSheetPage(&psp[0], IDD_DATETIMEPAGE, DateTimePageProc);
583 InitPropSheetPage(&psp[1], IDD_TIMEZONEPAGE, TimeZonePageProc);
584
585 return (LONG)(PropertySheet(&psh) != -1);
586 }
587
588
589 /* Control Panel Callback */
590 LONG CALLBACK
591 CPlApplet(HWND hwndCpl,
592 UINT uMsg,
593 LPARAM lParam1,
594 LPARAM lParam2)
595 {
596 int i = (int)lParam1;
597
598 switch (uMsg)
599 {
600 case CPL_INIT:
601 return TRUE;
602
603 case CPL_GETCOUNT:
604 return NUM_APPLETS;
605
606 case CPL_INQUIRE:
607 {
608 CPLINFO *CPlInfo = (CPLINFO*)lParam2;
609 CPlInfo->lData = 0;
610 CPlInfo->idIcon = Applets[i].idIcon;
611 CPlInfo->idName = Applets[i].idName;
612 CPlInfo->idInfo = Applets[i].idDescription;
613 break;
614 }
615
616 case CPL_DBLCLK:
617 {
618 Applets[i].AppletProc(hwndCpl, uMsg, lParam1, lParam2);
619 break;
620 }
621 }
622 return FALSE;
623 }
624
625
626 BOOL STDCALL
627 DllMain(HINSTANCE hinstDLL,
628 DWORD dwReason,
629 LPVOID lpReserved)
630 {
631 switch (dwReason)
632 {
633 case DLL_PROCESS_ATTACH:
634 {
635 INITCOMMONCONTROLSEX InitControls;
636
637 InitControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
638 InitControls.dwICC = ICC_DATE_CLASSES | ICC_PROGRESS_CLASS | ICC_UPDOWN_CLASS;
639 InitCommonControlsEx(&InitControls);
640
641 hApplet = hinstDLL;
642 }
643 break;
644 }
645
646 return TRUE;
647 }
648
649 /* EOF */