xref: /haiku/src/preferences/time/DateTimeView.cpp (revision 746cac055adc6ac3308c7bc2d29040fb95689cc9)
1 /*
2  * Copyright 2004-2007, 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 		default:
143 			BView::MessageReceived(message);
144 			break;
145 	}
146 }
147 
148 
149 bool
150 DateTimeView::CheckCanRevert()
151 {
152 	// check GMT vs Local setting
153 	bool enable = fUseGmtTime != fOldUseGmtTime;
154 
155 	// check for changed time
156 	time_t unchangedNow = fTimeAtStart + _PrefletUptime();
157 	time_t changedNow;
158 	time(&changedNow);
159 
160 	return enable || (changedNow != unchangedNow);
161 }
162 
163 
164 void
165 DateTimeView::_Revert()
166 {
167 	// Set the clock and calendar as they were at launch time +
168 	// time ellapsed since application launch.
169 
170 	fUseGmtTime = fOldUseGmtTime;
171 	_UpdateGmtSettings();
172 
173 	if (fUseGmtTime)
174 		fGmtTime->SetValue(B_CONTROL_ON);
175 	else
176 		fLocalTime->SetValue(B_CONTROL_ON);
177 
178 	time_t timeNow = fTimeAtStart + _PrefletUptime();
179 	struct tm result;
180 	struct tm* timeInfo;
181 	timeInfo = localtime_r(&timeNow, &result);
182 
183 	BDateTime dateTime = BDateTime::CurrentDateTime(B_LOCAL_TIME);
184 	BTime time = dateTime.Time();
185 	BDate date = dateTime.Date();
186 	time.SetTime(timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec % 60);
187 	date.SetDate(timeInfo->tm_year + 1900, timeInfo->tm_mon + 1,
188 		timeInfo->tm_mday);
189 	dateTime.SetTime(time);
190 	dateTime.SetDate(date);
191 
192 	set_real_time_clock(dateTime.Time_t());
193 }
194 
195 
196 time_t
197 DateTimeView::_PrefletUptime() const
198 {
199 	return (time_t)((system_time() - fSystemTimeAtStart) / 1000000);
200 }
201 
202 
203 void
204 DateTimeView::_InitView()
205 {
206 	font_height fontHeight;
207 	be_plain_font->GetHeight(&fontHeight);
208 	float textHeight = fontHeight.descent + fontHeight.ascent
209 		+ fontHeight.leading + 6.0;	// 6px border
210 
211 	// left side
212 	BRect bounds = Bounds();
213 	bounds.InsetBy(10.0, 10.0);
214 	bounds.top += textHeight + 10.0;
215 
216 	fCalendarView = new BCalendarView(bounds, "calendar");
217 	fCalendarView->SetWeekNumberHeaderVisible(false);
218 	fCalendarView->ResizeToPreferred();
219 	fCalendarView->SetSelectionMessage(new BMessage(kDayChanged));
220 	fCalendarView->SetInvocationMessage(new BMessage(kDayChanged));
221 
222 	bounds.top -= textHeight + 10.0;
223 	bounds.bottom = bounds.top + textHeight;
224 	bounds.right = fCalendarView->Frame().right;
225 
226 	fDateEdit = new TDateEdit(bounds, "dateEdit", 3);
227 	AddChild(fDateEdit);
228 	AddChild(fCalendarView);
229 
230 	// right side, 2px extra for separator
231 	bounds.OffsetBy(bounds.Width() + 22.0, 0.0);
232 	fTimeEdit = new TTimeEdit(bounds, "timeEdit", 4);
233 	AddChild(fTimeEdit);
234 
235 	bounds = fCalendarView->Frame();
236 	bounds.OffsetBy(bounds.Width() + 22.0, 0.0);
237 
238 	fClock = new TAnalogClock(bounds, "analogClock");
239 	AddChild(fClock);
240 	BTime time(BTime::CurrentTime(B_LOCAL_TIME));
241 	fClock->SetTime(time.Hour(), time.Minute(), time.Second());
242 
243 	// clock radio buttons
244 	bounds.top = fClock->Frame().bottom + 10.0;
245 	BStringView *text = new BStringView(bounds, "clockSetTo", "Clock set to:");
246 	AddChild(text);
247 	text->ResizeToPreferred();
248 
249 	bounds.left += 10.0f;
250 	bounds.top = text->Frame().bottom;
251 	fLocalTime = new BRadioButton(bounds, "localTime", "Local Time",
252 		new BMessage(kRTCUpdate));
253 	AddChild(fLocalTime);
254 	fLocalTime->ResizeToPreferred();
255 
256 	bounds.left = fLocalTime->Frame().right + 10.0;
257 	fGmtTime = new BRadioButton(bounds, "greenwichMeanTime", "GMT",
258 		new BMessage(kRTCUpdate));
259 	AddChild(fGmtTime);
260 	fGmtTime->ResizeToPreferred();
261 
262 	if (fUseGmtTime)
263 		fGmtTime->SetValue(B_CONTROL_ON);
264 	else
265 		fLocalTime->SetValue(B_CONTROL_ON);
266 
267 	fOldUseGmtTime = fUseGmtTime;
268 
269 	ResizeTo(fClock->Frame().right + 10.0, fGmtTime->Frame().bottom + 10.0);
270 }
271 
272 
273 void
274 DateTimeView::_ReadRTCSettings()
275 {
276 	BPath path;
277 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
278 		return;
279 
280 	path.Append("RTC_time_settings");
281 
282 	BEntry entry(path.Path());
283 	if (entry.Exists()) {
284 		BFile file(&entry, B_READ_ONLY);
285 		if (file.InitCheck() == B_OK) {
286 			char buffer[6];
287 			file.Read(buffer, 6);
288 			if (strncmp(buffer, "gmt", 3) == 0)
289 				fUseGmtTime = true;
290 		}
291 	} else
292 		_UpdateGmtSettings();
293 }
294 
295 
296 void
297 DateTimeView::_WriteRTCSettings()
298 {
299 	BPath path;
300 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != 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 	BPath path;
321 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
322 		return;
323 
324 	path.Append("timezone");
325 	BEntry entry(path.Path(), true);
326 
327 	if (!entry.Exists())
328 		return;
329 
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 }
335 
336 
337 void
338 DateTimeView::_UpdateDateTime(BMessage *message)
339 {
340 	int32 day;
341 	int32 month;
342 	int32 year;
343 	if (message->FindInt32("month", &month) == B_OK
344 		&& message->FindInt32("day", &day) == B_OK
345 		&& message->FindInt32("year", &year) == B_OK)
346 	{
347 		fDateEdit->SetDate(year, month, day);
348 		fCalendarView->SetDate(year, month, day);
349 	}
350 
351 	int32 hour;
352 	int32 minute;
353 	int32 second;
354 	if (message->FindInt32("hour", &hour) == B_OK
355 		&& message->FindInt32("minute", &minute) == B_OK
356 		&& message->FindInt32("second", &second) == B_OK)
357 	{
358 		fClock->SetTime(hour, minute, second);
359 		fTimeEdit->SetTime(hour, minute, second);
360 	}
361 }
362