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
1673de5837SJé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 {
operator ()TimeZoneItemLess69*52f87edcSAugustin Cavalier bool operator()(const BString& first, const BString& second) const
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
TimeZoneView(const char * name)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
CheckCanRevert()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
~TimeZoneView()10985b69a94SKarsten Heimrich TimeZoneView::~TimeZoneView()
110a10cf76eSAxel Dörfler {
111c2f3ee3bSAdrien Destugues _WriteRTCSettings();
112a10cf76eSAxel Dörfler }
113a10cf76eSAxel Dörfler
114a10cf76eSAxel Dörfler
115a10cf76eSAxel Dörfler void
AttachedToWindow()11685b69a94SKarsten Heimrich TimeZoneView::AttachedToWindow()
117a10cf76eSAxel Dörfler {
118447b7fdeSOliver Tappe BView::AttachedToWindow();
119f0650dc9Slooncraz AdoptParentColors();
120a10cf76eSAxel Dörfler
12185b69a94SKarsten Heimrich if (!fInitialized) {
12285b69a94SKarsten Heimrich fInitialized = true;
12385b69a94SKarsten Heimrich
12442b3a11fSStephan Aßmus fSetZone->SetTarget(this);
12538ac8defSOliver Tappe fZoneList->SetTarget(this);
12628ca7ae0Sanevilyak }
12728ca7ae0Sanevilyak }
12842b3a11fSStephan Aßmus
12928ca7ae0Sanevilyak
13028ca7ae0Sanevilyak void
DoLayout()13128ca7ae0Sanevilyak TimeZoneView::DoLayout()
13228ca7ae0Sanevilyak {
13328ca7ae0Sanevilyak BView::DoLayout();
13438ac8defSOliver Tappe if (fCurrentZoneItem != NULL) {
135447b7fdeSOliver Tappe fZoneList->Select(fZoneList->IndexOf(fCurrentZoneItem));
13638ac8defSOliver Tappe fCurrent->SetText(fCurrentZoneItem->Text());
13738ac8defSOliver Tappe fZoneList->ScrollToSelection();
138a10cf76eSAxel Dörfler }
139323ba9b7SRyan Leavengood }
140a10cf76eSAxel Dörfler
141a10cf76eSAxel Dörfler
142a10cf76eSAxel Dörfler void
MessageReceived(BMessage * message)14385b69a94SKarsten Heimrich TimeZoneView::MessageReceived(BMessage* message)
144a10cf76eSAxel Dörfler {
145a10cf76eSAxel Dörfler switch (message->what) {
146a10cf76eSAxel Dörfler case B_OBSERVER_NOTICE_CHANGE:
147317bd7ddSAxel Dörfler {
148317bd7ddSAxel Dörfler int32 change;
149a10cf76eSAxel Dörfler message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change);
150a10cf76eSAxel Dörfler switch(change) {
151a10cf76eSAxel Dörfler case H_TM_CHANGED:
1526d6408d6SOliver Tappe _UpdateDateTime(message);
153a10cf76eSAxel Dörfler break;
154a10cf76eSAxel Dörfler
155a10cf76eSAxel Dörfler default:
156a10cf76eSAxel Dörfler BView::MessageReceived(message);
157a10cf76eSAxel Dörfler break;
158a10cf76eSAxel Dörfler }
159a10cf76eSAxel Dörfler break;
160317bd7ddSAxel Dörfler }
161317bd7ddSAxel Dörfler
162447b7fdeSOliver Tappe case H_CITY_CHANGED:
163447b7fdeSOliver Tappe _UpdatePreview();
164447b7fdeSOliver Tappe break;
165447b7fdeSOliver Tappe
166a10cf76eSAxel Dörfler case H_SET_TIME_ZONE:
167310930fdSStephan Aßmus {
16838ac8defSOliver Tappe _SetSystemTimeZone();
169310930fdSStephan Aßmus break;
170310930fdSStephan Aßmus }
171310930fdSStephan Aßmus
172310930fdSStephan Aßmus case kMsgRevert:
173310930fdSStephan Aßmus _Revert();
174a10cf76eSAxel Dörfler break;
175a10cf76eSAxel Dörfler
176447b7fdeSOliver Tappe case kRTCUpdate:
177c2f3ee3bSAdrien Destugues fUseGmtTime = fGmtTime->Value() == B_CONTROL_ON;
178c2f3ee3bSAdrien Destugues _UpdateGmtSettings();
179447b7fdeSOliver Tappe _UpdateCurrent();
18038ac8defSOliver Tappe _UpdatePreview();
181a10cf76eSAxel Dörfler break;
182a10cf76eSAxel Dörfler
183a10cf76eSAxel Dörfler default:
184323ba9b7SRyan Leavengood BGroupView::MessageReceived(message);
185a10cf76eSAxel Dörfler break;
186a10cf76eSAxel Dörfler }
187a10cf76eSAxel Dörfler }
188a10cf76eSAxel Dörfler
189a10cf76eSAxel Dörfler
190a10cf76eSAxel Dörfler void
_UpdateDateTime(BMessage * message)1916d6408d6SOliver Tappe TimeZoneView::_UpdateDateTime(BMessage* message)
192a10cf76eSAxel Dörfler {
19338ac8defSOliver Tappe // only need to update once every minute
19442b3a11fSStephan Aßmus int32 minute;
19538ac8defSOliver Tappe if (message->FindInt32("minute", &minute) == B_OK) {
19638ac8defSOliver Tappe if (fLastUpdateMinute != minute) {
19738ac8defSOliver Tappe _UpdateCurrent();
19838ac8defSOliver Tappe _UpdatePreview();
199a10cf76eSAxel Dörfler
20038ac8defSOliver Tappe fLastUpdateMinute = minute;
201a10cf76eSAxel Dörfler }
202a10cf76eSAxel Dörfler }
203a10cf76eSAxel Dörfler }
204a10cf76eSAxel Dörfler
205a10cf76eSAxel Dörfler
206a10cf76eSAxel Dörfler void
_InitView()2076d6408d6SOliver Tappe TimeZoneView::_InitView()
208a10cf76eSAxel Dörfler {
209f0e995c8SZiusudra fZoneList = new TimeZoneListView();
21038ac8defSOliver Tappe fZoneList->SetSelectionMessage(new BMessage(H_CITY_CHANGED));
21138ac8defSOliver Tappe fZoneList->SetInvocationMessage(new BMessage(H_SET_TIME_ZONE));
212750e57b8SOliver Tappe _BuildZoneMenu();
21338ac8defSOliver Tappe BScrollView* scrollList = new BScrollView("scrollList", fZoneList,
214323ba9b7SRyan Leavengood B_FRAME_EVENTS | B_WILL_DRAW, false, true);
21564ee9242SAxel Dörfler scrollList->SetExplicitMinSize(
21664ee9242SAxel Dörfler BSize(200 * be_plain_font->Size() / 12.0f, 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
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)
248ad926b25SHumdinger .SetInsets(B_USE_WINDOW_SPACING, 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()
257ad926b25SHumdinger .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
258ad926b25SHumdinger B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING);
259a10cf76eSAxel Dörfler }
260a10cf76eSAxel Dörfler
261a10cf76eSAxel Dörfler
262a10cf76eSAxel Dörfler void
_BuildZoneMenu()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
271553211b4SJohn Scipione // Group timezones by regions, but filter out unwanted (duplicate) regions
272553211b4SJohn 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 };
285553211b4SJohn 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;
307553211b4SJohn Scipione BString timeZoneCode;
308eb78be9bSOliver Tappe for (int tz = 0; zoneList.FindString("timeZone", tz, &zoneID) == B_OK
309553211b4SJohn 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.
312553211b4SJohn Scipione if (timeZoneCode == "001" && strncmp(zoneID, "Etc/GMT", 7) != 0)
313eb78be9bSOliver Tappe continue;
314553211b4SJohn 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) {
3195b013eabSJohn Scipione const char* countryCode = countryIter->first.String();
3205b013eabSJohn Scipione if (countryCode == NULL)
3215b013eabSJohn Scipione continue;
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]);
3265b013eabSJohn Scipione BTimeZone* timeZone
3275b013eabSJohn Scipione = new(std::nothrow) BTimeZone(zoneID, &language);
3285b013eabSJohn Scipione if (timeZone == NULL)
3295b013eabSJohn Scipione continue;
330a10cf76eSAxel Dörfler
3315b013eabSJohn Scipione int32 slashPos = zoneID.FindFirst('/');
332750e57b8SOliver Tappe BString region(zoneID, slashPos);
33345f2f22bSOliver Tappe if (region == "Etc")
334447b7fdeSOliver Tappe region = kOtherRegion;
335447b7fdeSOliver Tappe
336fefa98aaSOliver Tappe // just accept timezones from our supported regions, others are
337fefa98aaSOliver Tappe // aliases and would just make the list even longer
338fefa98aaSOliver Tappe TranslatedRegionMap::iterator regionIter = regionMap.find(region);
339bafd2297SOliver Tappe if (regionIter == regionMap.end())
340750e57b8SOliver Tappe continue;
341750e57b8SOliver Tappe
342eb78be9bSOliver Tappe BString fullCountryID = regionIter->second;
3435b013eabSJohn Scipione BCountry* country = new(std::nothrow) BCountry(countryCode);
3445b013eabSJohn Scipione if (country == NULL)
3455b013eabSJohn Scipione continue;
3465b013eabSJohn Scipione
3475b013eabSJohn Scipione BString countryName;
3485b013eabSJohn Scipione country->GetName(countryName);
3495b013eabSJohn Scipione bool hasUsedCountry = false;
350553211b4SJohn Scipione bool countryIsRegion = countryName == regionIter->second
351553211b4SJohn Scipione || region == kOtherRegion;
352fefa98aaSOliver Tappe if (!countryIsRegion)
3533740f52dSOliver Tappe fullCountryID << "/" << countryName;
3543740f52dSOliver Tappe
3555b013eabSJohn Scipione BString timeZoneName;
356eb78be9bSOliver Tappe BString fullZoneID = fullCountryID;
357553211b4SJohn Scipione if (zoneCountInCountry > 1) {
358eb78be9bSOliver Tappe // we can't use the country name as timezone name, since there
359eb78be9bSOliver Tappe // are more than one timezones in this country - fetch the
360eb78be9bSOliver Tappe // localized name of the timezone and use that
3615b013eabSJohn Scipione timeZoneName = timeZone->Name();
3625b013eabSJohn Scipione int32 openParenthesisPos = timeZoneName.FindFirst('(');
363750e57b8SOliver Tappe if (openParenthesisPos >= 0) {
3645b013eabSJohn Scipione timeZoneName.Remove(0, openParenthesisPos + 1);
3655b013eabSJohn Scipione int32 closeParenthesisPos = timeZoneName.FindLast(')');
366750e57b8SOliver Tappe if (closeParenthesisPos >= 0)
3675b013eabSJohn Scipione timeZoneName.Truncate(closeParenthesisPos);
368750e57b8SOliver Tappe }
3695b013eabSJohn Scipione fullZoneID << "/" << timeZoneName;
370eb78be9bSOliver Tappe } else {
3715b013eabSJohn Scipione timeZoneName = countryName;
372eb78be9bSOliver Tappe fullZoneID << "/" << zoneID;
373eb78be9bSOliver Tappe }
374750e57b8SOliver Tappe
375750e57b8SOliver Tappe // skip duplicates
376fefa98aaSOliver Tappe ZoneItemMap::iterator zoneIter = zoneItemMap.find(fullZoneID);
377fefa98aaSOliver Tappe if (zoneIter != zoneItemMap.end()) {
378750e57b8SOliver Tappe delete timeZone;
379750e57b8SOliver Tappe continue;
380750e57b8SOliver Tappe }
381750e57b8SOliver Tappe
382750e57b8SOliver Tappe TimeZoneListItem* countryItem = NULL;
3833740f52dSOliver Tappe TimeZoneListItem* zoneItem = NULL;
384eb78be9bSOliver Tappe if (zoneCountInCountry > 1) {
385fefa98aaSOliver Tappe ZoneItemMap::iterator countryIter
386fefa98aaSOliver Tappe = zoneItemMap.find(fullCountryID);
387fefa98aaSOliver Tappe if (countryIter == zoneItemMap.end()) {
3885b013eabSJohn Scipione countryItem = new TimeZoneListItem(countryName.String(),
3895b013eabSJohn Scipione country, NULL);
390750e57b8SOliver Tappe countryItem->SetOutlineLevel(1);
391fefa98aaSOliver Tappe zoneItemMap[fullCountryID] = countryItem;
3925b013eabSJohn Scipione hasUsedCountry = true;
393750e57b8SOliver Tappe } else
394750e57b8SOliver Tappe countryItem = countryIter->second;
3954ae5a452SAdrien Destugues
3965b013eabSJohn Scipione zoneItem = new TimeZoneListItem(timeZoneName.String(),
3975b013eabSJohn Scipione NULL, timeZone);
398fefa98aaSOliver Tappe zoneItem->SetOutlineLevel(countryIsRegion ? 1 : 2);
3993740f52dSOliver Tappe } else {
4005b013eabSJohn Scipione zoneItem = new TimeZoneListItem(timeZoneName.String(),
4015b013eabSJohn Scipione country, timeZone);
4023740f52dSOliver Tappe zoneItem->SetOutlineLevel(1);
4035b013eabSJohn Scipione hasUsedCountry = true;
4043740f52dSOliver Tappe }
405fefa98aaSOliver Tappe zoneItemMap[fullZoneID] = zoneItem;
406750e57b8SOliver Tappe
407750e57b8SOliver Tappe if (timeZone->ID() == defaultTimeZone.ID()) {
408750e57b8SOliver Tappe fCurrentZoneItem = zoneItem;
409750e57b8SOliver Tappe if (countryItem != NULL)
41099d2aa98SOliver Tappe countryItem->SetExpanded(true);
411553211b4SJohn Scipione
412fefa98aaSOliver Tappe ZoneItemMap::iterator regionItemIter
413eb78be9bSOliver Tappe = zoneItemMap.find(regionIter->second);
414fefa98aaSOliver Tappe if (regionItemIter != zoneItemMap.end())
415fefa98aaSOliver Tappe regionItemIter->second->SetExpanded(true);
41699d2aa98SOliver Tappe }
4175b013eabSJohn Scipione
4185b013eabSJohn Scipione if (!hasUsedCountry)
4195b013eabSJohn Scipione delete country;
42042b3a11fSStephan Aßmus }
4213740f52dSOliver Tappe }
42299d2aa98SOliver Tappe
42338ac8defSOliver Tappe fOldZoneItem = fCurrentZoneItem;
42499d2aa98SOliver Tappe
425750e57b8SOliver Tappe ZoneItemMap::iterator zoneIter;
4263740f52dSOliver Tappe bool lastWasCountryItem = false;
4275b013eabSJohn Scipione TimeZoneListItem* currentItem = NULL;
428fefa98aaSOliver Tappe for (zoneIter = zoneItemMap.begin(); zoneIter != zoneItemMap.end();
429fefa98aaSOliver Tappe ++zoneIter) {
4303740f52dSOliver Tappe if (zoneIter->second->OutlineLevel() == 2 && lastWasCountryItem) {
431553211b4SJohn Scipione // Some countries (e.g. Spain and Chile) have their timezones
432553211b4SJohn Scipione // spread across different regions. As a result, there might still
433553211b4SJohn Scipione // be country items with only one timezone below them. We manually
434553211b4SJohn Scipione // filter those country items here.
4353740f52dSOliver Tappe ZoneItemMap::iterator next = zoneIter;
4363740f52dSOliver Tappe ++next;
437fefa98aaSOliver Tappe if (next != zoneItemMap.end()
438fefa98aaSOliver Tappe && next->second->OutlineLevel() != 2) {
4395b013eabSJohn Scipione fZoneList->RemoveItem(currentItem);
4405b013eabSJohn Scipione zoneIter->second->SetText(currentItem->Text());
4415b013eabSJohn Scipione zoneIter->second->SetCountry(currentItem->HasCountry()
4425b013eabSJohn Scipione ? new(std::nothrow) BCountry(currentItem->Country())
4435b013eabSJohn Scipione : NULL);
444ffbe5ecdSJosé Manuel Ferrer Ortiz if (currentItem->HasTimeZone()) {
445ffbe5ecdSJosé Manuel Ferrer Ortiz zoneIter->second->SetTimeZone(new(std::nothrow)
446ffbe5ecdSJosé Manuel Ferrer Ortiz BTimeZone(currentItem->TimeZone()));
447ffbe5ecdSJosé Manuel Ferrer Ortiz }
4483740f52dSOliver Tappe zoneIter->second->SetOutlineLevel(1);
4495b013eabSJohn Scipione delete currentItem;
4503740f52dSOliver Tappe }
4513740f52dSOliver Tappe }
4523740f52dSOliver Tappe
453750e57b8SOliver Tappe fZoneList->AddItem(zoneIter->second);
4543740f52dSOliver Tappe if (zoneIter->second->OutlineLevel() == 1) {
4553740f52dSOliver Tappe lastWasCountryItem = true;
4565b013eabSJohn Scipione currentItem = zoneIter->second;
4573740f52dSOliver Tappe } else
4583740f52dSOliver Tappe lastWasCountryItem = false;
4593740f52dSOliver Tappe }
46099d2aa98SOliver Tappe }
461750e57b8SOliver Tappe
462750e57b8SOliver Tappe
463750e57b8SOliver Tappe void
_Revert()464750e57b8SOliver Tappe TimeZoneView::_Revert()
465750e57b8SOliver Tappe {
466750e57b8SOliver Tappe fCurrentZoneItem = fOldZoneItem;
467750e57b8SOliver Tappe
468750e57b8SOliver Tappe if (fCurrentZoneItem != NULL) {
469750e57b8SOliver Tappe int32 currentZoneIndex = fZoneList->IndexOf(fCurrentZoneItem);
470750e57b8SOliver Tappe fZoneList->Select(currentZoneIndex);
471750e57b8SOliver Tappe } else
472750e57b8SOliver Tappe fZoneList->DeselectAll();
473750e57b8SOliver Tappe fZoneList->ScrollToSelection();
474750e57b8SOliver Tappe
475c2f3ee3bSAdrien Destugues fUseGmtTime = fOldUseGmtTime;
476c2f3ee3bSAdrien Destugues if (fUseGmtTime)
477c2f3ee3bSAdrien Destugues fGmtTime->SetValue(B_CONTROL_ON);
478c2f3ee3bSAdrien Destugues else
479c2f3ee3bSAdrien Destugues fLocalTime->SetValue(B_CONTROL_ON);
480f3b7dcd4SAdrien Destugues _ShowOrHidePreview();
481c2f3ee3bSAdrien Destugues
482c2f3ee3bSAdrien Destugues _UpdateGmtSettings();
483750e57b8SOliver Tappe _SetSystemTimeZone();
484750e57b8SOliver Tappe _UpdatePreview();
485750e57b8SOliver Tappe _UpdateCurrent();
486a10cf76eSAxel Dörfler }
487a10cf76eSAxel Dörfler
488a10cf76eSAxel Dörfler
489a10cf76eSAxel Dörfler void
_UpdatePreview()49038ac8defSOliver Tappe TimeZoneView::_UpdatePreview()
491a10cf76eSAxel Dörfler {
49238ac8defSOliver Tappe int32 selection = fZoneList->CurrentSelection();
493750e57b8SOliver Tappe TimeZoneListItem* item
494750e57b8SOliver Tappe = selection < 0
495750e57b8SOliver Tappe ? NULL
496553211b4SJohn Scipione : static_cast<TimeZoneListItem*>(fZoneList->ItemAt(selection));
497a10cf76eSAxel Dörfler
498750e57b8SOliver Tappe if (item == NULL || !item->HasTimeZone()) {
499750e57b8SOliver Tappe fPreview->SetText("");
500750e57b8SOliver Tappe fPreview->SetTime("");
50138ac8defSOliver Tappe return;
502750e57b8SOliver Tappe }
503a10cf76eSAxel Dörfler
504447b7fdeSOliver Tappe BString timeString = _FormatTime(item->TimeZone());
50538ac8defSOliver Tappe fPreview->SetText(item->Text());
50638ac8defSOliver Tappe fPreview->SetTime(timeString.String());
507a10cf76eSAxel Dörfler
50838ac8defSOliver Tappe fSetZone->SetEnabled((strcmp(fCurrent->Text(), item->Text()) != 0));
509a10cf76eSAxel Dörfler }
510a10cf76eSAxel Dörfler
511a10cf76eSAxel Dörfler
512a10cf76eSAxel Dörfler void
_UpdateCurrent()51338ac8defSOliver Tappe TimeZoneView::_UpdateCurrent()
514a10cf76eSAxel Dörfler {
51538ac8defSOliver Tappe if (fCurrentZoneItem == NULL)
51638ac8defSOliver Tappe return;
5171855b44cSAdrien Destugues
518447b7fdeSOliver Tappe BString timeString = _FormatTime(fCurrentZoneItem->TimeZone());
51938ac8defSOliver Tappe fCurrent->SetText(fCurrentZoneItem->Text());
52038ac8defSOliver Tappe fCurrent->SetTime(timeString.String());
521a10cf76eSAxel Dörfler }
522a10cf76eSAxel Dörfler
52338ac8defSOliver Tappe
52438ac8defSOliver Tappe void
_SetSystemTimeZone()52538ac8defSOliver Tappe TimeZoneView::_SetSystemTimeZone()
52638ac8defSOliver Tappe {
527d88a361cSOliver Tappe /* Set system timezone for all different API levels. How to do this?
52838ac8defSOliver Tappe * 1) tell locale-roster about new default timezone
52985b54438SOliver Tappe * 2) tell kernel about new timezone offset
53038ac8defSOliver Tappe */
53138ac8defSOliver Tappe
53238ac8defSOliver Tappe int32 selection = fZoneList->CurrentSelection();
53338ac8defSOliver Tappe if (selection < 0)
53438ac8defSOliver Tappe return;
53538ac8defSOliver Tappe
536447b7fdeSOliver Tappe TimeZoneListItem* item
537447b7fdeSOliver Tappe = static_cast<TimeZoneListItem*>(fZoneList->ItemAt(selection));
538447b7fdeSOliver Tappe if (item == NULL || !item->HasTimeZone())
539447b7fdeSOliver Tappe return;
540447b7fdeSOliver Tappe
541447b7fdeSOliver Tappe fCurrentZoneItem = item;
542447b7fdeSOliver Tappe const BTimeZone& timeZone = item->TimeZone();
54338ac8defSOliver Tappe
54425dc253dSIngo Weinhold MutableLocaleRoster::Default()->SetDefaultTimeZone(timeZone);
54538ac8defSOliver Tappe
546750e57b8SOliver Tappe _kern_set_timezone(timeZone.OffsetFromGMT(), timeZone.ID().String(),
547750e57b8SOliver Tappe timeZone.ID().Length());
54885b54438SOliver Tappe
54938ac8defSOliver Tappe fSetZone->SetEnabled(false);
55038ac8defSOliver Tappe fLastUpdateMinute = -1;
55138ac8defSOliver Tappe // just to trigger updating immediately
55238ac8defSOliver Tappe }
55338ac8defSOliver Tappe
55438ac8defSOliver Tappe
55538ac8defSOliver Tappe BString
_FormatTime(const BTimeZone & timeZone)5563740f52dSOliver Tappe TimeZoneView::_FormatTime(const BTimeZone& timeZone)
55738ac8defSOliver Tappe {
55838ac8defSOliver Tappe BString result;
55938ac8defSOliver Tappe
560a096bb65SOliver Tappe time_t now = time(NULL);
561a096bb65SOliver Tappe bool rtcIsGMT;
562a096bb65SOliver Tappe _kern_get_real_time_clock_is_gmt(&rtcIsGMT);
5633740f52dSOliver Tappe if (!rtcIsGMT) {
564447b7fdeSOliver Tappe int32 currentOffset
565447b7fdeSOliver Tappe = fCurrentZoneItem != NULL && fCurrentZoneItem->HasTimeZone()
566447b7fdeSOliver Tappe ? fCurrentZoneItem->OffsetFromGMT()
567447b7fdeSOliver Tappe : 0;
568447b7fdeSOliver Tappe now -= timeZone.OffsetFromGMT() - currentOffset;
569a096bb65SOliver Tappe }
57003b2550eSAdrien Destugues fTimeFormat.Format(result, now, B_SHORT_TIME_FORMAT, &timeZone);
57138ac8defSOliver Tappe
57238ac8defSOliver Tappe return result;
57338ac8defSOliver Tappe }
574c2f3ee3bSAdrien Destugues
575c2f3ee3bSAdrien Destugues
576c2f3ee3bSAdrien Destugues void
_ReadRTCSettings()577c2f3ee3bSAdrien Destugues TimeZoneView::_ReadRTCSettings()
578c2f3ee3bSAdrien Destugues {
579c2f3ee3bSAdrien Destugues BPath path;
580c2f3ee3bSAdrien Destugues if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
581c2f3ee3bSAdrien Destugues return;
582c2f3ee3bSAdrien Destugues
583c2f3ee3bSAdrien Destugues path.Append("RTC_time_settings");
584c2f3ee3bSAdrien Destugues
585c2f3ee3bSAdrien Destugues BEntry entry(path.Path());
586c2f3ee3bSAdrien Destugues if (entry.Exists()) {
587c2f3ee3bSAdrien Destugues BFile file(&entry, B_READ_ONLY);
588c2f3ee3bSAdrien Destugues if (file.InitCheck() == B_OK) {
589c2f3ee3bSAdrien Destugues char buffer[6];
590c2f3ee3bSAdrien Destugues file.Read(buffer, 6);
591c2f3ee3bSAdrien Destugues if (strncmp(buffer, "gmt", 3) == 0)
592c2f3ee3bSAdrien Destugues fUseGmtTime = true;
593c2f3ee3bSAdrien Destugues }
594c2f3ee3bSAdrien Destugues }
595c2f3ee3bSAdrien Destugues }
596c2f3ee3bSAdrien Destugues
597c2f3ee3bSAdrien Destugues
598c2f3ee3bSAdrien Destugues void
_WriteRTCSettings()599c2f3ee3bSAdrien Destugues TimeZoneView::_WriteRTCSettings()
600c2f3ee3bSAdrien Destugues {
601c2f3ee3bSAdrien Destugues BPath path;
602c2f3ee3bSAdrien Destugues if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
603c2f3ee3bSAdrien Destugues return;
604c2f3ee3bSAdrien Destugues
605c2f3ee3bSAdrien Destugues path.Append("RTC_time_settings");
606c2f3ee3bSAdrien Destugues
607c2f3ee3bSAdrien Destugues BFile file(path.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
608c2f3ee3bSAdrien Destugues if (file.InitCheck() == B_OK) {
609c2f3ee3bSAdrien Destugues if (fUseGmtTime)
610c2f3ee3bSAdrien Destugues file.Write("gmt", 3);
611c2f3ee3bSAdrien Destugues else
612c2f3ee3bSAdrien Destugues file.Write("local", 5);
613c2f3ee3bSAdrien Destugues }
614c2f3ee3bSAdrien Destugues }
615c2f3ee3bSAdrien Destugues
616c2f3ee3bSAdrien Destugues
617c2f3ee3bSAdrien Destugues void
_UpdateGmtSettings()618c2f3ee3bSAdrien Destugues TimeZoneView::_UpdateGmtSettings()
619c2f3ee3bSAdrien Destugues {
620c2f3ee3bSAdrien Destugues _WriteRTCSettings();
621c2f3ee3bSAdrien Destugues
622f3b7dcd4SAdrien Destugues _ShowOrHidePreview();
623c2f3ee3bSAdrien Destugues
624c2f3ee3bSAdrien Destugues _kern_set_real_time_clock_is_gmt(fUseGmtTime);
625c2f3ee3bSAdrien Destugues }
626c2f3ee3bSAdrien Destugues
627c2f3ee3bSAdrien Destugues
628c2f3ee3bSAdrien Destugues void
_ShowOrHidePreview()629f3b7dcd4SAdrien Destugues TimeZoneView::_ShowOrHidePreview()
630f3b7dcd4SAdrien Destugues {
631f3b7dcd4SAdrien Destugues if (fUseGmtTime) {
632f3b7dcd4SAdrien Destugues // Hardware clock uses GMT time, changing timezone will adjust the
633f3b7dcd4SAdrien Destugues // offset and we need to display a preview
634f3b7dcd4SAdrien Destugues fCurrent->Show();
635f3b7dcd4SAdrien Destugues fPreview->Show();
636f3b7dcd4SAdrien Destugues } else {
637f3b7dcd4SAdrien Destugues // Hardware clock uses local time, changing timezone will adjust the
638f3b7dcd4SAdrien Destugues // clock and there is no offset to manage, thus, no preview.
639f3b7dcd4SAdrien Destugues fCurrent->Hide();
640f3b7dcd4SAdrien Destugues fPreview->Hide();
641f3b7dcd4SAdrien Destugues }
642f3b7dcd4SAdrien Destugues }
643