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 // TODO : should this be another function ? 310 tm time = *localtime(&fTime); 311 312 fSeconds = time.tm_sec; 313 fMinute = time.tm_min; 314 fHour = time.tm_hour; 315 316 fLocale.FormatTime(fTimeStr, 64, fTime, fShowSeconds); 317 } 318 319 320 void 321 TTimeView::GetCurrentDate() 322 { 323 char tmp[64]; 324 325 fLocale.FormatDate(tmp, 64, fTime, fFullDate && CanShowFullDate()); 326 327 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 328 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 329 const char* str = tmp; 330 if (str[0] == '0') 331 str++; 332 333 strcpy(fDateStr, str); 334 } 335 336 337 void 338 TTimeView::Draw(BRect /*updateRect*/) 339 { 340 PushState(); 341 342 SetHighColor(ViewColor()); 343 SetLowColor(ViewColor()); 344 FillRect(Bounds()); 345 SetHighColor(0, 0, 0, 255); 346 347 if (fShowingDate) 348 DrawString(fDateStr, fDateLocation); 349 else 350 DrawString(fTimeStr, fTimeLocation); 351 352 PopState(); 353 } 354 355 356 void 357 TTimeView::MouseDown(BPoint point) 358 { 359 uint32 buttons; 360 361 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 362 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 363 ShowClockOptions(ConvertToScreen(point)); 364 return; 365 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) { 366 StartLongClickNotifier(point); 367 } 368 369 // flip to/from showing date or time 370 fShowingDate = !fShowingDate; 371 if (fShowingDate) 372 fLastTime = time(NULL); 373 374 // invalidate last time/date strings and call the pulse 375 // method directly to change the display instantly 376 fLastDateStr[0] = '\0'; 377 fLastTimeStr[0] = '\0'; 378 Pulse(); 379 } 380 381 382 void 383 TTimeView::MouseUp(BPoint point) 384 { 385 StopLongClickNotifier(); 386 } 387 388 389 void 390 TTimeView::Pulse() 391 { 392 time_t curTime = time(NULL); 393 tm ct = *localtime(&curTime); 394 fTime = curTime; 395 396 GetCurrentTime(); 397 GetCurrentDate(); 398 if ((!fShowingDate && strcmp(fTimeStr, fLastTimeStr) != 0) 399 || (fShowingDate && strcmp(fDateStr, fLastDateStr) != 0)) { 400 // Update bounds when the size of the strings has changed 401 // For dates, Update() could be called two times in a row, 402 // but that should only happen very rarely 403 if ((!fShowingDate && fLastTimeStr[1] != fTimeStr[1] 404 && (fLastTimeStr[1] == ':' || fTimeStr[1] == ':')) 405 || (fShowingDate && strlen(fDateStr) != strlen(fLastDateStr)) 406 || !fLastTimeStr[0]) 407 Update(); 408 409 strcpy(fLastTimeStr, fTimeStr); 410 strcpy(fLastDateStr, fDateStr); 411 fNeedToUpdate = true; 412 } 413 414 if (fShowingDate && (fLastTime + 5 <= time(NULL))) { 415 fShowingDate = false; 416 Update(); // Needs to happen since size can change here 417 } 418 419 if (fNeedToUpdate) { 420 fSeconds = ct.tm_sec; 421 fMinute = ct.tm_min; 422 fHour = ct.tm_hour; 423 fInterval = ct.tm_hour >= 12; 424 425 Draw(Bounds()); 426 fNeedToUpdate = false; 427 } 428 } 429 430 431 void 432 TTimeView::ShowSeconds(bool on) 433 { 434 fShowSeconds = on; 435 Update(); 436 } 437 438 439 void 440 TTimeView::ShowDate(bool on) 441 { 442 fShowingDate = on; 443 Update(); 444 } 445 446 447 void 448 TTimeView::ShowFullDate(bool on) 449 { 450 fFullDate = on; 451 Update(); 452 } 453 454 455 void 456 TTimeView::AllowFullDate(bool allow) 457 { 458 fCanShowFullDate = allow; 459 460 if (allow != ShowingFullDate()) 461 Update(); 462 } 463 464 465 void 466 TTimeView::Update() 467 { 468 be_locale_roster->GetDefaultLocale(&fLocale); 469 GetCurrentTime(); 470 GetCurrentDate(); 471 472 ResizeToPreferred(); 473 CalculateTextPlacement(); 474 475 if (fParent) { 476 BMessage reformat('Trfm'); 477 fParent->MessageReceived(&reformat); 478 // time string format realign 479 fParent->Invalidate(); 480 } 481 } 482 483 484 void 485 TTimeView::SetOrientation(bool o) 486 { 487 fOrientation = o; 488 CalculateTextPlacement(); 489 Invalidate(); 490 } 491 492 493 void 494 TTimeView::CalculateTextPlacement() 495 { 496 BRect bounds(Bounds()); 497 498 fDateLocation.x = 0.0; 499 fTimeLocation.x = 0.0; 500 501 BFont font; 502 GetFont(&font); 503 const char* stringArray[1]; 504 stringArray[0] = fTimeStr; 505 BRect rectArray[1]; 506 escapement_delta delta = { 0.0, 0.0 }; 507 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 508 rectArray); 509 510 fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height() 511 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 512 } 513 514 515 void 516 TTimeView::ShowClockOptions(BPoint point) 517 { 518 BPopUpMenu* menu = new BPopUpMenu("", false, false); 519 menu->SetFont(be_plain_font); 520 BMenuItem* item; 521 522 item = new BMenuItem(B_TRANSLATE("Change time" B_UTF8_ELLIPSIS), 523 new BMessage(kChangeClock)); 524 menu->AddItem(item); 525 526 item = new BMenuItem(B_TRANSLATE("Hide time"), new BMessage('time')); 527 menu->AddItem(item); 528 529 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 530 new BMessage(kShowCalendar)); 531 menu->AddItem(item); 532 533 menu->SetTargetForItems(this); 534 // Changed to accept screen coord system point; 535 // not constrained to this view now 536 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 537 point.x + 4, point.y +4), true); 538 } 539 540