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