xref: /haiku/src/preferences/time/DateTimeView.cpp (revision 1c09002cbee8e797a0f8bbfc5678dfadd39ee1a7)
1 /*
2  * Copyright 2004-2010, 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  */
11 
12 #include "DateTimeView.h"
13 #include "AnalogClock.h"
14 #include "DateTimeEdit.h"
15 #include "TimeMessages.h"
16 #include "TimeWindow.h"
17 
18 #include <CalendarView.h>
19 #include <Catalog.h>
20 #include <CheckBox.h>
21 #include <DateTime.h>
22 #include <Entry.h>
23 #include <File.h>
24 #include <FindDirectory.h>
25 #include <Message.h>
26 #include <Path.h>
27 #include <RadioButton.h>
28 #include <String.h>
29 #include <StringView.h>
30 #include <Window.h>
31 
32 #include <time.h>
33 
34 #include <syscalls.h>
35 
36 #undef B_TRANSLATE_CONTEXT
37 #define B_TRANSLATE_CONTEXT "Time"
38 
39 
40 using BPrivate::BCalendarView;
41 using BPrivate::BDateTime;
42 using BPrivate::B_LOCAL_TIME;
43 
44 
45 DateTimeView::DateTimeView(BRect frame)
46 	: BView(frame, "dateTimeView", B_FOLLOW_NONE, B_WILL_DRAW
47 		| B_NAVIGABLE_JUMP),
48 	fGmtTime(NULL),
49 	fUseGmtTime(false),
50 	fInitialized(false),
51 	fSystemTimeAtStart(system_time())
52 {
53 	_ReadRTCSettings();
54 	_InitView();
55 
56 	// record the current time to enable revert.
57 	time(&fTimeAtStart);
58 }
59 
60 
61 DateTimeView::~DateTimeView()
62 {
63 	_WriteRTCSettings();
64 }
65 
66 
67 void
68 DateTimeView::AttachedToWindow()
69 {
70 	if (Parent())
71 		SetViewColor(Parent()->ViewColor());
72 
73 	if (!fInitialized) {
74 		fInitialized = true;
75 
76 		fCalendarView->SetTarget(this);
77 	}
78 }
79 
80 
81 void
82 DateTimeView::Draw(BRect /*updateRect*/)
83 {
84 	rgb_color viewcolor = ViewColor();
85 	rgb_color dark = tint_color(viewcolor, B_DARKEN_4_TINT);
86 	rgb_color light = tint_color(viewcolor, B_LIGHTEN_MAX_TINT);
87 
88 	// draw a separator line
89 	BRect bounds(Bounds());
90 	BPoint start(bounds.Width() / 2.0f, bounds.top + 5.0f);
91 	BPoint end(bounds.Width() / 2.0, bounds.bottom - 5.0f);
92 
93 	BeginLineArray(2);
94 		AddLine(start, end, dark);
95 		start.x++;
96 		end.x++;
97 		AddLine(start, end, light);
98 	EndLineArray();
99 
100 	fTimeEdit->Draw(bounds);
101 	fDateEdit->Draw(bounds);
102 }
103 
104 
105 void
106 DateTimeView::MessageReceived(BMessage* message)
107 {
108 	int32 change;
109 	switch (message->what) {
110 		case B_OBSERVER_NOTICE_CHANGE:
111 			message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change);
112 			switch (change) {
113 				case H_TM_CHANGED:
114 					_UpdateDateTime(message);
115 					break;
116 
117 				default:
118 					BView::MessageReceived(message);
119 					break;
120 			}
121 			break;
122 
123 		case kDayChanged:
124 		{
125 			BMessage msg(*message);
126 			msg.what = H_USER_CHANGE;
127 			msg.AddBool("time", false);
128 			Window()->PostMessage(&msg);
129 			break;
130 		}
131 
132 		case kRTCUpdate:
133 			fUseGmtTime = fGmtTime->Value() == B_CONTROL_ON;
134 			_UpdateGmtSettings();
135 			break;
136 
137 		case kMsgRevert:
138 			_Revert();
139 			break;
140 
141 		case kChangeTimeFinished:
142 			if (fClock->IsChangingTime())
143 				fTimeEdit->MakeFocus(false);
144 			fClock->ChangeTimeFinished();
145 			break;
146 
147 		default:
148 			BView::MessageReceived(message);
149 			break;
150 	}
151 }
152 
153 
154 bool
155 DateTimeView::CheckCanRevert()
156 {
157 	// check GMT vs Local setting
158 	bool enable = fUseGmtTime != fOldUseGmtTime;
159 
160 	// check for changed time
161 	time_t unchangedNow = fTimeAtStart + _PrefletUptime();
162 	time_t changedNow;
163 	time(&changedNow);
164 
165 	return enable || (changedNow != unchangedNow);
166 }
167 
168 
169 void
170 DateTimeView::_Revert()
171 {
172 	// Set the clock and calendar as they were at launch time +
173 	// time elapsed since application launch.
174 
175 	fUseGmtTime = fOldUseGmtTime;
176 	_UpdateGmtSettings();
177 
178 	if (fUseGmtTime)
179 		fGmtTime->SetValue(B_CONTROL_ON);
180 	else
181 		fLocalTime->SetValue(B_CONTROL_ON);
182 
183 	time_t timeNow = fTimeAtStart + _PrefletUptime();
184 	struct tm result;
185 	struct tm* timeInfo;
186 	timeInfo = localtime_r(&timeNow, &result);
187 
188 	BDateTime dateTime = BDateTime::CurrentDateTime(B_LOCAL_TIME);
189 	BTime time = dateTime.Time();
190 	BDate date = dateTime.Date();
191 	time.SetTime(timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec % 60);
192 	date.SetDate(timeInfo->tm_year + 1900, timeInfo->tm_mon + 1,
193 		timeInfo->tm_mday);
194 	dateTime.SetTime(time);
195 	dateTime.SetDate(date);
196 
197 	set_real_time_clock(dateTime.Time_t());
198 }
199 
200 
201 time_t
202 DateTimeView::_PrefletUptime() const
203 {
204 	return (time_t)((system_time() - fSystemTimeAtStart) / 1000000);
205 }
206 
207 
208 void
209 DateTimeView::_InitView()
210 {
211 	font_height fontHeight;
212 	be_plain_font->GetHeight(&fontHeight);
213 	float textHeight = fontHeight.descent + fontHeight.ascent
214 		+ fontHeight.leading + 6.0;	// 6px border
215 
216 	// left side
217 	BRect bounds = Bounds();
218 	bounds.InsetBy(10.0, 10.0);
219 	bounds.top += textHeight + 10.0;
220 
221 	fCalendarView = new BCalendarView(bounds, "calendar");
222 	fCalendarView->SetWeekNumberHeaderVisible(false);
223 	fCalendarView->ResizeToPreferred();
224 	fCalendarView->SetSelectionMessage(new BMessage(kDayChanged));
225 	fCalendarView->SetInvocationMessage(new BMessage(kDayChanged));
226 
227 	bounds.top -= textHeight + 10.0;
228 	bounds.bottom = bounds.top + textHeight;
229 	bounds.right = fCalendarView->Frame().right;
230 
231 	fDateEdit = new TDateEdit(bounds, "dateEdit", 3);
232 	AddChild(fDateEdit);
233 	AddChild(fCalendarView);
234 
235 	// right side, 2px extra for separator
236 	bounds.OffsetBy(bounds.Width() + 22.0, 0.0);
237 	fTimeEdit = new TTimeEdit(bounds, "timeEdit", 4);
238 	AddChild(fTimeEdit);
239 
240 	bounds = fCalendarView->Frame();
241 	bounds.OffsetBy(bounds.Width() + 22.0, 0.0);
242 
243 	fClock = new TAnalogClock(bounds, "analogClock");
244 	AddChild(fClock);
245 	BTime time(BTime::CurrentTime(B_LOCAL_TIME));
246 	fClock->SetTime(time.Hour(), time.Minute(), time.Second());
247 
248 	// clock radio buttons
249 	bounds.top = fClock->Frame().bottom + 10.0;
250 	BStringView* text = new BStringView(bounds, "clockSetTo",
251 		B_TRANSLATE("Clock set to:"));
252 	AddChild(text);
253 	text->ResizeToPreferred();
254 
255 	bounds.left += 10.0f;
256 	bounds.top = text->Frame().bottom;
257 	fLocalTime = new BRadioButton(bounds, "localTime",
258 		B_TRANSLATE("Local time"), new BMessage(kRTCUpdate));
259 	AddChild(fLocalTime);
260 	fLocalTime->ResizeToPreferred();
261 
262 	bounds.left = fLocalTime->Frame().right + 10.0;
263 	fGmtTime = new BRadioButton(bounds, "greenwichMeanTime",
264 		B_TRANSLATE("GMT"),	new BMessage(kRTCUpdate));
265 	AddChild(fGmtTime);
266 	fGmtTime->ResizeToPreferred();
267 
268 	if (fUseGmtTime)
269 		fGmtTime->SetValue(B_CONTROL_ON);
270 	else
271 		fLocalTime->SetValue(B_CONTROL_ON);
272 
273 	fOldUseGmtTime = fUseGmtTime;
274 
275 	ResizeTo(fClock->Frame().right + 10.0, fGmtTime->Frame().bottom + 10.0);
276 }
277 
278 
279 void
280 DateTimeView::_ReadRTCSettings()
281 {
282 	BPath path;
283 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
284 		return;
285 
286 	path.Append("RTC_time_settings");
287 
288 	BEntry entry(path.Path());
289 	if (entry.Exists()) {
290 		BFile file(&entry, B_READ_ONLY);
291 		if (file.InitCheck() == B_OK) {
292 			char buffer[6];
293 			file.Read(buffer, 6);
294 			if (strncmp(buffer, "gmt", 3) == 0)
295 				fUseGmtTime = true;
296 		}
297 	}
298 }
299 
300 
301 void
302 DateTimeView::_WriteRTCSettings()
303 {
304 	BPath path;
305 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
306 		return;
307 
308 	path.Append("RTC_time_settings");
309 
310 	BFile file(path.Path(), B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
311 	if (file.InitCheck() == B_OK) {
312 		if (fUseGmtTime)
313 			file.Write("gmt", 3);
314 		else
315 			file.Write("local", 5);
316 	}
317 }
318 
319 
320 void
321 DateTimeView::_UpdateGmtSettings()
322 {
323 	_WriteRTCSettings();
324 
325 	_kern_set_real_time_clock_is_gmt(fUseGmtTime);
326 }
327 
328 
329 void
330 DateTimeView::_UpdateDateTime(BMessage* message)
331 {
332 	int32 day;
333 	int32 month;
334 	int32 year;
335 	if (message->FindInt32("month", &month) == B_OK
336 		&& message->FindInt32("day", &day) == B_OK
337 		&& message->FindInt32("year", &year) == B_OK) {
338 		fDateEdit->SetDate(year, month, day);
339 		fCalendarView->SetDate(year, month, day);
340 	}
341 
342 	int32 hour;
343 	int32 minute;
344 	int32 second;
345 	if (message->FindInt32("hour", &hour) == B_OK
346 		&& message->FindInt32("minute", &minute) == B_OK
347 		&& message->FindInt32("second", &second) == B_OK) {
348 		fClock->SetTime(hour, minute, second);
349 		fTimeEdit->SetTime(hour, minute, second);
350 	}
351 }
352