xref: /reactos/dll/cpl/timedate/timezone.c (revision b3194e32)
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 
15 typedef 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 
26 static HBITMAP hBitmap = NULL;
27 static int cxSource, cySource;
28 
29 PTIMEZONE_ENTRY TimeZoneListHead = NULL;
30 PTIMEZONE_ENTRY TimeZoneListTail = NULL;
31 
32 static
33 PTIMEZONE_ENTRY
34 GetLargerTimeZoneEntry(
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 
58 static LONG
59 RetrieveTimeZone(
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 
138 static VOID
139 CreateTimeZoneList(VOID)
140 {
141     EnumerateTimeZoneList(RetrieveTimeZone, NULL);
142 }
143 
144 static VOID
145 DestroyTimeZoneList(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 
166 static VOID
167 ShowTimeZoneList(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 
225 static VOID
226 SetLocalTimeZone(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 */
271 INT_PTR CALLBACK
272 TimeZonePageProc(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 }
359