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 "BarView.h" 56 #include "BarWindow.h" 57 #include "StatusView.h" 58 #include "CalendarMenuWindow.h" 59 #include "StatusView.h" 60 61 62 static const float kHMargin = 2.0; 63 64 65 #undef B_TRANSLATION_CONTEXT 66 #define B_TRANSLATION_CONTEXT "TimeView" 67 68 69 TTimeView::TTimeView(float maxWidth, float height, TBarView* barView) 70 : 71 BView(BRect(-100, -100, -90, -90), "_deskbar_tv_", 72 B_FOLLOW_RIGHT | B_FOLLOW_TOP, 73 B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS), 74 fBarView(barView), 75 fParent(NULL), 76 fMaxWidth(maxWidth), 77 fHeight(height), 78 fShowLevel(0), 79 fShowSeconds(false), 80 fShowDayOfWeek(false), 81 fShowTimeZone(false), 82 fCalendarWindow(NULL), 83 fTimeFormat(NULL), 84 fDateFormat(NULL) 85 { 86 fCurrentTime = fLastTime = time(NULL); 87 fSeconds = fMinute = fHour = 0; 88 fCurrentTimeStr[0] = 0; 89 fCurrentDateStr[0] = 0; 90 fLastTimeStr[0] = 0; 91 fLastDateStr[0] = 0; 92 fNeedToUpdate = true; 93 UpdateTimeFormat(); 94 } 95 96 97 #ifdef AS_REPLICANT 98 TTimeView::TTimeView(BMessage* data) 99 : BView(data), 100 fTimeFormat(NULL), 101 fDateFormat(NULL) 102 { 103 fCurrentTime = fLastTime = time(NULL); 104 data->FindBool("seconds", &fShowSeconds); 105 106 UpdateTimeFormat(); 107 } 108 #endif 109 110 111 TTimeView::~TTimeView() 112 { 113 if (fCalendarWindowMessenger.IsValid()) 114 fCalendarWindowMessenger.SendMessage(B_QUIT_REQUESTED); 115 116 delete fTimeFormat; 117 delete fDateFormat; 118 } 119 120 121 #ifdef AS_REPLICANT 122 BArchivable* 123 TTimeView::Instantiate(BMessage* data) 124 { 125 if (!validate_instantiation(data, "TTimeView")) 126 return NULL; 127 128 return new TTimeView(data); 129 } 130 131 132 status_t 133 TTimeView::Archive(BMessage* data, bool deep) const 134 { 135 BView::Archive(data, deep); 136 data->AddBool("orientation", Vertical()); 137 data->AddInt16("showLevel", fShowLevel); 138 data->AddBool("showSeconds", fShowSeconds); 139 data->AddBool("showDayOfWeek", fShowDayOfWeek); 140 data->AddBool("showTimeZone", fShowTimeZone); 141 data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT); 142 143 return B_OK; 144 } 145 #endif 146 147 148 void 149 TTimeView::AttachedToWindow() 150 { 151 fCurrentTime = time(NULL); 152 153 SetFont(be_plain_font); 154 if (Parent()) { 155 fParent = Parent(); 156 AdoptParentColors(); 157 } else 158 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 159 160 ResizeToPreferred(); 161 CalculateTextPlacement(); 162 } 163 164 165 void 166 TTimeView::Draw(BRect /*updateRect*/) 167 { 168 PushState(); 169 170 SetHighColor(ViewColor()); 171 SetLowColor(ViewColor()); 172 FillRect(Bounds()); 173 SetHighUIColor(B_MENU_ITEM_TEXT_COLOR); 174 175 DrawString(fCurrentTimeStr, fTimeLocation); 176 177 PopState(); 178 } 179 180 181 void 182 TTimeView::FrameMoved(BPoint) 183 { 184 Update(); 185 } 186 187 188 void 189 TTimeView::GetPreferredSize(float* width, float* height) 190 { 191 float timeWidth = StringWidth(fCurrentTimeStr); 192 193 // set the height based on the font size 194 font_height fontHeight; 195 GetFontHeight(&fontHeight); 196 fHeight = fontHeight.ascent + fontHeight.descent - 2; 197 // reduce height by 2px so that clock doesn't draw on top of border 198 199 if (Vertical()) { 200 float appWidth = static_cast<TBarApp*>(be_app)->Settings()->width; 201 *width = fMaxWidth 202 = std::min(appWidth - (kDragRegionWidth + kHMargin) * 2, timeWidth); 203 } else 204 *width = fMaxWidth = timeWidth; 205 206 *height = fHeight; 207 } 208 209 210 void 211 TTimeView::MessageReceived(BMessage* message) 212 { 213 switch (message->what) { 214 case kChangeTime: 215 { 216 // launch the time prefs app 217 be_roster->Launch("application/x-vnd.Haiku-Time"); 218 // tell Time preflet to switch to the clock tab 219 BMessenger messenger("application/x-vnd.Haiku-Time"); 220 BMessage switchToClock('SlCk'); 221 messenger.SendMessage(&switchToClock); 222 break; 223 } 224 225 case kShowHideTime: 226 { 227 be_app->MessageReceived(message); 228 break; 229 } 230 231 case kShowCalendar: 232 { 233 BRect bounds(Bounds()); 234 BPoint center(bounds.LeftTop()); 235 center += BPoint(bounds.Width() / 2, bounds.Height() / 2); 236 ShowCalendar(center); 237 break; 238 } 239 240 default: 241 BView::MessageReceived(message); 242 break; 243 } 244 } 245 246 247 void 248 TTimeView::MouseDown(BPoint point) 249 { 250 uint32 buttons; 251 252 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 253 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 254 ShowTimeOptions(ConvertToScreen(point)); 255 return; 256 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) 257 ShowCalendar(point); 258 259 // invalidate last time/date strings and call the pulse 260 // method directly to change the display instantly 261 fLastDateStr[0] = '\0'; 262 fLastTimeStr[0] = '\0'; 263 Pulse(); 264 } 265 266 267 void 268 TTimeView::Pulse() 269 { 270 time_t curTime = time(NULL); 271 tm* ct = localtime(&curTime); 272 if (ct == NULL) 273 return; 274 275 fCurrentTime = curTime; 276 277 GetCurrentTime(); 278 GetCurrentDate(); 279 if (strcmp(fCurrentTimeStr, fLastTimeStr) != 0) { 280 // Update bounds when the size of the strings has changed 281 Update(); 282 283 strlcpy(fLastTimeStr, fCurrentTimeStr, sizeof(fLastTimeStr)); 284 fNeedToUpdate = true; 285 } 286 287 // Update the tooltip if the date has changed 288 if (strcmp(fCurrentDateStr, fLastDateStr) != 0) { 289 strlcpy(fLastDateStr, fCurrentDateStr, sizeof(fLastDateStr)); 290 SetToolTip(fCurrentDateStr); 291 } 292 293 if (fNeedToUpdate) { 294 fSeconds = ct->tm_sec; 295 fMinute = ct->tm_min; 296 fHour = ct->tm_hour; 297 298 Draw(Bounds()); 299 fNeedToUpdate = false; 300 } 301 } 302 303 304 void 305 TTimeView::ResizeToPreferred() 306 { 307 float width; 308 float height; 309 float oldWidth = Bounds().Width(); 310 float oldHeight = Bounds().Height(); 311 312 GetPreferredSize(&width, &height); 313 if (height != oldHeight || width != oldWidth) { 314 ResizeTo(width, height); 315 MoveBy(oldWidth - width, 0); 316 fNeedToUpdate = true; 317 } 318 } 319 320 321 // # pragma mark - Public methods 322 323 324 bool 325 TTimeView::ShowSeconds() const 326 { 327 return fShowSeconds; 328 } 329 330 331 void 332 TTimeView::SetShowSeconds(bool show) 333 { 334 fShowSeconds = show; 335 UpdateTimeFormat(); 336 Update(); 337 } 338 339 340 bool 341 TTimeView::ShowDayOfWeek() const 342 { 343 return fShowDayOfWeek; 344 } 345 346 347 void 348 TTimeView::SetShowDayOfWeek(bool show) 349 { 350 fShowDayOfWeek = show; 351 UpdateTimeFormat(); 352 Update(); 353 } 354 355 356 bool 357 TTimeView::ShowTimeZone() const 358 { 359 return fShowTimeZone; 360 } 361 362 363 void 364 TTimeView::SetShowTimeZone(bool show) 365 { 366 fShowTimeZone = show; 367 UpdateTimeFormat(); 368 Update(); 369 } 370 371 372 void 373 TTimeView::ShowCalendar(BPoint where) 374 { 375 if (fCalendarWindowMessenger.IsValid()) { 376 // If the calendar is already shown, just activate it 377 BMessage activate(B_SET_PROPERTY); 378 activate.AddSpecifier("Active"); 379 activate.AddBool("data", true); 380 381 if (fCalendarWindowMessenger.SendMessage(&activate) == B_OK) 382 return; 383 } 384 385 where.y = Bounds().bottom + 4.0; 386 ConvertToScreen(&where); 387 388 if (where.y >= BScreen().Frame().bottom) 389 where.y -= (Bounds().Height() + 4.0); 390 391 fCalendarWindow = new CalendarMenuWindow(where); 392 fCalendarWindowMessenger = BMessenger(fCalendarWindow); 393 fCalendarWindow->Show(); 394 } 395 396 397 bool 398 TTimeView::IsShowingCalendar() 399 { 400 return fCalendarWindow != NULL && !fCalendarWindow->IsHidden(); 401 } 402 403 404 // # pragma mark - Private methods 405 406 407 void 408 TTimeView::UpdateTimeFormat() 409 { 410 int32 fields = B_DATE_ELEMENT_HOUR | B_DATE_ELEMENT_MINUTE; 411 if (fShowSeconds) 412 fields |= B_DATE_ELEMENT_SECOND; 413 if (fShowDayOfWeek) 414 fields |= B_DATE_ELEMENT_WEEKDAY; 415 if (fShowTimeZone) 416 fields |= B_DATE_ELEMENT_TIMEZONE; 417 418 delete fTimeFormat; 419 fTimeFormat = new BDateTimeFormat(BLocale::Default()); 420 fTimeFormat->SetDateTimeFormat(B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT, 421 fields); 422 423 delete fDateFormat; 424 fDateFormat = new BDateFormat(BLocale::Default()); 425 } 426 427 428 void 429 TTimeView::GetCurrentTime() 430 { 431 fTimeFormat->Format(fCurrentTimeStr, sizeof(fCurrentTimeStr), fCurrentTime, 432 B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT); 433 } 434 435 436 void 437 TTimeView::GetCurrentDate() 438 { 439 char tmp[sizeof(fCurrentDateStr)]; 440 441 fDateFormat->Format(tmp, sizeof(fCurrentDateStr), fCurrentTime, 442 B_FULL_DATE_FORMAT); 443 444 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 445 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 446 const char* str = tmp; 447 if (str[0] == '0') 448 str++; 449 450 strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr)); 451 } 452 453 454 void 455 TTimeView::CalculateTextPlacement() 456 { 457 fDateLocation.x = 0.0; 458 fTimeLocation.x = 0.0; 459 460 BFont font; 461 GetFont(&font); 462 463 const char* stringArray[1]; 464 stringArray[0] = fCurrentTimeStr; 465 BRect rectArray[1]; 466 escapement_delta delta = { 0.0, 0.0 }; 467 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 468 rectArray); 469 470 // center vertically 471 fTimeLocation.y = fDateLocation.y = ceilf((Bounds().Height() 472 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 473 474 if (Vertical()) { 475 float timeWidth = StringWidth(fCurrentTimeStr); 476 if (timeWidth > fMaxWidth) { 477 // time does not fit, push it over to truncate the left side 478 // to see the entire time string you must make the window wider 479 float difference = timeWidth - fMaxWidth; 480 fDateLocation.x -= difference; 481 fTimeLocation.x -= difference; 482 } 483 } 484 } 485 486 487 void 488 TTimeView::ShowTimeOptions(BPoint point) 489 { 490 BPopUpMenu* menu = new BPopUpMenu("", false, false); 491 menu->SetFont(be_plain_font); 492 BMenuItem* item; 493 494 item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS), 495 new BMessage(kChangeTime)); 496 menu->AddItem(item); 497 498 item = new BMenuItem(B_TRANSLATE("Hide clock"), 499 new BMessage(kShowHideTime)); 500 menu->AddItem(item); 501 502 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 503 new BMessage(kShowCalendar)); 504 menu->AddItem(item); 505 506 menu->SetTargetForItems(this); 507 // Changed to accept screen coord system point; 508 // not constrained to this view now 509 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 510 point.x + 4, point.y +4), true); 511 } 512 513 514 void 515 TTimeView::Update() 516 { 517 GetCurrentTime(); 518 GetCurrentDate(); 519 SetToolTip(fCurrentDateStr); 520 521 ResizeToPreferred(); 522 CalculateTextPlacement(); 523 524 if (fParent != NULL) 525 fParent->Invalidate(); 526 } 527 528 529 bool 530 TTimeView::Vertical() 531 { 532 if (fBarView == NULL) 533 return true; 534 535 return fBarView->Vertical(); 536 } 537