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