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_TRANSLATION_CONTEXT 67 #define B_TRANSLATION_CONTEXT "TimeView" 68 69 70 TTimeView::TTimeView(float maxWidth, float height, bool use24HourClock, 71 bool showSeconds, bool showDayOfWeek) 72 : 73 BView(BRect(-100, -100, -90, -90), "_deskbar_tv_", 74 B_FOLLOW_RIGHT | B_FOLLOW_TOP, 75 B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS), 76 fParent(NULL), 77 fMaxWidth(maxWidth), 78 fHeight(height), 79 fOrientation(true), 80 fUse24HourClock(use24HourClock), 81 fShowSeconds(showSeconds), 82 fShowDayOfWeek(showDayOfWeek) 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(ui_color(B_MENU_ITEM_TEXT_COLOR)); 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 kChangeTime: 198 // launch the time prefs app 199 be_roster->Launch("application/x-vnd.Haiku-Time"); 200 break; 201 202 case kShowHideTime: 203 Window()->PostMessage(message, Parent()); 204 break; 205 206 case kShowCalendar: 207 { 208 BRect bounds(Bounds()); 209 BPoint center(bounds.LeftTop()); 210 center += BPoint(bounds.Width() / 2, bounds.Height() / 2); 211 ShowCalendar(center); 212 break; 213 } 214 215 default: 216 BView::MessageReceived(message); 217 break; 218 } 219 } 220 221 222 void 223 TTimeView::MouseDown(BPoint point) 224 { 225 uint32 buttons; 226 227 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 228 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 229 ShowTimeOptions(ConvertToScreen(point)); 230 return; 231 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) 232 ShowCalendar(point); 233 234 // invalidate last time/date strings and call the pulse 235 // method directly to change the display instantly 236 fLastDateStr[0] = '\0'; 237 fLastTimeStr[0] = '\0'; 238 Pulse(); 239 } 240 241 242 void 243 TTimeView::Pulse() 244 { 245 time_t curTime = time(NULL); 246 tm* ct = localtime(&curTime); 247 if (ct == NULL) 248 return; 249 250 fCurrentTime = curTime; 251 252 GetCurrentTime(); 253 GetCurrentDate(); 254 if (strcmp(fCurrentTimeStr, fLastTimeStr) != 0) { 255 // Update bounds when the size of the strings has changed 256 // For dates, Update() could be called two times in a row, 257 // but that should only happen very rarely 258 if ((fLastTimeStr[1] != fCurrentTimeStr[1] 259 && (fLastTimeStr[1] == ':' || fCurrentTimeStr[1] == ':')) 260 || !fLastTimeStr[0]) 261 Update(); 262 263 strlcpy(fLastTimeStr, fCurrentTimeStr, sizeof(fLastTimeStr)); 264 fNeedToUpdate = true; 265 } 266 267 // Update the tooltip if the date has changed 268 if (strcmp(fCurrentDateStr, fLastDateStr) != 0) { 269 strlcpy(fLastDateStr, fCurrentDateStr, sizeof(fLastDateStr)); 270 SetToolTip(fCurrentDateStr); 271 } 272 273 if (fNeedToUpdate) { 274 fSeconds = ct->tm_sec; 275 fMinute = ct->tm_min; 276 fHour = ct->tm_hour; 277 278 Draw(Bounds()); 279 fNeedToUpdate = false; 280 } 281 } 282 283 284 void 285 TTimeView::ResizeToPreferred() 286 { 287 float width, height; 288 float oldWidth = Bounds().Width(), oldHeight = Bounds().Height(); 289 290 GetPreferredSize(&width, &height); 291 if (height != oldHeight || width != oldWidth) { 292 ResizeTo(width, height); 293 MoveBy(oldWidth - width, 0); 294 fNeedToUpdate = true; 295 } 296 } 297 298 299 // # pragma mark - Public methods 300 301 302 void 303 TTimeView::SetOrientation(bool orientation) 304 { 305 fOrientation = orientation; 306 CalculateTextPlacement(); 307 Invalidate(); 308 } 309 310 311 bool 312 TTimeView::Use24HourClock() const 313 { 314 return fUse24HourClock; 315 } 316 317 318 void 319 TTimeView::SetUse24HourClock(bool use24HourClock) 320 { 321 fUse24HourClock = use24HourClock; 322 Update(); 323 } 324 325 326 bool 327 TTimeView::ShowSeconds() const 328 { 329 return fShowSeconds; 330 } 331 332 333 void 334 TTimeView::SetShowSeconds(bool show) 335 { 336 fShowSeconds = show; 337 Update(); 338 } 339 340 341 bool 342 TTimeView::ShowDayOfWeek() const 343 { 344 return fShowDayOfWeek; 345 } 346 347 348 void 349 TTimeView::SetShowDayOfWeek(bool show) 350 { 351 fShowDayOfWeek = show; 352 Update(); 353 } 354 355 356 void 357 TTimeView::ShowCalendar(BPoint where) 358 { 359 if (fCalendarWindow.IsValid()) { 360 // If the calendar is already shown, just activate it 361 BMessage activate(B_SET_PROPERTY); 362 activate.AddSpecifier("Active"); 363 activate.AddBool("data", true); 364 365 if (fCalendarWindow.SendMessage(&activate) == B_OK) 366 return; 367 } 368 369 where.y = Bounds().bottom + 4.0; 370 ConvertToScreen(&where); 371 372 if (where.y >= BScreen().Frame().bottom) 373 where.y -= (Bounds().Height() + 4.0); 374 375 CalendarMenuWindow* window = new CalendarMenuWindow(where); 376 fCalendarWindow = BMessenger(window); 377 378 window->Show(); 379 } 380 381 382 // # pragma mark - Private methods 383 384 385 void 386 TTimeView::GetCurrentTime() 387 { 388 fLocale.FormatTime(fCurrentTimeStr, 64, fCurrentTime, fTimeFormat); 389 } 390 391 392 void 393 TTimeView::GetCurrentDate() 394 { 395 char tmp[64]; 396 397 fLocale.FormatDate(tmp, 64, fCurrentTime, B_FULL_DATE_FORMAT); 398 399 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 400 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 401 const char* str = tmp; 402 if (str[0] == '0') 403 str++; 404 405 strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr)); 406 } 407 408 409 void 410 TTimeView::CalculateTextPlacement() 411 { 412 BRect bounds(Bounds()); 413 414 fDateLocation.x = 0.0; 415 fTimeLocation.x = 0.0; 416 417 BFont font; 418 GetFont(&font); 419 420 const char* stringArray[1]; 421 stringArray[0] = fCurrentTimeStr; 422 BRect rectArray[1]; 423 escapement_delta delta = { 0.0, 0.0 }; 424 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 425 rectArray); 426 427 fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height() 428 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 429 } 430 431 432 void 433 TTimeView::ShowTimeOptions(BPoint point) 434 { 435 BPopUpMenu* menu = new BPopUpMenu("", false, false); 436 menu->SetFont(be_plain_font); 437 BMenuItem* item; 438 439 item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS), 440 new BMessage(kChangeTime)); 441 menu->AddItem(item); 442 443 item = new BMenuItem(B_TRANSLATE("Hide time"), 444 new BMessage(kShowHideTime)); 445 menu->AddItem(item); 446 447 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 448 new BMessage(kShowCalendar)); 449 menu->AddItem(item); 450 451 menu->SetTargetForItems(this); 452 // Changed to accept screen coord system point; 453 // not constrained to this view now 454 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 455 point.x + 4, point.y +4), true); 456 } 457 458 459 void 460 TTimeView::Update() 461 { 462 fLocale = *BLocale::Default(); 463 UpdateTimeFormat(); 464 465 GetCurrentTime(); 466 GetCurrentDate(); 467 SetToolTip(fCurrentDateStr); 468 469 CalculateTextPlacement(); 470 ResizeToPreferred(); 471 472 if (fParent != NULL) 473 fParent->Invalidate(); 474 } 475 476 477 void 478 TTimeView::UpdateTimeFormat() 479 { 480 BString timeFormat; 481 482 if (fShowDayOfWeek) 483 timeFormat.Append("eee "); 484 485 if (fUse24HourClock) 486 timeFormat.Append("H:mm"); 487 else 488 timeFormat.Append("h:mm"); 489 490 if (fShowSeconds) 491 timeFormat.Append(":ss"); 492 493 if (!fUse24HourClock) 494 timeFormat.Append(" a"); 495 496 fTimeFormat = timeFormat; 497 } 498