1/*
2 * Copyright 2004-2011, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Andrew McCall <mccall@@digitalparadise.co.uk>
7 *		Mike Berg <mike@berg-net.us>
8 *		Julun <host.haiku@gmx.de>
9 *		Philippe Saint-Pierre <stpere@gmail.com>
10 *		Hamish Morrison <hamish@lavabit.com>
11 */
12
13#include "DateTimeView.h"
14
15#include <time.h>
16#include <syscalls.h>
17
18#include <Box.h>
19#include <CalendarView.h>
20#include <Catalog.h>
21#include <CheckBox.h>
22#include <ControlLook.h>
23#include <DateTime.h>
24#include <Entry.h>
25#include <File.h>
26#include <FindDirectory.h>
27#include <LocaleRoster.h>
28#include <Message.h>
29#include <Path.h>
30#include <StringView.h>
31#include <Window.h>
32
33#include "AnalogClock.h"
34#include "DateTimeEdit.h"
35#include "TimeMessages.h"
36#include "TimeWindow.h"
37
38
39#undef B_TRANSLATION_CONTEXT
40#define B_TRANSLATION_CONTEXT "Time"
41
42
43using BPrivate::BCalendarView;
44using BPrivate::BDateTime;
45using BPrivate::B_LOCAL_TIME;
46
47
48DateTimeView::DateTimeView(const char* name)
49	:
50	BGroupView(name, B_HORIZONTAL, 5),
51	fInitialized(false),
52	fSystemTimeAtStart(system_time())
53{
54	_InitView();
55
56	// record the current time to enable revert.
57	time(&fTimeAtStart);
58}
59
60
61DateTimeView::~DateTimeView()
62{
63}
64
65
66void
67DateTimeView::AttachedToWindow()
68{
69	AdoptParentColors();
70
71	if (!fInitialized) {
72		fInitialized = true;
73
74		fCalendarView->SetTarget(this);
75	}
76}
77
78
79void
80DateTimeView::MessageReceived(BMessage* message)
81{
82	int32 change;
83	switch (message->what) {
84		case B_OBSERVER_NOTICE_CHANGE:
85			message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change);
86			switch (change) {
87				case H_TM_CHANGED:
88					_UpdateDateTime(message);
89					break;
90
91				default:
92					BView::MessageReceived(message);
93					break;
94			}
95			break;
96
97		case B_LOCALE_CHANGED:
98			fCalendarView->UpdateDayNameHeader();
99			break;
100
101		case kDayChanged:
102		{
103			BMessage msg(*message);
104			msg.what = H_USER_CHANGE;
105			msg.AddBool("time", false);
106			Window()->PostMessage(&msg);
107			break;
108		}
109
110		case kMsgRevert:
111			_Revert();
112			break;
113
114		case kChangeTimeFinished:
115			if (fClock->IsChangingTime())
116				fTimeEdit->MakeFocus(false);
117			fClock->ChangeTimeFinished();
118			break;
119
120		case kRTCUpdate:
121			break;
122
123		default:
124			BView::MessageReceived(message);
125			break;
126	}
127}
128
129
130bool
131DateTimeView::CheckCanRevert()
132{
133	// check for changed time
134	time_t unchangedNow = fTimeAtStart + _PrefletUptime();
135	time_t changedNow;
136	time(&changedNow);
137
138	return changedNow != unchangedNow;
139}
140
141
142void
143DateTimeView::_Revert()
144{
145	// Set the clock and calendar as they were at launch time +
146	// time elapsed since application launch.
147
148	time_t timeNow = fTimeAtStart + _PrefletUptime();
149	struct tm result;
150	struct tm* timeInfo;
151	timeInfo = localtime_r(&timeNow, &result);
152
153	BDateTime dateTime = BDateTime::CurrentDateTime(B_LOCAL_TIME);
154	BTime time = dateTime.Time();
155	BDate date = dateTime.Date();
156	time.SetTime(timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec % 60);
157	date.SetDate(timeInfo->tm_year + 1900, timeInfo->tm_mon + 1,
158		timeInfo->tm_mday);
159	dateTime.SetTime(time);
160	dateTime.SetDate(date);
161
162	set_real_time_clock(dateTime.Time_t());
163}
164
165
166time_t
167DateTimeView::_PrefletUptime() const
168{
169	return (time_t)((system_time() - fSystemTimeAtStart) / 1000000);
170}
171
172
173void
174DateTimeView::_InitView()
175{
176	fCalendarView = new BCalendarView("calendar");
177	fCalendarView->SetWeekNumberHeaderVisible(false);
178	fCalendarView->SetSelectionMessage(new BMessage(kDayChanged));
179	fCalendarView->SetInvocationMessage(new BMessage(kDayChanged));
180
181	fDateEdit = new TDateEdit("dateEdit", 3);
182	fTimeEdit = new TTimeEdit("timeEdit", 5);
183	fClock = new TAnalogClock("analogClock");
184
185	BTime time(BTime::CurrentTime(B_LOCAL_TIME));
186	fClock->SetTime(time.Hour(), time.Minute(), time.Second());
187
188	BBox* divider = new BBox(BRect(0, 0, 1, 1),
189		B_EMPTY_STRING, B_FOLLOW_ALL_SIDES,
190		B_WILL_DRAW | B_FRAME_EVENTS, B_FANCY_BORDER);
191	divider->SetExplicitMaxSize(BSize(1, B_SIZE_UNLIMITED));
192
193	BLayoutBuilder::Group<>(this, B_HORIZONTAL, B_USE_DEFAULT_SPACING)
194		.AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING)
195			.Add(fDateEdit)
196			.Add(fCalendarView)
197		.End()
198		.Add(divider)
199		.AddGroup(B_VERTICAL)
200			.Add(fTimeEdit)
201			.Add(fClock)
202		.End()
203		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
204			B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING);
205}
206
207
208void
209DateTimeView::_UpdateDateTime(BMessage* message)
210{
211	int32 day;
212	int32 month;
213	int32 year;
214	if (message->FindInt32("month", &month) == B_OK
215		&& message->FindInt32("day", &day) == B_OK
216		&& message->FindInt32("year", &year) == B_OK) {
217		static int32 lastDay;
218		static int32 lastMonth;
219		static int32 lastYear;
220		if (day != lastDay || month != lastMonth || year != lastYear) {
221			fDateEdit->SetDate(year, month, day);
222			fCalendarView->SetDate(year, month, day);
223			lastDay = day;
224			lastMonth = month;
225			lastYear = year;
226		}
227	}
228
229	int32 hour;
230	int32 minute;
231	int32 second;
232	if (message->FindInt32("hour", &hour) == B_OK
233		&& message->FindInt32("minute", &minute) == B_OK
234		&& message->FindInt32("second", &second) == B_OK) {
235		fClock->SetTime(hour, minute, second);
236		fTimeEdit->SetTime(hour, minute, second);
237	}
238}
239
240