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