1a10cf76eSAxel Dörfler /* 2f0e995c8SZiusudra * Copyright 2004-2012, 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> 11323ba9b7SRyan Leavengood * Hamish Morrison <hamish@lavabit.com> 1253d9356eSStephan Aßmus */ 1342b3a11fSStephan Aßmus 14a10cf76eSAxel Dörfler 1542b3a11fSStephan Aßmus #include "ZoneView.h" 16a10cf76eSAxel Dörfler 17317bd7ddSAxel Dörfler #include <stdlib.h> 18323ba9b7SRyan Leavengood #include <syscalls.h> 1942b3a11fSStephan Aßmus 20750e57b8SOliver Tappe #include <map> 2138ac8defSOliver Tappe #include <new> 22eb78be9bSOliver Tappe #include <vector> 2338ac8defSOliver Tappe 2438ac8defSOliver Tappe #include <AutoDeleter.h> 2542b3a11fSStephan Aßmus #include <Button.h> 26825d265eSAdrien Destugues #include <Catalog.h> 2799d2aa98SOliver Tappe #include <Collator.h> 28365839fbSRyan Leavengood #include <ControlLook.h> 293740f52dSOliver Tappe #include <Country.h> 30a10cf76eSAxel Dörfler #include <Directory.h> 31a10cf76eSAxel Dörfler #include <Entry.h> 3285b54438SOliver Tappe #include <File.h> 33a10cf76eSAxel Dörfler #include <FindDirectory.h> 3442b3a11fSStephan Aßmus #include <ListItem.h> 354ae5a452SAdrien Destugues #include <Locale.h> 3638ac8defSOliver Tappe #include <MutableLocaleRoster.h> 374ae5a452SAdrien Destugues #include <OutlineListView.h> 3885b54438SOliver Tappe #include <Path.h> 39c2f3ee3bSAdrien Destugues #include <RadioButton.h> 40a10cf76eSAxel Dörfler #include <ScrollView.h> 41a10cf76eSAxel Dörfler #include <StorageDefs.h> 42a10cf76eSAxel Dörfler #include <String.h> 43c2f3ee3bSAdrien Destugues #include <StringView.h> 444ae5a452SAdrien Destugues #include <TimeZone.h> 45a10cf76eSAxel Dörfler #include <View.h> 46310930fdSStephan Aßmus #include <Window.h> 47a10cf76eSAxel Dörfler 4838ac8defSOliver Tappe #include <unicode/datefmt.h> 4938ac8defSOliver Tappe #include <unicode/utmscale.h> 5038ac8defSOliver Tappe #include <ICUWrapper.h> 5138ac8defSOliver Tappe 52317bd7ddSAxel Dörfler #include "TimeMessages.h" 534ae5a452SAdrien Destugues #include "TimeZoneListItem.h" 54f0e995c8SZiusudra #include "TimeZoneListView.h" 55317bd7ddSAxel Dörfler #include "TZDisplay.h" 56a10cf76eSAxel Dörfler 57a10cf76eSAxel Dörfler 58546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT 59546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "Time" 60825d265eSAdrien Destugues 61825d265eSAdrien Destugues 6225dc253dSIngo Weinhold using BPrivate::MutableLocaleRoster; 6338ac8defSOliver Tappe using BPrivate::ObjectDeleter; 6499d2aa98SOliver Tappe 6599d2aa98SOliver Tappe 66750e57b8SOliver Tappe struct TimeZoneItemLess { 67750e57b8SOliver Tappe bool operator()(const BString& first, const BString& second) 68750e57b8SOliver Tappe { 69447b7fdeSOliver Tappe // sort anything starting with '<' behind anything else 70447b7fdeSOliver Tappe if (first.ByteAt(0) == '<') { 71447b7fdeSOliver Tappe if (second.ByteAt(0) != '<') 72447b7fdeSOliver Tappe return false; 73447b7fdeSOliver Tappe } else if (second.ByteAt(0) == '<') 74447b7fdeSOliver Tappe return true; 75750e57b8SOliver Tappe return fCollator.Compare(first.String(), second.String()) < 0; 76750e57b8SOliver Tappe } 77750e57b8SOliver Tappe private: 78750e57b8SOliver Tappe BCollator fCollator; 79750e57b8SOliver Tappe }; 80750e57b8SOliver Tappe 81750e57b8SOliver Tappe 82750e57b8SOliver Tappe 83323ba9b7SRyan Leavengood TimeZoneView::TimeZoneView(const char* name) 8499d2aa98SOliver Tappe : 85323ba9b7SRyan Leavengood BGroupView(name, B_HORIZONTAL, B_USE_DEFAULT_SPACING), 86c2f3ee3bSAdrien Destugues fGmtTime(NULL), 87c2f3ee3bSAdrien Destugues fUseGmtTime(false), 8838ac8defSOliver Tappe fCurrentZoneItem(NULL), 8938ac8defSOliver Tappe fOldZoneItem(NULL), 9099d2aa98SOliver Tappe fInitialized(false) 91a10cf76eSAxel Dörfler { 92c2f3ee3bSAdrien Destugues _ReadRTCSettings(); 936d6408d6SOliver Tappe _InitView(); 94a10cf76eSAxel Dörfler } 95a10cf76eSAxel Dörfler 96a10cf76eSAxel Dörfler 97310930fdSStephan Aßmus bool 98310930fdSStephan Aßmus TimeZoneView::CheckCanRevert() 99310930fdSStephan Aßmus { 100c2f3ee3bSAdrien Destugues // check GMT vs Local setting 101c2f3ee3bSAdrien Destugues bool enable = fUseGmtTime != fOldUseGmtTime; 102c2f3ee3bSAdrien Destugues 103c2f3ee3bSAdrien Destugues return enable || fCurrentZoneItem != fOldZoneItem; 104310930fdSStephan Aßmus } 105310930fdSStephan Aßmus 106310930fdSStephan Aßmus 10785b69a94SKarsten Heimrich TimeZoneView::~TimeZoneView() 108a10cf76eSAxel Dörfler { 109c2f3ee3bSAdrien Destugues _WriteRTCSettings(); 110a10cf76eSAxel Dörfler } 111a10cf76eSAxel Dörfler 112a10cf76eSAxel Dörfler 113a10cf76eSAxel Dörfler void 11485b69a94SKarsten Heimrich TimeZoneView::AttachedToWindow() 115a10cf76eSAxel Dörfler { 116447b7fdeSOliver Tappe BView::AttachedToWindow(); 117a10cf76eSAxel Dörfler if (Parent()) 118a10cf76eSAxel Dörfler SetViewColor(Parent()->ViewColor()); 119a10cf76eSAxel Dörfler 12085b69a94SKarsten Heimrich if (!fInitialized) { 12185b69a94SKarsten Heimrich fInitialized = true; 12285b69a94SKarsten Heimrich 12342b3a11fSStephan Aßmus fSetZone->SetTarget(this); 12438ac8defSOliver Tappe fZoneList->SetTarget(this); 12528ca7ae0Sanevilyak } 12628ca7ae0Sanevilyak } 12742b3a11fSStephan Aßmus 12828ca7ae0Sanevilyak 12928ca7ae0Sanevilyak void 13028ca7ae0Sanevilyak TimeZoneView::DoLayout() 13128ca7ae0Sanevilyak { 13228ca7ae0Sanevilyak BView::DoLayout(); 13338ac8defSOliver Tappe if (fCurrentZoneItem != NULL) { 134447b7fdeSOliver Tappe fZoneList->Select(fZoneList->IndexOf(fCurrentZoneItem)); 13538ac8defSOliver Tappe fCurrent->SetText(fCurrentZoneItem->Text()); 13638ac8defSOliver Tappe fZoneList->ScrollToSelection(); 137a10cf76eSAxel Dörfler } 138323ba9b7SRyan Leavengood } 139a10cf76eSAxel Dörfler 140a10cf76eSAxel Dörfler 141a10cf76eSAxel Dörfler void 14285b69a94SKarsten Heimrich TimeZoneView::MessageReceived(BMessage* message) 143a10cf76eSAxel Dörfler { 144a10cf76eSAxel Dörfler switch (message->what) { 145a10cf76eSAxel Dörfler case B_OBSERVER_NOTICE_CHANGE: 146317bd7ddSAxel Dörfler { 147317bd7ddSAxel Dörfler int32 change; 148a10cf76eSAxel Dörfler message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change); 149a10cf76eSAxel Dörfler switch(change) { 150a10cf76eSAxel Dörfler case H_TM_CHANGED: 1516d6408d6SOliver Tappe _UpdateDateTime(message); 152a10cf76eSAxel Dörfler break; 153a10cf76eSAxel Dörfler 154a10cf76eSAxel Dörfler default: 155a10cf76eSAxel Dörfler BView::MessageReceived(message); 156a10cf76eSAxel Dörfler break; 157a10cf76eSAxel Dörfler } 158a10cf76eSAxel Dörfler break; 159317bd7ddSAxel Dörfler } 160317bd7ddSAxel Dörfler 161447b7fdeSOliver Tappe case H_CITY_CHANGED: 162447b7fdeSOliver Tappe _UpdatePreview(); 163447b7fdeSOliver Tappe break; 164447b7fdeSOliver Tappe 165a10cf76eSAxel Dörfler case H_SET_TIME_ZONE: 166310930fdSStephan Aßmus { 16738ac8defSOliver Tappe _SetSystemTimeZone(); 168310930fdSStephan Aßmus break; 169310930fdSStephan Aßmus } 170310930fdSStephan Aßmus 171310930fdSStephan Aßmus case kMsgRevert: 172310930fdSStephan Aßmus _Revert(); 173a10cf76eSAxel Dörfler break; 174a10cf76eSAxel Dörfler 175447b7fdeSOliver Tappe case kRTCUpdate: 176c2f3ee3bSAdrien Destugues fUseGmtTime = fGmtTime->Value() == B_CONTROL_ON; 177c2f3ee3bSAdrien Destugues _UpdateGmtSettings(); 178447b7fdeSOliver Tappe _UpdateCurrent(); 17938ac8defSOliver Tappe _UpdatePreview(); 180a10cf76eSAxel Dörfler break; 181a10cf76eSAxel Dörfler 182a10cf76eSAxel Dörfler default: 183323ba9b7SRyan Leavengood BGroupView::MessageReceived(message); 184a10cf76eSAxel Dörfler break; 185a10cf76eSAxel Dörfler } 186a10cf76eSAxel Dörfler } 187a10cf76eSAxel Dörfler 188a10cf76eSAxel Dörfler 189a10cf76eSAxel Dörfler void 1906d6408d6SOliver Tappe TimeZoneView::_UpdateDateTime(BMessage* message) 191a10cf76eSAxel Dörfler { 19238ac8defSOliver Tappe // only need to update once every minute 19342b3a11fSStephan Aßmus int32 minute; 19438ac8defSOliver Tappe if (message->FindInt32("minute", &minute) == B_OK) { 19538ac8defSOliver Tappe if (fLastUpdateMinute != minute) { 19638ac8defSOliver Tappe _UpdateCurrent(); 19738ac8defSOliver Tappe _UpdatePreview(); 198a10cf76eSAxel Dörfler 19938ac8defSOliver Tappe fLastUpdateMinute = minute; 200a10cf76eSAxel Dörfler } 201a10cf76eSAxel Dörfler } 202a10cf76eSAxel Dörfler } 203a10cf76eSAxel Dörfler 204a10cf76eSAxel Dörfler 205a10cf76eSAxel Dörfler void 2066d6408d6SOliver Tappe TimeZoneView::_InitView() 207a10cf76eSAxel Dörfler { 208f0e995c8SZiusudra fZoneList = new TimeZoneListView(); 20938ac8defSOliver Tappe fZoneList->SetSelectionMessage(new BMessage(H_CITY_CHANGED)); 21038ac8defSOliver Tappe fZoneList->SetInvocationMessage(new BMessage(H_SET_TIME_ZONE)); 211750e57b8SOliver Tappe _BuildZoneMenu(); 21238ac8defSOliver Tappe BScrollView* scrollList = new BScrollView("scrollList", fZoneList, 213323ba9b7SRyan Leavengood B_FRAME_EVENTS | B_WILL_DRAW, false, true); 214c2f3ee3bSAdrien Destugues scrollList->SetExplicitMinSize(BSize(200, 0)); 215a10cf76eSAxel Dörfler 216323ba9b7SRyan Leavengood fCurrent = new TTZDisplay("currentTime", B_TRANSLATE("Current time:")); 217323ba9b7SRyan Leavengood fPreview = new TTZDisplay("previewTime", B_TRANSLATE("Preview time:")); 218a10cf76eSAxel Dörfler 219323ba9b7SRyan Leavengood fSetZone = new BButton("setTimeZone", B_TRANSLATE("Set time zone"), 22085b69a94SKarsten Heimrich new BMessage(H_SET_TIME_ZONE)); 22142b3a11fSStephan Aßmus fSetZone->SetEnabled(false); 222323ba9b7SRyan Leavengood fSetZone->SetExplicitAlignment( 223323ba9b7SRyan Leavengood BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM)); 224a10cf76eSAxel Dörfler 225c2f3ee3bSAdrien Destugues fLocalTime = new BRadioButton("localTime", 226*553211b4SJohn Scipione B_TRANSLATE("Local time (Windows compatible)"), 227*553211b4SJohn Scipione new BMessage(kRTCUpdate)); 228c2f3ee3bSAdrien Destugues fGmtTime = new BRadioButton("greenwichMeanTime", 229c2f3ee3bSAdrien Destugues B_TRANSLATE("GMT (UNIX compatible)"), new BMessage(kRTCUpdate)); 230c2f3ee3bSAdrien Destugues 231c2f3ee3bSAdrien Destugues if (fUseGmtTime) 232c2f3ee3bSAdrien Destugues fGmtTime->SetValue(B_CONTROL_ON); 233c2f3ee3bSAdrien Destugues else 234c2f3ee3bSAdrien Destugues fLocalTime->SetValue(B_CONTROL_ON); 235f3b7dcd4SAdrien Destugues _ShowOrHidePreview(); 236c2f3ee3bSAdrien Destugues fOldUseGmtTime = fUseGmtTime; 237c2f3ee3bSAdrien Destugues 238f496c69cSJohn Scipione const float kIndentSpacing 239f496c69cSJohn Scipione = be_control_look->DefaultItemSpacing() * 2; 240323ba9b7SRyan Leavengood BLayoutBuilder::Group<>(this) 241323ba9b7SRyan Leavengood .Add(scrollList) 242f496c69cSJohn Scipione .AddGroup(B_VERTICAL, 0) 243f496c69cSJohn Scipione .Add(new BStringView("clockSetTo", 244f496c69cSJohn Scipione B_TRANSLATE("Hardware clock set to:"))) 245f496c69cSJohn Scipione .AddGroup(B_VERTICAL, 0) 246c2f3ee3bSAdrien Destugues .Add(fLocalTime) 247c2f3ee3bSAdrien Destugues .Add(fGmtTime) 248f496c69cSJohn Scipione .SetInsets(kIndentSpacing, 0, 0, 0) 249c2f3ee3bSAdrien Destugues .End() 250f3b7dcd4SAdrien Destugues .AddGlue() 251f496c69cSJohn Scipione .AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING) 252f3b7dcd4SAdrien Destugues .Add(fCurrent) 253f3b7dcd4SAdrien Destugues .Add(fPreview) 254f496c69cSJohn Scipione .End() 255323ba9b7SRyan Leavengood .Add(fSetZone) 256323ba9b7SRyan Leavengood .End() 257f496c69cSJohn Scipione .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 258f496c69cSJohn Scipione B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING); 259a10cf76eSAxel Dörfler } 260a10cf76eSAxel Dörfler 261a10cf76eSAxel Dörfler 262a10cf76eSAxel Dörfler void 263750e57b8SOliver Tappe TimeZoneView::_BuildZoneMenu() 264a10cf76eSAxel Dörfler { 265447b7fdeSOliver Tappe BTimeZone defaultTimeZone; 26625dc253dSIngo Weinhold BLocaleRoster::Default()->GetDefaultTimeZone(&defaultTimeZone); 26799d2aa98SOliver Tappe 268b7f60965SOliver Tappe BLanguage language; 26925dc253dSIngo Weinhold BLocale::Default()->GetLanguage(&language); 270a10cf76eSAxel Dörfler 271*553211b4SJohn Scipione // Group timezones by regions, but filter out unwanted (duplicate) regions 272*553211b4SJohn Scipione // and add an additional region with generic GMT-offset timezones at the end 273750e57b8SOliver Tappe typedef std::map<BString, TimeZoneListItem*, TimeZoneItemLess> ZoneItemMap; 274fefa98aaSOliver Tappe ZoneItemMap zoneItemMap; 275fefa98aaSOliver Tappe const char* kOtherRegion = B_TRANSLATE_MARK("<Other>"); 276447b7fdeSOliver Tappe const char* kSupportedRegions[] = { 277fefa98aaSOliver Tappe B_TRANSLATE_MARK("Africa"), B_TRANSLATE_MARK("America"), 278fefa98aaSOliver Tappe B_TRANSLATE_MARK("Antarctica"), B_TRANSLATE_MARK("Arctic"), 279fefa98aaSOliver Tappe B_TRANSLATE_MARK("Asia"), B_TRANSLATE_MARK("Atlantic"), 280fefa98aaSOliver Tappe B_TRANSLATE_MARK("Australia"), B_TRANSLATE_MARK("Europe"), 281fefa98aaSOliver Tappe B_TRANSLATE_MARK("Indian"), B_TRANSLATE_MARK("Pacific"), 282fefa98aaSOliver Tappe kOtherRegion, 283fefa98aaSOliver Tappe NULL 284750e57b8SOliver Tappe }; 285*553211b4SJohn Scipione 286fefa98aaSOliver Tappe // Since the zone-map contains translated country-names (we get those from 287fefa98aaSOliver Tappe // ICU), we need to use translated region names in the zone-map, too: 288fefa98aaSOliver Tappe typedef std::map<BString, BString> TranslatedRegionMap; 289fefa98aaSOliver Tappe TranslatedRegionMap regionMap; 290fefa98aaSOliver Tappe for (const char** region = kSupportedRegions; *region != NULL; ++region) { 291fefa98aaSOliver Tappe BString translatedRegion = B_TRANSLATE_NOCOLLECT(*region); 292fefa98aaSOliver Tappe regionMap[*region] = translatedRegion; 293fefa98aaSOliver Tappe 294fefa98aaSOliver Tappe TimeZoneListItem* regionItem 295fefa98aaSOliver Tappe = new TimeZoneListItem(translatedRegion, NULL, NULL); 296fefa98aaSOliver Tappe regionItem->SetOutlineLevel(0); 297fefa98aaSOliver Tappe zoneItemMap[translatedRegion] = regionItem; 298fefa98aaSOliver Tappe } 299a10cf76eSAxel Dörfler 300eb78be9bSOliver Tappe // Get all time zones 301eb78be9bSOliver Tappe BMessage zoneList; 302eb78be9bSOliver Tappe BLocaleRoster::Default()->GetAvailableTimeZonesWithRegionInfo(&zoneList); 303eb78be9bSOliver Tappe 304eb78be9bSOliver Tappe typedef std::map<BString, std::vector<const char*> > ZonesByCountyMap; 305eb78be9bSOliver Tappe ZonesByCountyMap zonesByCountryMap; 306eb78be9bSOliver Tappe const char* zoneID; 307*553211b4SJohn Scipione BString timeZoneCode; 308eb78be9bSOliver Tappe for (int tz = 0; zoneList.FindString("timeZone", tz, &zoneID) == B_OK 309*553211b4SJohn Scipione && zoneList.FindString("region", tz, &timeZoneCode) == B_OK; tz++) { 310eb78be9bSOliver Tappe // From the global ("001") timezones, we only accept the generic GMT 311eb78be9bSOliver Tappe // timezones, as all the other world-zones are duplicates of others. 312*553211b4SJohn Scipione if (timeZoneCode == "001" && strncmp(zoneID, "Etc/GMT", 7) != 0) 313eb78be9bSOliver Tappe continue; 314*553211b4SJohn Scipione zonesByCountryMap[timeZoneCode].push_back(zoneID); 315eb78be9bSOliver Tappe } 316eb78be9bSOliver Tappe 317eb78be9bSOliver Tappe ZonesByCountyMap::const_iterator countryIter = zonesByCountryMap.begin(); 318eb78be9bSOliver Tappe for (; countryIter != zonesByCountryMap.end(); ++countryIter) { 319eb78be9bSOliver Tappe BCountry country(countryIter->first.String()); 3203740f52dSOliver Tappe BString countryName; 3213740f52dSOliver Tappe country.GetName(countryName); 3223740f52dSOliver Tappe 323eb78be9bSOliver Tappe size_t zoneCountInCountry = countryIter->second.size(); 324eb78be9bSOliver Tappe for (size_t tz = 0; tz < zoneCountInCountry; tz++) { 325eb78be9bSOliver Tappe BString zoneID(countryIter->second[tz]); 326750e57b8SOliver Tappe int32 slashPos = zoneID.FindFirst('/'); 327a10cf76eSAxel Dörfler 328750e57b8SOliver Tappe BString region(zoneID, slashPos); 32945f2f22bSOliver Tappe if (region == "Etc") 330447b7fdeSOliver Tappe region = kOtherRegion; 331447b7fdeSOliver Tappe 332fefa98aaSOliver Tappe // just accept timezones from our supported regions, others are 333fefa98aaSOliver Tappe // aliases and would just make the list even longer 334fefa98aaSOliver Tappe TranslatedRegionMap::iterator regionIter = regionMap.find(region); 335bafd2297SOliver Tappe if (regionIter == regionMap.end()) 336750e57b8SOliver Tappe continue; 337750e57b8SOliver Tappe 338eb78be9bSOliver Tappe BString fullCountryID = regionIter->second; 339*553211b4SJohn Scipione bool countryIsRegion = countryName == regionIter->second 340*553211b4SJohn Scipione || region == kOtherRegion; 341fefa98aaSOliver Tappe if (!countryIsRegion) 3423740f52dSOliver Tappe fullCountryID << "/" << countryName; 3433740f52dSOliver Tappe 344b7f60965SOliver Tappe BTimeZone* timeZone = new BTimeZone(zoneID, &language); 345eb78be9bSOliver Tappe BString tzName; 346eb78be9bSOliver Tappe BString fullZoneID = fullCountryID; 347*553211b4SJohn Scipione if (zoneCountInCountry > 1) { 348eb78be9bSOliver Tappe // we can't use the country name as timezone name, since there 349eb78be9bSOliver Tappe // are more than one timezones in this country - fetch the 350eb78be9bSOliver Tappe // localized name of the timezone and use that 351eb78be9bSOliver Tappe tzName = timeZone->Name(); 352750e57b8SOliver Tappe int32 openParenthesisPos = tzName.FindFirst('('); 353750e57b8SOliver Tappe if (openParenthesisPos >= 0) { 354750e57b8SOliver Tappe tzName.Remove(0, openParenthesisPos + 1); 355750e57b8SOliver Tappe int32 closeParenthesisPos = tzName.FindLast(')'); 356750e57b8SOliver Tappe if (closeParenthesisPos >= 0) 357750e57b8SOliver Tappe tzName.Truncate(closeParenthesisPos); 358750e57b8SOliver Tappe } 359750e57b8SOliver Tappe fullZoneID << "/" << tzName; 360eb78be9bSOliver Tappe } else { 361eb78be9bSOliver Tappe tzName = countryName; 362eb78be9bSOliver Tappe fullZoneID << "/" << zoneID; 363eb78be9bSOliver Tappe } 364750e57b8SOliver Tappe 365750e57b8SOliver Tappe // skip duplicates 366fefa98aaSOliver Tappe ZoneItemMap::iterator zoneIter = zoneItemMap.find(fullZoneID); 367fefa98aaSOliver Tappe if (zoneIter != zoneItemMap.end()) { 368750e57b8SOliver Tappe delete timeZone; 369750e57b8SOliver Tappe continue; 370750e57b8SOliver Tappe } 371750e57b8SOliver Tappe 372750e57b8SOliver Tappe TimeZoneListItem* countryItem = NULL; 3733740f52dSOliver Tappe TimeZoneListItem* zoneItem = NULL; 374eb78be9bSOliver Tappe if (zoneCountInCountry > 1) { 375fefa98aaSOliver Tappe ZoneItemMap::iterator countryIter 376fefa98aaSOliver Tappe = zoneItemMap.find(fullCountryID); 377fefa98aaSOliver Tappe if (countryIter == zoneItemMap.end()) { 3783740f52dSOliver Tappe countryItem = new TimeZoneListItem(countryName, NULL, NULL); 379750e57b8SOliver Tappe countryItem->SetOutlineLevel(1); 380fefa98aaSOliver Tappe zoneItemMap[fullCountryID] = countryItem; 381750e57b8SOliver Tappe } else 382750e57b8SOliver Tappe countryItem = countryIter->second; 3834ae5a452SAdrien Destugues 3843740f52dSOliver Tappe zoneItem = new TimeZoneListItem(tzName, NULL, timeZone); 385fefa98aaSOliver Tappe zoneItem->SetOutlineLevel(countryIsRegion ? 1 : 2); 3863740f52dSOliver Tappe } else { 387eb78be9bSOliver Tappe zoneItem = new TimeZoneListItem(tzName, NULL, timeZone); 3883740f52dSOliver Tappe zoneItem->SetOutlineLevel(1); 3893740f52dSOliver Tappe } 390fefa98aaSOliver Tappe zoneItemMap[fullZoneID] = zoneItem; 391750e57b8SOliver Tappe 392750e57b8SOliver Tappe if (timeZone->ID() == defaultTimeZone.ID()) { 393750e57b8SOliver Tappe fCurrentZoneItem = zoneItem; 394750e57b8SOliver Tappe if (countryItem != NULL) 39599d2aa98SOliver Tappe countryItem->SetExpanded(true); 396*553211b4SJohn Scipione 397fefa98aaSOliver Tappe ZoneItemMap::iterator regionItemIter 398eb78be9bSOliver Tappe = zoneItemMap.find(regionIter->second); 399fefa98aaSOliver Tappe if (regionItemIter != zoneItemMap.end()) 400fefa98aaSOliver Tappe regionItemIter->second->SetExpanded(true); 40199d2aa98SOliver Tappe } 40242b3a11fSStephan Aßmus } 4033740f52dSOliver Tappe } 40499d2aa98SOliver Tappe 40538ac8defSOliver Tappe fOldZoneItem = fCurrentZoneItem; 40699d2aa98SOliver Tappe 407750e57b8SOliver Tappe ZoneItemMap::iterator zoneIter; 4083740f52dSOliver Tappe bool lastWasCountryItem = false; 409fefa98aaSOliver Tappe TimeZoneListItem* currentCountryItem = NULL; 410fefa98aaSOliver Tappe for (zoneIter = zoneItemMap.begin(); zoneIter != zoneItemMap.end(); 411fefa98aaSOliver Tappe ++zoneIter) { 4123740f52dSOliver Tappe if (zoneIter->second->OutlineLevel() == 2 && lastWasCountryItem) { 413*553211b4SJohn Scipione // Some countries (e.g. Spain and Chile) have their timezones 414*553211b4SJohn Scipione // spread across different regions. As a result, there might still 415*553211b4SJohn Scipione // be country items with only one timezone below them. We manually 416*553211b4SJohn Scipione // filter those country items here. 4173740f52dSOliver Tappe ZoneItemMap::iterator next = zoneIter; 4183740f52dSOliver Tappe ++next; 419fefa98aaSOliver Tappe if (next != zoneItemMap.end() 420fefa98aaSOliver Tappe && next->second->OutlineLevel() != 2) { 421fefa98aaSOliver Tappe fZoneList->RemoveItem(currentCountryItem); 422fefa98aaSOliver Tappe zoneIter->second->SetText(currentCountryItem->Text()); 4233740f52dSOliver Tappe zoneIter->second->SetOutlineLevel(1); 424fefa98aaSOliver Tappe delete currentCountryItem; 4253740f52dSOliver Tappe } 4263740f52dSOliver Tappe } 4273740f52dSOliver Tappe 428750e57b8SOliver Tappe fZoneList->AddItem(zoneIter->second); 4293740f52dSOliver Tappe if (zoneIter->second->OutlineLevel() == 1) { 4303740f52dSOliver Tappe lastWasCountryItem = true; 431fefa98aaSOliver Tappe currentCountryItem = zoneIter->second; 4323740f52dSOliver Tappe } else 4333740f52dSOliver Tappe lastWasCountryItem = false; 4343740f52dSOliver Tappe } 43599d2aa98SOliver Tappe } 436750e57b8SOliver Tappe 437750e57b8SOliver Tappe 438750e57b8SOliver Tappe void 439750e57b8SOliver Tappe TimeZoneView::_Revert() 440750e57b8SOliver Tappe { 441750e57b8SOliver Tappe fCurrentZoneItem = fOldZoneItem; 442750e57b8SOliver Tappe 443750e57b8SOliver Tappe if (fCurrentZoneItem != NULL) { 444750e57b8SOliver Tappe int32 currentZoneIndex = fZoneList->IndexOf(fCurrentZoneItem); 445750e57b8SOliver Tappe fZoneList->Select(currentZoneIndex); 446750e57b8SOliver Tappe } else 447750e57b8SOliver Tappe fZoneList->DeselectAll(); 448750e57b8SOliver Tappe fZoneList->ScrollToSelection(); 449750e57b8SOliver Tappe 450c2f3ee3bSAdrien Destugues fUseGmtTime = fOldUseGmtTime; 451c2f3ee3bSAdrien Destugues if (fUseGmtTime) 452c2f3ee3bSAdrien Destugues fGmtTime->SetValue(B_CONTROL_ON); 453c2f3ee3bSAdrien Destugues else 454c2f3ee3bSAdrien Destugues fLocalTime->SetValue(B_CONTROL_ON); 455f3b7dcd4SAdrien Destugues _ShowOrHidePreview(); 456c2f3ee3bSAdrien Destugues 457c2f3ee3bSAdrien Destugues _UpdateGmtSettings(); 458750e57b8SOliver Tappe _SetSystemTimeZone(); 459750e57b8SOliver Tappe _UpdatePreview(); 460750e57b8SOliver Tappe _UpdateCurrent(); 461a10cf76eSAxel Dörfler } 462a10cf76eSAxel Dörfler 463a10cf76eSAxel Dörfler 464a10cf76eSAxel Dörfler void 46538ac8defSOliver Tappe TimeZoneView::_UpdatePreview() 466a10cf76eSAxel Dörfler { 46738ac8defSOliver Tappe int32 selection = fZoneList->CurrentSelection(); 468750e57b8SOliver Tappe TimeZoneListItem* item 469750e57b8SOliver Tappe = selection < 0 470750e57b8SOliver Tappe ? NULL 471*553211b4SJohn Scipione : static_cast<TimeZoneListItem*>(fZoneList->ItemAt(selection)); 472a10cf76eSAxel Dörfler 473750e57b8SOliver Tappe if (item == NULL || !item->HasTimeZone()) { 474750e57b8SOliver Tappe fPreview->SetText(""); 475750e57b8SOliver Tappe fPreview->SetTime(""); 47638ac8defSOliver Tappe return; 477750e57b8SOliver Tappe } 478a10cf76eSAxel Dörfler 479447b7fdeSOliver Tappe BString timeString = _FormatTime(item->TimeZone()); 48038ac8defSOliver Tappe fPreview->SetText(item->Text()); 48138ac8defSOliver Tappe fPreview->SetTime(timeString.String()); 482a10cf76eSAxel Dörfler 48338ac8defSOliver Tappe fSetZone->SetEnabled((strcmp(fCurrent->Text(), item->Text()) != 0)); 484a10cf76eSAxel Dörfler } 485a10cf76eSAxel Dörfler 486a10cf76eSAxel Dörfler 487a10cf76eSAxel Dörfler void 48838ac8defSOliver Tappe TimeZoneView::_UpdateCurrent() 489a10cf76eSAxel Dörfler { 49038ac8defSOliver Tappe if (fCurrentZoneItem == NULL) 49138ac8defSOliver Tappe return; 4921855b44cSAdrien Destugues 493447b7fdeSOliver Tappe BString timeString = _FormatTime(fCurrentZoneItem->TimeZone()); 49438ac8defSOliver Tappe fCurrent->SetText(fCurrentZoneItem->Text()); 49538ac8defSOliver Tappe fCurrent->SetTime(timeString.String()); 496a10cf76eSAxel Dörfler } 497a10cf76eSAxel Dörfler 49838ac8defSOliver Tappe 49938ac8defSOliver Tappe void 50038ac8defSOliver Tappe TimeZoneView::_SetSystemTimeZone() 50138ac8defSOliver Tappe { 502d88a361cSOliver Tappe /* Set system timezone for all different API levels. How to do this? 50338ac8defSOliver Tappe * 1) tell locale-roster about new default timezone 50485b54438SOliver Tappe * 2) tell kernel about new timezone offset 50538ac8defSOliver Tappe */ 50638ac8defSOliver Tappe 50738ac8defSOliver Tappe int32 selection = fZoneList->CurrentSelection(); 50838ac8defSOliver Tappe if (selection < 0) 50938ac8defSOliver Tappe return; 51038ac8defSOliver Tappe 511447b7fdeSOliver Tappe TimeZoneListItem* item 512447b7fdeSOliver Tappe = static_cast<TimeZoneListItem*>(fZoneList->ItemAt(selection)); 513447b7fdeSOliver Tappe if (item == NULL || !item->HasTimeZone()) 514447b7fdeSOliver Tappe return; 515447b7fdeSOliver Tappe 516447b7fdeSOliver Tappe fCurrentZoneItem = item; 517447b7fdeSOliver Tappe const BTimeZone& timeZone = item->TimeZone(); 51838ac8defSOliver Tappe 51925dc253dSIngo Weinhold MutableLocaleRoster::Default()->SetDefaultTimeZone(timeZone); 52038ac8defSOliver Tappe 521750e57b8SOliver Tappe _kern_set_timezone(timeZone.OffsetFromGMT(), timeZone.ID().String(), 522750e57b8SOliver Tappe timeZone.ID().Length()); 52385b54438SOliver Tappe 52438ac8defSOliver Tappe fSetZone->SetEnabled(false); 52538ac8defSOliver Tappe fLastUpdateMinute = -1; 52638ac8defSOliver Tappe // just to trigger updating immediately 52738ac8defSOliver Tappe } 52838ac8defSOliver Tappe 52938ac8defSOliver Tappe 53038ac8defSOliver Tappe BString 5313740f52dSOliver Tappe TimeZoneView::_FormatTime(const BTimeZone& timeZone) 53238ac8defSOliver Tappe { 53338ac8defSOliver Tappe BString result; 53438ac8defSOliver Tappe 535a096bb65SOliver Tappe time_t now = time(NULL); 536a096bb65SOliver Tappe bool rtcIsGMT; 537a096bb65SOliver Tappe _kern_get_real_time_clock_is_gmt(&rtcIsGMT); 5383740f52dSOliver Tappe if (!rtcIsGMT) { 539447b7fdeSOliver Tappe int32 currentOffset 540447b7fdeSOliver Tappe = fCurrentZoneItem != NULL && fCurrentZoneItem->HasTimeZone() 541447b7fdeSOliver Tappe ? fCurrentZoneItem->OffsetFromGMT() 542447b7fdeSOliver Tappe : 0; 543447b7fdeSOliver Tappe now -= timeZone.OffsetFromGMT() - currentOffset; 544a096bb65SOliver Tappe } 54525dc253dSIngo Weinhold BLocale::Default()->FormatTime(&result, now, B_SHORT_TIME_FORMAT, 54625dc253dSIngo Weinhold &timeZone); 54738ac8defSOliver Tappe 54838ac8defSOliver Tappe return result; 54938ac8defSOliver Tappe } 550c2f3ee3bSAdrien Destugues 551c2f3ee3bSAdrien Destugues 552c2f3ee3bSAdrien Destugues void 553c2f3ee3bSAdrien Destugues TimeZoneView::_ReadRTCSettings() 554c2f3ee3bSAdrien Destugues { 555c2f3ee3bSAdrien Destugues BPath path; 556c2f3ee3bSAdrien Destugues if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 557c2f3ee3bSAdrien Destugues return; 558c2f3ee3bSAdrien Destugues 559c2f3ee3bSAdrien Destugues path.Append("RTC_time_settings"); 560c2f3ee3bSAdrien Destugues 561c2f3ee3bSAdrien Destugues BEntry entry(path.Path()); 562c2f3ee3bSAdrien Destugues if (entry.Exists()) { 563c2f3ee3bSAdrien Destugues BFile file(&entry, B_READ_ONLY); 564c2f3ee3bSAdrien Destugues if (file.InitCheck() == B_OK) { 565c2f3ee3bSAdrien Destugues char buffer[6]; 566c2f3ee3bSAdrien Destugues file.Read(buffer, 6); 567c2f3ee3bSAdrien Destugues if (strncmp(buffer, "gmt", 3) == 0) 568c2f3ee3bSAdrien Destugues fUseGmtTime = true; 569c2f3ee3bSAdrien Destugues } 570c2f3ee3bSAdrien Destugues } 571c2f3ee3bSAdrien Destugues } 572c2f3ee3bSAdrien Destugues 573c2f3ee3bSAdrien Destugues 574c2f3ee3bSAdrien Destugues void 575c2f3ee3bSAdrien Destugues TimeZoneView::_WriteRTCSettings() 576c2f3ee3bSAdrien Destugues { 577c2f3ee3bSAdrien Destugues BPath path; 578c2f3ee3bSAdrien Destugues if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 579c2f3ee3bSAdrien Destugues return; 580c2f3ee3bSAdrien Destugues 581c2f3ee3bSAdrien Destugues path.Append("RTC_time_settings"); 582c2f3ee3bSAdrien Destugues 583c2f3ee3bSAdrien Destugues BFile file(path.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 584c2f3ee3bSAdrien Destugues if (file.InitCheck() == B_OK) { 585c2f3ee3bSAdrien Destugues if (fUseGmtTime) 586c2f3ee3bSAdrien Destugues file.Write("gmt", 3); 587c2f3ee3bSAdrien Destugues else 588c2f3ee3bSAdrien Destugues file.Write("local", 5); 589c2f3ee3bSAdrien Destugues } 590c2f3ee3bSAdrien Destugues } 591c2f3ee3bSAdrien Destugues 592c2f3ee3bSAdrien Destugues 593c2f3ee3bSAdrien Destugues void 594c2f3ee3bSAdrien Destugues TimeZoneView::_UpdateGmtSettings() 595c2f3ee3bSAdrien Destugues { 596c2f3ee3bSAdrien Destugues _WriteRTCSettings(); 597c2f3ee3bSAdrien Destugues 598f3b7dcd4SAdrien Destugues _ShowOrHidePreview(); 599c2f3ee3bSAdrien Destugues 600c2f3ee3bSAdrien Destugues _kern_set_real_time_clock_is_gmt(fUseGmtTime); 601c2f3ee3bSAdrien Destugues } 602c2f3ee3bSAdrien Destugues 603c2f3ee3bSAdrien Destugues 604c2f3ee3bSAdrien Destugues void 605f3b7dcd4SAdrien Destugues TimeZoneView::_ShowOrHidePreview() 606f3b7dcd4SAdrien Destugues { 607f3b7dcd4SAdrien Destugues if (fUseGmtTime) { 608f3b7dcd4SAdrien Destugues // Hardware clock uses GMT time, changing timezone will adjust the 609f3b7dcd4SAdrien Destugues // offset and we need to display a preview 610f3b7dcd4SAdrien Destugues fCurrent->Show(); 611f3b7dcd4SAdrien Destugues fPreview->Show(); 612f3b7dcd4SAdrien Destugues } else { 613f3b7dcd4SAdrien Destugues // Hardware clock uses local time, changing timezone will adjust the 614f3b7dcd4SAdrien Destugues // clock and there is no offset to manage, thus, no preview. 615f3b7dcd4SAdrien Destugues fCurrent->Hide(); 616f3b7dcd4SAdrien Destugues fPreview->Hide(); 617f3b7dcd4SAdrien Destugues } 618f3b7dcd4SAdrien Destugues } 619