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 30 trademarks of Be Incorporated in the United States and other countries. Other 31 brand product names are registered trademarks or trademarks of their respective 32 holders. 33 All rights reserved. 34 */ 35 36 37 #include "TimeView.h" 38 39 #include <string.h> 40 41 #include <Catalog.h> 42 #include <Debug.h> 43 #include <Locale.h> 44 #include <MenuItem.h> 45 #include <MessageRunner.h> 46 #include <PopUpMenu.h> 47 #include <Roster.h> 48 #include <Screen.h> 49 #include <Window.h> 50 51 #include "CalendarMenuWindow.h" 52 53 54 static const char* const kMinString = "99:99 AM"; 55 static const float kHMargin = 2.0; 56 57 58 enum { 59 kShowTime, 60 kChangeTime, 61 kHide, 62 kShowCalendar 63 }; 64 65 66 #undef B_TRANSLATE_CONTEXT 67 #define B_TRANSLATE_CONTEXT "TimeView" 68 69 TTimeView::TTimeView(float maxWidth, float height, bool use24HourClock, 70 bool showSeconds, bool showDayOfWeek, bool showTimeZone) 71 : 72 BView(BRect(-100, -100, -90, -90), "_deskbar_tv_", 73 B_FOLLOW_RIGHT | B_FOLLOW_TOP, 74 B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS), 75 fParent(NULL), 76 fMaxWidth(maxWidth), 77 fHeight(height), 78 fOrientation(true), 79 fUse24HourClock(use24HourClock), 80 fShowSeconds(showSeconds), 81 fShowDayOfWeek(showDayOfWeek), 82 fShowTimeZone(showTimeZone) 83 { 84 fCurrentTime = fLastTime = time(NULL); 85 fSeconds = fMinute = fHour = 0; 86 fCurrentTimeStr[0] = 0; 87 fCurrentDateStr[0] = 0; 88 fLastTimeStr[0] = 0; 89 fLastDateStr[0] = 0; 90 fNeedToUpdate = true; 91 UpdateTimeFormat(); 92 93 fLocale = *BLocale::Default(); 94 } 95 96 97 #ifdef AS_REPLICANT 98 TTimeView::TTimeView(BMessage* data) 99 : BView(data) 100 { 101 fCurrentTime = fLastTime = time(NULL); 102 data->FindBool("seconds", &fShowSeconds); 103 104 fLocale = *BLocale::Default(); 105 } 106 #endif 107 108 109 TTimeView::~TTimeView() 110 { 111 } 112 113 114 #ifdef AS_REPLICANT 115 BArchivable* 116 TTimeView::Instantiate(BMessage* data) 117 { 118 if (!validate_instantiation(data, "TTimeView")) 119 return NULL; 120 121 return new TTimeView(data); 122 } 123 124 125 status_t 126 TTimeView::Archive(BMessage* data, bool deep) const 127 { 128 BView::Archive(data, deep); 129 data->AddBool("seconds", fShowSeconds); 130 data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT); 131 132 return B_OK; 133 } 134 #endif 135 136 137 void 138 TTimeView::AttachedToWindow() 139 { 140 fCurrentTime = time(NULL); 141 142 SetFont(be_plain_font); 143 if (Parent()) { 144 fParent = Parent(); 145 SetViewColor(Parent()->ViewColor()); 146 } else 147 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 148 149 CalculateTextPlacement(); 150 ResizeToPreferred(); 151 } 152 153 154 void 155 TTimeView::Draw(BRect /*updateRect*/) 156 { 157 PushState(); 158 159 SetHighColor(ViewColor()); 160 SetLowColor(ViewColor()); 161 FillRect(Bounds()); 162 SetHighColor(0, 0, 0, 255); 163 164 DrawString(fCurrentTimeStr, fTimeLocation); 165 166 PopState(); 167 } 168 169 170 void 171 TTimeView::FrameMoved(BPoint) 172 { 173 Update(); 174 } 175 176 177 void 178 TTimeView::GetPreferredSize(float* width, float* height) 179 { 180 *height = fHeight; 181 182 GetCurrentTime(); 183 184 // TODO: SetOrientation never gets called, fix that when in vertical mode, 185 // we want to limit the width so that it can't overlap the bevels in the 186 // parent view. 187 *width = fOrientation ? 188 min_c(fMaxWidth - kHMargin, kHMargin + StringWidth(fCurrentTimeStr)) 189 : kHMargin + StringWidth(fCurrentTimeStr); 190 } 191 192 193 void 194 TTimeView::MessageReceived(BMessage* message) 195 { 196 switch (message->what) { 197 case B_LOCALE_CHANGED: 198 Update(); 199 break; 200 201 case kChangeTime: 202 // launch the time prefs app 203 be_roster->Launch("application/x-vnd.Haiku-Time"); 204 break; 205 206 case kShowHideTime: 207 Window()->PostMessage(message, Parent()); 208 break; 209 210 case kShowCalendar: 211 { 212 BRect bounds(Bounds()); 213 BPoint center(bounds.LeftTop()); 214 center += BPoint(bounds.Width() / 2, bounds.Height() / 2); 215 ShowCalendar(center); 216 break; 217 } 218 219 default: 220 BView::MessageReceived(message); 221 break; 222 } 223 } 224 225 226 void 227 TTimeView::MouseDown(BPoint point) 228 { 229 uint32 buttons; 230 231 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 232 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 233 ShowTimeOptions(ConvertToScreen(point)); 234 return; 235 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) 236 ShowCalendar(point); 237 238 // invalidate last time/date strings and call the pulse 239 // method directly to change the display instantly 240 fLastDateStr[0] = '\0'; 241 fLastTimeStr[0] = '\0'; 242 Pulse(); 243 } 244 245 246 void 247 TTimeView::Pulse() 248 { 249 time_t curTime = time(NULL); 250 tm* ct = localtime(&curTime); 251 if (ct == NULL) 252 return; 253 254 fCurrentTime = curTime; 255 256 GetCurrentTime(); 257 GetCurrentDate(); 258 if (strcmp(fCurrentTimeStr, fLastTimeStr) != 0) { 259 // Update bounds when the size of the strings has changed 260 // For dates, Update() could be called two times in a row, 261 // but that should only happen very rarely 262 if ((fLastTimeStr[1] != fCurrentTimeStr[1] 263 && (fLastTimeStr[1] == ':' || fCurrentTimeStr[1] == ':')) 264 || !fLastTimeStr[0]) 265 Update(); 266 267 strlcpy(fLastTimeStr, fCurrentTimeStr, sizeof(fLastTimeStr)); 268 fNeedToUpdate = true; 269 } 270 271 // Update the tooltip if the date has changed 272 if (strcmp(fCurrentDateStr, fLastDateStr) != 0) { 273 strlcpy(fLastDateStr, fCurrentDateStr, sizeof(fLastDateStr)); 274 SetToolTip(fCurrentDateStr); 275 } 276 277 if (fNeedToUpdate) { 278 fSeconds = ct->tm_sec; 279 fMinute = ct->tm_min; 280 fHour = ct->tm_hour; 281 282 Draw(Bounds()); 283 fNeedToUpdate = false; 284 } 285 } 286 287 288 void 289 TTimeView::ResizeToPreferred() 290 { 291 float width, height; 292 float oldWidth = Bounds().Width(), oldHeight = Bounds().Height(); 293 294 GetPreferredSize(&width, &height); 295 if (height != oldHeight || width != oldWidth) { 296 ResizeTo(width, height); 297 MoveBy(oldWidth - width, 0); 298 fNeedToUpdate = true; 299 } 300 } 301 302 303 // # pragma mark - Public methods 304 305 306 void 307 TTimeView::SetOrientation(bool orientation) 308 { 309 fOrientation = orientation; 310 CalculateTextPlacement(); 311 Invalidate(); 312 } 313 314 315 bool 316 TTimeView::Use24HourClock() const 317 { 318 return fUse24HourClock; 319 } 320 321 322 void 323 TTimeView::SetUse24HourClock(bool use24HourClock) 324 { 325 fUse24HourClock = use24HourClock; 326 Update(); 327 } 328 329 330 bool 331 TTimeView::ShowSeconds() const 332 { 333 return fShowSeconds; 334 } 335 336 337 void 338 TTimeView::SetShowSeconds(bool show) 339 { 340 fShowSeconds = show; 341 Update(); 342 } 343 344 345 bool 346 TTimeView::ShowDayOfWeek() const 347 { 348 return fShowDayOfWeek; 349 } 350 351 352 void 353 TTimeView::SetShowDayOfWeek(bool show) 354 { 355 fShowDayOfWeek = show; 356 Update(); 357 } 358 359 360 bool 361 TTimeView::ShowTimeZone() const 362 { 363 return fShowTimeZone; 364 } 365 366 367 void 368 TTimeView::SetShowTimeZone(bool show) 369 { 370 fShowTimeZone = show; 371 Update(); 372 } 373 374 375 void 376 TTimeView::ShowCalendar(BPoint where) 377 { 378 if (fCalendarWindow.IsValid()) { 379 // If the calendar is already shown, just activate it 380 BMessage activate(B_SET_PROPERTY); 381 activate.AddSpecifier("Active"); 382 activate.AddBool("data", true); 383 384 if (fCalendarWindow.SendMessage(&activate) == B_OK) 385 return; 386 } 387 388 where.y = Bounds().bottom + 4.0; 389 ConvertToScreen(&where); 390 391 if (where.y >= BScreen().Frame().bottom) 392 where.y -= (Bounds().Height() + 4.0); 393 394 CalendarMenuWindow* window = new CalendarMenuWindow(where); 395 fCalendarWindow = BMessenger(window); 396 397 window->Show(); 398 } 399 400 401 // # pragma mark - Private methods 402 403 404 void 405 TTimeView::GetCurrentTime() 406 { 407 fLocale.FormatTime(fCurrentTimeStr, 64, fCurrentTime, fTimeFormat); 408 } 409 410 411 void 412 TTimeView::GetCurrentDate() 413 { 414 char tmp[64]; 415 416 fLocale.FormatDate(tmp, 64, fCurrentTime, B_FULL_DATE_FORMAT); 417 418 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 419 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 420 const char* str = tmp; 421 if (str[0] == '0') 422 str++; 423 424 strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr)); 425 } 426 427 428 void 429 TTimeView::CalculateTextPlacement() 430 { 431 BRect bounds(Bounds()); 432 433 fDateLocation.x = 0.0; 434 fTimeLocation.x = 0.0; 435 436 BFont font; 437 GetFont(&font); 438 439 // If 12 hour clock with all options turned on shrink font size to fit. 440 if (!fUse24HourClock && fShowSeconds && fShowDayOfWeek && fShowTimeZone) 441 font.SetSize(11.0); 442 else 443 font.SetSize(12.0); 444 445 SetFont(&font, B_FONT_SIZE); 446 447 const char* stringArray[1]; 448 stringArray[0] = fCurrentTimeStr; 449 BRect rectArray[1]; 450 escapement_delta delta = { 0.0, 0.0 }; 451 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 452 rectArray); 453 454 fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height() 455 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 456 } 457 458 459 void 460 TTimeView::ShowTimeOptions(BPoint point) 461 { 462 BPopUpMenu* menu = new BPopUpMenu("", false, false); 463 menu->SetFont(be_plain_font); 464 BMenuItem* item; 465 466 item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS), 467 new BMessage(kChangeTime)); 468 menu->AddItem(item); 469 470 item = new BMenuItem(B_TRANSLATE("Hide time"), 471 new BMessage(kShowHideTime)); 472 menu->AddItem(item); 473 474 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 475 new BMessage(kShowCalendar)); 476 menu->AddItem(item); 477 478 menu->SetTargetForItems(this); 479 // Changed to accept screen coord system point; 480 // not constrained to this view now 481 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 482 point.x + 4, point.y +4), true); 483 } 484 485 486 void 487 TTimeView::Update() 488 { 489 fLocale = *BLocale::Default(); 490 GetCurrentTime(); 491 GetCurrentDate(); 492 SetToolTip(fCurrentDateStr); 493 494 UpdateTimeFormat(); 495 496 CalculateTextPlacement(); 497 ResizeToPreferred(); 498 499 if (fParent) 500 fParent->Invalidate(); 501 } 502 503 504 void 505 TTimeView::UpdateTimeFormat() 506 { 507 BString timeFormat; 508 509 if (fShowDayOfWeek) 510 timeFormat.Append("eee "); 511 512 if (fUse24HourClock) 513 timeFormat.Append("H:mm"); 514 else 515 timeFormat.Append("h:mm"); 516 517 if (fShowSeconds) 518 timeFormat.Append(":ss"); 519 520 if (!fUse24HourClock) 521 timeFormat.Append(" a"); 522 523 if (fShowTimeZone) 524 timeFormat.Append(" V"); 525 526 fTimeFormat = timeFormat; 527 } 528