xref: /reactos/dll/cpl/timedate/timezone.c (revision 09dde2cf)
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     BOOL bDoAdvancedTest;
172     DWORD dwIndex;
173     DWORD i;
174 
175     GetTimeZoneInformation(&TimeZoneInfo);
176     bDoAdvancedTest = (!*TimeZoneInfo.StandardName);
177 
178     dwIndex = 0;
179     i = 0;
180     Entry = TimeZoneListHead;
181     while (Entry != NULL)
182     {
183         SendMessageW(hwnd,
184                      CB_ADDSTRING,
185                      0,
186                      (LPARAM)Entry->Description);
187 
188         if ( (!bDoAdvancedTest && *Entry->StandardName &&
189                 wcscmp(Entry->StandardName, TimeZoneInfo.StandardName) == 0) ||
190              ( (Entry->TimezoneInfo.Bias == TimeZoneInfo.Bias) &&
191                (Entry->TimezoneInfo.StandardBias == TimeZoneInfo.StandardBias) &&
192                (Entry->TimezoneInfo.DaylightBias == TimeZoneInfo.DaylightBias) &&
193                (memcmp(&Entry->TimezoneInfo.StandardDate, &TimeZoneInfo.StandardDate, sizeof(SYSTEMTIME)) == 0) &&
194                (memcmp(&Entry->TimezoneInfo.DaylightDate, &TimeZoneInfo.DaylightDate, sizeof(SYSTEMTIME)) == 0) ) )
195         {
196             dwIndex = i;
197         }
198 
199         i++;
200         Entry = Entry->Next;
201     }
202 
203     SendMessageW(hwnd,
204                  CB_SETCURSEL,
205                  (WPARAM)dwIndex,
206                  0);
207 }
208 
209 
210 static VOID
211 SetLocalTimeZone(HWND hwnd)
212 {
213     TIME_ZONE_INFORMATION TimeZoneInformation;
214     PTIMEZONE_ENTRY Entry;
215     DWORD dwIndex;
216     DWORD i;
217 
218     dwIndex = (DWORD)SendMessageW(hwnd,
219                                   CB_GETCURSEL,
220                                   0,
221                                   0);
222 
223     i = 0;
224     Entry = TimeZoneListHead;
225     while (i < dwIndex)
226     {
227         if (Entry == NULL)
228             return;
229 
230         i++;
231         Entry = Entry->Next;
232     }
233 
234     wcscpy(TimeZoneInformation.StandardName,
235            Entry->StandardName);
236     wcscpy(TimeZoneInformation.DaylightName,
237            Entry->DaylightName);
238 
239     TimeZoneInformation.Bias = Entry->TimezoneInfo.Bias;
240     TimeZoneInformation.StandardBias = Entry->TimezoneInfo.StandardBias;
241     TimeZoneInformation.DaylightBias = Entry->TimezoneInfo.DaylightBias;
242 
243     memcpy(&TimeZoneInformation.StandardDate,
244            &Entry->TimezoneInfo.StandardDate,
245            sizeof(SYSTEMTIME));
246     memcpy(&TimeZoneInformation.DaylightDate,
247            &Entry->TimezoneInfo.DaylightDate,
248            sizeof(SYSTEMTIME));
249 
250     /* Set time zone information */
251     SetTimeZoneInformation(&TimeZoneInformation);
252 }
253 
254 
255 /* Property page dialog callback */
256 INT_PTR CALLBACK
257 TimeZonePageProc(HWND hwndDlg,
258                  UINT uMsg,
259                  WPARAM wParam,
260                  LPARAM lParam)
261 {
262     BITMAP bitmap;
263 
264     switch (uMsg)
265     {
266         case WM_INITDIALOG:
267         {
268             CreateTimeZoneList();
269             ShowTimeZoneList(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
270 
271             SendDlgItemMessage(hwndDlg, IDC_AUTODAYLIGHT, BM_SETCHECK,
272                                (WPARAM)(GetAutoDaylight() ? BST_CHECKED : BST_UNCHECKED), 0);
273 
274             hBitmap = LoadImageW(hApplet, MAKEINTRESOURCEW(IDC_WORLD), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
275             if (hBitmap != NULL)
276             {
277                 GetObjectW(hBitmap, sizeof(bitmap), &bitmap);
278 
279                 cxSource = bitmap.bmWidth;
280                 cySource = bitmap.bmHeight;
281             }
282             break;
283         }
284 
285         case WM_DRAWITEM:
286         {
287             LPDRAWITEMSTRUCT lpDrawItem;
288             lpDrawItem = (LPDRAWITEMSTRUCT)lParam;
289             if(lpDrawItem->CtlID == IDC_WORLD_BACKGROUND)
290             {
291                 HDC hdcMem;
292                 hdcMem = CreateCompatibleDC(lpDrawItem->hDC);
293                 if (hdcMem != NULL)
294                 {
295                     SelectObject(hdcMem, hBitmap);
296                     StretchBlt(lpDrawItem->hDC, lpDrawItem->rcItem.left, lpDrawItem->rcItem.top,
297                                lpDrawItem->rcItem.right - lpDrawItem->rcItem.left,
298                                lpDrawItem->rcItem.bottom - lpDrawItem->rcItem.top,
299                                hdcMem, 0, 0, cxSource, cySource, SRCCOPY);
300                     DeleteDC(hdcMem);
301                 }
302             }
303         }
304         break;
305 
306         case WM_COMMAND:
307             if ((LOWORD(wParam) == IDC_TIMEZONELIST && HIWORD(wParam) == CBN_SELCHANGE) ||
308                 (LOWORD(wParam) == IDC_AUTODAYLIGHT && HIWORD(wParam) == BN_CLICKED))
309             {
310                 /* Enable the 'Apply' button */
311                 PropSheet_Changed(GetParent(hwndDlg), hwndDlg);
312             }
313             break;
314 
315         case WM_DESTROY:
316             DestroyTimeZoneList();
317             DeleteObject(hBitmap);
318             break;
319 
320         case WM_NOTIFY:
321         {
322             LPNMHDR lpnm = (LPNMHDR)lParam;
323 
324             switch (lpnm->code)
325             {
326                 case PSN_APPLY:
327                 {
328                     SetAutoDaylight(SendDlgItemMessage(hwndDlg, IDC_AUTODAYLIGHT,
329                                                        BM_GETCHECK, 0, 0) != BST_UNCHECKED);
330                     SetLocalTimeZone(GetDlgItem(hwndDlg, IDC_TIMEZONELIST));
331                     SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_NOERROR);
332                     return TRUE;
333                 }
334 
335                 default:
336                     break;
337             }
338         }
339         break;
340     }
341 
342   return FALSE;
343 }
344