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