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