xref: /reactos/dll/cpl/timedate/timezone.c (revision d6d1efe7)
1 /*
2  * PROJECT:     ReactOS Timedate Control Panel
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        dll/cpl/timedate/timezone.c
5  * PURPOSE:     Time Zone property page
6  * COPYRIGHT:   Copyright 2004-2005 Eric Kohl
7  *              Copyright 2006 Ged Murphy <gedmurphy@gmail.com>
8  *              Copyright 2006 Christoph v. Wittich <Christoph@ActiveVB.de>
9  *
10  */
11 
12 #include "timedate.h"
13 
14 // See also sdk/include/reactos/libs/syssetup/syssetup.h
15 typedef struct _TZ_INFO
16 {
17     LONG Bias;
18     LONG StandardBias;
19     LONG DaylightBias;
20     SYSTEMTIME StandardDate;
21     SYSTEMTIME DaylightDate;
22 } TZ_INFO, *PTZ_INFO;
23 
24 typedef struct _TIMEZONE_ENTRY
25 {
26     struct _TIMEZONE_ENTRY *Prev;
27     struct _TIMEZONE_ENTRY *Next;
28     WCHAR Description[128];  /* 'Display' */
29     WCHAR StandardName[33];  /* 'Std' */
30     WCHAR DaylightName[33];  /* 'Dlt' */
31     TZ_INFO TimezoneInfo;    /* 'TZI' */
32 } TIMEZONE_ENTRY, *PTIMEZONE_ENTRY;
33 
34 
35 static HBITMAP hBitmap = NULL;
36 static int cxSource, cySource;
37 
38 PTIMEZONE_ENTRY TimeZoneListHead = NULL;
39 PTIMEZONE_ENTRY TimeZoneListTail = NULL;
40 
41 static
42 PTIMEZONE_ENTRY
43 GetLargerTimeZoneEntry(
44     LONG Bias,
45     LPWSTR lpDescription)
46 {
47     PTIMEZONE_ENTRY Entry;
48 
49     Entry = TimeZoneListHead;
50     while (Entry != NULL)
51     {
52         if (Entry->TimezoneInfo.Bias < Bias)
53             return Entry;
54 
55         if (Entry->TimezoneInfo.Bias == Bias)
56         {
57             if (_wcsicmp(Entry->Description, lpDescription) > 0)
58                 return Entry;
59         }
60 
61         Entry = Entry->Next;
62     }
63 
64     return NULL;
65 }
66 
67 
68 static
69 LONG
70 QueryTimezoneData(
71     HKEY hZoneKey,
72     PTIMEZONE_ENTRY Entry)
73 {
74     DWORD dwValueSize;
75     LONG lError;
76 
77     dwValueSize = sizeof(Entry->Description);
78     lError = RegQueryValueExW(hZoneKey,
79                               L"Display",
80                               NULL,
81                               NULL,
82                               (LPBYTE)&Entry->Description,
83                               &dwValueSize);
84     if (lError != ERROR_SUCCESS)
85         return lError;
86 
87     dwValueSize = sizeof(Entry->StandardName);
88     lError = RegQueryValueExW(hZoneKey,
89                               L"Std",
90                               NULL,
91                               NULL,
92                               (LPBYTE)&Entry->StandardName,
93                               &dwValueSize);
94     if (lError != ERROR_SUCCESS)
95         return lError;
96 
97     dwValueSize = sizeof(Entry->DaylightName);
98     lError = RegQueryValueExW(hZoneKey,
99                               L"Dlt",
100                               NULL,
101                               NULL,
102                               (LPBYTE)&Entry->DaylightName,
103                               &dwValueSize);
104     if (lError != ERROR_SUCCESS)
105         return lError;
106 
107     dwValueSize = sizeof(Entry->TimezoneInfo);
108     lError = RegQueryValueExW(hZoneKey,
109                               L"TZI",
110                               NULL,
111                               NULL,
112                               (LPBYTE)&Entry->TimezoneInfo,
113                               &dwValueSize);
114     return lError;
115 }
116 
117 
118 static VOID
119 CreateTimeZoneList(VOID)
120 {
121     WCHAR szKeyName[256];
122     DWORD dwIndex;
123     DWORD dwNameSize;
124     LONG lError;
125     HKEY hZonesKey;
126     HKEY hZoneKey;
127     PTIMEZONE_ENTRY Entry;
128     PTIMEZONE_ENTRY Current;
129 
130     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
131                       L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones",
132                       0,
133                       KEY_ENUMERATE_SUB_KEYS,
134                       &hZonesKey))
135         return;
136 
137     for (dwIndex = 0; ; dwIndex++)
138     {
139         dwNameSize = sizeof(szKeyName);
140         lError = RegEnumKeyExW(hZonesKey,
141                                dwIndex,
142                                szKeyName,
143                                &dwNameSize,
144                                NULL,
145                                NULL,
146                                NULL,
147                                NULL);
148         if (lError == ERROR_NO_MORE_ITEMS)
149             break;
150 
151         if (RegOpenKeyEx (hZonesKey,
152                           szKeyName,
153                           0,
154                           KEY_QUERY_VALUE,
155                           &hZoneKey))
156             break;
157 
158         Entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TIMEZONE_ENTRY));
159         if (Entry == NULL)
160         {
161             RegCloseKey(hZoneKey);
162             break;
163         }
164 
165         lError = QueryTimezoneData(hZoneKey,
166                                    Entry);
167 
168         RegCloseKey(hZoneKey);
169 
170         if (lError != ERROR_SUCCESS)
171         {
172             HeapFree(GetProcessHeap(), 0, Entry);
173             break;
174         }
175 
176         if (TimeZoneListHead == NULL &&
177             TimeZoneListTail == NULL)
178         {
179             Entry->Prev = NULL;
180             Entry->Next = NULL;
181             TimeZoneListHead = Entry;
182             TimeZoneListTail = Entry;
183         }
184         else
185         {
186             Current = GetLargerTimeZoneEntry(Entry->TimezoneInfo.Bias, Entry->Description);
187             if (Current != NULL)
188             {
189                 if (Current == TimeZoneListHead)
190                 {
191                     /* Prepend to head */
192                     Entry->Prev = NULL;
193                     Entry->Next = TimeZoneListHead;
194                     TimeZoneListHead->Prev = Entry;
195                     TimeZoneListHead = Entry;
196                 }
197                 else
198                 {
199                     /* Insert before current */
200                     Entry->Prev = Current->Prev;
201                     Entry->Next = Current;
202                     Current->Prev->Next = Entry;
203                     Current->Prev = Entry;
204                 }
205             }
206             else
207             {
208                 /* Append to tail */
209                 Entry->Prev = TimeZoneListTail;
210                 Entry->Next = NULL;
211                 TimeZoneListTail->Next = Entry;
212                 TimeZoneListTail = Entry;
213             }
214         }
215     }
216 
217     RegCloseKey(hZonesKey);
218 }
219 
220 
221 static VOID
222 DestroyTimeZoneList(VOID)
223 {
224     PTIMEZONE_ENTRY Entry;
225 
226     while (TimeZoneListHead != NULL)
227     {
228         Entry = TimeZoneListHead;
229 
230         TimeZoneListHead = Entry->Next;
231         if (TimeZoneListHead != NULL)
232         {
233             TimeZoneListHead->Prev = NULL;
234         }
235 
236         HeapFree(GetProcessHeap(), 0, Entry);
237     }
238 
239     TimeZoneListTail = NULL;
240 }
241 
242 
243 static VOID
244 ShowTimeZoneList(HWND hwnd)
245 {
246     TIME_ZONE_INFORMATION TimeZoneInfo;
247     PTIMEZONE_ENTRY Entry;
248     DWORD dwIndex;
249     DWORD i;
250 
251     GetTimeZoneInformation(&TimeZoneInfo);
252 
253     dwIndex = 0;
254     i = 0;
255     Entry = TimeZoneListHead;
256     while (Entry != NULL)
257     {
258         SendMessageW(hwnd,
259                      CB_ADDSTRING,
260                      0,
261                      (LPARAM)Entry->Description);
262 
263         if (!wcscmp(Entry->StandardName, TimeZoneInfo.StandardName))
264             dwIndex = i;
265 
266         i++;
267         Entry = Entry->Next;
268     }
269 
270     SendMessageW(hwnd,
271                  CB_SETCURSEL,
272                  (WPARAM)dwIndex,
273                  0);
274 }
275 
276 
277 static VOID
278 SetLocalTimeZone(HWND hwnd)
279 {
280     TIME_ZONE_INFORMATION TimeZoneInformation;
281     PTIMEZONE_ENTRY Entry;
282     DWORD dwIndex;
283     DWORD i;
284 
285     dwIndex = (DWORD)SendMessageW(hwnd,
286                                   CB_GETCURSEL,
287                                   0,
288                                   0);
289 
290     i = 0;
291     Entry = TimeZoneListHead;
292     while (i < dwIndex)
293     {
294         if (Entry == NULL)
295             return;
296 
297         i++;
298         Entry = Entry->Next;
299     }
300 
301     wcscpy(TimeZoneInformation.StandardName,
302             Entry->StandardName);
303     wcscpy(TimeZoneInformation.DaylightName,
304             Entry->DaylightName);
305 
306     TimeZoneInformation.Bias = Entry->TimezoneInfo.Bias;
307     TimeZoneInformation.StandardBias = Entry->TimezoneInfo.StandardBias;
308     TimeZoneInformation.DaylightBias = Entry->TimezoneInfo.DaylightBias;
309 
310     memcpy(&TimeZoneInformation.StandardDate,
311            &Entry->TimezoneInfo.StandardDate,
312            sizeof(SYSTEMTIME));
313     memcpy(&TimeZoneInformation.DaylightDate,
314            &Entry->TimezoneInfo.DaylightDate,
315            sizeof(SYSTEMTIME));
316 
317     /* Set time zone information */
318     SetTimeZoneInformation(&TimeZoneInformation);
319 }
320 
321 
322 static VOID
323 GetAutoDaylightInfo(HWND hwnd)
324 {
325     HKEY hKey;
326 
327     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
328                       L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
329                       0,
330                       KEY_QUERY_VALUE,
331                       &hKey))
332         return;
333 
334     /* If the call fails (non zero), the reg value isn't available,
335      * which means it shouldn't be disabled, so we should check the button.
336      */
337     if (RegQueryValueExW(hKey,
338                          L"DisableAutoDaylightTimeSet",
339                          NULL,
340                          NULL,
341                          NULL,
342                          NULL))
343     {
344         SendMessageW(hwnd, BM_SETCHECK, (WPARAM)BST_CHECKED, 0);
345     }
346     else
347     {
348         SendMessageW(hwnd, BM_SETCHECK, (WPARAM)BST_UNCHECKED, 0);
349     }
350 
351     RegCloseKey(hKey);
352 }
353 
354 
355 static VOID
356 SetAutoDaylightInfo(HWND hwnd)
357 {
358     HKEY hKey;
359     DWORD dwValue = 1;
360 
361     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
362                       L"SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation",
363                       0,
364                       KEY_SET_VALUE,
365                       &hKey))
366         return;
367 
368     if (SendMessageW(hwnd, BM_GETCHECK, 0, 0) == BST_UNCHECKED)
369     {
370         RegSetValueExW(hKey,
371                        L"DisableAutoDaylightTimeSet",
372                        0,
373                        REG_DWORD,
374                        (LPBYTE)&dwValue,
375                        sizeof(dwValue));
376     }
377     else
378     {
379         RegDeleteValueW(hKey,
380                        L"DisableAutoDaylightTimeSet");
381     }
382 
383     RegCloseKey(hKey);
384 }
385 
386 
387 /* Property page dialog callback */
388 INT_PTR CALLBACK
389 TimeZonePageProc(HWND hwndDlg,
390                  UINT uMsg,
391                  WPARAM wParam,
392                  LPARAM lParam)
393 {
394     BITMAP bitmap;
395 
396     switch (uMsg)
397     {
398         case WM_INITDIALOG:
399             CreateTimeZoneList();
400             ShowTimeZoneList(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
401             GetAutoDaylightInfo(GetDlgItem(hwndDlg, IDC_AUTODAYLIGHT));
402             hBitmap = LoadImageW(hApplet, MAKEINTRESOURCEW(IDC_WORLD), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
403             if (hBitmap != NULL)
404             {
405                 GetObjectW(hBitmap, sizeof(bitmap), &bitmap);
406 
407                 cxSource = bitmap.bmWidth;
408                 cySource = bitmap.bmHeight;
409             }
410             break;
411 
412         case WM_DRAWITEM:
413         {
414             LPDRAWITEMSTRUCT lpDrawItem;
415             lpDrawItem = (LPDRAWITEMSTRUCT) lParam;
416             if(lpDrawItem->CtlID == IDC_WORLD_BACKGROUND)
417             {
418                 HDC hdcMem;
419                 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
420                 if (hdcMem != NULL)
421                 {
422                     SelectObject(hdcMem, hBitmap);
423                     StretchBlt(lpDrawItem->hDC, lpDrawItem->rcItem.left, lpDrawItem->rcItem.top,
424                                lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
425                                lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
426                                hdcMem, 0, 0, cxSource, cySource, SRCCOPY);
427                     DeleteDC(hdcMem);
428                 }
429             }
430         }
431         break;
432 
433         case WM_COMMAND:
434             if ((LOWORD(wParam) == IDC_TIMEZONELIST && HIWORD(wParam) == CBN_SELCHANGE) ||
435                 (LOWORD(wParam) == IDC_AUTODAYLIGHT && HIWORD(wParam) == BN_CLICKED))
436             {
437                 /* Enable the 'Apply' button */
438                 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
439             }
440             break;
441 
442         case WM_DESTROY:
443             DestroyTimeZoneList();
444             DeleteObject(hBitmap);
445             break;
446 
447         case WM_NOTIFY:
448         {
449             LPNMHDR lpnm = (LPNMHDR)lParam;
450 
451             switch (lpnm->code)
452             {
453                 case PSN_APPLY:
454                 {
455                     SetAutoDaylightInfo(GetDlgItem(hwndDlg, IDC_AUTODAYLIGHT));
456                     SetLocalTimeZone(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
457                     SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
458                     return TRUE;
459                 }
460 
461                 default:
462                     break;
463             }
464         }
465         break;
466     }
467 
468   return FALSE;
469 }
470