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