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