1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered 30 trademarks of Be Incorporated in the United States and other countries. Other 31 brand product names are registered trademarks or trademarks of their respective 32 holders. 33 All rights reserved. 34 */ 35 36 37 #include "TimeView.h" 38 39 #include <algorithm> 40 41 #include <string.h> 42 43 #include <Application.h> 44 #include <Catalog.h> 45 #include <Debug.h> 46 #include <Locale.h> 47 #include <MenuItem.h> 48 #include <MessageRunner.h> 49 #include <PopUpMenu.h> 50 #include <Roster.h> 51 #include <Screen.h> 52 #include <Window.h> 53 54 #include "BarApp.h" 55 #include "StatusView.h" 56 #include "CalendarMenuWindow.h" 57 58 59 static const char* const kMinString = "99:99 AM"; 60 static const float kHMargin = 2.0; 61 62 63 #undef B_TRANSLATION_CONTEXT 64 #define B_TRANSLATION_CONTEXT "TimeView" 65 66 67 TTimeView::TTimeView(float maxWidth, float height) 68 : 69 BView(BRect(-100, -100, -90, -90), "_deskbar_tv_", 70 B_FOLLOW_RIGHT | B_FOLLOW_TOP, 71 B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS), 72 fParent(NULL), 73 fMaxWidth(maxWidth), 74 fHeight(height), 75 fOrientation(true), 76 fShowLevel(0), 77 fShowSeconds(false), 78 fShowDayOfWeek(false), 79 fShowTimeZone(false), 80 fTimeFormat(NULL), 81 fDateFormat(NULL) 82 { 83 fCurrentTime = fLastTime = time(NULL); 84 fSeconds = fMinute = fHour = 0; 85 fCurrentTimeStr[0] = 0; 86 fCurrentDateStr[0] = 0; 87 fLastTimeStr[0] = 0; 88 fLastDateStr[0] = 0; 89 fNeedToUpdate = true; 90 UpdateTimeFormat(); 91 } 92 93 94 #ifdef AS_REPLICANT 95 TTimeView::TTimeView(BMessage* data) 96 : BView(data), 97 fTimeFormat(NULL), 98 fDateFormat(NULL) 99 { 100 fCurrentTime = fLastTime = time(NULL); 101 data->FindBool("seconds", &fShowSeconds); 102 103 UpdateTimeFormat(); 104 } 105 #endif 106 107 108 TTimeView::~TTimeView() 109 { 110 delete fTimeFormat; 111 delete fDateFormat; 112 } 113 114 115 #ifdef AS_REPLICANT 116 BArchivable* 117 TTimeView::Instantiate(BMessage* data) 118 { 119 if (!validate_instantiation(data, "TTimeView")) 120 return NULL; 121 122 return new TTimeView(data); 123 } 124 125 126 status_t 127 TTimeView::Archive(BMessage* data, bool deep) const 128 { 129 BView::Archive(data, deep); 130 data->AddBool("orientation", fOrientation); 131 data->AddInt16("showLevel", fShowLevel); 132 data->AddBool("showSeconds", fShowSeconds); 133 data->AddBool("showDayOfWeek", fShowDayOfWeek); 134 data->AddBool("showTimeZone", fShowTimeZone); 135 data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT); 136 137 return B_OK; 138 } 139 #endif 140 141 142 void 143 TTimeView::AttachedToWindow() 144 { 145 fCurrentTime = time(NULL); 146 147 SetFont(be_plain_font); 148 if (Parent()) { 149 fParent = Parent(); 150 AdoptParentColors(); 151 } else 152 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 153 154 ResizeToPreferred(); 155 CalculateTextPlacement(); 156 } 157 158 159 void 160 TTimeView::Draw(BRect /*updateRect*/) 161 { 162 PushState(); 163 164 SetHighColor(ViewColor()); 165 SetLowColor(ViewColor()); 166 FillRect(Bounds()); 167 SetHighUIColor(B_MENU_ITEM_TEXT_COLOR); 168 169 DrawString(fCurrentTimeStr, fTimeLocation); 170 171 PopState(); 172 } 173 174 175 void 176 TTimeView::FrameMoved(BPoint) 177 { 178 Update(); 179 } 180 181 182 void 183 TTimeView::GetPreferredSize(float* width, float* height) 184 { 185 *height = fHeight; 186 187 float timeWidth = StringWidth(fCurrentTimeStr); 188 189 if (fOrientation) { 190 float appWidth = static_cast<TBarApp*>(be_app)->Settings()->width; 191 *width = fMaxWidth 192 = std::min(appWidth - (kDragRegionWidth + kHMargin) * 2, timeWidth); 193 } else 194 *width = fMaxWidth = timeWidth; 195 } 196 197 198 void 199 TTimeView::MessageReceived(BMessage* message) 200 { 201 switch (message->what) { 202 case kChangeTime: 203 { 204 // launch the time prefs app 205 be_roster->Launch("application/x-vnd.Haiku-Time"); 206 // tell Time preflet to switch to the clock tab 207 BMessenger messenger("application/x-vnd.Haiku-Time"); 208 BMessage switchToClock('SlCk'); 209 messenger.SendMessage(&switchToClock); 210 break; 211 } 212 213 case kShowHideTime: 214 { 215 be_app->MessageReceived(message); 216 break; 217 } 218 219 case kShowCalendar: 220 { 221 BRect bounds(Bounds()); 222 BPoint center(bounds.LeftTop()); 223 center += BPoint(bounds.Width() / 2, bounds.Height() / 2); 224 ShowCalendar(center); 225 break; 226 } 227 228 default: 229 BView::MessageReceived(message); 230 break; 231 } 232 } 233 234 235 void 236 TTimeView::MouseDown(BPoint point) 237 { 238 uint32 buttons; 239 240 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 241 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 242 ShowTimeOptions(ConvertToScreen(point)); 243 return; 244 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) 245 ShowCalendar(point); 246 247 // invalidate last time/date strings and call the pulse 248 // method directly to change the display instantly 249 fLastDateStr[0] = '\0'; 250 fLastTimeStr[0] = '\0'; 251 Pulse(); 252 } 253 254 255 void 256 TTimeView::Pulse() 257 { 258 time_t curTime = time(NULL); 259 tm* ct = localtime(&curTime); 260 if (ct == NULL) 261 return; 262 263 fCurrentTime = curTime; 264 265 GetCurrentTime(); 266 GetCurrentDate(); 267 if (strcmp(fCurrentTimeStr, fLastTimeStr) != 0) { 268 // Update bounds when the size of the strings has changed 269 Update(); 270 271 strlcpy(fLastTimeStr, fCurrentTimeStr, sizeof(fLastTimeStr)); 272 fNeedToUpdate = true; 273 } 274 275 // Update the tooltip if the date has changed 276 if (strcmp(fCurrentDateStr, fLastDateStr) != 0) { 277 strlcpy(fLastDateStr, fCurrentDateStr, sizeof(fLastDateStr)); 278 SetToolTip(fCurrentDateStr); 279 } 280 281 if (fNeedToUpdate) { 282 fSeconds = ct->tm_sec; 283 fMinute = ct->tm_min; 284 fHour = ct->tm_hour; 285 286 Draw(Bounds()); 287 fNeedToUpdate = false; 288 } 289 } 290 291 292 void 293 TTimeView::ResizeToPreferred() 294 { 295 float width; 296 float height; 297 float oldWidth = Bounds().Width(); 298 float oldHeight = Bounds().Height(); 299 300 GetPreferredSize(&width, &height); 301 if (height != oldHeight || width != oldWidth) { 302 ResizeTo(width, height); 303 MoveBy(oldWidth - width, 0); 304 fNeedToUpdate = true; 305 } 306 } 307 308 309 // # pragma mark - Public methods 310 311 312 void 313 TTimeView::SetOrientation(bool orientation) 314 { 315 fOrientation = orientation; 316 ResizeToPreferred(); 317 CalculateTextPlacement(); 318 Invalidate(); 319 } 320 321 322 bool 323 TTimeView::ShowSeconds() const 324 { 325 return fShowSeconds; 326 } 327 328 329 void 330 TTimeView::SetShowSeconds(bool show) 331 { 332 fShowSeconds = show; 333 UpdateTimeFormat(); 334 Update(); 335 } 336 337 338 bool 339 TTimeView::ShowDayOfWeek() const 340 { 341 return fShowDayOfWeek; 342 } 343 344 345 void 346 TTimeView::SetShowDayOfWeek(bool show) 347 { 348 fShowDayOfWeek = show; 349 UpdateTimeFormat(); 350 Update(); 351 } 352 353 354 bool 355 TTimeView::ShowTimeZone() const 356 { 357 return fShowTimeZone; 358 } 359 360 361 void 362 TTimeView::SetShowTimeZone(bool show) 363 { 364 fShowTimeZone = show; 365 UpdateTimeFormat(); 366 Update(); 367 } 368 369 370 void 371 TTimeView::ShowCalendar(BPoint where) 372 { 373 if (fCalendarWindow.IsValid()) { 374 // If the calendar is already shown, just activate it 375 BMessage activate(B_SET_PROPERTY); 376 activate.AddSpecifier("Active"); 377 activate.AddBool("data", true); 378 379 if (fCalendarWindow.SendMessage(&activate) == B_OK) 380 return; 381 } 382 383 where.y = Bounds().bottom + 4.0; 384 ConvertToScreen(&where); 385 386 if (where.y >= BScreen().Frame().bottom) 387 where.y -= (Bounds().Height() + 4.0); 388 389 CalendarMenuWindow* window = new CalendarMenuWindow(where); 390 fCalendarWindow = BMessenger(window); 391 392 window->Show(); 393 } 394 395 396 // # pragma mark - Private methods 397 398 399 void 400 TTimeView::UpdateTimeFormat() 401 { 402 int32 fields = B_DATE_ELEMENT_HOUR | B_DATE_ELEMENT_MINUTE; 403 if (fShowSeconds) 404 fields |= B_DATE_ELEMENT_SECOND; 405 if (fShowDayOfWeek) 406 fields |= B_DATE_ELEMENT_WEEKDAY; 407 if (fShowTimeZone) 408 fields |= B_DATE_ELEMENT_TIMEZONE; 409 410 delete fTimeFormat; 411 fTimeFormat = new BDateTimeFormat(BLocale::Default()); 412 fTimeFormat->SetDateTimeFormat(B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT, fields); 413 414 delete fDateFormat; 415 fDateFormat = new BDateFormat(BLocale::Default()); 416 } 417 418 void 419 TTimeView::GetCurrentTime() 420 { 421 fTimeFormat->Format(fCurrentTimeStr, sizeof(fCurrentTimeStr), fCurrentTime, 422 B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT); 423 } 424 425 426 void 427 TTimeView::GetCurrentDate() 428 { 429 char tmp[sizeof(fCurrentDateStr)]; 430 431 fDateFormat->Format(tmp, sizeof(fCurrentDateStr), fCurrentTime, 432 B_FULL_DATE_FORMAT); 433 434 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 435 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 436 const char* str = tmp; 437 if (str[0] == '0') 438 str++; 439 440 strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr)); 441 } 442 443 444 void 445 TTimeView::CalculateTextPlacement() 446 { 447 BRect bounds(Bounds()); 448 449 fDateLocation.x = 0.0; 450 fTimeLocation.x = 0.0; 451 452 BFont font; 453 GetFont(&font); 454 455 const char* stringArray[1]; 456 stringArray[0] = fCurrentTimeStr; 457 BRect rectArray[1]; 458 escapement_delta delta = { 0.0, 0.0 }; 459 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 460 rectArray); 461 462 fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height() 463 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 464 465 if (fOrientation) { 466 float timeWidth = StringWidth(fCurrentTimeStr); 467 if (timeWidth > fMaxWidth) { 468 // time does not fit, push it over to truncate the left side 469 // to see the entire time string you must make the window wider 470 float difference = timeWidth - fMaxWidth; 471 fDateLocation.x -= difference; 472 fTimeLocation.x -= difference; 473 } 474 } 475 } 476 477 478 void 479 TTimeView::ShowTimeOptions(BPoint point) 480 { 481 BPopUpMenu* menu = new BPopUpMenu("", false, false); 482 menu->SetFont(be_plain_font); 483 BMenuItem* item; 484 485 item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS), 486 new BMessage(kChangeTime)); 487 menu->AddItem(item); 488 489 item = new BMenuItem(B_TRANSLATE("Hide clock"), 490 new BMessage(kShowHideTime)); 491 menu->AddItem(item); 492 493 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 494 new BMessage(kShowCalendar)); 495 menu->AddItem(item); 496 497 menu->SetTargetForItems(this); 498 // Changed to accept screen coord system point; 499 // not constrained to this view now 500 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 501 point.x + 4, point.y +4), true); 502 } 503 504 505 void 506 TTimeView::Update() 507 { 508 GetCurrentTime(); 509 GetCurrentDate(); 510 SetToolTip(fCurrentDateStr); 511 512 ResizeToPreferred(); 513 CalculateTextPlacement(); 514 515 if (fParent != NULL) 516 fParent->Invalidate(); 517 } 518