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.Haiku-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 DrawString(fDateStr, fDateLocation); 304 else 305 DrawString(fTimeStr, fTimeLocation); 306 307 PopState(); 308 } 309 310 311 void 312 TTimeView::MouseDown(BPoint point) 313 { 314 uint32 buttons; 315 316 Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons); 317 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 318 ShowClockOptions(ConvertToScreen(point)); 319 return; 320 } 321 322 // flip to/from showing date or time 323 fShowingDate = !fShowingDate; 324 if (fShowingDate) 325 fLastTime = time(NULL); 326 327 // invalidate last time/date strings and call the pulse 328 // method directly to change the display instantly 329 fLastDateStr[0] = '\0'; 330 fLastTimeStr[0] = '\0'; 331 Pulse(); 332 333 #ifdef _SHOW_CALENDAR_MENU_ITEM 334 // see if the user holds down the button long enough to show him the calendar 335 336 bigtime_t startTime = system_time(); 337 338 // use the doubleClickSpeed as a treshold 339 bigtime_t doubleClickSpeed; 340 get_click_speed(&doubleClickSpeed); 341 342 while (buttons) { 343 BPoint where; 344 GetMouse(&where, &buttons, false); 345 346 if ((system_time() - startTime) > doubleClickSpeed) { 347 BPopUpMenu *menu = new BPopUpMenu("", false, false); 348 menu->SetFont(be_plain_font); 349 350 menu->AddItem(new CalendarMenuItem()); 351 menu->ResizeToPreferred(); 352 353 point = where; 354 BScreen screen; 355 where.y = Bounds().bottom + 4; 356 357 // make sure the menu is visible at doesn't hide the date 358 ConvertToScreen(&where); 359 if (where.y + menu->Bounds().Height() > screen.Frame().bottom) 360 where.y -= menu->Bounds().Height() + 2 * Bounds().Height(); 361 362 ConvertToScreen(&point); 363 menu->Go(where, true, true, BRect(point.x - 4, point.y - 4, 364 point.x + 4, point.y + 4), true); 365 return; 366 } 367 368 snooze(15000); 369 } 370 #endif 371 } 372 373 374 void 375 TTimeView::Pulse() 376 { 377 time_t curTime = time(NULL); 378 tm ct = *localtime(&curTime); 379 fTime = curTime; 380 381 GetCurrentTime(); 382 GetCurrentDate(); 383 if ((!fShowingDate && strcmp(fTimeStr, fLastTimeStr) != 0) 384 || (fShowingDate && strcmp(fDateStr, fLastDateStr) != 0)) { 385 // Update bounds when the size of the strings has changed 386 // For dates, Update() could be called two times in a row, 387 // but that should only happen very rarely 388 if ((!fShowingDate && fLastTimeStr[1] != fTimeStr[1] 389 && (fLastTimeStr[1] == ':' || fTimeStr[1] == ':')) 390 || (fShowingDate && strlen(fDateStr) != strlen(fLastDateStr)) 391 || !fLastTimeStr[0]) 392 Update(); 393 394 strcpy(fLastTimeStr, fTimeStr); 395 strcpy(fLastDateStr, fDateStr); 396 fNeedToUpdate = true; 397 } 398 399 if (fShowingDate && (fLastTime + 5 <= time(NULL))) { 400 fShowingDate = false; 401 Update(); // Needs to happen since size can change here 402 } 403 404 if (fNeedToUpdate) { 405 fSeconds = ct.tm_sec; 406 fMinute = ct.tm_min; 407 fHour = ct.tm_hour; 408 fInterval = ct.tm_hour >= 12; 409 410 Draw(Bounds()); 411 fNeedToUpdate = false; 412 } 413 } 414 415 416 void 417 TTimeView::ShowSeconds(bool on) 418 { 419 fShowSeconds = on; 420 Update(); 421 } 422 423 424 void 425 TTimeView::ShowMilTime(bool on) 426 { 427 fMilTime = on; 428 Update(); 429 } 430 431 432 void 433 TTimeView::ShowDate(bool on) 434 { 435 fShowingDate = on; 436 Update(); 437 } 438 439 440 void 441 TTimeView::ShowFullDate(bool on) 442 { 443 fFullDate = on; 444 Update(); 445 } 446 447 448 void 449 TTimeView::ShowEuroDate(bool on) 450 { 451 fEuroDate = on; 452 Update(); 453 } 454 455 456 void 457 TTimeView::AllowFullDate(bool allow) 458 { 459 fCanShowFullDate = allow; 460 461 if (allow != ShowingFullDate()) 462 Update(); 463 } 464 465 466 void 467 TTimeView::Update() 468 { 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 if (fOrientation) { // vertical mode 499 fDateLocation.x = bounds.Width()/2 - StringWidth(fDateStr)/2; 500 fTimeLocation.x = bounds.Width()/2 - StringWidth(fTimeStr)/2; 501 } else { 502 fTimeLocation.x = bounds.Width() - StringWidth(fTimeStr) - 5; 503 fDateLocation.x = bounds.Width() - StringWidth(fDateStr) - 5; 504 } 505 // center vertically 506 fDateLocation.y = fTimeLocation.y = bounds.Height()/2 + fFontHeight/2; 507 } 508 509 510 void 511 TTimeView::ShowClockOptions(BPoint point) 512 { 513 BPopUpMenu *menu = new BPopUpMenu("", false, false); 514 menu->SetFont(be_plain_font); 515 BMenuItem *item; 516 517 item = new BMenuItem("Change Time" B_UTF8_ELLIPSIS, new BMessage(kMsgChangeClock)); 518 menu->AddItem(item); 519 520 item = new BMenuItem("Hide Time", new BMessage('time')); 521 menu->AddItem(item); 522 523 menu->SetTargetForItems(this); 524 // Changed to accept screen coord system point; 525 // not constrained to this view now 526 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 527 point.x + 4, point.y +4), true); 528 } 529