xref: /haiku/src/preferences/time/ZoneView.cpp (revision 553211b49970e5de8cf5e569f578f3afb3deecf6)
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