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