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 // see if the user holds down the button long enough to show him the calendar 339 340 bigtime_t startTime = system_time(); 341 342 // use the doubleClickSpeed as a treshold 343 bigtime_t doubleClickSpeed; 344 get_click_speed(&doubleClickSpeed); 345 346 while (buttons) { 347 BPoint where; 348 GetMouse(&where, &buttons, false); 349 350 if ((system_time() - startTime) > doubleClickSpeed) { 351 BPopUpMenu *menu = new BPopUpMenu("", false, false); 352 menu->SetFont(be_plain_font); 353 354 menu->AddItem(new CalendarMenuItem()); 355 menu->ResizeToPreferred(); 356 357 point = where; 358 BScreen screen; 359 where.y = Bounds().bottom + 4; 360 361 // make sure the menu is visible at doesn't hide the date 362 ConvertToScreen(&where); 363 if (where.y + menu->Bounds().Height() > screen.Frame().bottom) 364 where.y -= menu->Bounds().Height() + 2 * Bounds().Height(); 365 366 ConvertToScreen(&point); 367 menu->Go(where, true, true, BRect(point.x - 4, point.y - 4, 368 point.x + 4, point.y + 4), true); 369 return; 370 } 371 372 snooze(15000); 373 } 374 #endif 375 } 376 377 378 void 379 TTimeView::Pulse() 380 { 381 time_t curTime = time(NULL); 382 tm ct = *localtime(&curTime); 383 fTime = curTime; 384 385 GetCurrentTime(); 386 GetCurrentDate(); 387 if ((!fShowingDate && strcmp(fTimeStr, fLastTimeStr) != 0) 388 || (fShowingDate && strcmp(fDateStr, fLastDateStr) != 0)) { 389 // Update bounds when the size of the strings has changed 390 // For dates, Update() could be called two times in a row, 391 // but that should only happen very rarely 392 if ((!fShowingDate && fLastTimeStr[1] != fTimeStr[1] 393 && (fLastTimeStr[1] == ':' || fTimeStr[1] == ':')) 394 || (fShowingDate && strlen(fDateStr) != strlen(fLastDateStr)) 395 || !fLastTimeStr[0]) 396 Update(); 397 398 strcpy(fLastTimeStr, fTimeStr); 399 strcpy(fLastDateStr, fDateStr); 400 fNeedToUpdate = true; 401 } 402 403 if (fShowingDate && (fLastTime + 5 <= time(NULL))) { 404 fShowingDate = false; 405 Update(); // Needs to happen since size can change here 406 } 407 408 if (fNeedToUpdate) { 409 fSeconds = ct.tm_sec; 410 fMinute = ct.tm_min; 411 fHour = ct.tm_hour; 412 fInterval = ct.tm_hour >= 12; 413 414 Draw(Bounds()); 415 fNeedToUpdate = false; 416 } 417 } 418 419 420 void 421 TTimeView::ShowSeconds(bool on) 422 { 423 fShowSeconds = on; 424 Update(); 425 } 426 427 428 void 429 TTimeView::ShowMilTime(bool on) 430 { 431 fMilTime = on; 432 Update(); 433 } 434 435 436 void 437 TTimeView::ShowDate(bool on) 438 { 439 fShowingDate = on; 440 Update(); 441 } 442 443 444 void 445 TTimeView::ShowFullDate(bool on) 446 { 447 fFullDate = on; 448 Update(); 449 } 450 451 452 void 453 TTimeView::ShowEuroDate(bool on) 454 { 455 fEuroDate = on; 456 Update(); 457 } 458 459 460 void 461 TTimeView::AllowFullDate(bool allow) 462 { 463 fCanShowFullDate = allow; 464 465 if (allow != ShowingFullDate()) 466 Update(); 467 } 468 469 470 void 471 TTimeView::Update() 472 { 473 GetCurrentTime(); 474 GetCurrentDate(); 475 476 ResizeToPreferred(); 477 CalculateTextPlacement(); 478 479 if (fParent) { 480 fParent->MessageReceived(new BMessage('trfm')); 481 // time string format realign 482 fParent->Invalidate(); 483 } 484 } 485 486 487 void 488 TTimeView::SetOrientation(bool o) 489 { 490 fOrientation = o; 491 CalculateTextPlacement(); 492 Invalidate(); 493 } 494 495 496 void 497 TTimeView::CalculateTextPlacement() 498 { 499 BRect bounds(Bounds()); 500 501 if (fOrientation) { // vertical mode 502 fDateLocation.x = bounds.Width()/2 - StringWidth(fDateStr)/2; 503 fTimeLocation.x = bounds.Width()/2 - StringWidth(fTimeStr)/2; 504 } else { 505 fTimeLocation.x = bounds.Width() - StringWidth(fTimeStr) - 5; 506 fDateLocation.x = bounds.Width() - StringWidth(fDateStr) - 5; 507 } 508 // center vertically 509 fDateLocation.y = fTimeLocation.y = bounds.Height()/2 + fFontHeight/2; 510 } 511 512 513 void 514 TTimeView::ShowClockOptions(BPoint point) 515 { 516 BPopUpMenu *menu = new BPopUpMenu("", false, false); 517 menu->SetFont(be_plain_font); 518 BMenuItem *item; 519 520 item = new BMenuItem("Change Time" B_UTF8_ELLIPSIS, new BMessage(kMsgChangeClock)); 521 menu->AddItem(item); 522 523 item = new BMenuItem("Hide Time", new BMessage('time')); 524 menu->AddItem(item); 525 526 menu->SetTargetForItems(this); 527 // Changed to accept screen coord system point; 528 // not constrained to this view now 529 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 530 point.x + 4, point.y +4), true); 531 } 532