xref: /haiku/src/preferences/time/ZoneView.cpp (revision 825d265efd33da2c0d6b22fa9e9e88e97ad33086)
1a10cf76eSAxel Dörfler /*
24ae5a452SAdrien Destugues  * Copyright 2004-2010, 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>
1153d9356eSStephan Aßmus  */
1242b3a11fSStephan Aßmus 
13a10cf76eSAxel Dörfler 
1442b3a11fSStephan Aßmus #include "ZoneView.h"
15a10cf76eSAxel Dörfler 
16317bd7ddSAxel Dörfler #include <stdlib.h>
1742b3a11fSStephan Aßmus 
18750e57b8SOliver Tappe #include <map>
1938ac8defSOliver Tappe #include <new>
2038ac8defSOliver Tappe 
2138ac8defSOliver Tappe #include <AutoDeleter.h>
2242b3a11fSStephan Aßmus #include <Button.h>
23*825d265eSAdrien Destugues #include <Catalog.h>
2499d2aa98SOliver Tappe #include <Collator.h>
253740f52dSOliver Tappe #include <Country.h>
26a10cf76eSAxel Dörfler #include <Directory.h>
27a10cf76eSAxel Dörfler #include <Entry.h>
2885b54438SOliver Tappe #include <File.h>
29a10cf76eSAxel Dörfler #include <FindDirectory.h>
3042b3a11fSStephan Aßmus #include <ListItem.h>
314ae5a452SAdrien Destugues #include <Locale.h>
3238ac8defSOliver Tappe #include <MutableLocaleRoster.h>
334ae5a452SAdrien Destugues #include <OutlineListView.h>
3485b54438SOliver Tappe #include <Path.h>
35a10cf76eSAxel Dörfler #include <ScrollView.h>
36a10cf76eSAxel Dörfler #include <StorageDefs.h>
37a10cf76eSAxel Dörfler #include <String.h>
384ae5a452SAdrien Destugues #include <TimeZone.h>
39a10cf76eSAxel Dörfler #include <View.h>
40310930fdSStephan Aßmus #include <Window.h>
41a10cf76eSAxel Dörfler 
42317bd7ddSAxel Dörfler #include <syscalls.h>
43750e57b8SOliver Tappe #include <ToolTip.h>
44a10cf76eSAxel Dörfler 
4538ac8defSOliver Tappe #include <unicode/datefmt.h>
4638ac8defSOliver Tappe #include <unicode/utmscale.h>
4738ac8defSOliver Tappe #include <ICUWrapper.h>
4838ac8defSOliver Tappe 
49317bd7ddSAxel Dörfler #include "TimeMessages.h"
504ae5a452SAdrien Destugues #include "TimeZoneListItem.h"
51317bd7ddSAxel Dörfler #include "TZDisplay.h"
52317bd7ddSAxel Dörfler #include "TimeWindow.h"
53a10cf76eSAxel Dörfler 
54a10cf76eSAxel Dörfler 
55*825d265eSAdrien Destugues #undef B_TRANSLATE_CONTEXT
56*825d265eSAdrien Destugues #define B_TRANSLATE_CONTEXT "Time"
57*825d265eSAdrien Destugues 
58*825d265eSAdrien Destugues 
5925dc253dSIngo Weinhold using BPrivate::MutableLocaleRoster;
6038ac8defSOliver Tappe using BPrivate::ObjectDeleter;
6199d2aa98SOliver Tappe 
6299d2aa98SOliver Tappe 
63750e57b8SOliver Tappe struct TimeZoneItemLess {
64750e57b8SOliver Tappe 	bool operator()(const BString& first, const BString& second)
65750e57b8SOliver Tappe 	{
66447b7fdeSOliver Tappe 		// sort anything starting with '<' behind anything else
67447b7fdeSOliver Tappe 		if (first.ByteAt(0) == '<') {
68447b7fdeSOliver Tappe 			if (second.ByteAt(0) != '<')
69447b7fdeSOliver Tappe 				return false;
70447b7fdeSOliver Tappe 		} else if (second.ByteAt(0) == '<')
71447b7fdeSOliver Tappe 			return true;
72750e57b8SOliver Tappe 		return fCollator.Compare(first.String(), second.String()) < 0;
73750e57b8SOliver Tappe 	}
74750e57b8SOliver Tappe private:
75750e57b8SOliver Tappe 	BCollator fCollator;
76750e57b8SOliver Tappe };
77750e57b8SOliver Tappe 
78750e57b8SOliver Tappe 
79750e57b8SOliver Tappe 
8085b69a94SKarsten Heimrich TimeZoneView::TimeZoneView(BRect frame)
8199d2aa98SOliver Tappe 	:
8299d2aa98SOliver Tappe 	BView(frame, "timeZoneView", B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE_JUMP),
83750e57b8SOliver Tappe 	fToolTip(NULL),
8438ac8defSOliver Tappe 	fCurrentZoneItem(NULL),
8538ac8defSOliver Tappe 	fOldZoneItem(NULL),
8699d2aa98SOliver Tappe 	fInitialized(false)
87a10cf76eSAxel Dörfler {
886d6408d6SOliver Tappe 	_InitView();
89a10cf76eSAxel Dörfler }
90a10cf76eSAxel Dörfler 
91a10cf76eSAxel Dörfler 
92310930fdSStephan Aßmus bool
93310930fdSStephan Aßmus TimeZoneView::CheckCanRevert()
94310930fdSStephan Aßmus {
9538ac8defSOliver Tappe 	return fCurrentZoneItem != fOldZoneItem;
96310930fdSStephan Aßmus }
97310930fdSStephan Aßmus 
98310930fdSStephan Aßmus 
9985b69a94SKarsten Heimrich TimeZoneView::~TimeZoneView()
100a10cf76eSAxel Dörfler {
1013740f52dSOliver Tappe 	if (fToolTip != NULL)
10200a2914fSRene Gollent 		fToolTip->ReleaseReference();
103a10cf76eSAxel Dörfler }
104a10cf76eSAxel Dörfler 
105a10cf76eSAxel Dörfler 
106a10cf76eSAxel Dörfler void
10785b69a94SKarsten Heimrich TimeZoneView::AttachedToWindow()
108a10cf76eSAxel Dörfler {
109447b7fdeSOliver Tappe 	BView::AttachedToWindow();
110a10cf76eSAxel Dörfler 	if (Parent())
111a10cf76eSAxel Dörfler 		SetViewColor(Parent()->ViewColor());
112447b7fdeSOliver Tappe }
113a10cf76eSAxel Dörfler 
114447b7fdeSOliver Tappe 
115447b7fdeSOliver Tappe void
116447b7fdeSOliver Tappe TimeZoneView::AllAttached()
117447b7fdeSOliver Tappe {
118447b7fdeSOliver Tappe 	BView::AllAttached();
11985b69a94SKarsten Heimrich 	if (!fInitialized) {
12085b69a94SKarsten Heimrich 		fInitialized = true;
12185b69a94SKarsten Heimrich 
12242b3a11fSStephan Aßmus 		fSetZone->SetTarget(this);
12338ac8defSOliver Tappe 		fZoneList->SetTarget(this);
12442b3a11fSStephan Aßmus 
125a10cf76eSAxel Dörfler 		// update displays
12638ac8defSOliver Tappe 		if (fCurrentZoneItem != NULL) {
127447b7fdeSOliver Tappe 			fZoneList->Select(fZoneList->IndexOf(fCurrentZoneItem));
12838ac8defSOliver Tappe 			fCurrent->SetText(fCurrentZoneItem->Text());
129a10cf76eSAxel Dörfler 		}
130447b7fdeSOliver Tappe 	}
13138ac8defSOliver Tappe 	fZoneList->ScrollToSelection();
132a10cf76eSAxel Dörfler }
133a10cf76eSAxel Dörfler 
134a10cf76eSAxel Dörfler 
135a10cf76eSAxel Dörfler void
13685b69a94SKarsten Heimrich TimeZoneView::MessageReceived(BMessage* message)
137a10cf76eSAxel Dörfler {
138a10cf76eSAxel Dörfler 	switch (message->what) {
139a10cf76eSAxel Dörfler 		case B_OBSERVER_NOTICE_CHANGE:
140317bd7ddSAxel Dörfler 		{
141317bd7ddSAxel Dörfler 			int32 change;
142a10cf76eSAxel Dörfler 			message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change);
143a10cf76eSAxel Dörfler 			switch(change) {
144a10cf76eSAxel Dörfler 				case H_TM_CHANGED:
1456d6408d6SOliver Tappe 					_UpdateDateTime(message);
146a10cf76eSAxel Dörfler 					break;
147a10cf76eSAxel Dörfler 
148a10cf76eSAxel Dörfler 				default:
149a10cf76eSAxel Dörfler 					BView::MessageReceived(message);
150a10cf76eSAxel Dörfler 					break;
151a10cf76eSAxel Dörfler 			}
152a10cf76eSAxel Dörfler 			break;
153317bd7ddSAxel Dörfler 		}
154317bd7ddSAxel Dörfler 
155447b7fdeSOliver Tappe 		case H_CITY_CHANGED:
156447b7fdeSOliver Tappe 			_UpdatePreview();
157447b7fdeSOliver Tappe 			break;
158447b7fdeSOliver Tappe 
159a10cf76eSAxel Dörfler 		case H_SET_TIME_ZONE:
160310930fdSStephan Aßmus 		{
16138ac8defSOliver Tappe 			_SetSystemTimeZone();
162310930fdSStephan Aßmus 			((TTimeWindow*)Window())->SetRevertStatus();
163310930fdSStephan Aßmus 			break;
164310930fdSStephan Aßmus 		}
165310930fdSStephan Aßmus 
166310930fdSStephan Aßmus 		case kMsgRevert:
167310930fdSStephan Aßmus 			_Revert();
168a10cf76eSAxel Dörfler 			break;
169a10cf76eSAxel Dörfler 
170447b7fdeSOliver Tappe 		case kRTCUpdate:
171447b7fdeSOliver Tappe 			_UpdateCurrent();
17238ac8defSOliver Tappe 			_UpdatePreview();
173a10cf76eSAxel Dörfler 			break;
174a10cf76eSAxel Dörfler 
175a10cf76eSAxel Dörfler 		default:
176a10cf76eSAxel Dörfler 			BView::MessageReceived(message);
177a10cf76eSAxel Dörfler 			break;
178a10cf76eSAxel Dörfler 	}
179a10cf76eSAxel Dörfler }
180a10cf76eSAxel Dörfler 
181a10cf76eSAxel Dörfler 
182750e57b8SOliver Tappe bool
183750e57b8SOliver Tappe TimeZoneView::GetToolTipAt(BPoint point, BToolTip** _tip)
184750e57b8SOliver Tappe {
185750e57b8SOliver Tappe 	TimeZoneListItem* item = static_cast<TimeZoneListItem*>(
186750e57b8SOliver Tappe 		fZoneList->ItemAt(fZoneList->IndexOf(point)));
187750e57b8SOliver Tappe 	if (item == NULL || !item->HasTimeZone())
188750e57b8SOliver Tappe 		return false;
189750e57b8SOliver Tappe 
1903740f52dSOliver Tappe 	BString nowInTimeZone;
1913740f52dSOliver Tappe 	time_t now = time(NULL);
19225dc253dSIngo Weinhold 	BLocale::Default()->FormatTime(&nowInTimeZone, now, B_SHORT_TIME_FORMAT,
1936fd2f4a0SOliver Tappe 		&item->TimeZone());
1943740f52dSOliver Tappe 
1953740f52dSOliver Tappe 	BString dateInTimeZone;
19625dc253dSIngo Weinhold 	BLocale::Default()->FormatDate(&dateInTimeZone, now, B_SHORT_DATE_FORMAT,
1976fd2f4a0SOliver Tappe 		&item->TimeZone());
1983740f52dSOliver Tappe 
199750e57b8SOliver Tappe 	BString toolTip = item->Text();
200750e57b8SOliver Tappe 	toolTip << '\n' << item->TimeZone().ShortName() << " / "
201447b7fdeSOliver Tappe 			<< item->TimeZone().ShortDaylightSavingName()
202*825d265eSAdrien Destugues 			<< B_TRANSLATE("\nNow: ") << nowInTimeZone
203*825d265eSAdrien Destugues 			<< " (" << dateInTimeZone << ')';
204750e57b8SOliver Tappe 
2053740f52dSOliver Tappe 	if (fToolTip != NULL)
2063740f52dSOliver Tappe 		fToolTip->ReleaseReference();
207750e57b8SOliver Tappe 	fToolTip = new (std::nothrow) BTextToolTip(toolTip.String());
208750e57b8SOliver Tappe 	if (fToolTip == NULL)
209750e57b8SOliver Tappe 		return false;
210750e57b8SOliver Tappe 
211750e57b8SOliver Tappe 	*_tip = fToolTip;
212750e57b8SOliver Tappe 
213750e57b8SOliver Tappe 	return true;
214750e57b8SOliver Tappe }
215750e57b8SOliver Tappe 
216750e57b8SOliver Tappe 
217a10cf76eSAxel Dörfler void
2186d6408d6SOliver Tappe TimeZoneView::_UpdateDateTime(BMessage* message)
219a10cf76eSAxel Dörfler {
22038ac8defSOliver Tappe 	// only need to update once every minute
22142b3a11fSStephan Aßmus 	int32 minute;
22238ac8defSOliver Tappe 	if (message->FindInt32("minute", &minute) == B_OK) {
22338ac8defSOliver Tappe 		if (fLastUpdateMinute != minute) {
22438ac8defSOliver Tappe 			_UpdateCurrent();
22538ac8defSOliver Tappe 			_UpdatePreview();
226a10cf76eSAxel Dörfler 
22738ac8defSOliver Tappe 			fLastUpdateMinute = minute;
228a10cf76eSAxel Dörfler 		}
229a10cf76eSAxel Dörfler 	}
230a10cf76eSAxel Dörfler }
231a10cf76eSAxel Dörfler 
232a10cf76eSAxel Dörfler 
233a10cf76eSAxel Dörfler void
2346d6408d6SOliver Tappe TimeZoneView::_InitView()
235a10cf76eSAxel Dörfler {
23642b3a11fSStephan Aßmus 	// left side
23742b3a11fSStephan Aßmus 	BRect frameLeft(Bounds());
23885b69a94SKarsten Heimrich 	frameLeft.right = frameLeft.Width() / 2.0;
23942b3a11fSStephan Aßmus 	frameLeft.InsetBy(10.0f, 10.0f);
240a10cf76eSAxel Dörfler 
241a10cf76eSAxel Dörfler 	// City Listing
24238ac8defSOliver Tappe 	fZoneList = new BOutlineListView(frameLeft, "cityList",
2434ae5a452SAdrien Destugues 		B_SINGLE_SELECTION_LIST);
24438ac8defSOliver Tappe 	fZoneList->SetSelectionMessage(new BMessage(H_CITY_CHANGED));
24538ac8defSOliver Tappe 	fZoneList->SetInvocationMessage(new BMessage(H_SET_TIME_ZONE));
246a10cf76eSAxel Dörfler 
247750e57b8SOliver Tappe 	_BuildZoneMenu();
2484ae5a452SAdrien Destugues 
24938ac8defSOliver Tappe 	BScrollView* scrollList = new BScrollView("scrollList", fZoneList,
25042b3a11fSStephan Aßmus 		B_FOLLOW_ALL, 0, false, true);
251a10cf76eSAxel Dörfler 	AddChild(scrollList);
252a10cf76eSAxel Dörfler 
25342b3a11fSStephan Aßmus 	// right side
25442b3a11fSStephan Aßmus 	BRect frameRight(Bounds());
25585b69a94SKarsten Heimrich 	frameRight.left = frameRight.Width() / 2.0;
25642b3a11fSStephan Aßmus 	frameRight.InsetBy(10.0f, 10.0f);
25742b3a11fSStephan Aßmus 	frameRight.top = frameLeft.top;
258a10cf76eSAxel Dörfler 
259a10cf76eSAxel Dörfler 	// Time Displays
260*825d265eSAdrien Destugues 	fCurrent = new TTZDisplay(frameRight, "currentTime",
261*825d265eSAdrien Destugues 		B_TRANSLATE("Current time:"));
26242b3a11fSStephan Aßmus 	AddChild(fCurrent);
26342b3a11fSStephan Aßmus 	fCurrent->ResizeToPreferred();
264a10cf76eSAxel Dörfler 
26585b69a94SKarsten Heimrich 	frameRight.top = fCurrent->Frame().bottom + 10.0;
266*825d265eSAdrien Destugues 	fPreview = new TTZDisplay(frameRight, "previewTime",
267*825d265eSAdrien Destugues 		B_TRANSLATE("Preview time:"));
26842b3a11fSStephan Aßmus 	AddChild(fPreview);
26942b3a11fSStephan Aßmus 	fPreview->ResizeToPreferred();
270a10cf76eSAxel Dörfler 
271a10cf76eSAxel Dörfler 	// set button
272*825d265eSAdrien Destugues 	fSetZone = new BButton(frameRight, "setTimeZone",
273*825d265eSAdrien Destugues 		B_TRANSLATE("Set time zone"),
27485b69a94SKarsten Heimrich 		new BMessage(H_SET_TIME_ZONE));
27542b3a11fSStephan Aßmus 	AddChild(fSetZone);
27642b3a11fSStephan Aßmus 	fSetZone->SetEnabled(false);
27742b3a11fSStephan Aßmus 	fSetZone->ResizeToPreferred();
278a10cf76eSAxel Dörfler 
27942b3a11fSStephan Aßmus 	fSetZone->MoveTo(frameRight.right - fSetZone->Bounds().Width(),
28042b3a11fSStephan Aßmus 		scrollList->Frame().bottom - fSetZone->Bounds().Height());
281a10cf76eSAxel Dörfler }
282a10cf76eSAxel Dörfler 
283a10cf76eSAxel Dörfler 
284a10cf76eSAxel Dörfler void
285750e57b8SOliver Tappe TimeZoneView::_BuildZoneMenu()
286a10cf76eSAxel Dörfler {
287447b7fdeSOliver Tappe 	BTimeZone defaultTimeZone;
28825dc253dSIngo Weinhold 	BLocaleRoster::Default()->GetDefaultTimeZone(&defaultTimeZone);
28999d2aa98SOliver Tappe 
290b7f60965SOliver Tappe 	BLanguage language;
29125dc253dSIngo Weinhold 	BLocale::Default()->GetLanguage(&language);
292a10cf76eSAxel Dörfler 
2933740f52dSOliver Tappe 	BMessage countryList;
29425dc253dSIngo Weinhold 	BLocaleRoster::Default()->GetAvailableCountries(&countryList);
2956f43fabaSOliver Tappe 	countryList.AddString("country", "");
2963740f52dSOliver Tappe 
297447b7fdeSOliver Tappe 	/*
298447b7fdeSOliver Tappe 	 * Group timezones by regions, but filter out unwanted (duplicate) regions
299447b7fdeSOliver Tappe 	 * and add an additional region with generic GMT-offset timezones at the end
300447b7fdeSOliver Tappe 	 */
301750e57b8SOliver Tappe 	typedef	std::map<BString, TimeZoneListItem*, TimeZoneItemLess> ZoneItemMap;
302750e57b8SOliver Tappe 	ZoneItemMap zoneMap;
303*825d265eSAdrien Destugues 	const char* kOtherRegion = B_TRANSLATE("<Other>");
304447b7fdeSOliver Tappe 	const char* kSupportedRegions[] = {
305750e57b8SOliver Tappe 		"Africa", "America", "Antarctica", "Arctic", "Asia", "Atlantic",
306447b7fdeSOliver Tappe 		"Australia", "Europe", "Indian", "Pacific", kOtherRegion, NULL
307750e57b8SOliver Tappe 	};
308447b7fdeSOliver Tappe 	for (const char** region = kSupportedRegions; *region != NULL; ++region)
309750e57b8SOliver Tappe 		zoneMap[*region] = NULL;
310a10cf76eSAxel Dörfler 
3113740f52dSOliver Tappe 	BString countryCode;
3126f43fabaSOliver Tappe 	for (int c = 0; countryList.FindString("country", c, &countryCode)
3133740f52dSOliver Tappe 			== B_OK; c++) {
3146fd2f4a0SOliver Tappe 		BCountry country(countryCode);
3153740f52dSOliver Tappe 		BString countryName;
3163740f52dSOliver Tappe 		country.GetName(countryName);
3173740f52dSOliver Tappe 
3183740f52dSOliver Tappe 		// Now list the timezones for this country
3193740f52dSOliver Tappe 		BMessage zoneList;
32025dc253dSIngo Weinhold 		BLocaleRoster::Default()->GetAvailableTimeZonesForCountry(&zoneList,
3213740f52dSOliver Tappe 			countryCode.Length() == 0 ? NULL : countryCode.String());
3223740f52dSOliver Tappe 
3233740f52dSOliver Tappe 		int32 count = 0;
3243740f52dSOliver Tappe 		type_code dummy;
3253740f52dSOliver Tappe 		zoneList.GetInfo("timeZone", &dummy, &count);
3263740f52dSOliver Tappe 
327750e57b8SOliver Tappe 		BString zoneID;
3283740f52dSOliver Tappe 		for (int tz = 0; zoneList.FindString("timeZone", tz, &zoneID) == B_OK;
3293740f52dSOliver Tappe 			tz++) {
330750e57b8SOliver Tappe 			int32 slashPos = zoneID.FindFirst('/');
331a10cf76eSAxel Dörfler 
3323740f52dSOliver Tappe 			// ignore any "global" timezones, as those are just aliases of
3333740f52dSOliver Tappe 			// regional ones
334750e57b8SOliver Tappe 			if (slashPos <= 0)
335750e57b8SOliver Tappe 				continue;
336750e57b8SOliver Tappe 
337750e57b8SOliver Tappe 			BString region(zoneID, slashPos);
338750e57b8SOliver Tappe 
339*825d265eSAdrien Destugues 			if (region == B_TRANSLATE("Etc"))
340447b7fdeSOliver Tappe 				region = kOtherRegion;
3413740f52dSOliver Tappe 			else if (countryName.Length() == 0) {
3423740f52dSOliver Tappe 				// skip global timezones from other regions, we are just
3433740f52dSOliver Tappe 				// interested in the generic GMT-based ones under "Etc/"
3443740f52dSOliver Tappe 				continue;
3453740f52dSOliver Tappe 			}
346447b7fdeSOliver Tappe 
3473740f52dSOliver Tappe 
3483740f52dSOliver Tappe 			// just accept timezones from "proper" regions, others are aliases
349750e57b8SOliver Tappe 			ZoneItemMap::iterator regionIter = zoneMap.find(region);
350750e57b8SOliver Tappe 			if (regionIter == zoneMap.end())
351750e57b8SOliver Tappe 				continue;
352750e57b8SOliver Tappe 
3533740f52dSOliver Tappe 			BString fullCountryID = region;
3543740f52dSOliver Tappe 			if (countryName != region)
3553740f52dSOliver Tappe 				fullCountryID << "/" << countryName;
3563740f52dSOliver Tappe 
357750e57b8SOliver Tappe 			TimeZoneListItem* regionItem = regionIter->second;
358750e57b8SOliver Tappe 			if (regionItem == NULL) {
359750e57b8SOliver Tappe 				regionItem = new TimeZoneListItem(region, NULL, NULL);
360750e57b8SOliver Tappe 				regionItem->SetOutlineLevel(0);
361750e57b8SOliver Tappe 				zoneMap[region] = regionItem;
362750e57b8SOliver Tappe 			}
363750e57b8SOliver Tappe 
364b7f60965SOliver Tappe 			BTimeZone* timeZone = new BTimeZone(zoneID, &language);
365750e57b8SOliver Tappe 			BString tzName = timeZone->Name();
366750e57b8SOliver Tappe 			if (tzName == "GMT+00:00")
367750e57b8SOliver Tappe 				tzName = "GMT";
3683740f52dSOliver Tappe 
369750e57b8SOliver Tappe 			int32 openParenthesisPos = tzName.FindFirst('(');
370750e57b8SOliver Tappe 			if (openParenthesisPos >= 0) {
371750e57b8SOliver Tappe 				tzName.Remove(0, openParenthesisPos + 1);
372750e57b8SOliver Tappe 				int32 closeParenthesisPos = tzName.FindLast(')');
373750e57b8SOliver Tappe 				if (closeParenthesisPos >= 0)
374750e57b8SOliver Tappe 					tzName.Truncate(closeParenthesisPos);
375750e57b8SOliver Tappe 			}
376750e57b8SOliver Tappe 			BString fullZoneID = fullCountryID;
377750e57b8SOliver Tappe 			fullZoneID << "/" << tzName;
378750e57b8SOliver Tappe 
379750e57b8SOliver Tappe 			// skip duplicates
380750e57b8SOliver Tappe 			ZoneItemMap::iterator zoneIter = zoneMap.find(fullZoneID);
381750e57b8SOliver Tappe 			if (zoneIter != zoneMap.end()) {
382750e57b8SOliver Tappe 				delete timeZone;
383750e57b8SOliver Tappe 				continue;
384750e57b8SOliver Tappe 			}
385750e57b8SOliver Tappe 
386750e57b8SOliver Tappe 			TimeZoneListItem* countryItem = NULL;
3873740f52dSOliver Tappe 			TimeZoneListItem* zoneItem = NULL;
3883740f52dSOliver Tappe 			if (count > 1 && countryName.Length() > 0) {
389750e57b8SOliver Tappe 				ZoneItemMap::iterator countryIter = zoneMap.find(fullCountryID);
390750e57b8SOliver Tappe 				if (countryIter == zoneMap.end()) {
3913740f52dSOliver Tappe 					countryItem = new TimeZoneListItem(countryName, NULL, NULL);
392750e57b8SOliver Tappe 					countryItem->SetOutlineLevel(1);
393750e57b8SOliver Tappe 					zoneMap[fullCountryID] = countryItem;
394750e57b8SOliver Tappe 				} else
395750e57b8SOliver Tappe 					countryItem = countryIter->second;
3964ae5a452SAdrien Destugues 
3973740f52dSOliver Tappe 				zoneItem = new TimeZoneListItem(tzName, NULL, timeZone);
3983740f52dSOliver Tappe 				zoneItem->SetOutlineLevel(2);
3993740f52dSOliver Tappe 			} else {
4003740f52dSOliver Tappe 				BString& name = countryName.Length() > 0 ? countryName : tzName;
4013740f52dSOliver Tappe 				zoneItem = new TimeZoneListItem(name, NULL, timeZone);
4023740f52dSOliver Tappe 				zoneItem->SetOutlineLevel(1);
4033740f52dSOliver Tappe 			}
404750e57b8SOliver Tappe 			zoneMap[fullZoneID] = zoneItem;
405750e57b8SOliver Tappe 
406750e57b8SOliver Tappe 			if (timeZone->ID() == defaultTimeZone.ID()) {
407750e57b8SOliver Tappe 				fCurrentZoneItem = zoneItem;
408750e57b8SOliver Tappe 				if (countryItem != NULL)
40999d2aa98SOliver Tappe 					countryItem->SetExpanded(true);
410750e57b8SOliver Tappe 				regionItem->SetExpanded(true);
41199d2aa98SOliver Tappe 			}
41242b3a11fSStephan Aßmus 		}
4133740f52dSOliver Tappe 	}
41499d2aa98SOliver Tappe 
41538ac8defSOliver Tappe 	fOldZoneItem = fCurrentZoneItem;
41699d2aa98SOliver Tappe 
417750e57b8SOliver Tappe 	ZoneItemMap::iterator zoneIter;
4183740f52dSOliver Tappe 	bool lastWasCountryItem = false;
4193740f52dSOliver Tappe 	TimeZoneListItem* lastCountryItem = NULL;
4203740f52dSOliver Tappe 	for (zoneIter = zoneMap.begin(); zoneIter != zoneMap.end(); ++zoneIter) {
4213740f52dSOliver Tappe 		if (zoneIter->second->OutlineLevel() == 2 && lastWasCountryItem) {
4223740f52dSOliver Tappe 			/* Some countries (e.g. Spain and Chile) have their timezones
4233740f52dSOliver Tappe 			 * spread across different regions. As a result, there might still
4243740f52dSOliver Tappe 			 * be country items with only one timezone below them. We manually
4253740f52dSOliver Tappe 			 * filter those country items here.
4263740f52dSOliver Tappe 			 */
4273740f52dSOliver Tappe 			ZoneItemMap::iterator next = zoneIter;
4283740f52dSOliver Tappe 			++next;
4293740f52dSOliver Tappe 			if (next != zoneMap.end() && next->second->OutlineLevel() != 2) {
4303740f52dSOliver Tappe 				fZoneList->RemoveItem(lastCountryItem);
4313740f52dSOliver Tappe 				zoneIter->second->SetText(lastCountryItem->Text());
4323740f52dSOliver Tappe 				zoneIter->second->SetOutlineLevel(1);
4333740f52dSOliver Tappe 				delete lastCountryItem;
4343740f52dSOliver Tappe 			}
4353740f52dSOliver Tappe 		}
4363740f52dSOliver Tappe 
437750e57b8SOliver Tappe 		fZoneList->AddItem(zoneIter->second);
4383740f52dSOliver Tappe 		if (zoneIter->second->OutlineLevel() == 1) {
4393740f52dSOliver Tappe 			lastWasCountryItem = true;
4403740f52dSOliver Tappe 			lastCountryItem = zoneIter->second;
4413740f52dSOliver Tappe 		} else
4423740f52dSOliver Tappe 			lastWasCountryItem = false;
4433740f52dSOliver Tappe 	}
44499d2aa98SOliver Tappe }
445750e57b8SOliver Tappe 
446750e57b8SOliver Tappe 
447750e57b8SOliver Tappe void
448750e57b8SOliver Tappe TimeZoneView::_Revert()
449750e57b8SOliver Tappe {
450750e57b8SOliver Tappe 	fCurrentZoneItem = fOldZoneItem;
451750e57b8SOliver Tappe 
452750e57b8SOliver Tappe 	if (fCurrentZoneItem != NULL) {
453750e57b8SOliver Tappe 		int32 currentZoneIndex = fZoneList->IndexOf(fCurrentZoneItem);
454750e57b8SOliver Tappe 		fZoneList->Select(currentZoneIndex);
455750e57b8SOliver Tappe 	} else
456750e57b8SOliver Tappe 		fZoneList->DeselectAll();
457750e57b8SOliver Tappe 	fZoneList->ScrollToSelection();
458750e57b8SOliver Tappe 
459750e57b8SOliver Tappe 	_SetSystemTimeZone();
460750e57b8SOliver Tappe 	_UpdatePreview();
461750e57b8SOliver Tappe 	_UpdateCurrent();
462a10cf76eSAxel Dörfler }
463a10cf76eSAxel Dörfler 
464a10cf76eSAxel Dörfler 
465a10cf76eSAxel Dörfler void
46638ac8defSOliver Tappe TimeZoneView::_UpdatePreview()
467a10cf76eSAxel Dörfler {
46838ac8defSOliver Tappe 	int32 selection = fZoneList->CurrentSelection();
469750e57b8SOliver Tappe 	TimeZoneListItem* item
470750e57b8SOliver Tappe 		= selection < 0
471750e57b8SOliver Tappe 			? NULL
472750e57b8SOliver Tappe 			: (TimeZoneListItem*)fZoneList->ItemAt(selection);
473a10cf76eSAxel Dörfler 
474750e57b8SOliver Tappe 	if (item == NULL || !item->HasTimeZone()) {
475750e57b8SOliver Tappe 		fPreview->SetText("");
476750e57b8SOliver Tappe 		fPreview->SetTime("");
47738ac8defSOliver Tappe 		return;
478750e57b8SOliver Tappe 	}
479a10cf76eSAxel Dörfler 
480447b7fdeSOliver Tappe 	BString timeString = _FormatTime(item->TimeZone());
48138ac8defSOliver Tappe 	fPreview->SetText(item->Text());
48238ac8defSOliver Tappe 	fPreview->SetTime(timeString.String());
483a10cf76eSAxel Dörfler 
48438ac8defSOliver Tappe 	fSetZone->SetEnabled((strcmp(fCurrent->Text(), item->Text()) != 0));
485a10cf76eSAxel Dörfler }
486a10cf76eSAxel Dörfler 
487a10cf76eSAxel Dörfler 
488a10cf76eSAxel Dörfler void
48938ac8defSOliver Tappe TimeZoneView::_UpdateCurrent()
490a10cf76eSAxel Dörfler {
49138ac8defSOliver Tappe 	if (fCurrentZoneItem == NULL)
49238ac8defSOliver Tappe 		return;
4931855b44cSAdrien Destugues 
494447b7fdeSOliver Tappe 	BString timeString = _FormatTime(fCurrentZoneItem->TimeZone());
49538ac8defSOliver Tappe 	fCurrent->SetText(fCurrentZoneItem->Text());
49638ac8defSOliver Tappe 	fCurrent->SetTime(timeString.String());
497a10cf76eSAxel Dörfler }
498a10cf76eSAxel Dörfler 
49938ac8defSOliver Tappe 
50038ac8defSOliver Tappe void
50138ac8defSOliver Tappe TimeZoneView::_SetSystemTimeZone()
50238ac8defSOliver Tappe {
50338ac8defSOliver Tappe 	/*	Set sytem timezone for all different API levels. How to do this?
50438ac8defSOliver Tappe 	 *	1) tell locale-roster about new default timezone
50585b54438SOliver Tappe 	 *	2) tell kernel about new timezone offset
50638ac8defSOliver Tappe 	 */
50738ac8defSOliver Tappe 
50838ac8defSOliver Tappe 	int32 selection = fZoneList->CurrentSelection();
50938ac8defSOliver Tappe 	if (selection < 0)
51038ac8defSOliver Tappe 		return;
51138ac8defSOliver Tappe 
512447b7fdeSOliver Tappe 	TimeZoneListItem* item
513447b7fdeSOliver Tappe 		= static_cast<TimeZoneListItem*>(fZoneList->ItemAt(selection));
514447b7fdeSOliver Tappe 	if (item == NULL || !item->HasTimeZone())
515447b7fdeSOliver Tappe 		return;
516447b7fdeSOliver Tappe 
517447b7fdeSOliver Tappe 	fCurrentZoneItem = item;
518447b7fdeSOliver Tappe 	const BTimeZone& timeZone = item->TimeZone();
51938ac8defSOliver Tappe 
52025dc253dSIngo Weinhold 	MutableLocaleRoster::Default()->SetDefaultTimeZone(timeZone);
52138ac8defSOliver Tappe 
522750e57b8SOliver Tappe 	_kern_set_timezone(timeZone.OffsetFromGMT(), timeZone.ID().String(),
523750e57b8SOliver Tappe 		timeZone.ID().Length());
52485b54438SOliver Tappe 
52538ac8defSOliver Tappe 	fSetZone->SetEnabled(false);
52638ac8defSOliver Tappe 	fLastUpdateMinute = -1;
52738ac8defSOliver Tappe 		// just to trigger updating immediately
52838ac8defSOliver Tappe }
52938ac8defSOliver Tappe 
53038ac8defSOliver Tappe 
53138ac8defSOliver Tappe BString
5323740f52dSOliver Tappe TimeZoneView::_FormatTime(const BTimeZone& timeZone)
53338ac8defSOliver Tappe {
53438ac8defSOliver Tappe 	BString result;
53538ac8defSOliver Tappe 
536a096bb65SOliver Tappe 	time_t now = time(NULL);
537a096bb65SOliver Tappe 	bool rtcIsGMT;
538a096bb65SOliver Tappe 	_kern_get_real_time_clock_is_gmt(&rtcIsGMT);
5393740f52dSOliver Tappe 	if (!rtcIsGMT) {
540447b7fdeSOliver Tappe 		int32 currentOffset
541447b7fdeSOliver Tappe 			= fCurrentZoneItem != NULL && fCurrentZoneItem->HasTimeZone()
542447b7fdeSOliver Tappe 				? fCurrentZoneItem->OffsetFromGMT()
543447b7fdeSOliver Tappe 				: 0;
544447b7fdeSOliver Tappe 		now -= timeZone.OffsetFromGMT() - currentOffset;
545a096bb65SOliver Tappe 	}
54625dc253dSIngo Weinhold 	BLocale::Default()->FormatTime(&result, now, B_SHORT_TIME_FORMAT,
54725dc253dSIngo Weinhold 		&timeZone);
54838ac8defSOliver Tappe 
54938ac8defSOliver Tappe 	return result;
55038ac8defSOliver Tappe }
551