Reactos
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#include <tzlib.h>
14
15typedef struct _TIMEZONE_ENTRY
16{
17 struct _TIMEZONE_ENTRY *Prev;
18 struct _TIMEZONE_ENTRY *Next;
19 WCHAR Description[128]; /* 'Display' */
20 WCHAR StandardName[33]; /* 'Std' */
21 WCHAR DaylightName[33]; /* 'Dlt' */
22 REG_TZI_FORMAT TimezoneInfo; /* 'TZI' */
23} TIMEZONE_ENTRY, *PTIMEZONE_ENTRY;
24
25
26static HBITMAP hBitmap = NULL;
27static int cxSource, cySource;
28
29PTIMEZONE_ENTRY TimeZoneListHead = NULL;
30PTIMEZONE_ENTRY TimeZoneListTail = NULL;
31
32static
33PTIMEZONE_ENTRY
34GetLargerTimeZoneEntry(
35 LONG Bias,
36 LPWSTR lpDescription)
37{
38 PTIMEZONE_ENTRY Entry;
39
40 Entry = TimeZoneListHead;
41 while (Entry != NULL)
42 {
43 if (Entry->TimezoneInfo.Bias < Bias)
44 return Entry;
45
46 if (Entry->TimezoneInfo.Bias == Bias)
47 {
48 if (_wcsicmp(Entry->Description, lpDescription) > 0)
49 return Entry;
50 }
51
52 Entry = Entry->Next;
53 }
54
55 return NULL;
56}
57
58static LONG
59RetrieveTimeZone(
60 IN HKEY hZoneKey,
61 IN PVOID Context)
62{
63 LONG lError;
64 PTIMEZONE_ENTRY Entry;
65 PTIMEZONE_ENTRY Current;
66 ULONG DescriptionSize;
67 ULONG StandardNameSize;
68 ULONG DaylightNameSize;
69
70 Entry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TIMEZONE_ENTRY));
71 if (Entry == NULL)
72 {
73 return ERROR_NOT_ENOUGH_MEMORY;
74 }
75
76 DescriptionSize = sizeof(Entry->Description);
77 StandardNameSize = sizeof(Entry->StandardName);
78 DaylightNameSize = sizeof(Entry->DaylightName);
79
80 lError = QueryTimeZoneData(hZoneKey,
81 NULL,
82 &Entry->TimezoneInfo,
83 Entry->Description,
84 &DescriptionSize,
85 Entry->StandardName,
86 &StandardNameSize,
87 Entry->DaylightName,
88 &DaylightNameSize);
89 if (lError != ERROR_SUCCESS)
90 {
91 HeapFree(GetProcessHeap(), 0, Entry);
92 return lError;
93 }
94
95 if (TimeZoneListHead == NULL &&
96 TimeZoneListTail == NULL)
97 {
98 Entry->Prev = NULL;
99 Entry->Next = NULL;
100 TimeZoneListHead = Entry;
101 TimeZoneListTail = Entry;
102 }
103 else
104 {
105 Current = GetLargerTimeZoneEntry(Entry->TimezoneInfo.Bias, Entry->Description);
106 if (Current != NULL)
107 {
108 if (Current == TimeZoneListHead)
109 {
110 /* Prepend to head */
111 Entry->Prev = NULL;
112 Entry->Next = TimeZoneListHead;
113 TimeZoneListHead->Prev = Entry;
114 TimeZoneListHead = Entry;
115 }
116 else
117 {
118 /* Insert before current */
119 Entry->Prev = Current->Prev;
120 Entry->Next = Current;
121 Current->Prev->Next = Entry;
122 Current->Prev = Entry;
123 }
124 }
125 else
126 {
127 /* Append to tail */
128 Entry->Prev = TimeZoneListTail;
129 Entry->Next = NULL;
130 TimeZoneListTail->Next = Entry;
131 TimeZoneListTail = Entry;
132 }
133 }
134
135 return ERROR_SUCCESS;
136}
137
138static VOID
139CreateTimeZoneList(VOID)
140{
141 EnumerateTimeZoneList(RetrieveTimeZone, NULL);
142}
143
144static VOID
145DestroyTimeZoneList(VOID)
146{
147 PTIMEZONE_ENTRY Entry;
148
149 while (TimeZoneListHead != NULL)
150 {
151 Entry = TimeZoneListHead;
152
153 TimeZoneListHead = Entry->Next;
154 if (TimeZoneListHead != NULL)
155 {
156 TimeZoneListHead->Prev = NULL;
157 }
158
159 HeapFree(GetProcessHeap(), 0, Entry);
160 }
161
162 TimeZoneListTail = NULL;
163}
164
165
166static VOID
167ShowTimeZoneList(HWND hwnd)
168{
169 TIME_ZONE_INFORMATION TimeZoneInfo;
170 PTIMEZONE_ENTRY Entry;
171 DWORD dwCount;
172 DWORD dwIndex = 0;
173 BOOL bFound = FALSE;
174
175 if (GetTimeZoneInformation(&TimeZoneInfo) == TIME_ZONE_ID_INVALID)
176 {
177 /* Failed to retrieve current time-zone info, reset it */
178 ZeroMemory(&TimeZoneInfo, sizeof(TimeZoneInfo));
179 }
180
181 for (Entry = TimeZoneListHead; Entry != NULL; Entry = Entry->Next)
182 {
183 dwCount = SendMessageW(hwnd,
184 CB_ADDSTRING,
185 0,
186 (LPARAM)Entry->Description);
187 if (dwCount == CB_ERR || dwCount == CB_ERRSPACE)
188 continue;
189
190 /* If the time-zone was found in the list, skip the tests */
191 if (bFound)
192 continue;
193
194 if (*TimeZoneInfo.StandardName && *Entry->StandardName)
195 {
196 /* Compare by name */
197 if (wcscmp(Entry->StandardName, TimeZoneInfo.StandardName) == 0)
198 {
199 dwIndex = dwCount;
200 bFound = TRUE;
201 }
202 }
203 else
204 {
205 /* Compare by date and bias */
206 if ((Entry->TimezoneInfo.Bias == TimeZoneInfo.Bias) &&
207 (Entry->TimezoneInfo.StandardBias == TimeZoneInfo.StandardBias) &&
208 (Entry->TimezoneInfo.DaylightBias == TimeZoneInfo.DaylightBias) &&
209 (memcmp(&Entry->TimezoneInfo.StandardDate, &TimeZoneInfo.StandardDate, sizeof(SYSTEMTIME)) == 0) &&
210 (memcmp(&Entry->TimezoneInfo.DaylightDate, &TimeZoneInfo.DaylightDate, sizeof(SYSTEMTIME)) == 0))
211 {
212 dwIndex = dwCount;
213 bFound = TRUE;
214 }
215 }
216 }
217
218 SendMessageW(hwnd,
219 CB_SETCURSEL,
220 (WPARAM)dwIndex,
221 0);
222}
223
224
225static VOID
226SetLocalTimeZone(HWND hwnd)
227{
228 TIME_ZONE_INFORMATION TimeZoneInformation;
229 PTIMEZONE_ENTRY Entry;
230 DWORD dwIndex;
231 DWORD i;
232
233 dwIndex = (DWORD)SendMessageW(hwnd,
234 CB_GETCURSEL,
235 0,
236 0);
237
238 i = 0;
239 Entry = TimeZoneListHead;
240 while (i < dwIndex)
241 {
242 if (Entry == NULL)
243 return;
244
245 i++;
246 Entry = Entry->Next;
247 }
248
249 wcscpy(TimeZoneInformation.StandardName,
250 Entry->StandardName);
251 wcscpy(TimeZoneInformation.DaylightName,
252 Entry->DaylightName);
253
254 TimeZoneInformation.Bias = Entry->TimezoneInfo.Bias;
255 TimeZoneInformation.StandardBias = Entry->TimezoneInfo.StandardBias;
256 TimeZoneInformation.DaylightBias = Entry->TimezoneInfo.DaylightBias;
257
258 memcpy(&TimeZoneInformation.StandardDate,
259 &Entry->TimezoneInfo.StandardDate,
260 sizeof(SYSTEMTIME));
261 memcpy(&TimeZoneInformation.DaylightDate,
262 &Entry->TimezoneInfo.DaylightDate,
263 sizeof(SYSTEMTIME));
264
265 /* Set time zone information */
266 SetTimeZoneInformation(&TimeZoneInformation);
267}
268
269
270/* Property page dialog callback */
271INT_PTR CALLBACK
272TimeZonePageProc(HWND hwndDlg,
273 UINT uMsg,
274 WPARAM wParam,
275 LPARAM lParam)
276{
277 BITMAP bitmap;
278
279 switch (uMsg)
280 {
281 case WM_INITDIALOG:
282 {
283 CreateTimeZoneList();
284 ShowTimeZoneList(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
285
286 SendDlgItemMessage(hwndDlg, IDC_AUTODAYLIGHT, BM_SETCHECK,
287 (WPARAM)(GetAutoDaylight() ? BST_CHECKED : BST_UNCHECKED), 0);
288
289 hBitmap = LoadImageW(hApplet, MAKEINTRESOURCEW(IDC_WORLD), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
290 if (hBitmap != NULL)
291 {
292 GetObjectW(hBitmap, sizeof(bitmap), &bitmap);
293
294 cxSource = bitmap.bmWidth;
295 cySource = bitmap.bmHeight;
296 }
297 break;
298 }
299
300 case WM_DRAWITEM:
301 {
302 LPDRAWITEMSTRUCT lpDrawItem;
303 lpDrawItem = (LPDRAWITEMSTRUCT)lParam;
304 if(lpDrawItem->CtlID == IDC_WORLD_BACKGROUND)
305 {
306 HDC hdcMem;
307 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
308 if (hdcMem != NULL)
309 {
310 SelectObject(hdcMem, hBitmap);
311 StretchBlt(lpDrawItem->hDC, lpDrawItem->rcItem.left, lpDrawItem->rcItem.top,
312 lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
313 lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
314 hdcMem, 0, 0, cxSource, cySource, SRCCOPY);
315 DeleteDC(hdcMem);
316 }
317 }
318 }
319 break;
320
321 case WM_COMMAND:
322 if ((LOWORD(wParam) == IDC_TIMEZONELIST && HIWORD(wParam) == CBN_SELCHANGE) ||
323 (LOWORD(wParam) == IDC_AUTODAYLIGHT && HIWORD(wParam) == BN_CLICKED))
324 {
325 /* Enable the 'Apply' button */
326 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
327 }
328 break;
329
330 case WM_DESTROY:
331 DestroyTimeZoneList();
332 DeleteObject(hBitmap);
333 break;
334
335 case WM_NOTIFY:
336 {
337 LPNMHDR lpnm = (LPNMHDR)lParam;
338
339 switch (lpnm->code)
340 {
341 case PSN_APPLY:
342 {
343 SetAutoDaylight(SendDlgItemMessage(hwndDlg, IDC_AUTODAYLIGHT,
344 BM_GETCHECK, 0, 0) != BST_UNCHECKED);
345 SetLocalTimeZone(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
346 SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
347 return TRUE;
348 }
349
350 default:
351 break;
352 }
353 }
354 break;
355 }
356
357 return FALSE;
358}