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