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