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