1a10cf76eSAxel Dörfler /* 24ae5a452SAdrien Destugues * Copyright 2004-2010, Haiku, Inc. All Rights Reserved. 353d9356eSStephan Aßmus * Distributed under the terms of the MIT License. 453d9356eSStephan Aßmus * 553d9356eSStephan Aßmus * Authors: 600826781SKarsten Heimrich * Mike Berg <mike@berg-net.us> 753d9356eSStephan Aßmus * Julun <host.haiku@gmx.de> 8310930fdSStephan Aßmus * Philippe Saint-Pierre <stpere@gmail.com> 94ae5a452SAdrien Destugues * Adrien Destugues <pulkomandy@pulkomandy.ath.cx> 1099d2aa98SOliver Tappe * Oliver Tappe <zooey@hirschkaefer.de> 1153d9356eSStephan Aßmus */ 1242b3a11fSStephan Aßmus 13a10cf76eSAxel Dörfler 1442b3a11fSStephan Aßmus #include "ZoneView.h" 15a10cf76eSAxel Dörfler 16317bd7ddSAxel Dörfler #include <stdlib.h> 1742b3a11fSStephan Aßmus 18750e57b8SOliver Tappe #include <map> 1938ac8defSOliver Tappe #include <new> 2038ac8defSOliver Tappe 2138ac8defSOliver Tappe #include <AutoDeleter.h> 2242b3a11fSStephan Aßmus #include <Button.h> 23*825d265eSAdrien Destugues #include <Catalog.h> 2499d2aa98SOliver Tappe #include <Collator.h> 253740f52dSOliver Tappe #include <Country.h> 26a10cf76eSAxel Dörfler #include <Directory.h> 27a10cf76eSAxel Dörfler #include <Entry.h> 2885b54438SOliver Tappe #include <File.h> 29a10cf76eSAxel Dörfler #include <FindDirectory.h> 3042b3a11fSStephan Aßmus #include <ListItem.h> 314ae5a452SAdrien Destugues #include <Locale.h> 3238ac8defSOliver Tappe #include <MutableLocaleRoster.h> 334ae5a452SAdrien Destugues #include <OutlineListView.h> 3485b54438SOliver Tappe #include <Path.h> 35a10cf76eSAxel Dörfler #include <ScrollView.h> 36a10cf76eSAxel Dörfler #include <StorageDefs.h> 37a10cf76eSAxel Dörfler #include <String.h> 384ae5a452SAdrien Destugues #include <TimeZone.h> 39a10cf76eSAxel Dörfler #include <View.h> 40310930fdSStephan Aßmus #include <Window.h> 41a10cf76eSAxel Dörfler 42317bd7ddSAxel Dörfler #include <syscalls.h> 43750e57b8SOliver Tappe #include <ToolTip.h> 44a10cf76eSAxel Dörfler 4538ac8defSOliver Tappe #include <unicode/datefmt.h> 4638ac8defSOliver Tappe #include <unicode/utmscale.h> 4738ac8defSOliver Tappe #include <ICUWrapper.h> 4838ac8defSOliver Tappe 49317bd7ddSAxel Dörfler #include "TimeMessages.h" 504ae5a452SAdrien Destugues #include "TimeZoneListItem.h" 51317bd7ddSAxel Dörfler #include "TZDisplay.h" 52317bd7ddSAxel Dörfler #include "TimeWindow.h" 53a10cf76eSAxel Dörfler 54a10cf76eSAxel Dörfler 55*825d265eSAdrien Destugues #undef B_TRANSLATE_CONTEXT 56*825d265eSAdrien Destugues #define B_TRANSLATE_CONTEXT "Time" 57*825d265eSAdrien Destugues 58*825d265eSAdrien Destugues 5925dc253dSIngo Weinhold using BPrivate::MutableLocaleRoster; 6038ac8defSOliver Tappe using BPrivate::ObjectDeleter; 6199d2aa98SOliver Tappe 6299d2aa98SOliver Tappe 63750e57b8SOliver Tappe struct TimeZoneItemLess { 64750e57b8SOliver Tappe bool operator()(const BString& first, const BString& second) 65750e57b8SOliver Tappe { 66447b7fdeSOliver Tappe // sort anything starting with '<' behind anything else 67447b7fdeSOliver Tappe if (first.ByteAt(0) == '<') { 68447b7fdeSOliver Tappe if (second.ByteAt(0) != '<') 69447b7fdeSOliver Tappe return false; 70447b7fdeSOliver Tappe } else if (second.ByteAt(0) == '<') 71447b7fdeSOliver Tappe return true; 72750e57b8SOliver Tappe return fCollator.Compare(first.String(), second.String()) < 0; 73750e57b8SOliver Tappe } 74750e57b8SOliver Tappe private: 75750e57b8SOliver Tappe BCollator fCollator; 76750e57b8SOliver Tappe }; 77750e57b8SOliver Tappe 78750e57b8SOliver Tappe 79750e57b8SOliver Tappe 8085b69a94SKarsten Heimrich TimeZoneView::TimeZoneView(BRect frame) 8199d2aa98SOliver Tappe : 8299d2aa98SOliver Tappe BView(frame, "timeZoneView", B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE_JUMP), 83750e57b8SOliver Tappe fToolTip(NULL), 8438ac8defSOliver Tappe fCurrentZoneItem(NULL), 8538ac8defSOliver Tappe fOldZoneItem(NULL), 8699d2aa98SOliver Tappe fInitialized(false) 87a10cf76eSAxel Dörfler { 886d6408d6SOliver Tappe _InitView(); 89a10cf76eSAxel Dörfler } 90a10cf76eSAxel Dörfler 91a10cf76eSAxel Dörfler 92310930fdSStephan Aßmus bool 93310930fdSStephan Aßmus TimeZoneView::CheckCanRevert() 94310930fdSStephan Aßmus { 9538ac8defSOliver Tappe return fCurrentZoneItem != fOldZoneItem; 96310930fdSStephan Aßmus } 97310930fdSStephan Aßmus 98310930fdSStephan Aßmus 9985b69a94SKarsten Heimrich TimeZoneView::~TimeZoneView() 100a10cf76eSAxel Dörfler { 1013740f52dSOliver Tappe if (fToolTip != NULL) 10200a2914fSRene Gollent fToolTip->ReleaseReference(); 103a10cf76eSAxel Dörfler } 104a10cf76eSAxel Dörfler 105a10cf76eSAxel Dörfler 106a10cf76eSAxel Dörfler void 10785b69a94SKarsten Heimrich TimeZoneView::AttachedToWindow() 108a10cf76eSAxel Dörfler { 109447b7fdeSOliver Tappe BView::AttachedToWindow(); 110a10cf76eSAxel Dörfler if (Parent()) 111a10cf76eSAxel Dörfler SetViewColor(Parent()->ViewColor()); 112447b7fdeSOliver Tappe } 113a10cf76eSAxel Dörfler 114447b7fdeSOliver Tappe 115447b7fdeSOliver Tappe void 116447b7fdeSOliver Tappe TimeZoneView::AllAttached() 117447b7fdeSOliver Tappe { 118447b7fdeSOliver Tappe BView::AllAttached(); 11985b69a94SKarsten Heimrich if (!fInitialized) { 12085b69a94SKarsten Heimrich fInitialized = true; 12185b69a94SKarsten Heimrich 12242b3a11fSStephan Aßmus fSetZone->SetTarget(this); 12338ac8defSOliver Tappe fZoneList->SetTarget(this); 12442b3a11fSStephan Aßmus 125a10cf76eSAxel Dörfler // update displays 12638ac8defSOliver Tappe if (fCurrentZoneItem != NULL) { 127447b7fdeSOliver Tappe fZoneList->Select(fZoneList->IndexOf(fCurrentZoneItem)); 12838ac8defSOliver Tappe fCurrent->SetText(fCurrentZoneItem->Text()); 129a10cf76eSAxel Dörfler } 130447b7fdeSOliver Tappe } 13138ac8defSOliver Tappe fZoneList->ScrollToSelection(); 132a10cf76eSAxel Dörfler } 133a10cf76eSAxel Dörfler 134a10cf76eSAxel Dörfler 135a10cf76eSAxel Dörfler void 13685b69a94SKarsten Heimrich TimeZoneView::MessageReceived(BMessage* message) 137a10cf76eSAxel Dörfler { 138a10cf76eSAxel Dörfler switch (message->what) { 139a10cf76eSAxel Dörfler case B_OBSERVER_NOTICE_CHANGE: 140317bd7ddSAxel Dörfler { 141317bd7ddSAxel Dörfler int32 change; 142a10cf76eSAxel Dörfler message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change); 143a10cf76eSAxel Dörfler switch(change) { 144a10cf76eSAxel Dörfler case H_TM_CHANGED: 1456d6408d6SOliver Tappe _UpdateDateTime(message); 146a10cf76eSAxel Dörfler break; 147a10cf76eSAxel Dörfler 148a10cf76eSAxel Dörfler default: 149a10cf76eSAxel Dörfler BView::MessageReceived(message); 150a10cf76eSAxel Dörfler break; 151a10cf76eSAxel Dörfler } 152a10cf76eSAxel Dörfler break; 153317bd7ddSAxel Dörfler } 154317bd7ddSAxel Dörfler 155447b7fdeSOliver Tappe case H_CITY_CHANGED: 156447b7fdeSOliver Tappe _UpdatePreview(); 157447b7fdeSOliver Tappe break; 158447b7fdeSOliver Tappe 159a10cf76eSAxel Dörfler case H_SET_TIME_ZONE: 160310930fdSStephan Aßmus { 16138ac8defSOliver Tappe _SetSystemTimeZone(); 162310930fdSStephan Aßmus ((TTimeWindow*)Window())->SetRevertStatus(); 163310930fdSStephan Aßmus break; 164310930fdSStephan Aßmus } 165310930fdSStephan Aßmus 166310930fdSStephan Aßmus case kMsgRevert: 167310930fdSStephan Aßmus _Revert(); 168a10cf76eSAxel Dörfler break; 169a10cf76eSAxel Dörfler 170447b7fdeSOliver Tappe case kRTCUpdate: 171447b7fdeSOliver Tappe _UpdateCurrent(); 17238ac8defSOliver Tappe _UpdatePreview(); 173a10cf76eSAxel Dörfler break; 174a10cf76eSAxel Dörfler 175a10cf76eSAxel Dörfler default: 176a10cf76eSAxel Dörfler BView::MessageReceived(message); 177a10cf76eSAxel Dörfler break; 178a10cf76eSAxel Dörfler } 179a10cf76eSAxel Dörfler } 180a10cf76eSAxel Dörfler 181a10cf76eSAxel Dörfler 182750e57b8SOliver Tappe bool 183750e57b8SOliver Tappe TimeZoneView::GetToolTipAt(BPoint point, BToolTip** _tip) 184750e57b8SOliver Tappe { 185750e57b8SOliver Tappe TimeZoneListItem* item = static_cast<TimeZoneListItem*>( 186750e57b8SOliver Tappe fZoneList->ItemAt(fZoneList->IndexOf(point))); 187750e57b8SOliver Tappe if (item == NULL || !item->HasTimeZone()) 188750e57b8SOliver Tappe return false; 189750e57b8SOliver Tappe 1903740f52dSOliver Tappe BString nowInTimeZone; 1913740f52dSOliver Tappe time_t now = time(NULL); 19225dc253dSIngo Weinhold BLocale::Default()->FormatTime(&nowInTimeZone, now, B_SHORT_TIME_FORMAT, 1936fd2f4a0SOliver Tappe &item->TimeZone()); 1943740f52dSOliver Tappe 1953740f52dSOliver Tappe BString dateInTimeZone; 19625dc253dSIngo Weinhold BLocale::Default()->FormatDate(&dateInTimeZone, now, B_SHORT_DATE_FORMAT, 1976fd2f4a0SOliver Tappe &item->TimeZone()); 1983740f52dSOliver Tappe 199750e57b8SOliver Tappe BString toolTip = item->Text(); 200750e57b8SOliver Tappe toolTip << '\n' << item->TimeZone().ShortName() << " / " 201447b7fdeSOliver Tappe << item->TimeZone().ShortDaylightSavingName() 202*825d265eSAdrien Destugues << B_TRANSLATE("\nNow: ") << nowInTimeZone 203*825d265eSAdrien Destugues << " (" << dateInTimeZone << ')'; 204750e57b8SOliver Tappe 2053740f52dSOliver Tappe if (fToolTip != NULL) 2063740f52dSOliver Tappe fToolTip->ReleaseReference(); 207750e57b8SOliver Tappe fToolTip = new (std::nothrow) BTextToolTip(toolTip.String()); 208750e57b8SOliver Tappe if (fToolTip == NULL) 209750e57b8SOliver Tappe return false; 210750e57b8SOliver Tappe 211750e57b8SOliver Tappe *_tip = fToolTip; 212750e57b8SOliver Tappe 213750e57b8SOliver Tappe return true; 214750e57b8SOliver Tappe } 215750e57b8SOliver Tappe 216750e57b8SOliver Tappe 217a10cf76eSAxel Dörfler void 2186d6408d6SOliver Tappe TimeZoneView::_UpdateDateTime(BMessage* message) 219a10cf76eSAxel Dörfler { 22038ac8defSOliver Tappe // only need to update once every minute 22142b3a11fSStephan Aßmus int32 minute; 22238ac8defSOliver Tappe if (message->FindInt32("minute", &minute) == B_OK) { 22338ac8defSOliver Tappe if (fLastUpdateMinute != minute) { 22438ac8defSOliver Tappe _UpdateCurrent(); 22538ac8defSOliver Tappe _UpdatePreview(); 226a10cf76eSAxel Dörfler 22738ac8defSOliver Tappe fLastUpdateMinute = minute; 228a10cf76eSAxel Dörfler } 229a10cf76eSAxel Dörfler } 230a10cf76eSAxel Dörfler } 231a10cf76eSAxel Dörfler 232a10cf76eSAxel Dörfler 233a10cf76eSAxel Dörfler void 2346d6408d6SOliver Tappe TimeZoneView::_InitView() 235a10cf76eSAxel Dörfler { 23642b3a11fSStephan Aßmus // left side 23742b3a11fSStephan Aßmus BRect frameLeft(Bounds()); 23885b69a94SKarsten Heimrich frameLeft.right = frameLeft.Width() / 2.0; 23942b3a11fSStephan Aßmus frameLeft.InsetBy(10.0f, 10.0f); 240a10cf76eSAxel Dörfler 241a10cf76eSAxel Dörfler // City Listing 24238ac8defSOliver Tappe fZoneList = new BOutlineListView(frameLeft, "cityList", 2434ae5a452SAdrien Destugues B_SINGLE_SELECTION_LIST); 24438ac8defSOliver Tappe fZoneList->SetSelectionMessage(new BMessage(H_CITY_CHANGED)); 24538ac8defSOliver Tappe fZoneList->SetInvocationMessage(new BMessage(H_SET_TIME_ZONE)); 246a10cf76eSAxel Dörfler 247750e57b8SOliver Tappe _BuildZoneMenu(); 2484ae5a452SAdrien Destugues 24938ac8defSOliver Tappe BScrollView* scrollList = new BScrollView("scrollList", fZoneList, 25042b3a11fSStephan Aßmus B_FOLLOW_ALL, 0, false, true); 251a10cf76eSAxel Dörfler AddChild(scrollList); 252a10cf76eSAxel Dörfler 25342b3a11fSStephan Aßmus // right side 25442b3a11fSStephan Aßmus BRect frameRight(Bounds()); 25585b69a94SKarsten Heimrich frameRight.left = frameRight.Width() / 2.0; 25642b3a11fSStephan Aßmus frameRight.InsetBy(10.0f, 10.0f); 25742b3a11fSStephan Aßmus frameRight.top = frameLeft.top; 258a10cf76eSAxel Dörfler 259a10cf76eSAxel Dörfler // Time Displays 260*825d265eSAdrien Destugues fCurrent = new TTZDisplay(frameRight, "currentTime", 261*825d265eSAdrien Destugues B_TRANSLATE("Current time:")); 26242b3a11fSStephan Aßmus AddChild(fCurrent); 26342b3a11fSStephan Aßmus fCurrent->ResizeToPreferred(); 264a10cf76eSAxel Dörfler 26585b69a94SKarsten Heimrich frameRight.top = fCurrent->Frame().bottom + 10.0; 266*825d265eSAdrien Destugues fPreview = new TTZDisplay(frameRight, "previewTime", 267*825d265eSAdrien Destugues B_TRANSLATE("Preview time:")); 26842b3a11fSStephan Aßmus AddChild(fPreview); 26942b3a11fSStephan Aßmus fPreview->ResizeToPreferred(); 270a10cf76eSAxel Dörfler 271a10cf76eSAxel Dörfler // set button 272*825d265eSAdrien Destugues fSetZone = new BButton(frameRight, "setTimeZone", 273*825d265eSAdrien Destugues B_TRANSLATE("Set time zone"), 27485b69a94SKarsten Heimrich new BMessage(H_SET_TIME_ZONE)); 27542b3a11fSStephan Aßmus AddChild(fSetZone); 27642b3a11fSStephan Aßmus fSetZone->SetEnabled(false); 27742b3a11fSStephan Aßmus fSetZone->ResizeToPreferred(); 278a10cf76eSAxel Dörfler 27942b3a11fSStephan Aßmus fSetZone->MoveTo(frameRight.right - fSetZone->Bounds().Width(), 28042b3a11fSStephan Aßmus scrollList->Frame().bottom - fSetZone->Bounds().Height()); 281a10cf76eSAxel Dörfler } 282a10cf76eSAxel Dörfler 283a10cf76eSAxel Dörfler 284a10cf76eSAxel Dörfler void 285750e57b8SOliver Tappe TimeZoneView::_BuildZoneMenu() 286a10cf76eSAxel Dörfler { 287447b7fdeSOliver Tappe BTimeZone defaultTimeZone; 28825dc253dSIngo Weinhold BLocaleRoster::Default()->GetDefaultTimeZone(&defaultTimeZone); 28999d2aa98SOliver Tappe 290b7f60965SOliver Tappe BLanguage language; 29125dc253dSIngo Weinhold BLocale::Default()->GetLanguage(&language); 292a10cf76eSAxel Dörfler 2933740f52dSOliver Tappe BMessage countryList; 29425dc253dSIngo Weinhold BLocaleRoster::Default()->GetAvailableCountries(&countryList); 2956f43fabaSOliver Tappe countryList.AddString("country", ""); 2963740f52dSOliver Tappe 297447b7fdeSOliver Tappe /* 298447b7fdeSOliver Tappe * Group timezones by regions, but filter out unwanted (duplicate) regions 299447b7fdeSOliver Tappe * and add an additional region with generic GMT-offset timezones at the end 300447b7fdeSOliver Tappe */ 301750e57b8SOliver Tappe typedef std::map<BString, TimeZoneListItem*, TimeZoneItemLess> ZoneItemMap; 302750e57b8SOliver Tappe ZoneItemMap zoneMap; 303*825d265eSAdrien Destugues const char* kOtherRegion = B_TRANSLATE("<Other>"); 304447b7fdeSOliver Tappe const char* kSupportedRegions[] = { 305750e57b8SOliver Tappe "Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic", 306447b7fdeSOliver Tappe "Australia", "Europe", "Indian", "Pacific", kOtherRegion, NULL 307750e57b8SOliver Tappe }; 308447b7fdeSOliver Tappe for (const char** region = kSupportedRegions; *region != NULL; ++region) 309750e57b8SOliver Tappe zoneMap[*region] = NULL; 310a10cf76eSAxel Dörfler 3113740f52dSOliver Tappe BString countryCode; 3126f43fabaSOliver Tappe for (int c = 0; countryList.FindString("country", c, &countryCode) 3133740f52dSOliver Tappe == B_OK; c++) { 3146fd2f4a0SOliver Tappe BCountry country(countryCode); 3153740f52dSOliver Tappe BString countryName; 3163740f52dSOliver Tappe country.GetName(countryName); 3173740f52dSOliver Tappe 3183740f52dSOliver Tappe // Now list the timezones for this country 3193740f52dSOliver Tappe BMessage zoneList; 32025dc253dSIngo Weinhold BLocaleRoster::Default()->GetAvailableTimeZonesForCountry(&zoneList, 3213740f52dSOliver Tappe countryCode.Length() == 0 ? NULL : countryCode.String()); 3223740f52dSOliver Tappe 3233740f52dSOliver Tappe int32 count = 0; 3243740f52dSOliver Tappe type_code dummy; 3253740f52dSOliver Tappe zoneList.GetInfo("timeZone", &dummy, &count); 3263740f52dSOliver Tappe 327750e57b8SOliver Tappe BString zoneID; 3283740f52dSOliver Tappe for (int tz = 0; zoneList.FindString("timeZone", tz, &zoneID) == B_OK; 3293740f52dSOliver Tappe tz++) { 330750e57b8SOliver Tappe int32 slashPos = zoneID.FindFirst('/'); 331a10cf76eSAxel Dörfler 3323740f52dSOliver Tappe // ignore any "global" timezones, as those are just aliases of 3333740f52dSOliver Tappe // regional ones 334750e57b8SOliver Tappe if (slashPos <= 0) 335750e57b8SOliver Tappe continue; 336750e57b8SOliver Tappe 337750e57b8SOliver Tappe BString region(zoneID, slashPos); 338750e57b8SOliver Tappe 339*825d265eSAdrien Destugues if (region == B_TRANSLATE("Etc")) 340447b7fdeSOliver Tappe region = kOtherRegion; 3413740f52dSOliver Tappe else if (countryName.Length() == 0) { 3423740f52dSOliver Tappe // skip global timezones from other regions, we are just 3433740f52dSOliver Tappe // interested in the generic GMT-based ones under "Etc/" 3443740f52dSOliver Tappe continue; 3453740f52dSOliver Tappe } 346447b7fdeSOliver Tappe 3473740f52dSOliver Tappe 3483740f52dSOliver Tappe // just accept timezones from "proper" regions, others are aliases 349750e57b8SOliver Tappe ZoneItemMap::iterator regionIter = zoneMap.find(region); 350750e57b8SOliver Tappe if (regionIter == zoneMap.end()) 351750e57b8SOliver Tappe continue; 352750e57b8SOliver Tappe 3533740f52dSOliver Tappe BString fullCountryID = region; 3543740f52dSOliver Tappe if (countryName != region) 3553740f52dSOliver Tappe fullCountryID << "/" << countryName; 3563740f52dSOliver Tappe 357750e57b8SOliver Tappe TimeZoneListItem* regionItem = regionIter->second; 358750e57b8SOliver Tappe if (regionItem == NULL) { 359750e57b8SOliver Tappe regionItem = new TimeZoneListItem(region, NULL, NULL); 360750e57b8SOliver Tappe regionItem->SetOutlineLevel(0); 361750e57b8SOliver Tappe zoneMap[region] = regionItem; 362750e57b8SOliver Tappe } 363750e57b8SOliver Tappe 364b7f60965SOliver Tappe BTimeZone* timeZone = new BTimeZone(zoneID, &language); 365750e57b8SOliver Tappe BString tzName = timeZone->Name(); 366750e57b8SOliver Tappe if (tzName == "GMT+00:00") 367750e57b8SOliver Tappe tzName = "GMT"; 3683740f52dSOliver Tappe 369750e57b8SOliver Tappe int32 openParenthesisPos = tzName.FindFirst('('); 370750e57b8SOliver Tappe if (openParenthesisPos >= 0) { 371750e57b8SOliver Tappe tzName.Remove(0, openParenthesisPos + 1); 372750e57b8SOliver Tappe int32 closeParenthesisPos = tzName.FindLast(')'); 373750e57b8SOliver Tappe if (closeParenthesisPos >= 0) 374750e57b8SOliver Tappe tzName.Truncate(closeParenthesisPos); 375750e57b8SOliver Tappe } 376750e57b8SOliver Tappe BString fullZoneID = fullCountryID; 377750e57b8SOliver Tappe fullZoneID << "/" << tzName; 378750e57b8SOliver Tappe 379750e57b8SOliver Tappe // skip duplicates 380750e57b8SOliver Tappe ZoneItemMap::iterator zoneIter = zoneMap.find(fullZoneID); 381750e57b8SOliver Tappe if (zoneIter != zoneMap.end()) { 382750e57b8SOliver Tappe delete timeZone; 383750e57b8SOliver Tappe continue; 384750e57b8SOliver Tappe } 385750e57b8SOliver Tappe 386750e57b8SOliver Tappe TimeZoneListItem* countryItem = NULL; 3873740f52dSOliver Tappe TimeZoneListItem* zoneItem = NULL; 3883740f52dSOliver Tappe if (count > 1 && countryName.Length() > 0) { 389750e57b8SOliver Tappe ZoneItemMap::iterator countryIter = zoneMap.find(fullCountryID); 390750e57b8SOliver Tappe if (countryIter == zoneMap.end()) { 3913740f52dSOliver Tappe countryItem = new TimeZoneListItem(countryName, NULL, NULL); 392750e57b8SOliver Tappe countryItem->SetOutlineLevel(1); 393750e57b8SOliver Tappe zoneMap[fullCountryID] = countryItem; 394750e57b8SOliver Tappe } else 395750e57b8SOliver Tappe countryItem = countryIter->second; 3964ae5a452SAdrien Destugues 3973740f52dSOliver Tappe zoneItem = new TimeZoneListItem(tzName, NULL, timeZone); 3983740f52dSOliver Tappe zoneItem->SetOutlineLevel(2); 3993740f52dSOliver Tappe } else { 4003740f52dSOliver Tappe BString& name = countryName.Length() > 0 ? countryName : tzName; 4013740f52dSOliver Tappe zoneItem = new TimeZoneListItem(name, NULL, timeZone); 4023740f52dSOliver Tappe zoneItem->SetOutlineLevel(1); 4033740f52dSOliver Tappe } 404750e57b8SOliver Tappe zoneMap[fullZoneID] = zoneItem; 405750e57b8SOliver Tappe 406750e57b8SOliver Tappe if (timeZone->ID() == defaultTimeZone.ID()) { 407750e57b8SOliver Tappe fCurrentZoneItem = zoneItem; 408750e57b8SOliver Tappe if (countryItem != NULL) 40999d2aa98SOliver Tappe countryItem->SetExpanded(true); 410750e57b8SOliver Tappe regionItem->SetExpanded(true); 41199d2aa98SOliver Tappe } 41242b3a11fSStephan Aßmus } 4133740f52dSOliver Tappe } 41499d2aa98SOliver Tappe 41538ac8defSOliver Tappe fOldZoneItem = fCurrentZoneItem; 41699d2aa98SOliver Tappe 417750e57b8SOliver Tappe ZoneItemMap::iterator zoneIter; 4183740f52dSOliver Tappe bool lastWasCountryItem = false; 4193740f52dSOliver Tappe TimeZoneListItem* lastCountryItem = NULL; 4203740f52dSOliver Tappe for (zoneIter = zoneMap.begin(); zoneIter != zoneMap.end(); ++zoneIter) { 4213740f52dSOliver Tappe if (zoneIter->second->OutlineLevel() == 2 && lastWasCountryItem) { 4223740f52dSOliver Tappe /* Some countries (e.g. Spain and Chile) have their timezones 4233740f52dSOliver Tappe * spread across different regions. As a result, there might still 4243740f52dSOliver Tappe * be country items with only one timezone below them. We manually 4253740f52dSOliver Tappe * filter those country items here. 4263740f52dSOliver Tappe */ 4273740f52dSOliver Tappe ZoneItemMap::iterator next = zoneIter; 4283740f52dSOliver Tappe ++next; 4293740f52dSOliver Tappe if (next != zoneMap.end() && next->second->OutlineLevel() != 2) { 4303740f52dSOliver Tappe fZoneList->RemoveItem(lastCountryItem); 4313740f52dSOliver Tappe zoneIter->second->SetText(lastCountryItem->Text()); 4323740f52dSOliver Tappe zoneIter->second->SetOutlineLevel(1); 4333740f52dSOliver Tappe delete lastCountryItem; 4343740f52dSOliver Tappe } 4353740f52dSOliver Tappe } 4363740f52dSOliver Tappe 437750e57b8SOliver Tappe fZoneList->AddItem(zoneIter->second); 4383740f52dSOliver Tappe if (zoneIter->second->OutlineLevel() == 1) { 4393740f52dSOliver Tappe lastWasCountryItem = true; 4403740f52dSOliver Tappe lastCountryItem = zoneIter->second; 4413740f52dSOliver Tappe } else 4423740f52dSOliver Tappe lastWasCountryItem = false; 4433740f52dSOliver Tappe } 44499d2aa98SOliver Tappe } 445750e57b8SOliver Tappe 446750e57b8SOliver Tappe 447750e57b8SOliver Tappe void 448750e57b8SOliver Tappe TimeZoneView::_Revert() 449750e57b8SOliver Tappe { 450750e57b8SOliver Tappe fCurrentZoneItem = fOldZoneItem; 451750e57b8SOliver Tappe 452750e57b8SOliver Tappe if (fCurrentZoneItem != NULL) { 453750e57b8SOliver Tappe int32 currentZoneIndex = fZoneList->IndexOf(fCurrentZoneItem); 454750e57b8SOliver Tappe fZoneList->Select(currentZoneIndex); 455750e57b8SOliver Tappe } else 456750e57b8SOliver Tappe fZoneList->DeselectAll(); 457750e57b8SOliver Tappe fZoneList->ScrollToSelection(); 458750e57b8SOliver Tappe 459750e57b8SOliver Tappe _SetSystemTimeZone(); 460750e57b8SOliver Tappe _UpdatePreview(); 461750e57b8SOliver Tappe _UpdateCurrent(); 462a10cf76eSAxel Dörfler } 463a10cf76eSAxel Dörfler 464a10cf76eSAxel Dörfler 465a10cf76eSAxel Dörfler void 46638ac8defSOliver Tappe TimeZoneView::_UpdatePreview() 467a10cf76eSAxel Dörfler { 46838ac8defSOliver Tappe int32 selection = fZoneList->CurrentSelection(); 469750e57b8SOliver Tappe TimeZoneListItem* item 470750e57b8SOliver Tappe = selection < 0 471750e57b8SOliver Tappe ? NULL 472750e57b8SOliver Tappe : (TimeZoneListItem*)fZoneList->ItemAt(selection); 473a10cf76eSAxel Dörfler 474750e57b8SOliver Tappe if (item == NULL || !item->HasTimeZone()) { 475750e57b8SOliver Tappe fPreview->SetText(""); 476750e57b8SOliver Tappe fPreview->SetTime(""); 47738ac8defSOliver Tappe return; 478750e57b8SOliver Tappe } 479a10cf76eSAxel Dörfler 480447b7fdeSOliver Tappe BString timeString = _FormatTime(item->TimeZone()); 48138ac8defSOliver Tappe fPreview->SetText(item->Text()); 48238ac8defSOliver Tappe fPreview->SetTime(timeString.String()); 483a10cf76eSAxel Dörfler 48438ac8defSOliver Tappe fSetZone->SetEnabled((strcmp(fCurrent->Text(), item->Text()) != 0)); 485a10cf76eSAxel Dörfler } 486a10cf76eSAxel Dörfler 487a10cf76eSAxel Dörfler 488a10cf76eSAxel Dörfler void 48938ac8defSOliver Tappe TimeZoneView::_UpdateCurrent() 490a10cf76eSAxel Dörfler { 49138ac8defSOliver Tappe if (fCurrentZoneItem == NULL) 49238ac8defSOliver Tappe return; 4931855b44cSAdrien Destugues 494447b7fdeSOliver Tappe BString timeString = _FormatTime(fCurrentZoneItem->TimeZone()); 49538ac8defSOliver Tappe fCurrent->SetText(fCurrentZoneItem->Text()); 49638ac8defSOliver Tappe fCurrent->SetTime(timeString.String()); 497a10cf76eSAxel Dörfler } 498a10cf76eSAxel Dörfler 49938ac8defSOliver Tappe 50038ac8defSOliver Tappe void 50138ac8defSOliver Tappe TimeZoneView::_SetSystemTimeZone() 50238ac8defSOliver Tappe { 50338ac8defSOliver Tappe /* Set sytem timezone for all different API levels. How to do this? 50438ac8defSOliver Tappe * 1) tell locale-roster about new default timezone 50585b54438SOliver Tappe * 2) tell kernel about new timezone offset 50638ac8defSOliver Tappe */ 50738ac8defSOliver Tappe 50838ac8defSOliver Tappe int32 selection = fZoneList->CurrentSelection(); 50938ac8defSOliver Tappe if (selection < 0) 51038ac8defSOliver Tappe return; 51138ac8defSOliver Tappe 512447b7fdeSOliver Tappe TimeZoneListItem* item 513447b7fdeSOliver Tappe = static_cast<TimeZoneListItem*>(fZoneList->ItemAt(selection)); 514447b7fdeSOliver Tappe if (item == NULL || !item->HasTimeZone()) 515447b7fdeSOliver Tappe return; 516447b7fdeSOliver Tappe 517447b7fdeSOliver Tappe fCurrentZoneItem = item; 518447b7fdeSOliver Tappe const BTimeZone& timeZone = item->TimeZone(); 51938ac8defSOliver Tappe 52025dc253dSIngo Weinhold MutableLocaleRoster::Default()->SetDefaultTimeZone(timeZone); 52138ac8defSOliver Tappe 522750e57b8SOliver Tappe _kern_set_timezone(timeZone.OffsetFromGMT(), timeZone.ID().String(), 523750e57b8SOliver Tappe timeZone.ID().Length()); 52485b54438SOliver Tappe 52538ac8defSOliver Tappe fSetZone->SetEnabled(false); 52638ac8defSOliver Tappe fLastUpdateMinute = -1; 52738ac8defSOliver Tappe // just to trigger updating immediately 52838ac8defSOliver Tappe } 52938ac8defSOliver Tappe 53038ac8defSOliver Tappe 53138ac8defSOliver Tappe BString 5323740f52dSOliver Tappe TimeZoneView::_FormatTime(const BTimeZone& timeZone) 53338ac8defSOliver Tappe { 53438ac8defSOliver Tappe BString result; 53538ac8defSOliver Tappe 536a096bb65SOliver Tappe time_t now = time(NULL); 537a096bb65SOliver Tappe bool rtcIsGMT; 538a096bb65SOliver Tappe _kern_get_real_time_clock_is_gmt(&rtcIsGMT); 5393740f52dSOliver Tappe if (!rtcIsGMT) { 540447b7fdeSOliver Tappe int32 currentOffset 541447b7fdeSOliver Tappe = fCurrentZoneItem != NULL && fCurrentZoneItem->HasTimeZone() 542447b7fdeSOliver Tappe ? fCurrentZoneItem->OffsetFromGMT() 543447b7fdeSOliver Tappe : 0; 544447b7fdeSOliver Tappe now -= timeZone.OffsetFromGMT() - currentOffset; 545a096bb65SOliver Tappe } 54625dc253dSIngo Weinhold BLocale::Default()->FormatTime(&result, now, B_SHORT_TIME_FORMAT, 54725dc253dSIngo Weinhold &timeZone); 54838ac8defSOliver Tappe 54938ac8defSOliver Tappe return result; 55038ac8defSOliver Tappe } 551