edec78490ceb994f906ac9aed6ef153a1503f6ea
[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 WCHAR TimeZoneText[128];
106 WCHAR TimeZoneName[128];
107 DWORD TimeZoneId;
108
109 TimeZoneId = GetTimeZoneInformation(&TimeZoneInfo);
110
111 LoadString(hApplet, IDS_TIMEZONETEXT, TimeZoneText, 128);
112
113 switch (TimeZoneId)
114 {
115 case TIME_ZONE_ID_STANDARD:
116 wcscpy(TimeZoneName, TimeZoneInfo.StandardName);
117 break;
118
119 case TIME_ZONE_ID_DAYLIGHT:
120 wcscpy(TimeZoneName, TimeZoneInfo.DaylightName);
121 break;
122
123 case TIME_ZONE_ID_UNKNOWN:
124 LoadString(hApplet, IDS_TIMEZONEUNKNOWN, TimeZoneName, 128);
125 break;
126
127 case TIME_ZONE_ID_INVALID:
128 default:
129 LoadString(hApplet, IDS_TIMEZONEINVALID, TimeZoneName, 128);
130 break;
131 }
132
133 wsprintf(TimeZoneString, TimeZoneText, TimeZoneName);
134 SendDlgItemMessageW(hwnd, IDC_TIMEZONE, WM_SETTEXT, 0, (LPARAM)TimeZoneString);
135 }
136
137
138 /* Property page dialog callback */
139 INT_PTR CALLBACK
140 DateTimePageProc(HWND hwndDlg,
141 UINT uMsg,
142 WPARAM wParam,
143 LPARAM lParam)
144 {
145 switch (uMsg)
146 {
147 case WM_NOTIFY:
148 {
149 LPNMHDR lpnm = (LPNMHDR)lParam;
150
151 switch (lpnm->code)
152 {
153 case DTN_DATETIMECHANGE:
154 case MCN_SELECT:
155 /* Enable the 'Apply' button */
156 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
157 break;
158
159 case PSN_SETACTIVE:
160 SetTimeZoneName(hwndDlg);
161 return 0;
162
163 case PSN_APPLY:
164 SetLocalSystemTime(hwndDlg);
165 SetWindowLong(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
166 return TRUE;
167
168 default:
169 break;
170 }
171 }
172 break;
173 }
174
175 return FALSE;
176 }
177
178
179 static PTIMEZONE_ENTRY
180 GetLargerTimeZoneEntry(DWORD Index)
181 {
182 PTIMEZONE_ENTRY Entry;
183
184 Entry = TimeZoneListHead;
185 while (Entry != NULL)
186 {
187 if (Entry->Index >= Index)
188 return Entry;
189
190 Entry = Entry->Next;
191 }
192
193 return NULL;
194 }
195
196
197 static VOID
198 CreateTimeZoneList(VOID)
199 {
200 WCHAR szKeyName[256];
201 DWORD dwIndex;
202 DWORD dwNameSize;
203 DWORD dwValueSize;
204 LONG lError;
205 HKEY hZonesKey;
206 HKEY hZoneKey;
207
208 PTIMEZONE_ENTRY Entry;
209 PTIMEZONE_ENTRY Current;
210
211 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
212 L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
213 0,
214 KEY_ALL_ACCESS,
215 &hZonesKey))
216 return;
217
218 dwIndex = 0;
219 while (TRUE)
220 {
221 dwNameSize = 256;
222 lError = RegEnumKeyExW(hZonesKey,
223 dwIndex,
224 szKeyName,
225 &dwNameSize,
226 NULL,
227 NULL,
228 NULL,
229 NULL);
230 if (lError != ERROR_SUCCESS && lError != ERROR_MORE_DATA)
231 break;
232
233 if (RegOpenKeyExW(hZonesKey,
234 szKeyName,
235 0,
236 KEY_ALL_ACCESS,
237 &hZoneKey))
238 break;
239
240 Entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TIMEZONE_ENTRY));
241 if (Entry == NULL)
242 {
243 RegCloseKey(hZoneKey);
244 break;
245 }
246
247 dwValueSize = 64 * sizeof(WCHAR);
248 if (RegQueryValueExW(hZoneKey,
249 L"Display",
250 NULL,
251 NULL,
252 (LPBYTE)&Entry->Description,
253 &dwValueSize))
254 {
255 RegCloseKey(hZoneKey);
256 break;
257 }
258
259 dwValueSize = 32 * sizeof(WCHAR);
260 if (RegQueryValueExW(hZoneKey,
261 L"Std",
262 NULL,
263 NULL,
264 (LPBYTE)&Entry->StandardName,
265 &dwValueSize))
266 {
267 RegCloseKey(hZoneKey);
268 break;
269 }
270
271 dwValueSize = 32 * sizeof(WCHAR);
272 if (RegQueryValueExW(hZoneKey,
273 L"Dlt",
274 NULL,
275 NULL,
276 (LPBYTE)&Entry->DaylightName,
277 &dwValueSize))
278 {
279 RegCloseKey(hZoneKey);
280 break;
281 }
282
283 dwValueSize = sizeof(DWORD);
284 if (RegQueryValueExW(hZoneKey,
285 L"Index",
286 NULL,
287 NULL,
288 (LPBYTE)&Entry->Index,
289 &dwValueSize))
290 {
291 RegCloseKey(hZoneKey);
292 break;
293 }
294
295 dwValueSize = sizeof(TZ_INFO);
296 if (RegQueryValueExW(hZoneKey,
297 L"TZI",
298 NULL,
299 NULL,
300 (LPBYTE)&Entry->TimezoneInfo,
301 &dwValueSize))
302 {
303 RegCloseKey(hZoneKey);
304 break;
305 }
306
307 RegCloseKey(hZoneKey);
308
309 if (TimeZoneListHead == NULL &&
310 TimeZoneListTail == NULL)
311 {
312 Entry->Prev = NULL;
313 Entry->Next = NULL;
314 TimeZoneListHead = Entry;
315 TimeZoneListTail = Entry;
316 }
317 else
318 {
319 Current = GetLargerTimeZoneEntry(Entry->Index);
320 if (Current != NULL)
321 {
322 if (Current == TimeZoneListHead)
323 {
324 /* Prepend to head */
325 Entry->Prev = NULL;
326 Entry->Next = TimeZoneListHead;
327 TimeZoneListHead->Prev = Entry;
328 TimeZoneListHead = Entry;
329 }
330 else
331 {
332 /* Insert before current */
333 Entry->Prev = Current->Prev;
334 Entry->Next = Current;
335 Current->Prev->Next = Entry;
336 Current->Prev = Entry;
337 }
338 }
339 else
340 {
341 /* Append to tail */
342 Entry->Prev = TimeZoneListTail;
343 Entry->Next = NULL;
344 TimeZoneListTail->Next = Entry;
345 TimeZoneListTail = Entry;
346 }
347 }
348
349 dwIndex++;
350 }
351
352 RegCloseKey(hZonesKey);
353 }
354
355
356 static VOID
357 DestroyTimeZoneList(VOID)
358 {
359 PTIMEZONE_ENTRY Entry;
360
361 while (TimeZoneListHead != NULL)
362 {
363 Entry = TimeZoneListHead;
364
365 TimeZoneListHead = Entry->Next;
366 if (TimeZoneListHead != NULL)
367 {
368 TimeZoneListHead->Prev = NULL;
369 }
370
371 HeapFree(GetProcessHeap(), 0, Entry);
372 }
373
374 TimeZoneListTail = NULL;
375 }
376
377
378 static VOID
379 ShowTimeZoneList(HWND hwnd)
380 {
381 TIME_ZONE_INFORMATION TimeZoneInfo;
382 PTIMEZONE_ENTRY Entry;
383 DWORD dwIndex;
384 DWORD i;
385
386 GetTimeZoneInformation(&TimeZoneInfo);
387
388 dwIndex = 0;
389 i = 0;
390 Entry = TimeZoneListHead;
391 while (Entry != NULL)
392 {
393 SendMessageW(hwnd,
394 CB_ADDSTRING,
395 0,
396 (LPARAM)Entry->Description);
397
398 if (!wcscmp(Entry->StandardName, TimeZoneInfo.StandardName))
399 dwIndex = i;
400
401 i++;
402 Entry = Entry->Next;
403 }
404
405 SendMessageW(hwnd,
406 CB_SETCURSEL,
407 (WPARAM)dwIndex,
408 0);
409 }
410
411
412 static VOID
413 SetLocalTimeZone(HWND hwnd)
414 {
415 TIME_ZONE_INFORMATION TimeZoneInformation;
416 PTIMEZONE_ENTRY Entry;
417 DWORD dwIndex;
418 DWORD i;
419
420 dwIndex = SendMessage(hwnd,
421 CB_GETCURSEL,
422 0,
423 0);
424
425 i = 0;
426 Entry = TimeZoneListHead;
427 while (i < dwIndex)
428 {
429 if (Entry == NULL)
430 return;
431
432 i++;
433 Entry = Entry->Next;
434 }
435
436 wcscpy(TimeZoneInformation.StandardName,
437 Entry->StandardName);
438 wcscpy(TimeZoneInformation.DaylightName,
439 Entry->DaylightName);
440
441 TimeZoneInformation.Bias = Entry->TimezoneInfo.Bias;
442 TimeZoneInformation.StandardBias = Entry->TimezoneInfo.StandardBias;
443 TimeZoneInformation.DaylightBias = Entry->TimezoneInfo.DaylightBias;
444
445 memcpy(&TimeZoneInformation.StandardDate,
446 &Entry->TimezoneInfo.StandardDate,
447 sizeof(SYSTEMTIME));
448 memcpy(&TimeZoneInformation.DaylightDate,
449 &Entry->TimezoneInfo.DaylightDate,
450 sizeof(SYSTEMTIME));
451
452 /* Set time zone information */
453 SetTimeZoneInformation(&TimeZoneInformation);
454 }
455
456
457 static VOID
458 GetAutoDaylightInfo(HWND hwnd)
459 {
460 HKEY hKey;
461
462 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
463 L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
464 0,
465 KEY_QUERY_VALUE,
466 &hKey))
467 return;
468
469 if (RegQueryValueExW(hKey,
470 L"DisableAutoDaylightTimeSet",
471 NULL,
472 NULL,
473 NULL,
474 NULL))
475 {
476 SendMessage(hwnd, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
477 }
478 else
479 {
480 SendMessage(hwnd, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
481 }
482
483 RegCloseKey(hKey);
484 }
485
486
487 static VOID
488 SetAutoDaylightInfo(HWND hwnd)
489 {
490 HKEY hKey;
491 DWORD dwValue = 1;
492
493 if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
494 L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
495 0,
496 KEY_SET_VALUE,
497 &hKey))
498 return;
499
500 if (SendMessage(hwnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED)
501 {
502 RegSetValueExW(hKey,
503 L"DisableAutoDaylightTimeSet",
504 0,
505 REG_DWORD,
506 (LPBYTE)&dwValue,
507 sizeof(DWORD));
508 }
509 else
510 {
511 RegDeleteValueW(hKey,
512 L"DisableAutoDaylightTimeSet");
513 }
514
515 RegCloseKey(hKey);
516 }
517
518
519 /* Property page dialog callback */
520 INT_PTR CALLBACK
521 TimeZonePageProc(HWND hwndDlg,
522 UINT uMsg,
523 WPARAM wParam,
524 LPARAM lParam)
525 {
526 switch (uMsg)
527 {
528 case WM_INITDIALOG:
529 CreateTimeZoneList();
530 ShowTimeZoneList(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
531 GetAutoDaylightInfo(GetDlgItem(hwndDlg, IDC_AUTODAYLIGHT));
532 break;
533
534 case WM_COMMAND:
535 if ((LOWORD(wParam) == IDC_TIMEZONELIST && HIWORD(wParam) == CBN_SELCHANGE) ||
536 (LOWORD(wParam) == IDC_AUTODAYLIGHT && HIWORD(wParam) == BN_CLICKED))
537 {
538 /* Enable the 'Apply' button */
539 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
540 }
541 break;
542
543 case WM_DESTROY:
544 DestroyTimeZoneList();
545 break;
546
547 case WM_NOTIFY:
548 {
549 LPNMHDR lpnm = (LPNMHDR)lParam;
550
551 switch (lpnm->code)
552 {
553 case PSN_APPLY:
554 SetAutoDaylightInfo(GetDlgItem(hwndDlg, IDC_AUTODAYLIGHT));
555 SetLocalTimeZone(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
556 SetWindowLong(hwndDlg, DWL_MSGRESULT, PSNRET_NOERROR);
557 return TRUE;
558
559 default:
560 break;
561 }
562
563 }
564 break;
565 }
566
567 return FALSE;
568 }
569
570
571 static VOID
572 InitPropSheetPage(PROPSHEETPAGE *psp, WORD idDlg, DLGPROC DlgProc)
573 {
574 ZeroMemory(psp, sizeof(PROPSHEETPAGE));
575 psp->dwSize = sizeof(PROPSHEETPAGE);
576 psp->dwFlags = PSP_DEFAULT;
577 psp->hInstance = hApplet;
578 psp->pszTemplate = MAKEINTRESOURCE(idDlg);
579 psp->pfnDlgProc = DlgProc;
580 }
581
582
583 LONG APIENTRY
584 Applet(HWND hwnd, UINT uMsg, LONG wParam, LONG lParam)
585 {
586 PROPSHEETHEADER psh;
587 PROPSHEETPAGE psp[2];
588 TCHAR Caption[256];
589
590 LoadString(hApplet, IDS_CPLNAME, Caption, sizeof(Caption) / sizeof(TCHAR));
591
592 ZeroMemory(&psh, sizeof(PROPSHEETHEADER));
593 psh.dwSize = sizeof(PROPSHEETHEADER);
594 psh.dwFlags = PSH_PROPSHEETPAGE | PSH_PROPTITLE;
595 psh.hwndParent = NULL;
596 psh.hInstance = hApplet;
597 psh.hIcon = LoadIcon(hApplet, MAKEINTRESOURCE(IDC_CPLICON));
598 psh.pszCaption = Caption;
599 psh.nPages = sizeof(psp) / sizeof(PROPSHEETPAGE);
600 psh.nStartPage = 0;
601 psh.ppsp = psp;
602
603 InitPropSheetPage(&psp[0], IDD_DATETIMEPAGE, DateTimePageProc);
604 InitPropSheetPage(&psp[1], IDD_TIMEZONEPAGE, TimeZonePageProc);
605
606 return (LONG)(PropertySheet(&psh) != -1);
607 }
608
609
610 /* Control Panel Callback */
611 LONG CALLBACK
612 CPlApplet(HWND hwndCpl,
613 UINT uMsg,
614 LPARAM lParam1,
615 LPARAM lParam2)
616 {
617 int i = (int)lParam1;
618
619 switch (uMsg)
620 {
621 case CPL_INIT:
622 return TRUE;
623
624 case CPL_GETCOUNT:
625 return NUM_APPLETS;
626
627 case CPL_INQUIRE:
628 {
629 CPLINFO *CPlInfo = (CPLINFO*)lParam2;
630 CPlInfo->lData = 0;
631 CPlInfo->idIcon = Applets[i].idIcon;
632 CPlInfo->idName = Applets[i].idName;
633 CPlInfo->idInfo = Applets[i].idDescription;
634 break;
635 }
636
637 case CPL_DBLCLK:
638 {
639 Applets[i].AppletProc(hwndCpl, uMsg, lParam1, lParam2);
640 break;
641 }
642 }
643 return FALSE;
644 }
645
646
647 BOOL STDCALL
648 DllMain(HINSTANCE hinstDLL,
649 DWORD dwReason,
650 LPVOID lpReserved)
651 {
652 switch (dwReason)
653 {
654 case DLL_PROCESS_ATTACH:
655 {
656 INITCOMMONCONTROLSEX InitControls;
657
658 InitControls.dwSize = sizeof(INITCOMMONCONTROLSEX);
659 InitControls.dwICC = ICC_DATE_CLASSES | ICC_PROGRESS_CLASS | ICC_UPDOWN_CLASS;
660 InitCommonControlsEx(&InitControls);
661
662 hApplet = hinstDLL;
663 }
664 break;
665 }
666
667 return TRUE;
668 }
669
670 /* EOF */