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