1a10cf76eSAxel Dörfler /* 25b013eabSJohn Scipione * Copyright 2004-2013 Haiku, Inc. All rights reserved. 353d9356eSStephan Aßmus * Distributed under the terms of the MIT License. 453d9356eSStephan Aßmus * 553d9356eSStephan Aßmus * Authors: 65b013eabSJohn Scipione * Mike Berg, mike@berg-net.us 75b013eabSJohn Scipione * Adrien Destugues, pulkomandy@pulkomandy.ath.cx 85b013eabSJohn Scipione * Julun, host.haiku@gmx.de 95b013eabSJohn Scipione * Hamish Morrison, hamish@lavabit.com 105b013eabSJohn Scipione * Philippe Saint-Pierre, stpere@gmail.com 115b013eabSJohn Scipione * John Scipione, jscipione@gmail.com 125b013eabSJohn Scipione * Oliver Tappe, zooey@hirschkaefer.de 1353d9356eSStephan Aßmus */ 1442b3a11fSStephan Aßmus 15a10cf76eSAxel Dörfler 16*73de5837SJérôme Duval #include <unicode/uversion.h> 1742b3a11fSStephan Aßmus #include "ZoneView.h" 18a10cf76eSAxel Dörfler 19317bd7ddSAxel Dörfler #include <stdlib.h> 20323ba9b7SRyan Leavengood #include <syscalls.h> 2142b3a11fSStephan Aßmus 22750e57b8SOliver Tappe #include <map> 2338ac8defSOliver Tappe #include <new> 24eb78be9bSOliver Tappe #include <vector> 2538ac8defSOliver Tappe 2638ac8defSOliver Tappe #include <AutoDeleter.h> 2742b3a11fSStephan Aßmus #include <Button.h> 28825d265eSAdrien Destugues #include <Catalog.h> 2999d2aa98SOliver Tappe #include <Collator.h> 30365839fbSRyan Leavengood #include <ControlLook.h> 313740f52dSOliver Tappe #include <Country.h> 32a10cf76eSAxel Dörfler #include <Directory.h> 33a10cf76eSAxel Dörfler #include <Entry.h> 3485b54438SOliver Tappe #include <File.h> 35a10cf76eSAxel Dörfler #include <FindDirectory.h> 3642b3a11fSStephan Aßmus #include <ListItem.h> 374ae5a452SAdrien Destugues #include <Locale.h> 3838ac8defSOliver Tappe #include <MutableLocaleRoster.h> 394ae5a452SAdrien Destugues #include <OutlineListView.h> 4085b54438SOliver Tappe #include <Path.h> 41c2f3ee3bSAdrien Destugues #include <RadioButton.h> 42a10cf76eSAxel Dörfler #include <ScrollView.h> 43a10cf76eSAxel Dörfler #include <StorageDefs.h> 44a10cf76eSAxel Dörfler #include <String.h> 45c2f3ee3bSAdrien Destugues #include <StringView.h> 464ae5a452SAdrien Destugues #include <TimeZone.h> 47a10cf76eSAxel Dörfler #include <View.h> 48310930fdSStephan Aßmus #include <Window.h> 49a10cf76eSAxel Dörfler 5038ac8defSOliver Tappe #include <unicode/datefmt.h> 5138ac8defSOliver Tappe #include <unicode/utmscale.h> 5238ac8defSOliver Tappe #include <ICUWrapper.h> 5338ac8defSOliver Tappe 54317bd7ddSAxel Dörfler #include "TimeMessages.h" 554ae5a452SAdrien Destugues #include "TimeZoneListItem.h" 56f0e995c8SZiusudra #include "TimeZoneListView.h" 57317bd7ddSAxel Dörfler #include "TZDisplay.h" 58a10cf76eSAxel Dörfler 59a10cf76eSAxel Dörfler 60546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT 61546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "Time" 62825d265eSAdrien Destugues 63825d265eSAdrien Destugues 6425dc253dSIngo Weinhold using BPrivate::MutableLocaleRoster; 6538ac8defSOliver Tappe using BPrivate::ObjectDeleter; 6699d2aa98SOliver Tappe 6799d2aa98SOliver Tappe 68750e57b8SOliver Tappe struct TimeZoneItemLess { 69750e57b8SOliver Tappe bool operator()(const BString& first, const BString& second) 70750e57b8SOliver Tappe { 71447b7fdeSOliver Tappe // sort anything starting with '<' behind anything else 72447b7fdeSOliver Tappe if (first.ByteAt(0) == '<') { 73447b7fdeSOliver Tappe if (second.ByteAt(0) != '<') 74447b7fdeSOliver Tappe return false; 75447b7fdeSOliver Tappe } else if (second.ByteAt(0) == '<') 76447b7fdeSOliver Tappe return true; 77750e57b8SOliver Tappe return fCollator.Compare(first.String(), second.String()) < 0; 78750e57b8SOliver Tappe } 79750e57b8SOliver Tappe private: 80750e57b8SOliver Tappe BCollator fCollator; 81750e57b8SOliver Tappe }; 82750e57b8SOliver Tappe 83750e57b8SOliver Tappe 84750e57b8SOliver Tappe 85323ba9b7SRyan Leavengood TimeZoneView::TimeZoneView(const char* name) 8699d2aa98SOliver Tappe : 87323ba9b7SRyan Leavengood BGroupView(name, B_HORIZONTAL, B_USE_DEFAULT_SPACING), 88c2f3ee3bSAdrien Destugues fGmtTime(NULL), 89c2f3ee3bSAdrien Destugues fUseGmtTime(false), 9038ac8defSOliver Tappe fCurrentZoneItem(NULL), 9138ac8defSOliver Tappe fOldZoneItem(NULL), 9299d2aa98SOliver Tappe fInitialized(false) 93a10cf76eSAxel Dörfler { 94c2f3ee3bSAdrien Destugues _ReadRTCSettings(); 956d6408d6SOliver Tappe _InitView(); 96a10cf76eSAxel Dörfler } 97a10cf76eSAxel Dörfler 98a10cf76eSAxel Dörfler 99310930fdSStephan Aßmus bool 100310930fdSStephan Aßmus TimeZoneView::CheckCanRevert() 101310930fdSStephan Aßmus { 102c2f3ee3bSAdrien Destugues // check GMT vs Local setting 103c2f3ee3bSAdrien Destugues bool enable = fUseGmtTime != fOldUseGmtTime; 104c2f3ee3bSAdrien Destugues 105c2f3ee3bSAdrien Destugues return enable || fCurrentZoneItem != fOldZoneItem; 106310930fdSStephan Aßmus } 107310930fdSStephan Aßmus 108310930fdSStephan Aßmus 10985b69a94SKarsten Heimrich TimeZoneView::~TimeZoneView() 110a10cf76eSAxel Dörfler { 111c2f3ee3bSAdrien Destugues _WriteRTCSettings(); 112a10cf76eSAxel Dörfler } 113a10cf76eSAxel Dörfler 114a10cf76eSAxel Dörfler 115a10cf76eSAxel Dörfler void 11685b69a94SKarsten Heimrich TimeZoneView::AttachedToWindow() 117a10cf76eSAxel Dörfler { 118447b7fdeSOliver Tappe BView::AttachedToWindow(); 119a10cf76eSAxel Dörfler if (Parent()) 120a10cf76eSAxel Dörfler SetViewColor(Parent()->ViewColor()); 121a10cf76eSAxel Dörfler 12285b69a94SKarsten Heimrich if (!fInitialized) { 12385b69a94SKarsten Heimrich fInitialized = true; 12485b69a94SKarsten Heimrich 12542b3a11fSStephan Aßmus fSetZone->SetTarget(this); 12638ac8defSOliver Tappe fZoneList->SetTarget(this); 12728ca7ae0Sanevilyak } 12828ca7ae0Sanevilyak } 12942b3a11fSStephan Aßmus 13028ca7ae0Sanevilyak 13128ca7ae0Sanevilyak void 13228ca7ae0Sanevilyak TimeZoneView::DoLayout() 13328ca7ae0Sanevilyak { 13428ca7ae0Sanevilyak BView::DoLayout(); 13538ac8defSOliver Tappe if (fCurrentZoneItem != NULL) { 136447b7fdeSOliver Tappe fZoneList->Select(fZoneList->IndexOf(fCurrentZoneItem)); 13738ac8defSOliver Tappe fCurrent->SetText(fCurrentZoneItem->Text()); 13838ac8defSOliver Tappe fZoneList->ScrollToSelection(); 139a10cf76eSAxel Dörfler } 140323ba9b7SRyan Leavengood } 141a10cf76eSAxel Dörfler 142a10cf76eSAxel Dörfler 143a10cf76eSAxel Dörfler void 14485b69a94SKarsten Heimrich TimeZoneView::MessageReceived(BMessage* message) 145a10cf76eSAxel Dörfler { 146a10cf76eSAxel Dörfler switch (message->what) { 147a10cf76eSAxel Dörfler case B_OBSERVER_NOTICE_CHANGE: 148317bd7ddSAxel Dörfler { 149317bd7ddSAxel Dörfler int32 change; 150a10cf76eSAxel Dörfler message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change); 151a10cf76eSAxel Dörfler switch(change) { 152a10cf76eSAxel Dörfler case H_TM_CHANGED: 1536d6408d6SOliver Tappe _UpdateDateTime(message); 154a10cf76eSAxel Dörfler break; 155a10cf76eSAxel Dörfler 156a10cf76eSAxel Dörfler default: 157a10cf76eSAxel Dörfler BView::MessageReceived(message); 158a10cf76eSAxel Dörfler break; 159a10cf76eSAxel Dörfler } 160a10cf76eSAxel Dörfler break; 161317bd7ddSAxel Dörfler } 162317bd7ddSAxel Dörfler 163447b7fdeSOliver Tappe case H_CITY_CHANGED: 164447b7fdeSOliver Tappe _UpdatePreview(); 165447b7fdeSOliver Tappe break; 166447b7fdeSOliver Tappe 167a10cf76eSAxel Dörfler case H_SET_TIME_ZONE: 168310930fdSStephan Aßmus { 16938ac8defSOliver Tappe _SetSystemTimeZone(); 170310930fdSStephan Aßmus break; 171310930fdSStephan Aßmus } 172310930fdSStephan Aßmus 173310930fdSStephan Aßmus case kMsgRevert: 174310930fdSStephan Aßmus _Revert(); 175a10cf76eSAxel Dörfler break; 176a10cf76eSAxel Dörfler 177447b7fdeSOliver Tappe case kRTCUpdate: 178c2f3ee3bSAdrien Destugues fUseGmtTime = fGmtTime->Value() == B_CONTROL_ON; 179c2f3ee3bSAdrien Destugues _UpdateGmtSettings(); 180447b7fdeSOliver Tappe _UpdateCurrent(); 18138ac8defSOliver Tappe _UpdatePreview(); 182a10cf76eSAxel Dörfler break; 183a10cf76eSAxel Dörfler 184a10cf76eSAxel Dörfler default: 185323ba9b7SRyan Leavengood BGroupView::MessageReceived(message); 186a10cf76eSAxel Dörfler break; 187a10cf76eSAxel Dörfler } 188a10cf76eSAxel Dörfler } 189a10cf76eSAxel Dörfler 190a10cf76eSAxel Dörfler 191a10cf76eSAxel Dörfler void 1926d6408d6SOliver Tappe TimeZoneView::_UpdateDateTime(BMessage* message) 193a10cf76eSAxel Dörfler { 19438ac8defSOliver Tappe // only need to update once every minute 19542b3a11fSStephan Aßmus int32 minute; 19638ac8defSOliver Tappe if (message->FindInt32("minute", &minute) == B_OK) { 19738ac8defSOliver Tappe if (fLastUpdateMinute != minute) { 19838ac8defSOliver Tappe _UpdateCurrent(); 19938ac8defSOliver Tappe _UpdatePreview(); 200a10cf76eSAxel Dörfler 20138ac8defSOliver Tappe fLastUpdateMinute = minute; 202a10cf76eSAxel Dörfler } 203a10cf76eSAxel Dörfler } 204a10cf76eSAxel Dörfler } 205a10cf76eSAxel Dörfler 206a10cf76eSAxel Dörfler 207a10cf76eSAxel Dörfler void 2086d6408d6SOliver Tappe TimeZoneView::_InitView() 209a10cf76eSAxel Dörfler { 210f0e995c8SZiusudra fZoneList = new TimeZoneListView(); 21138ac8defSOliver Tappe fZoneList->SetSelectionMessage(new BMessage(H_CITY_CHANGED)); 21238ac8defSOliver Tappe fZoneList->SetInvocationMessage(new BMessage(H_SET_TIME_ZONE)); 213750e57b8SOliver Tappe _BuildZoneMenu(); 21438ac8defSOliver Tappe BScrollView* scrollList = new BScrollView("scrollList", fZoneList, 215323ba9b7SRyan Leavengood B_FRAME_EVENTS | B_WILL_DRAW, false, true); 216c2f3ee3bSAdrien Destugues scrollList->SetExplicitMinSize(BSize(200, 0)); 217a10cf76eSAxel Dörfler 218323ba9b7SRyan Leavengood fCurrent = new TTZDisplay("currentTime", B_TRANSLATE("Current time:")); 219323ba9b7SRyan Leavengood fPreview = new TTZDisplay("previewTime", B_TRANSLATE("Preview time:")); 220a10cf76eSAxel Dörfler 221323ba9b7SRyan Leavengood fSetZone = new BButton("setTimeZone", B_TRANSLATE("Set time zone"), 22285b69a94SKarsten Heimrich new BMessage(H_SET_TIME_ZONE)); 22342b3a11fSStephan Aßmus fSetZone->SetEnabled(false); 224323ba9b7SRyan Leavengood fSetZone->SetExplicitAlignment( 225323ba9b7SRyan Leavengood BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM)); 226a10cf76eSAxel Dörfler 227c2f3ee3bSAdrien Destugues fLocalTime = new BRadioButton("localTime", 228553211b4SJohn Scipione B_TRANSLATE("Local time (Windows compatible)"), 229553211b4SJohn Scipione new BMessage(kRTCUpdate)); 230c2f3ee3bSAdrien Destugues fGmtTime = new BRadioButton("greenwichMeanTime", 231c2f3ee3bSAdrien Destugues B_TRANSLATE("GMT (UNIX compatible)"), new BMessage(kRTCUpdate)); 232c2f3ee3bSAdrien Destugues 233c2f3ee3bSAdrien Destugues if (fUseGmtTime) 234c2f3ee3bSAdrien Destugues fGmtTime->SetValue(B_CONTROL_ON); 235c2f3ee3bSAdrien Destugues else 236c2f3ee3bSAdrien Destugues fLocalTime->SetValue(B_CONTROL_ON); 237f3b7dcd4SAdrien Destugues _ShowOrHidePreview(); 238c2f3ee3bSAdrien Destugues fOldUseGmtTime = fUseGmtTime; 239c2f3ee3bSAdrien Destugues 240f496c69cSJohn Scipione const float kIndentSpacing 241f496c69cSJohn Scipione = be_control_look->DefaultItemSpacing() * 2; 242323ba9b7SRyan Leavengood BLayoutBuilder::Group<>(this) 243323ba9b7SRyan Leavengood .Add(scrollList) 244f496c69cSJohn Scipione .AddGroup(B_VERTICAL, 0) 245f496c69cSJohn Scipione .Add(new BStringView("clockSetTo", 246f496c69cSJohn Scipione B_TRANSLATE("Hardware clock set to:"))) 247f496c69cSJohn Scipione .AddGroup(B_VERTICAL, 0) 248c2f3ee3bSAdrien Destugues .Add(fLocalTime) 249c2f3ee3bSAdrien Destugues .Add(fGmtTime) 250f496c69cSJohn Scipione .SetInsets(kIndentSpacing, 0, 0, 0) 251c2f3ee3bSAdrien Destugues .End() 252f3b7dcd4SAdrien Destugues .AddGlue() 253f496c69cSJohn Scipione .AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING) 254f3b7dcd4SAdrien Destugues .Add(fCurrent) 255f3b7dcd4SAdrien Destugues .Add(fPreview) 256f496c69cSJohn Scipione .End() 257323ba9b7SRyan Leavengood .Add(fSetZone) 258323ba9b7SRyan Leavengood .End() 259f496c69cSJohn Scipione .SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING, 260f496c69cSJohn Scipione B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING); 261a10cf76eSAxel Dörfler } 262a10cf76eSAxel Dörfler 263a10cf76eSAxel Dörfler 264a10cf76eSAxel Dörfler void 265750e57b8SOliver Tappe TimeZoneView::_BuildZoneMenu() 266a10cf76eSAxel Dörfler { 267447b7fdeSOliver Tappe BTimeZone defaultTimeZone; 26825dc253dSIngo Weinhold BLocaleRoster::Default()->GetDefaultTimeZone(&defaultTimeZone); 26999d2aa98SOliver Tappe 270b7f60965SOliver Tappe BLanguage language; 27125dc253dSIngo Weinhold BLocale::Default()->GetLanguage(&language); 272a10cf76eSAxel Dörfler 273553211b4SJohn Scipione // Group timezones by regions, but filter out unwanted (duplicate) regions 274553211b4SJohn Scipione // and add an additional region with generic GMT-offset timezones at the end 275750e57b8SOliver Tappe typedef std::map<BString, TimeZoneListItem*, TimeZoneItemLess> ZoneItemMap; 276fefa98aaSOliver Tappe ZoneItemMap zoneItemMap; 277fefa98aaSOliver Tappe const char* kOtherRegion = B_TRANSLATE_MARK("<Other>"); 278447b7fdeSOliver Tappe const char* kSupportedRegions[] = { 279fefa98aaSOliver Tappe B_TRANSLATE_MARK("Africa"), B_TRANSLATE_MARK("America"), 280fefa98aaSOliver Tappe B_TRANSLATE_MARK("Antarctica"), B_TRANSLATE_MARK("Arctic"), 281fefa98aaSOliver Tappe B_TRANSLATE_MARK("Asia"), B_TRANSLATE_MARK("Atlantic"), 282fefa98aaSOliver Tappe B_TRANSLATE_MARK("Australia"), B_TRANSLATE_MARK("Europe"), 283fefa98aaSOliver Tappe B_TRANSLATE_MARK("Indian"), B_TRANSLATE_MARK("Pacific"), 284fefa98aaSOliver Tappe kOtherRegion, 285fefa98aaSOliver Tappe NULL 286750e57b8SOliver Tappe }; 287553211b4SJohn Scipione 288fefa98aaSOliver Tappe // Since the zone-map contains translated country-names (we get those from 289fefa98aaSOliver Tappe // ICU), we need to use translated region names in the zone-map, too: 290fefa98aaSOliver Tappe typedef std::map<BString, BString> TranslatedRegionMap; 291fefa98aaSOliver Tappe TranslatedRegionMap regionMap; 292fefa98aaSOliver Tappe for (const char** region = kSupportedRegions; *region != NULL; ++region) { 293fefa98aaSOliver Tappe BString translatedRegion = B_TRANSLATE_NOCOLLECT(*region); 294fefa98aaSOliver Tappe regionMap[*region] = translatedRegion; 295fefa98aaSOliver Tappe 296fefa98aaSOliver Tappe TimeZoneListItem* regionItem 297fefa98aaSOliver Tappe = new TimeZoneListItem(translatedRegion, NULL, NULL); 298fefa98aaSOliver Tappe regionItem->SetOutlineLevel(0); 299fefa98aaSOliver Tappe zoneItemMap[translatedRegion] = regionItem; 300fefa98aaSOliver Tappe } 301a10cf76eSAxel Dörfler 302eb78be9bSOliver Tappe // Get all time zones 303eb78be9bSOliver Tappe BMessage zoneList; 304eb78be9bSOliver Tappe BLocaleRoster::Default()->GetAvailableTimeZonesWithRegionInfo(&zoneList); 305eb78be9bSOliver Tappe 306eb78be9bSOliver Tappe typedef std::map<BString, std::vector<const char*> > ZonesByCountyMap; 307eb78be9bSOliver Tappe ZonesByCountyMap zonesByCountryMap; 308eb78be9bSOliver Tappe const char* zoneID; 309553211b4SJohn Scipione BString timeZoneCode; 310eb78be9bSOliver Tappe for (int tz = 0; zoneList.FindString("timeZone", tz, &zoneID) == B_OK 311553211b4SJohn Scipione && zoneList.FindString("region", tz, &timeZoneCode) == B_OK; tz++) { 312eb78be9bSOliver Tappe // From the global ("001") timezones, we only accept the generic GMT 313eb78be9bSOliver Tappe // timezones, as all the other world-zones are duplicates of others. 314553211b4SJohn Scipione if (timeZoneCode == "001" && strncmp(zoneID, "Etc/GMT", 7) != 0) 315eb78be9bSOliver Tappe continue; 316553211b4SJohn Scipione zonesByCountryMap[timeZoneCode].push_back(zoneID); 317eb78be9bSOliver Tappe } 318eb78be9bSOliver Tappe 319eb78be9bSOliver Tappe ZonesByCountyMap::const_iterator countryIter = zonesByCountryMap.begin(); 320eb78be9bSOliver Tappe for (; countryIter != zonesByCountryMap.end(); ++countryIter) { 3215b013eabSJohn Scipione const char* countryCode = countryIter->first.String(); 3225b013eabSJohn Scipione if (countryCode == NULL) 3235b013eabSJohn Scipione continue; 3243740f52dSOliver Tappe 325eb78be9bSOliver Tappe size_t zoneCountInCountry = countryIter->second.size(); 326eb78be9bSOliver Tappe for (size_t tz = 0; tz < zoneCountInCountry; tz++) { 327eb78be9bSOliver Tappe BString zoneID(countryIter->second[tz]); 3285b013eabSJohn Scipione BTimeZone* timeZone 3295b013eabSJohn Scipione = new(std::nothrow) BTimeZone(zoneID, &language); 3305b013eabSJohn Scipione if (timeZone == NULL) 3315b013eabSJohn Scipione continue; 332a10cf76eSAxel Dörfler 3335b013eabSJohn Scipione int32 slashPos = zoneID.FindFirst('/'); 334750e57b8SOliver Tappe BString region(zoneID, slashPos); 33545f2f22bSOliver Tappe if (region == "Etc") 336447b7fdeSOliver Tappe region = kOtherRegion; 337447b7fdeSOliver Tappe 338fefa98aaSOliver Tappe // just accept timezones from our supported regions, others are 339fefa98aaSOliver Tappe // aliases and would just make the list even longer 340fefa98aaSOliver Tappe TranslatedRegionMap::iterator regionIter = regionMap.find(region); 341bafd2297SOliver Tappe if (regionIter == regionMap.end()) 342750e57b8SOliver Tappe continue; 343750e57b8SOliver Tappe 344eb78be9bSOliver Tappe BString fullCountryID = regionIter->second; 3455b013eabSJohn Scipione BCountry* country = new(std::nothrow) BCountry(countryCode); 3465b013eabSJohn Scipione if (country == NULL) 3475b013eabSJohn Scipione continue; 3485b013eabSJohn Scipione 3495b013eabSJohn Scipione BString countryName; 3505b013eabSJohn Scipione country->GetName(countryName); 3515b013eabSJohn Scipione bool hasUsedCountry = false; 352553211b4SJohn Scipione bool countryIsRegion = countryName == regionIter->second 353553211b4SJohn Scipione || region == kOtherRegion; 354fefa98aaSOliver Tappe if (!countryIsRegion) 3553740f52dSOliver Tappe fullCountryID << "/" << countryName; 3563740f52dSOliver Tappe 3575b013eabSJohn Scipione BString timeZoneName; 358eb78be9bSOliver Tappe BString fullZoneID = fullCountryID; 359553211b4SJohn Scipione if (zoneCountInCountry > 1) { 360eb78be9bSOliver Tappe // we can't use the country name as timezone name, since there 361eb78be9bSOliver Tappe // are more than one timezones in this country - fetch the 362eb78be9bSOliver Tappe // localized name of the timezone and use that 3635b013eabSJohn Scipione timeZoneName = timeZone->Name(); 3645b013eabSJohn Scipione int32 openParenthesisPos = timeZoneName.FindFirst('('); 365750e57b8SOliver Tappe if (openParenthesisPos >= 0) { 3665b013eabSJohn Scipione timeZoneName.Remove(0, openParenthesisPos + 1); 3675b013eabSJohn Scipione int32 closeParenthesisPos = timeZoneName.FindLast(')'); 368750e57b8SOliver Tappe if (closeParenthesisPos >= 0) 3695b013eabSJohn Scipione timeZoneName.Truncate(closeParenthesisPos); 370750e57b8SOliver Tappe } 3715b013eabSJohn Scipione fullZoneID << "/" << timeZoneName; 372eb78be9bSOliver Tappe } else { 3735b013eabSJohn Scipione timeZoneName = countryName; 374eb78be9bSOliver Tappe fullZoneID << "/" << zoneID; 375eb78be9bSOliver Tappe } 376750e57b8SOliver Tappe 377750e57b8SOliver Tappe // skip duplicates 378fefa98aaSOliver Tappe ZoneItemMap::iterator zoneIter = zoneItemMap.find(fullZoneID); 379fefa98aaSOliver Tappe if (zoneIter != zoneItemMap.end()) { 380750e57b8SOliver Tappe delete timeZone; 381750e57b8SOliver Tappe continue; 382750e57b8SOliver Tappe } 383750e57b8SOliver Tappe 384750e57b8SOliver Tappe TimeZoneListItem* countryItem = NULL; 3853740f52dSOliver Tappe TimeZoneListItem* zoneItem = NULL; 386eb78be9bSOliver Tappe if (zoneCountInCountry > 1) { 387fefa98aaSOliver Tappe ZoneItemMap::iterator countryIter 388fefa98aaSOliver Tappe = zoneItemMap.find(fullCountryID); 389fefa98aaSOliver Tappe if (countryIter == zoneItemMap.end()) { 3905b013eabSJohn Scipione countryItem = new TimeZoneListItem(countryName.String(), 3915b013eabSJohn Scipione country, NULL); 392750e57b8SOliver Tappe countryItem->SetOutlineLevel(1); 393fefa98aaSOliver Tappe zoneItemMap[fullCountryID] = countryItem; 3945b013eabSJohn Scipione hasUsedCountry = true; 395750e57b8SOliver Tappe } else 396750e57b8SOliver Tappe countryItem = countryIter->second; 3974ae5a452SAdrien Destugues 3985b013eabSJohn Scipione zoneItem = new TimeZoneListItem(timeZoneName.String(), 3995b013eabSJohn Scipione NULL, timeZone); 400fefa98aaSOliver Tappe zoneItem->SetOutlineLevel(countryIsRegion ? 1 : 2); 4013740f52dSOliver Tappe } else { 4025b013eabSJohn Scipione zoneItem = new TimeZoneListItem(timeZoneName.String(), 4035b013eabSJohn Scipione country, timeZone); 4043740f52dSOliver Tappe zoneItem->SetOutlineLevel(1); 4055b013eabSJohn Scipione hasUsedCountry = true; 4063740f52dSOliver Tappe } 407fefa98aaSOliver Tappe zoneItemMap[fullZoneID] = zoneItem; 408750e57b8SOliver Tappe 409750e57b8SOliver Tappe if (timeZone->ID() == defaultTimeZone.ID()) { 410750e57b8SOliver Tappe fCurrentZoneItem = zoneItem; 411750e57b8SOliver Tappe if (countryItem != NULL) 41299d2aa98SOliver Tappe countryItem->SetExpanded(true); 413553211b4SJohn Scipione 414fefa98aaSOliver Tappe ZoneItemMap::iterator regionItemIter 415eb78be9bSOliver Tappe = zoneItemMap.find(regionIter->second); 416fefa98aaSOliver Tappe if (regionItemIter != zoneItemMap.end()) 417fefa98aaSOliver Tappe regionItemIter->second->SetExpanded(true); 41899d2aa98SOliver Tappe } 4195b013eabSJohn Scipione 4205b013eabSJohn Scipione if (!hasUsedCountry) 4215b013eabSJohn Scipione delete country; 42242b3a11fSStephan Aßmus } 4233740f52dSOliver Tappe } 42499d2aa98SOliver Tappe 42538ac8defSOliver Tappe fOldZoneItem = fCurrentZoneItem; 42699d2aa98SOliver Tappe 427750e57b8SOliver Tappe ZoneItemMap::iterator zoneIter; 4283740f52dSOliver Tappe bool lastWasCountryItem = false; 4295b013eabSJohn Scipione TimeZoneListItem* currentItem = NULL; 430fefa98aaSOliver Tappe for (zoneIter = zoneItemMap.begin(); zoneIter != zoneItemMap.end(); 431fefa98aaSOliver Tappe ++zoneIter) { 4323740f52dSOliver Tappe if (zoneIter->second->OutlineLevel() == 2 && lastWasCountryItem) { 433553211b4SJohn Scipione // Some countries (e.g. Spain and Chile) have their timezones 434553211b4SJohn Scipione // spread across different regions. As a result, there might still 435553211b4SJohn Scipione // be country items with only one timezone below them. We manually 436553211b4SJohn Scipione // filter those country items here. 4373740f52dSOliver Tappe ZoneItemMap::iterator next = zoneIter; 4383740f52dSOliver Tappe ++next; 439fefa98aaSOliver Tappe if (next != zoneItemMap.end() 440fefa98aaSOliver Tappe && next->second->OutlineLevel() != 2) { 4415b013eabSJohn Scipione fZoneList->RemoveItem(currentItem); 4425b013eabSJohn Scipione zoneIter->second->SetText(currentItem->Text()); 4435b013eabSJohn Scipione zoneIter->second->SetCountry(currentItem->HasCountry() 4445b013eabSJohn Scipione ? new(std::nothrow) BCountry(currentItem->Country()) 4455b013eabSJohn Scipione : NULL); 4465b013eabSJohn Scipione zoneIter->second->SetTimeZone(currentItem->HasTimeZone() 4475b013eabSJohn Scipione ? new(std::nothrow) BTimeZone(currentItem->TimeZone()) 4485b013eabSJohn Scipione : NULL); 4493740f52dSOliver Tappe zoneIter->second->SetOutlineLevel(1); 4505b013eabSJohn Scipione delete currentItem; 4513740f52dSOliver Tappe } 4523740f52dSOliver Tappe } 4533740f52dSOliver Tappe 454750e57b8SOliver Tappe fZoneList->AddItem(zoneIter->second); 4553740f52dSOliver Tappe if (zoneIter->second->OutlineLevel() == 1) { 4563740f52dSOliver Tappe lastWasCountryItem = true; 4575b013eabSJohn Scipione currentItem = zoneIter->second; 4583740f52dSOliver Tappe } else 4593740f52dSOliver Tappe lastWasCountryItem = false; 4603740f52dSOliver Tappe } 46199d2aa98SOliver Tappe } 462750e57b8SOliver Tappe 463750e57b8SOliver Tappe 464750e57b8SOliver Tappe void 465750e57b8SOliver Tappe TimeZoneView::_Revert() 466750e57b8SOliver Tappe { 467750e57b8SOliver Tappe fCurrentZoneItem = fOldZoneItem; 468750e57b8SOliver Tappe 469750e57b8SOliver Tappe if (fCurrentZoneItem != NULL) { 470750e57b8SOliver Tappe int32 currentZoneIndex = fZoneList->IndexOf(fCurrentZoneItem); 471750e57b8SOliver Tappe fZoneList->Select(currentZoneIndex); 472750e57b8SOliver Tappe } else 473750e57b8SOliver Tappe fZoneList->DeselectAll(); 474750e57b8SOliver Tappe fZoneList->ScrollToSelection(); 475750e57b8SOliver Tappe 476c2f3ee3bSAdrien Destugues fUseGmtTime = fOldUseGmtTime; 477c2f3ee3bSAdrien Destugues if (fUseGmtTime) 478c2f3ee3bSAdrien Destugues fGmtTime->SetValue(B_CONTROL_ON); 479c2f3ee3bSAdrien Destugues else 480c2f3ee3bSAdrien Destugues fLocalTime->SetValue(B_CONTROL_ON); 481f3b7dcd4SAdrien Destugues _ShowOrHidePreview(); 482c2f3ee3bSAdrien Destugues 483c2f3ee3bSAdrien Destugues _UpdateGmtSettings(); 484750e57b8SOliver Tappe _SetSystemTimeZone(); 485750e57b8SOliver Tappe _UpdatePreview(); 486750e57b8SOliver Tappe _UpdateCurrent(); 487a10cf76eSAxel Dörfler } 488a10cf76eSAxel Dörfler 489a10cf76eSAxel Dörfler 490a10cf76eSAxel Dörfler void 49138ac8defSOliver Tappe TimeZoneView::_UpdatePreview() 492a10cf76eSAxel Dörfler { 49338ac8defSOliver Tappe int32 selection = fZoneList->CurrentSelection(); 494750e57b8SOliver Tappe TimeZoneListItem* item 495750e57b8SOliver Tappe = selection < 0 496750e57b8SOliver Tappe ? NULL 497553211b4SJohn Scipione : static_cast<TimeZoneListItem*>(fZoneList->ItemAt(selection)); 498a10cf76eSAxel Dörfler 499750e57b8SOliver Tappe if (item == NULL || !item->HasTimeZone()) { 500750e57b8SOliver Tappe fPreview->SetText(""); 501750e57b8SOliver Tappe fPreview->SetTime(""); 50238ac8defSOliver Tappe return; 503750e57b8SOliver Tappe } 504a10cf76eSAxel Dörfler 505447b7fdeSOliver Tappe BString timeString = _FormatTime(item->TimeZone()); 50638ac8defSOliver Tappe fPreview->SetText(item->Text()); 50738ac8defSOliver Tappe fPreview->SetTime(timeString.String()); 508a10cf76eSAxel Dörfler 50938ac8defSOliver Tappe fSetZone->SetEnabled((strcmp(fCurrent->Text(), item->Text()) != 0)); 510a10cf76eSAxel Dörfler } 511a10cf76eSAxel Dörfler 512a10cf76eSAxel Dörfler 513a10cf76eSAxel Dörfler void 51438ac8defSOliver Tappe TimeZoneView::_UpdateCurrent() 515a10cf76eSAxel Dörfler { 51638ac8defSOliver Tappe if (fCurrentZoneItem == NULL) 51738ac8defSOliver Tappe return; 5181855b44cSAdrien Destugues 519447b7fdeSOliver Tappe BString timeString = _FormatTime(fCurrentZoneItem->TimeZone()); 52038ac8defSOliver Tappe fCurrent->SetText(fCurrentZoneItem->Text()); 52138ac8defSOliver Tappe fCurrent->SetTime(timeString.String()); 522a10cf76eSAxel Dörfler } 523a10cf76eSAxel Dörfler 52438ac8defSOliver Tappe 52538ac8defSOliver Tappe void 52638ac8defSOliver Tappe TimeZoneView::_SetSystemTimeZone() 52738ac8defSOliver Tappe { 528d88a361cSOliver Tappe /* Set system timezone for all different API levels. How to do this? 52938ac8defSOliver Tappe * 1) tell locale-roster about new default timezone 53085b54438SOliver Tappe * 2) tell kernel about new timezone offset 53138ac8defSOliver Tappe */ 53238ac8defSOliver Tappe 53338ac8defSOliver Tappe int32 selection = fZoneList->CurrentSelection(); 53438ac8defSOliver Tappe if (selection < 0) 53538ac8defSOliver Tappe return; 53638ac8defSOliver Tappe 537447b7fdeSOliver Tappe TimeZoneListItem* item 538447b7fdeSOliver Tappe = static_cast<TimeZoneListItem*>(fZoneList->ItemAt(selection)); 539447b7fdeSOliver Tappe if (item == NULL || !item->HasTimeZone()) 540447b7fdeSOliver Tappe return; 541447b7fdeSOliver Tappe 542447b7fdeSOliver Tappe fCurrentZoneItem = item; 543447b7fdeSOliver Tappe const BTimeZone& timeZone = item->TimeZone(); 54438ac8defSOliver Tappe 54525dc253dSIngo Weinhold MutableLocaleRoster::Default()->SetDefaultTimeZone(timeZone); 54638ac8defSOliver Tappe 547750e57b8SOliver Tappe _kern_set_timezone(timeZone.OffsetFromGMT(), timeZone.ID().String(), 548750e57b8SOliver Tappe timeZone.ID().Length()); 54985b54438SOliver Tappe 55038ac8defSOliver Tappe fSetZone->SetEnabled(false); 55138ac8defSOliver Tappe fLastUpdateMinute = -1; 55238ac8defSOliver Tappe // just to trigger updating immediately 55338ac8defSOliver Tappe } 55438ac8defSOliver Tappe 55538ac8defSOliver Tappe 55638ac8defSOliver Tappe BString 5573740f52dSOliver Tappe TimeZoneView::_FormatTime(const BTimeZone& timeZone) 55838ac8defSOliver Tappe { 55938ac8defSOliver Tappe BString result; 56038ac8defSOliver Tappe 561a096bb65SOliver Tappe time_t now = time(NULL); 562a096bb65SOliver Tappe bool rtcIsGMT; 563a096bb65SOliver Tappe _kern_get_real_time_clock_is_gmt(&rtcIsGMT); 5643740f52dSOliver Tappe if (!rtcIsGMT) { 565447b7fdeSOliver Tappe int32 currentOffset 566447b7fdeSOliver Tappe = fCurrentZoneItem != NULL && fCurrentZoneItem->HasTimeZone() 567447b7fdeSOliver Tappe ? fCurrentZoneItem->OffsetFromGMT() 568447b7fdeSOliver Tappe : 0; 569447b7fdeSOliver Tappe now -= timeZone.OffsetFromGMT() - currentOffset; 570a096bb65SOliver Tappe } 57103b2550eSAdrien Destugues fTimeFormat.Format(result, now, B_SHORT_TIME_FORMAT, &timeZone); 57238ac8defSOliver Tappe 57338ac8defSOliver Tappe return result; 57438ac8defSOliver Tappe } 575c2f3ee3bSAdrien Destugues 576c2f3ee3bSAdrien Destugues 577c2f3ee3bSAdrien Destugues void 578c2f3ee3bSAdrien Destugues TimeZoneView::_ReadRTCSettings() 579c2f3ee3bSAdrien Destugues { 580c2f3ee3bSAdrien Destugues BPath path; 581c2f3ee3bSAdrien Destugues if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK) 582c2f3ee3bSAdrien Destugues return; 583c2f3ee3bSAdrien Destugues 584c2f3ee3bSAdrien Destugues path.Append("RTC_time_settings"); 585c2f3ee3bSAdrien Destugues 586c2f3ee3bSAdrien Destugues BEntry entry(path.Path()); 587c2f3ee3bSAdrien Destugues if (entry.Exists()) { 588c2f3ee3bSAdrien Destugues BFile file(&entry, B_READ_ONLY); 589c2f3ee3bSAdrien Destugues if (file.InitCheck() == B_OK) { 590c2f3ee3bSAdrien Destugues char buffer[6]; 591c2f3ee3bSAdrien Destugues file.Read(buffer, 6); 592c2f3ee3bSAdrien Destugues if (strncmp(buffer, "gmt", 3) == 0) 593c2f3ee3bSAdrien Destugues fUseGmtTime = true; 594c2f3ee3bSAdrien Destugues } 595c2f3ee3bSAdrien Destugues } 596c2f3ee3bSAdrien Destugues } 597c2f3ee3bSAdrien Destugues 598c2f3ee3bSAdrien Destugues 599c2f3ee3bSAdrien Destugues void 600c2f3ee3bSAdrien Destugues TimeZoneView::_WriteRTCSettings() 601c2f3ee3bSAdrien Destugues { 602c2f3ee3bSAdrien Destugues BPath path; 603c2f3ee3bSAdrien Destugues if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 604c2f3ee3bSAdrien Destugues return; 605c2f3ee3bSAdrien Destugues 606c2f3ee3bSAdrien Destugues path.Append("RTC_time_settings"); 607c2f3ee3bSAdrien Destugues 608c2f3ee3bSAdrien Destugues BFile file(path.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY); 609c2f3ee3bSAdrien Destugues if (file.InitCheck() == B_OK) { 610c2f3ee3bSAdrien Destugues if (fUseGmtTime) 611c2f3ee3bSAdrien Destugues file.Write("gmt", 3); 612c2f3ee3bSAdrien Destugues else 613c2f3ee3bSAdrien Destugues file.Write("local", 5); 614c2f3ee3bSAdrien Destugues } 615c2f3ee3bSAdrien Destugues } 616c2f3ee3bSAdrien Destugues 617c2f3ee3bSAdrien Destugues 618c2f3ee3bSAdrien Destugues void 619c2f3ee3bSAdrien Destugues TimeZoneView::_UpdateGmtSettings() 620c2f3ee3bSAdrien Destugues { 621c2f3ee3bSAdrien Destugues _WriteRTCSettings(); 622c2f3ee3bSAdrien Destugues 623f3b7dcd4SAdrien Destugues _ShowOrHidePreview(); 624c2f3ee3bSAdrien Destugues 625c2f3ee3bSAdrien Destugues _kern_set_real_time_clock_is_gmt(fUseGmtTime); 626c2f3ee3bSAdrien Destugues } 627c2f3ee3bSAdrien Destugues 628c2f3ee3bSAdrien Destugues 629c2f3ee3bSAdrien Destugues void 630f3b7dcd4SAdrien Destugues TimeZoneView::_ShowOrHidePreview() 631f3b7dcd4SAdrien Destugues { 632f3b7dcd4SAdrien Destugues if (fUseGmtTime) { 633f3b7dcd4SAdrien Destugues // Hardware clock uses GMT time, changing timezone will adjust the 634f3b7dcd4SAdrien Destugues // offset and we need to display a preview 635f3b7dcd4SAdrien Destugues fCurrent->Show(); 636f3b7dcd4SAdrien Destugues fPreview->Show(); 637f3b7dcd4SAdrien Destugues } else { 638f3b7dcd4SAdrien Destugues // Hardware clock uses local time, changing timezone will adjust the 639f3b7dcd4SAdrien Destugues // clock and there is no offset to manage, thus, no preview. 640f3b7dcd4SAdrien Destugues fCurrent->Hide(); 641f3b7dcd4SAdrien Destugues fPreview->Hide(); 642f3b7dcd4SAdrien Destugues } 643f3b7dcd4SAdrien Destugues } 644