xref: /haiku/src/preferences/time/DateTimeView.cpp (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
1 /*
2  * Copyright 2004-2009, 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 
19 #include <CalendarView.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 #include <time.h>
32 
33 
34 using BPrivate::BCalendarView;
35 using BPrivate::BDateTime;
36 using BPrivate::B_LOCAL_TIME;
37 
38 
39 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
40 #	include <syscalls.h>
41 #else
42 extern "C" void _kset_tzfilename_(const char *name, size_t length, bool isGMT);
43 #	define _kern_set_tzfilename _kset_tzfilename_
44 #endif
45 
46 
47 DateTimeView::DateTimeView(BRect frame)
48 	: BView(frame, "dateTimeView", B_FOLLOW_NONE, B_WILL_DRAW | B_NAVIGABLE_JUMP),
49 	  fGmtTime(NULL),
50 	  fUseGmtTime(false),
51 	  fInitialized(false),
52 	  fSystemTimeAtStart(system_time())
53 {
54 	_ReadRTCSettings();
55 	_InitView();
56 
57 	// record the current time to enable revert.
58 	time(&fTimeAtStart);
59 }
60 
61 
62 DateTimeView::~DateTimeView()
63 {
64 	_WriteRTCSettings();
65 }
66 
67 
68 void
69 DateTimeView::AttachedToWindow()
70 {
71 	if (Parent())
72 		SetViewColor(Parent()->ViewColor());
73 
74 	if (!fInitialized) {
75 		fInitialized = true;
76 
77 		fCalendarView->SetTarget(this);
78 	}
79 }
80 
81 
82 void
83 DateTimeView::Draw(BRect /*updateRect*/)
84 {
85 	rgb_color viewcolor = ViewColor();
86 	rgb_color dark = tint_color(viewcolor, B_DARKEN_4_TINT);
87 	rgb_color light = tint_color(viewcolor, B_LIGHTEN_MAX_TINT);
88 
89 	//draw a separator line
90 	BRect bounds(Bounds());
91 	BPoint start(bounds.Width() / 2.0f, bounds.top + 5.0f);
92 	BPoint end(bounds.Width() / 2.0, bounds.bottom - 5.0f);
93 
94 	BeginLineArray(2);
95 		AddLine(start, end, dark);
96 		start.x++;
97 		end.x++;
98 		AddLine(start, end, light);
99 	EndLineArray();
100 
101 	fTimeEdit->Draw(bounds);
102 	fDateEdit->Draw(bounds);
103 }
104 
105 
106 void
107 DateTimeView::MessageReceived(BMessage *message)
108 {
109 	int32 change;
110 	switch (message->what) {
111 		case B_OBSERVER_NOTICE_CHANGE:
112 			message->FindInt32(B_OBSERVE_WHAT_CHANGE, &change);
113 			switch (change) {
114 				case H_TM_CHANGED:
115 					_UpdateDateTime(message);
116 					break;
117 
118 				default:
119 					BView::MessageReceived(message);
120 					break;
121 			}
122 			break;
123 
124 		case kDayChanged:
125 		{
126 			BMessage msg(*message);
127 			msg.what = H_USER_CHANGE;
128 			msg.AddBool("time", false);
129 			Window()->PostMessage(&msg);
130 			break;
131 		}
132 
133 		case kRTCUpdate:
134 			fUseGmtTime = fGmtTime->Value() == B_CONTROL_ON;
135 			_UpdateGmtSettings();
136 			break;
137 
138 		case kMsgRevert:
139 			_Revert();
140 			break;
141 
142 		case kChangeTimeFinished:
143 			if (fClock->IsChangingTime())
144 				fTimeEdit->MakeFocus(false);
145 			fClock->ChangeTimeFinished();
146 			break;
147 
148 		default:
149 			BView::MessageReceived(message);
150 			break;
151 	}
152 }
153 
154 
155 bool
156 DateTimeView::CheckCanRevert()
157 {
158 	// check GMT vs Local setting
159 	bool enable = fUseGmtTime != fOldUseGmtTime;
160 
161 	// check for changed time
162 	time_t unchangedNow = fTimeAtStart + _PrefletUptime();
163 	time_t changedNow;
164 	time(&changedNow);
165 
166 	return enable || (changedNow != unchangedNow);
167 }
168 
169 
170 void
171 DateTimeView::_Revert()
172 {
173 	// Set the clock and calendar as they were at launch time +
174 	// time elapsed since application launch.
175 
176 	fUseGmtTime = fOldUseGmtTime;
177 	_UpdateGmtSettings();
178 
179 	if (fUseGmtTime)
180 		fGmtTime->SetValue(B_CONTROL_ON);
181 	else
182 		fLocalTime->SetValue(B_CONTROL_ON);
183 
184 	time_t timeNow = fTimeAtStart + _PrefletUptime();
185 	struct tm result;
186 	struct tm* timeInfo;
187 	timeInfo = localtime_r(&timeNow, &result);
188 
189 	BDateTime dateTime = BDateTime::CurrentDateTime(B_LOCAL_TIME);
190 	BTime time = dateTime.Time();
191 	BDate date = dateTime.Date();
192 	time.SetTime(timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec % 60);
193 	date.SetDate(timeInfo->tm_year + 1900, timeInfo->tm_mon + 1,
194 		timeInfo->tm_mday);
195 	dateTime.SetTime(time);
196 	dateTime.SetDate(date);
197 
198 	set_real_time_clock(dateTime.Time_t());
199 }
200 
201 
202 time_t
203 DateTimeView::_PrefletUptime() const
204 {
205 	return (time_t)((system_time() - fSystemTimeAtStart) / 1000000);
206 }
207 
208 
209 void
210 DateTimeView::_InitView()
211 {
212 	font_height fontHeight;
213 	be_plain_font->GetHeight(&fontHeight);
214 	float textHeight = fontHeight.descent + fontHeight.ascent
215 		+ fontHeight.leading + 6.0;	// 6px border
216 
217 	// left side
218 	BRect bounds = Bounds();
219 	bounds.InsetBy(10.0, 10.0);
220 	bounds.top += textHeight + 10.0;
221 
222 	fCalendarView = new BCalendarView(bounds, "calendar");
223 	fCalendarView->SetWeekNumberHeaderVisible(false);
224 	fCalendarView->ResizeToPreferred();
225 	fCalendarView->SetSelectionMessage(new BMessage(kDayChanged));
226 	fCalendarView->SetInvocationMessage(new BMessage(kDayChanged));
227 
228 	bounds.top -= textHeight + 10.0;
229 	bounds.bottom = bounds.top + textHeight;
230 	bounds.right = fCalendarView->Frame().right;
231 
232 	fDateEdit = new TDateEdit(bounds, "dateEdit", 3);
233 	AddChild(fDateEdit);
234 	AddChild(fCalendarView);
235 
236 	// right side, 2px extra for separator
237 	bounds.OffsetBy(bounds.Width() + 22.0, 0.0);
238 	fTimeEdit = new TTimeEdit(bounds, "timeEdit", 4);
239 	AddChild(fTimeEdit);
240 
241 	bounds = fCalendarView->Frame();
242 	bounds.OffsetBy(bounds.Width() + 22.0, 0.0);
243 
244 	fClock = new TAnalogClock(bounds, "analogClock");
245 	AddChild(fClock);
246 	BTime time(BTime::CurrentTime(B_LOCAL_TIME));
247 	fClock->SetTime(time.Hour(), time.Minute(), time.Second());
248 
249 	// clock radio buttons
250 	bounds.top = fClock->Frame().bottom + 10.0;
251 	BStringView *text = new BStringView(bounds, "clockSetTo", "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", "Local Time",
258 		new BMessage(kRTCUpdate));
259 	AddChild(fLocalTime);
260 	fLocalTime->ResizeToPreferred();
261 
262 	bounds.left = fLocalTime->Frame().right + 10.0;
263 	fGmtTime = new BRadioButton(bounds, "greenwichMeanTime", "GMT",
264 		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 	BPath path;
326 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
327 		path.Append("timezone");
328 		BEntry entry(path.Path(), true);
329 		if (entry.Exists()) {
330 			entry.GetPath(&path);
331 
332 			// take the existing timezone and set it's gmt use
333 			_kern_set_tzfilename(path.Path(), B_PATH_NAME_LENGTH, fUseGmtTime);
334 			return;
335 		}
336 	}
337 
338 	// Only update GMT
339 	_kern_set_tzfilename(NULL, 0, fUseGmtTime);
340 }
341 
342 
343 void
344 DateTimeView::_UpdateDateTime(BMessage *message)
345 {
346 	int32 day;
347 	int32 month;
348 	int32 year;
349 	if (message->FindInt32("month", &month) == B_OK
350 		&& message->FindInt32("day", &day) == B_OK
351 		&& message->FindInt32("year", &year) == B_OK) {
352 		fDateEdit->SetDate(year, month, day);
353 		fCalendarView->SetDate(year, month, day);
354 	}
355 
356 	int32 hour;
357 	int32 minute;
358 	int32 second;
359 	if (message->FindInt32("hour", &hour) == B_OK
360 		&& message->FindInt32("minute", &minute) == B_OK
361 		&& message->FindInt32("second", &second) == B_OK) {
362 		fClock->SetTime(hour, minute, second);
363 		fTimeEdit->SetTime(hour, minute, second);
364 	}
365 }
366