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 #ifdef _SHOW_CALENDAR_MENU_ITEM 39 # include "CalendarMenuItem.h" 40 #endif 41 42 #include <Debug.h> 43 #include <MenuItem.h> 44 #include <PopUpMenu.h> 45 #include <Roster.h> 46 #include <Window.h> 47 #include <Screen.h> 48 49 #include <string.h> 50 51 52 static const char * const kMinString = "99:99 AM"; 53 54 55 static float 56 FontHeight(BView *target, bool full) 57 { 58 font_height fontInfo; 59 target->GetFontHeight(&fontInfo); 60 float h = fontInfo.ascent + fontInfo.descent; 61 62 if (full) 63 h += fontInfo.leading; 64 65 return h; 66 } 67 68 69 enum { 70 kMsgShowClock, 71 kMsgChangeClock, 72 kMsgHide 73 }; 74 75 76 TTimeView::TTimeView(bool showSeconds, bool milTime, bool fullDate, bool euroDate, bool) 77 : BView(BRect(-100,-100,-90,-90), "_deskbar_tv_", 78 B_FOLLOW_RIGHT | B_FOLLOW_TOP, 79 B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS), 80 fParent(NULL), 81 fShowInterval(true), // ToDo: defaulting this to true until UI is in place 82 fShowSeconds(showSeconds), 83 fMilTime(milTime), 84 fFullDate(fullDate), 85 fCanShowFullDate(false), 86 fEuroDate(euroDate), 87 fOrientation(false) 88 { 89 fShowingDate = false; 90 fTime = fLastTime = time(NULL); 91 fSeconds = fMinute = fHour = 0; 92 fLastTimeStr[0] = 0; 93 fLastDateStr[0] = 0; 94 fNeedToUpdate = true; 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("miltime", &fMilTime); 105 data->FindBool("fulldate", &fFullDate); 106 data->FindBool("eurodate", &fEuroDate); 107 data->FindBool("interval", &fInterval); 108 fShowingDate = false; 109 } 110 #endif 111 112 113 TTimeView::~TTimeView() 114 { 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("miltime", fMilTime); 135 data->AddBool("fulldate", fFullDate); 136 data->AddBool("eurodate", fEuroDate); 137 data->AddBool("interval", fInterval); 138 data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT); 139 140 return B_OK; 141 } 142 #endif 143 144 145 void 146 TTimeView::AttachedToWindow() 147 { 148 fTime = time(NULL); 149 150 SetFont(be_plain_font); 151 if (Parent()) { 152 fParent = Parent(); 153 SetViewColor(Parent()->ViewColor()); 154 } else 155 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 156 157 fFontHeight = FontHeight(this, true); 158 ResizeToPreferred(); 159 CalculateTextPlacement(); 160 } 161 162 163 void 164 TTimeView::GetPreferredSize(float *width, float *height) 165 { 166 *height = fFontHeight; 167 168 GetCurrentTime(); 169 GetCurrentDate(); 170 171 if (ShowingDate()) 172 *width = 6 + StringWidth(fDateStr); 173 else { 174 *width = 6 + StringWidth(fTimeStr); 175 // Changed this from 10 to 6 so even with interval + seconds, there still 176 // is room for two replicants in the default tray. 177 } 178 } 179 180 181 void 182 TTimeView::ResizeToPreferred() 183 { 184 float width, height; 185 float oldWidth = Bounds().Width(), oldHeight = Bounds().Height(); 186 187 GetPreferredSize(&width, &height); 188 if (height != oldHeight || width != oldWidth) { 189 ResizeTo(width, height); 190 MoveBy(oldWidth - width, 0); 191 fNeedToUpdate = true; 192 } 193 } 194 195 196 void 197 TTimeView::FrameMoved(BPoint) 198 { 199 Update(); 200 } 201 202 203 void 204 TTimeView::MessageReceived(BMessage* message) 205 { 206 switch (message->what) { 207 case kMsgFullDate: 208 ShowFullDate(!ShowingFullDate()); 209 break; 210 211 case kMsgShowSeconds: 212 ShowSeconds(!ShowingSeconds()); 213 break; 214 215 case kMsgMilTime: 216 ShowMilTime(!ShowingMilTime()); 217 break; 218 219 case kMsgEuroDate: 220 ShowEuroDate(!ShowingEuroDate()); 221 break; 222 223 case kMsgChangeClock: 224 // launch the time prefs app 225 be_roster->Launch("application/x-vnd.Be-TIME"); 226 break; 227 228 case 'time': 229 Window()->PostMessage(message, Parent()); 230 break; 231 232 default: 233 BView::MessageReceived(message); 234 } 235 } 236 237 238 void 239 TTimeView::GetCurrentTime() 240 { 241 char tmp[64]; 242 tm time = *localtime(&fTime); 243 244 if (fMilTime) { 245 strftime(tmp, 64, fShowSeconds ? "%H:%M:%S" : "%H:%M", &time); 246 } else { 247 if (fShowInterval) 248 strftime(tmp, 64, fShowSeconds ? "%I:%M:%S %p" : "%I:%M %p", &time); 249 else 250 strftime(tmp, 64, fShowSeconds ? "%I:%M:%S" : "%I:%M", &time); 251 } 252 253 // remove leading 0 from time when hour is less than 10 254 const char *str = tmp; 255 if (str[0] == '0') 256 str++; 257 258 strcpy(fTimeStr, str); 259 260 fSeconds = time.tm_sec; 261 fMinute = time.tm_min; 262 fHour = time.tm_hour; 263 } 264 265 266 const char *kShortDateFormat = "%m/%d/%y"; 267 const char *kShortEuroDateFormat = "%d/%m/%y"; 268 const char *kLongDateFormat = "%a, %B %d, %Y"; 269 const char *kLongEuroDateFormat = "%a, %d %B, %Y"; 270 271 272 void 273 TTimeView::GetCurrentDate() 274 { 275 char tmp[64]; 276 tm time = *localtime(&fTime); 277 278 if (fFullDate && CanShowFullDate()) 279 strftime(tmp, 64, fEuroDate ? kLongEuroDateFormat : kLongDateFormat, &time); 280 else 281 strftime(tmp, 64, fEuroDate ? kShortEuroDateFormat : kShortDateFormat, &time); 282 283 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 284 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 285 const char* str = tmp; 286 if (str[0] == '0') 287 str++; 288 289 strcpy(fDateStr, str); 290 } 291 292 293 void 294 TTimeView::Draw(BRect /*updateRect*/) 295 { 296 PushState(); 297 298 SetHighColor(ViewColor()); 299 SetLowColor(ViewColor()); 300 FillRect(Bounds()); 301 SetHighColor(0, 0, 0, 255); 302 303 if (fShowingDate) { 304 MovePenTo(fDateLocation); 305 DrawString(fDateStr); 306 } else { 307 MovePenTo(fTimeLocation); 308 DrawString(fTimeStr); 309 } 310 311 PopState(); 312 } 313 314 315 void 316 TTimeView::MouseDown(BPoint point) 317 { 318 uint32 buttons; 319 320 Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons); 321 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 322 ShowClockOptions(ConvertToScreen(point)); 323 return; 324 } 325 326 // flip to/from showing date or time 327 fShowingDate = !fShowingDate; 328 if (fShowingDate) 329 fLastTime = time(NULL); 330 331 // invalidate last time/date strings and call the pulse 332 // method directly to change the display instantly 333 fLastDateStr[0] = '\0'; 334 fLastTimeStr[0] = '\0'; 335 Pulse(); 336 337 #ifdef _SHOW_CALENDAR_MENU_ITEM 338 if (!fShowingDate) 339 return; 340 341 // see if the user holds down the button long enough to show him the calendar 342 343 bigtime_t startTime = system_time(); 344 345 // use the doubleClickSpeed as a treshold 346 bigtime_t doubleClickSpeed; 347 get_click_speed(&doubleClickSpeed); 348 349 while (buttons) { 350 BPoint where; 351 GetMouse(&where, &buttons, false); 352 353 if ((system_time() - startTime) > doubleClickSpeed) { 354 BPopUpMenu *menu = new BPopUpMenu("", false, false); 355 menu->SetFont(be_plain_font); 356 357 menu->AddItem(new CalendarMenuItem()); 358 menu->ResizeToPreferred(); 359 360 point = where; 361 BScreen screen; 362 where.y = Bounds().bottom + 4; 363 364 // make sure the menu is visible at doesn't hide the date 365 ConvertToScreen(&where); 366 if (where.y + menu->Bounds().Height() > screen.Frame().bottom) 367 where.y -= menu->Bounds().Height() + 2 * Bounds().Height(); 368 369 ConvertToScreen(&point); 370 menu->Go(where, true, true, BRect(point.x - 4, point.y - 4, 371 point.x + 4, point.y + 4), true); 372 return; 373 } 374 375 snooze(15000); 376 } 377 #endif 378 } 379 380 381 void 382 TTimeView::Pulse() 383 { 384 time_t curTime = time(NULL); 385 tm ct = *localtime(&curTime); 386 fTime = curTime; 387 388 GetCurrentTime(); 389 GetCurrentDate(); 390 if ((!fShowingDate && strcmp(fTimeStr, fLastTimeStr) != 0) 391 || (fShowingDate && strcmp(fDateStr, fLastDateStr) != 0)) { 392 // Update bounds when the size of the strings has changed 393 // For dates, Update() could be called two times in a row, 394 // but that should only happen very rarely 395 if ((!fShowingDate && fLastTimeStr[1] != fTimeStr[1] 396 && (fLastTimeStr[1] == ':' || fTimeStr[1] == ':')) 397 || (fShowingDate && strlen(fDateStr) != strlen(fLastDateStr)) 398 || !fLastTimeStr[0]) 399 Update(); 400 401 strcpy(fLastTimeStr, fTimeStr); 402 strcpy(fLastDateStr, fDateStr); 403 fNeedToUpdate = true; 404 } 405 406 if (fShowingDate && (fLastTime + 5 <= time(NULL))) { 407 fShowingDate = false; 408 Update(); // Needs to happen since size can change here 409 } 410 411 if (fNeedToUpdate) { 412 fSeconds = ct.tm_sec; 413 fMinute = ct.tm_min; 414 fHour = ct.tm_hour; 415 fInterval = ct.tm_hour >= 12; 416 417 Draw(Bounds()); 418 fNeedToUpdate = false; 419 } 420 } 421 422 423 void 424 TTimeView::ShowSeconds(bool on) 425 { 426 fShowSeconds = on; 427 Update(); 428 } 429 430 431 void 432 TTimeView::ShowMilTime(bool on) 433 { 434 fMilTime = 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::ShowEuroDate(bool on) 457 { 458 fEuroDate = on; 459 Update(); 460 } 461 462 463 void 464 TTimeView::AllowFullDate(bool allow) 465 { 466 fCanShowFullDate = allow; 467 468 if (allow != ShowingFullDate()) 469 Update(); 470 } 471 472 473 void 474 TTimeView::Update() 475 { 476 GetCurrentTime(); 477 GetCurrentDate(); 478 479 ResizeToPreferred(); 480 CalculateTextPlacement(); 481 482 if (fParent) { 483 fParent->MessageReceived(new BMessage('trfm')); 484 // time string format realign 485 fParent->Invalidate(); 486 } 487 } 488 489 490 void 491 TTimeView::SetOrientation(bool o) 492 { 493 fOrientation = o; 494 CalculateTextPlacement(); 495 Invalidate(); 496 } 497 498 499 void 500 TTimeView::CalculateTextPlacement() 501 { 502 BRect bounds(Bounds()); 503 504 if (fOrientation) { // vertical mode 505 fDateLocation.x = bounds.Width()/2 - StringWidth(fDateStr)/2; 506 fTimeLocation.x = bounds.Width()/2 - StringWidth(fTimeStr)/2; 507 } else { 508 fTimeLocation.x = bounds.Width() - StringWidth(fTimeStr) - 5; 509 fDateLocation.x = bounds.Width() - StringWidth(fDateStr) - 5; 510 } 511 // center vertically 512 fDateLocation.y = fTimeLocation.y = bounds.Height()/2 + fFontHeight/2; 513 } 514 515 516 void 517 TTimeView::ShowClockOptions(BPoint point) 518 { 519 BPopUpMenu *menu = new BPopUpMenu("", false, false); 520 menu->SetFont(be_plain_font); 521 BMenuItem *item; 522 523 item = new BMenuItem("Change Time" B_UTF8_ELLIPSIS, new BMessage(kMsgChangeClock)); 524 menu->AddItem(item); 525 526 item = new BMenuItem("Hide Time", new BMessage('time')); 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