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 trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include "TimeView.h" 37 38 #include <string.h> 39 40 #include <Catalog.h> 41 #include <Country.h> 42 #include <Debug.h> 43 #include <Locale.h> 44 #include <MenuItem.h> 45 #include <MessageRunner.h> 46 #include <PopUpMenu.h> 47 #include <Roster.h> 48 #include <Screen.h> 49 #include <Window.h> 50 51 #include "CalendarMenuWindow.h" 52 53 54 static const char* const kMinString = "99:99 AM"; 55 static const float kHMargin = 2.0; 56 57 58 enum { 59 kShowClock, 60 kChangeClock, 61 kHide, 62 kLongClick, 63 kShowCalendar 64 }; 65 66 67 #undef B_TRANSLATE_CONTEXT 68 #define B_TRANSLATE_CONTEXT "TimeView" 69 70 TTimeView::TTimeView(float maxWidth, float height, bool showSeconds, 71 bool fullDate, bool) 72 : 73 BView(BRect(-100, -100, -90, -90), "_deskbar_tv_", 74 B_FOLLOW_RIGHT | B_FOLLOW_TOP, 75 B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS), 76 fParent(NULL), 77 fShowInterval(true), // ToDo: defaulting this to true until UI is in place 78 fShowSeconds(showSeconds), 79 fFullDate(fullDate), 80 fCanShowFullDate(false), 81 fMaxWidth(maxWidth), 82 fHeight(height), 83 fOrientation(true), 84 fLongClickMessageRunner(NULL) 85 { 86 fShowingDate = false; 87 fTime = fLastTime = time(NULL); 88 fSeconds = fMinute = fHour = 0; 89 fLastTimeStr[0] = 0; 90 fLastDateStr[0] = 0; 91 fNeedToUpdate = true; 92 93 fLocale = *be_locale; 94 } 95 96 97 #ifdef AS_REPLICANT 98 TTimeView::TTimeView(BMessage* data) 99 : BView(data) 100 { 101 fTime = fLastTime = time(NULL); 102 data->FindBool("seconds", &fShowSeconds); 103 data->FindBool("fulldate", &fFullDate); 104 data->FindBool("interval", &fInterval); 105 fShowingDate = false; 106 107 fLocale = *be_locale; 108 } 109 #endif 110 111 112 TTimeView::~TTimeView() 113 { 114 StopLongClickNotifier(); 115 } 116 117 118 #ifdef AS_REPLICANT 119 BArchivable* 120 TTimeView::Instantiate(BMessage* data) 121 { 122 if (!validate_instantiation(data, "TTimeView")) 123 return NULL; 124 125 return new TTimeView(data); 126 } 127 128 129 status_t 130 TTimeView::Archive(BMessage* data, bool deep) const 131 { 132 BView::Archive(data, deep); 133 data->AddBool("seconds", fShowSeconds); 134 data->AddBool("fulldate", fFullDate); 135 data->AddBool("interval", fInterval); 136 data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT); 137 138 return B_OK; 139 } 140 #endif 141 142 143 void 144 TTimeView::AttachedToWindow() 145 { 146 fTime = time(NULL); 147 148 SetFont(be_plain_font); 149 if (Parent()) { 150 fParent = Parent(); 151 SetViewColor(Parent()->ViewColor()); 152 } else 153 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 154 155 ResizeToPreferred(); 156 CalculateTextPlacement(); 157 } 158 159 160 void 161 TTimeView::GetPreferredSize(float* width, float* height) 162 { 163 *height = fHeight; 164 165 GetCurrentTime(); 166 GetCurrentDate(); 167 168 // TODO: SetOrientation never gets called, fix that 169 // When in vertical mode, we want to limit the width so that it can't 170 // overlap the bevels in the parent view. 171 if (ShowingDate()) 172 *width = fOrientation ? 173 min_c(fMaxWidth - kHMargin, kHMargin + StringWidth(fDateStr)) 174 : kHMargin + StringWidth(fDateStr); 175 else { 176 *width = fOrientation ? 177 min_c(fMaxWidth - kHMargin, kHMargin + StringWidth(fTimeStr)) 178 : kHMargin + StringWidth(fTimeStr); 179 } 180 } 181 182 183 void 184 TTimeView::ResizeToPreferred() 185 { 186 float width, height; 187 float oldWidth = Bounds().Width(), oldHeight = Bounds().Height(); 188 189 GetPreferredSize(&width, &height); 190 if (height != oldHeight || width != oldWidth) { 191 ResizeTo(width, height); 192 MoveBy(oldWidth - width, 0); 193 fNeedToUpdate = true; 194 } 195 } 196 197 198 void 199 TTimeView::FrameMoved(BPoint) 200 { 201 Update(); 202 } 203 204 205 void 206 TTimeView::MessageReceived(BMessage* message) 207 { 208 switch (message->what) { 209 case kFullDate: 210 ShowFullDate(!ShowingFullDate()); 211 break; 212 213 case kShowSeconds: 214 ShowSeconds(!ShowingSeconds()); 215 break; 216 217 case B_LOCALE_CHANGED: 218 Update(); 219 break; 220 221 case kChangeClock: 222 // launch the time prefs app 223 be_roster->Launch("application/x-vnd.Haiku-Time"); 224 break; 225 226 case 'time': 227 Window()->PostMessage(message, Parent()); 228 break; 229 230 case kLongClick: 231 { 232 StopLongClickNotifier(); 233 BPoint where; 234 message->FindPoint("where", &where); 235 ShowCalendar(where); 236 break; 237 } 238 239 case kShowCalendar: 240 { 241 BRect bounds(Bounds()); 242 BPoint center(bounds.LeftTop()); 243 center += BPoint(bounds.Width() / 2, bounds.Height() / 2); 244 ShowCalendar(center); 245 break; 246 } 247 248 default: 249 BView::MessageReceived(message); 250 } 251 } 252 253 254 void 255 TTimeView::ShowCalendar(BPoint where) 256 { 257 if (fCalendarWindow.IsValid()) { 258 // If the calendar is already shown, just activate it 259 BMessage activate(B_SET_PROPERTY); 260 activate.AddSpecifier("Active"); 261 activate.AddBool("data", true); 262 263 if (fCalendarWindow.SendMessage(&activate) == B_OK) 264 return; 265 } 266 267 where.y = Bounds().bottom + 4.0; 268 ConvertToScreen(&where); 269 270 if (where.y >= BScreen().Frame().bottom) 271 where.y -= (Bounds().Height() + 4.0); 272 273 CalendarMenuWindow* window = new CalendarMenuWindow(where); 274 fCalendarWindow = BMessenger(window); 275 276 window->Show(); 277 } 278 279 280 void 281 TTimeView::StartLongClickNotifier(BPoint where) 282 { 283 StopLongClickNotifier(); 284 285 BMessage longClickMessage(kLongClick); 286 longClickMessage.AddPoint("where", where); 287 288 bigtime_t longClickThreshold; 289 get_click_speed(&longClickThreshold); 290 // use the doubleClickSpeed as a threshold 291 292 fLongClickMessageRunner = new BMessageRunner(BMessenger(this), 293 &longClickMessage, longClickThreshold, 1); 294 } 295 296 297 void 298 TTimeView::StopLongClickNotifier() 299 { 300 delete fLongClickMessageRunner; 301 fLongClickMessageRunner = NULL; 302 } 303 304 305 void 306 TTimeView::GetCurrentTime() 307 { 308 fLocale.FormatTime(fTimeStr, 64, fTime, fShowSeconds); 309 } 310 311 312 void 313 TTimeView::GetCurrentDate() 314 { 315 char tmp[64]; 316 317 fLocale.FormatDate(tmp, 64, fTime, fFullDate && CanShowFullDate()); 318 319 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 320 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 321 const char* str = tmp; 322 if (str[0] == '0') 323 str++; 324 325 strcpy(fDateStr, str); 326 } 327 328 329 void 330 TTimeView::Draw(BRect /*updateRect*/) 331 { 332 PushState(); 333 334 SetHighColor(ViewColor()); 335 SetLowColor(ViewColor()); 336 FillRect(Bounds()); 337 SetHighColor(0, 0, 0, 255); 338 339 if (fShowingDate) 340 DrawString(fDateStr, fDateLocation); 341 else 342 DrawString(fTimeStr, fTimeLocation); 343 344 PopState(); 345 } 346 347 348 void 349 TTimeView::MouseDown(BPoint point) 350 { 351 uint32 buttons; 352 353 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 354 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 355 ShowClockOptions(ConvertToScreen(point)); 356 return; 357 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) { 358 StartLongClickNotifier(point); 359 } 360 361 // flip to/from showing date or time 362 fShowingDate = !fShowingDate; 363 if (fShowingDate) 364 fLastTime = time(NULL); 365 366 // invalidate last time/date strings and call the pulse 367 // method directly to change the display instantly 368 fLastDateStr[0] = '\0'; 369 fLastTimeStr[0] = '\0'; 370 Pulse(); 371 } 372 373 374 void 375 TTimeView::MouseUp(BPoint point) 376 { 377 StopLongClickNotifier(); 378 } 379 380 381 void 382 TTimeView::Pulse() 383 { 384 time_t curTime = time(NULL); 385 tm* ct = localtime(&curTime); 386 if (ct == NULL) 387 return; 388 389 fTime = curTime; 390 391 GetCurrentTime(); 392 GetCurrentDate(); 393 if ((!fShowingDate && strcmp(fTimeStr, fLastTimeStr) != 0) 394 || (fShowingDate && strcmp(fDateStr, fLastDateStr) != 0)) { 395 // Update bounds when the size of the strings has changed 396 // For dates, Update() could be called two times in a row, 397 // but that should only happen very rarely 398 if ((!fShowingDate && fLastTimeStr[1] != fTimeStr[1] 399 && (fLastTimeStr[1] == ':' || fTimeStr[1] == ':')) 400 || (fShowingDate && strlen(fDateStr) != strlen(fLastDateStr)) 401 || !fLastTimeStr[0]) 402 Update(); 403 404 strcpy(fLastTimeStr, fTimeStr); 405 strcpy(fLastDateStr, fDateStr); 406 fNeedToUpdate = true; 407 } 408 409 if (fShowingDate && (fLastTime + 5 <= time(NULL))) { 410 fShowingDate = false; 411 Update(); // Needs to happen since size can change here 412 } 413 414 if (fNeedToUpdate) { 415 fSeconds = ct->tm_sec; 416 fMinute = ct->tm_min; 417 fHour = ct->tm_hour; 418 fInterval = ct->tm_hour >= 12; 419 420 Draw(Bounds()); 421 fNeedToUpdate = false; 422 } 423 } 424 425 426 void 427 TTimeView::ShowSeconds(bool on) 428 { 429 fShowSeconds = on; 430 Update(); 431 } 432 433 434 void 435 TTimeView::ShowDate(bool on) 436 { 437 fShowingDate = on; 438 Update(); 439 } 440 441 442 void 443 TTimeView::ShowFullDate(bool on) 444 { 445 fFullDate = on; 446 Update(); 447 } 448 449 450 void 451 TTimeView::AllowFullDate(bool allow) 452 { 453 fCanShowFullDate = allow; 454 455 if (allow != ShowingFullDate()) 456 Update(); 457 } 458 459 460 void 461 TTimeView::Update() 462 { 463 fLocale = *be_locale; 464 GetCurrentTime(); 465 GetCurrentDate(); 466 467 ResizeToPreferred(); 468 CalculateTextPlacement(); 469 470 if (fParent) { 471 BMessage reformat('Trfm'); 472 fParent->MessageReceived(&reformat); 473 // time string format realign 474 fParent->Invalidate(); 475 } 476 } 477 478 479 void 480 TTimeView::SetOrientation(bool o) 481 { 482 fOrientation = o; 483 CalculateTextPlacement(); 484 Invalidate(); 485 } 486 487 488 void 489 TTimeView::CalculateTextPlacement() 490 { 491 BRect bounds(Bounds()); 492 493 fDateLocation.x = 0.0; 494 fTimeLocation.x = 0.0; 495 496 BFont font; 497 GetFont(&font); 498 const char* stringArray[1]; 499 stringArray[0] = fTimeStr; 500 BRect rectArray[1]; 501 escapement_delta delta = { 0.0, 0.0 }; 502 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 503 rectArray); 504 505 fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height() 506 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 507 } 508 509 510 void 511 TTimeView::ShowClockOptions(BPoint point) 512 { 513 BPopUpMenu* menu = new BPopUpMenu("", false, false); 514 menu->SetFont(be_plain_font); 515 BMenuItem* item; 516 517 item = new BMenuItem(B_TRANSLATE("Change time" B_UTF8_ELLIPSIS), 518 new BMessage(kChangeClock)); 519 menu->AddItem(item); 520 521 item = new BMenuItem(B_TRANSLATE("Hide time"), new BMessage('time')); 522 menu->AddItem(item); 523 524 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 525 new BMessage(kShowCalendar)); 526 menu->AddItem(item); 527 528 menu->SetTargetForItems(this); 529 // Changed to accept screen coord system point; 530 // not constrained to this view now 531 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 532 point.x + 4, point.y +4), true); 533 } 534 535