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