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