git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13263 a95241bf-73f2-0310-859d-f6bbb57e9c96
548 lines
12 KiB
C++
548 lines
12 KiB
C++
/*
|
|
ZoneView.cpp
|
|
by Mike Berg (inseculous)
|
|
|
|
Status: mimics original Time Zone tab of Time Pref App
|
|
Exceptions: doesn't calc "Time in" time.
|
|
Issues: After experimenting with both Time Prefs, it seems the original doesn't
|
|
use the link file in the users settings file to get the current Timezone.
|
|
Need to find the call it uses to get its inital info so I can get exact duplication.
|
|
*/
|
|
|
|
#include <Button.h>
|
|
|
|
#include <Directory.h>
|
|
#include <Entry.h>
|
|
#include <FindDirectory.h>
|
|
#include <ListView.h>
|
|
#include <MenuField.h>
|
|
#include <MenuItem.h>
|
|
#include <MenuBar.h>
|
|
#include <PopUpMenu.h>
|
|
#include <ScrollView.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <StorageDefs.h>
|
|
#include <String.h>
|
|
#include <View.h>
|
|
#include <Window.h>
|
|
|
|
#include "TimeMessages.h"
|
|
#include "ZoneView.h"
|
|
|
|
|
|
/*=====> TZoneItem <=====*/
|
|
TZoneItem::TZoneItem(const char *text, const char *zone)
|
|
: BStringItem(text)
|
|
{
|
|
f_zone = new BPath(zone);
|
|
}
|
|
|
|
|
|
TZoneItem::~TZoneItem()
|
|
{
|
|
delete f_zone;
|
|
}
|
|
|
|
const char *
|
|
TZoneItem::Zone() const
|
|
{
|
|
return f_zone->Leaf();
|
|
}
|
|
|
|
|
|
const char *
|
|
TZoneItem::Path() const
|
|
{
|
|
return f_zone->Path();
|
|
}
|
|
|
|
|
|
/*=====> TZoneView <=====*/
|
|
|
|
TZoneView::TZoneView(BRect frame)
|
|
: BView(frame, B_EMPTY_STRING, B_FOLLOW_ALL, B_WILL_DRAW|B_NAVIGABLE_JUMP)
|
|
, f_first(true)
|
|
{
|
|
ReadTimeZoneLink();
|
|
InitView();
|
|
}
|
|
|
|
|
|
TZoneView::~TZoneView()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
void
|
|
TZoneView::AllAttached()
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::AttachedToWindow()
|
|
{
|
|
if (Parent())
|
|
SetViewColor(Parent()->ViewColor());
|
|
|
|
if (f_first) // stupid hack
|
|
{
|
|
f_regionpopup->SetTargetForItems(this);
|
|
f_setzone->SetTarget(this);
|
|
f_citylist->SetTarget(this);
|
|
// update displays
|
|
BPath parent;
|
|
f_currentzone.GetParent(&parent);
|
|
int czone = FillCityList(parent.Path());
|
|
if (czone> -1) {
|
|
f_citylist->Select(czone);
|
|
f_current->SetText( ((TZoneItem *)f_citylist->ItemAt(czone))->Text() );
|
|
}
|
|
f_first = false;
|
|
}
|
|
f_citylist->ScrollToSelection();
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::Draw(BRect updaterect)
|
|
{
|
|
f_current->Draw(updaterect);
|
|
f_preview->Draw(updaterect);
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::MessageReceived(BMessage *message)
|
|
{
|
|
int32 change;
|
|
switch(message->what) {
|
|
case B_OBSERVER_NOTICE_CHANGE:
|
|
message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change);
|
|
switch(change) {
|
|
case H_TM_CHANGED:
|
|
UpdateDateTime(message);
|
|
break;
|
|
|
|
default:
|
|
BView::MessageReceived(message);
|
|
break;
|
|
}
|
|
break;
|
|
case H_REGION_CHANGED:
|
|
ChangeRegion(message);
|
|
break;
|
|
|
|
case H_SET_TIME_ZONE:
|
|
SetTimeZone();
|
|
break;
|
|
|
|
case H_CITY_CHANGED:
|
|
SetPreview();
|
|
break;
|
|
|
|
default:
|
|
BView::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::UpdateDateTime(BMessage *message)
|
|
{
|
|
// only need hour and minute
|
|
int32 hour, minute;
|
|
|
|
if (message->FindInt32("hour", &hour) == B_OK
|
|
&& message->FindInt32("minute", &minute) == B_OK) {
|
|
if (f_hour != hour || f_minute != minute) {
|
|
f_hour = hour;
|
|
f_minute = minute;
|
|
f_current->SetTo(hour, minute);
|
|
|
|
if (f_citylist->CurrentSelection()> -1) {
|
|
// do calc to get other zone time
|
|
SetPreview();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView:: InitView()
|
|
{
|
|
font_height finfo;
|
|
be_plain_font->GetHeight(&finfo);
|
|
float text_height = finfo.ascent +finfo.descent +finfo.leading;
|
|
|
|
BRect bounds(Bounds().InsetByCopy(4, 2));
|
|
BRect frame(bounds);
|
|
|
|
// Zone menu
|
|
f_regionpopup = new BPopUpMenu(B_EMPTY_STRING, true, true, B_ITEMS_IN_COLUMN);
|
|
|
|
float widest = 0;
|
|
BuildRegionMenu(&widest);
|
|
|
|
frame.right = frame.left +widest +25;
|
|
frame.bottom = frame.top +text_height -1;
|
|
|
|
BMenuField *mField;
|
|
mField= new BMenuField(frame, "regions", NULL, f_regionpopup, true);
|
|
mField->MenuBar()->SetBorder(B_BORDER_CONTENTS);
|
|
mField->MenuBar()->ResizeToPreferred();
|
|
AddChild(mField);
|
|
|
|
|
|
// City Listing
|
|
|
|
frame = mField->MenuBar()->Frame();
|
|
frame.top += frame.bottom +1;
|
|
frame.right = mField->Bounds().Width() -B_V_SCROLL_BAR_WIDTH +2;
|
|
frame.bottom = bounds.bottom;
|
|
frame.OffsetBy(2, 0);
|
|
frame.InsetBy(3, 5);
|
|
|
|
f_citylist = new BListView(frame, "cities", B_SINGLE_SELECTION_LIST,
|
|
B_FOLLOW_LEFT|B_FOLLOW_TOP,
|
|
B_WILL_DRAW|B_NAVIGABLE|B_FRAME_EVENTS);
|
|
|
|
BMessage *citychange = new BMessage(H_CITY_CHANGED);
|
|
f_citylist->SetSelectionMessage(citychange);
|
|
|
|
BMessage *cityinvoke = new BMessage(H_SET_TIME_ZONE);
|
|
f_citylist->SetInvocationMessage(cityinvoke);
|
|
|
|
BScrollView *scrollList;
|
|
scrollList = new BScrollView("scroll_list", f_citylist,
|
|
B_FOLLOW_LEFT|B_FOLLOW_TOP, 0, false, true);
|
|
AddChild(scrollList);
|
|
|
|
|
|
// Time Displays
|
|
|
|
frame.OffsetBy(scrollList->Bounds().Width() +9, 3);
|
|
frame.bottom = frame.top +text_height *2;
|
|
frame.right = bounds.right -6;
|
|
f_current = new TTZDisplay(frame, "current",
|
|
B_FOLLOW_LEFT|B_FOLLOW_TOP, 0,
|
|
"Current time zone:", B_EMPTY_STRING);
|
|
f_current->ResizeToPreferred();
|
|
|
|
frame.OffsetBy(0, (text_height *3) +2);
|
|
f_preview = new TTZDisplay(frame, "timein",
|
|
B_FOLLOW_LEFT|B_FOLLOW_TOP, 0,
|
|
"Time in: ", B_EMPTY_STRING);
|
|
f_preview->ResizeToPreferred();
|
|
|
|
AddChild(f_current);
|
|
AddChild(f_preview);
|
|
|
|
// set button
|
|
|
|
frame.Set(bounds.right -75, bounds.bottom -24,
|
|
bounds.right, bounds.bottom);
|
|
frame.OffsetBy(-2, -2);
|
|
f_setzone = new BButton(frame, "set", "Set", new BMessage(H_SET_TIME_ZONE));
|
|
f_setzone->SetEnabled(false);
|
|
AddChild(f_setzone);
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::BuildRegionMenu(float *widest)
|
|
{
|
|
BPath path;
|
|
// return if /etc directory is not found
|
|
if (!(find_directory(B_BEOS_ETC_DIRECTORY, &path) == B_OK))
|
|
return;
|
|
|
|
path.Append("timezones");
|
|
|
|
// get current region
|
|
BPath region;
|
|
f_currentzone.GetParent(®ion);
|
|
|
|
float width = 0;
|
|
bool markit;
|
|
BEntry entry;
|
|
BMenuItem *item;
|
|
BDirectory dir(path.Path());
|
|
|
|
dir.Rewind();
|
|
|
|
// walk timezones dir and add entries to menu
|
|
BString itemtext;
|
|
while (dir.GetNextEntry(&entry) == B_NO_ERROR) {
|
|
if (entry.IsDirectory()) {
|
|
path.SetTo(&entry);
|
|
itemtext = path.Leaf();
|
|
|
|
// skip Etc directory
|
|
if (itemtext.Compare("Etc", 3) == 0)
|
|
continue;
|
|
|
|
markit = itemtext.Compare(region.Leaf()) == 0;
|
|
|
|
// add Ocean to oceans :)
|
|
if (itemtext.Compare("Atlantic", 8) == 0
|
|
||itemtext.Compare("Pacific", 7) == 0
|
|
||itemtext.Compare("Indian", 6) == 0)
|
|
itemtext.Append(" Ocean");
|
|
|
|
itemtext = itemtext.ReplaceAll('_', ' '); // underscores are spaces
|
|
|
|
width = be_plain_font->StringWidth(itemtext.String());
|
|
if (width> *widest) *widest = width;
|
|
|
|
BMessage *msg = new BMessage(H_REGION_CHANGED);
|
|
msg->AddString("region", path.Path());
|
|
|
|
item = new BMenuItem(itemtext.String(), msg);
|
|
item->SetMarked(markit);
|
|
f_regionpopup->AddItem(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
TZoneView::FillCityList(const char *area)
|
|
{
|
|
// clear list
|
|
|
|
// MakeEmpty() doesn't free items so...
|
|
int32 idx = f_citylist->CountItems();
|
|
if (idx>= 1)
|
|
for (; idx>= 0; idx--)
|
|
delete f_citylist->RemoveItem(idx);
|
|
|
|
//Enter time zones directory. Find subdir with name that matches string stored in area.
|
|
//Enter subdirectory and count the items. For each item, add a StringItem to f_CityList
|
|
//Time zones directory
|
|
|
|
BPath path;
|
|
|
|
// return if /etc directory is not found
|
|
if (!(find_directory(B_BEOS_ETC_DIRECTORY, &path) == B_OK))
|
|
return 0;
|
|
|
|
path.Append("timezones");
|
|
|
|
BPath Area(area);
|
|
BDirectory zoneDir(path.Path());
|
|
BDirectory cityDir;
|
|
BStringItem *city;
|
|
BString city_name;
|
|
BEntry entry;
|
|
int index = -1;
|
|
//locate subdirectory:
|
|
|
|
if ( zoneDir.Contains(Area.Leaf(), B_DIRECTORY_NODE)) {
|
|
cityDir.SetTo(&zoneDir, Area.Leaf());
|
|
//There is a subdir with a name that matches 'area'. That's the one!!
|
|
|
|
|
|
//iterate over the items in the subdir and fill the listview with TZoneItems:
|
|
|
|
while(cityDir.GetNextEntry(&entry) == B_NO_ERROR) {
|
|
if (!entry.IsDirectory()) {
|
|
BPath zone(&entry);
|
|
city_name = zone.Leaf();
|
|
city_name.ReplaceAll("_IN", ", Indiana");
|
|
city_name.ReplaceAll("__Calif", ", Calif");
|
|
city_name.ReplaceAll("__", ", ");
|
|
city_name.ReplaceAll("_", " ");
|
|
city = new TZoneItem(city_name.String(), zone.Path());
|
|
f_citylist->AddItem(city);
|
|
if (strcmp(f_currentzone.Leaf(), zone.Leaf()) == 0)
|
|
index = f_citylist->IndexOf(city);
|
|
}
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::ChangeRegion(BMessage *message)
|
|
{
|
|
BString area;
|
|
message->FindString("region", &area);
|
|
|
|
FillCityList(area.String());
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::ReadTimeZoneLink()
|
|
{
|
|
/* reads the timezone symlink from B_USER_SETTINGS_DIRECTORY
|
|
currently this sets f_currentzone to the symlink value.
|
|
this is wrong. the original can get users timezone without
|
|
a timezone symlink present.
|
|
|
|
defaults are set to different values to clue in on what error was returned
|
|
GMT is set when the link is invalid
|
|
EST is set when the settings dir can't be found **should never happen**
|
|
*/
|
|
|
|
BPath path;
|
|
BEntry tzLink;
|
|
|
|
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
|
|
|
|
path.Append("timezone");
|
|
tzLink.SetTo(path.Path(), true);
|
|
|
|
if (tzLink.InitCheck() != B_OK) {// link is broken
|
|
tzLink.SetTo("/boot/beos/etc/timezones/Pacific/fiji");
|
|
// do something like set to a default GMT???
|
|
}
|
|
} else {
|
|
// set tzlink to a default
|
|
tzLink.SetTo("/boot/beos/etc/timezones/EST");
|
|
}
|
|
|
|
// we need something in the current zone
|
|
f_currentzone.SetTo(&tzLink);
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::SetPreview()
|
|
{
|
|
// calc and display time based on users selection in city list
|
|
int32 selection = f_citylist->CurrentSelection();
|
|
if (selection>= 0) {
|
|
TZoneItem *item = (TZoneItem *)f_citylist->ItemAt(selection);
|
|
|
|
BString text;
|
|
text = item->Text();
|
|
|
|
time_t current;
|
|
struct tm *ltime;
|
|
|
|
// calc time to display and update
|
|
SetTimeZone(item->Path());
|
|
current = time(0);
|
|
ltime = localtime(¤t);
|
|
SetTimeZone(f_currentzone.Path());
|
|
|
|
f_preview->SetTo(ltime->tm_hour, ltime->tm_min);
|
|
f_preview->SetText(text.String());
|
|
|
|
f_setzone->SetEnabled((strcmp(f_current->Text(), text.String()) != 0));
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::SetCurrent(const char *text)
|
|
{
|
|
time_t current;
|
|
struct tm *ltime;
|
|
SetTimeZone(f_currentzone.Path());
|
|
current = time(0);
|
|
ltime = localtime(¤t);
|
|
|
|
f_current->SetTo(ltime->tm_hour, ltime->tm_min);
|
|
f_current->SetText(text);
|
|
}
|
|
|
|
|
|
void
|
|
TZoneView::SetTimeZone()
|
|
{
|
|
/*
|
|
set time based on supplied timezone. How to do this?
|
|
1) replace symlink, "timezone", in B_USER_SETTINGS_DIR with a link to the new timezone
|
|
2) set TZ environment var
|
|
3) call settz()
|
|
4) call set_timezone from OS.h passing path to timezone file
|
|
*/
|
|
|
|
|
|
// update/create timezone symlink in B_USER_SETTINGS_DIRECTORY
|
|
|
|
// get path to current link
|
|
BPath path;
|
|
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
|
|
return;
|
|
|
|
path.Append("timezone");
|
|
|
|
|
|
// build target for new link
|
|
|
|
int32 selection = f_citylist->CurrentSelection();
|
|
if (selection < 0)
|
|
// nothing selected??
|
|
return;
|
|
|
|
BPath target( ((TZoneItem *)f_citylist->ItemAt(selection))->Path());
|
|
|
|
// remove old
|
|
BEntry entry(path.Path());
|
|
if (entry.Exists())
|
|
entry.Remove();
|
|
|
|
// create new
|
|
BDirectory dir(target.Path());
|
|
if (dir.CreateSymLink(path.Path(), target.Path(), NULL) != B_OK)
|
|
printf("timezone not linked\n");
|
|
|
|
// update environment
|
|
|
|
char tz[B_PATH_NAME_LENGTH];
|
|
sprintf(tz, "TZ=%s", target.Path());
|
|
|
|
putenv( tz);
|
|
tzset();
|
|
|
|
// update display
|
|
time_t current = time(0);
|
|
struct tm *ltime = localtime(¤t);
|
|
|
|
|
|
char tza[B_PATH_NAME_LENGTH];
|
|
sprintf(tza, "%s", target.Path());
|
|
set_timezone(tza);
|
|
|
|
// disable button
|
|
f_setzone -> SetEnabled(false);
|
|
|
|
time_t newtime = mktime(ltime);
|
|
ltime = localtime(&newtime);
|
|
stime(&newtime);
|
|
|
|
f_hour = ltime->tm_hour;
|
|
f_minute = ltime->tm_min;
|
|
f_currentzone.SetTo(target.Path());
|
|
SetCurrent(((TZoneItem *)f_citylist->ItemAt(selection))->Text());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
TZoneView::SetTimeZone(const char *zone)
|
|
{
|
|
BString tz;
|
|
|
|
tz << "TZ=" << zone;
|
|
|
|
putenv(tz.String());
|
|
tzset();
|
|
}
|
|
|
|
|
|
//---//
|