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 "CalendarMenuWindow.h" 55 56 57 static const char* const kMinString = "99:99 AM"; 58 static const float kHMargin = 2.0; 59 60 61 #undef B_TRANSLATION_CONTEXT 62 #define B_TRANSLATION_CONTEXT "TimeView" 63 64 65 TTimeView::TTimeView(float maxWidth, float height) 66 : 67 BView(BRect(-100, -100, -90, -90), "_deskbar_tv_", 68 B_FOLLOW_RIGHT | B_FOLLOW_TOP, 69 B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS), 70 fParent(NULL), 71 fMaxWidth(maxWidth), 72 fHeight(height), 73 fOrientation(true), 74 fShowLevel(0), 75 fShowSeconds(false), 76 fShowDayOfWeek(false), 77 fShowTimeZone(false), 78 fTimeFormat(NULL), 79 fDateFormat(NULL) 80 { 81 fCurrentTime = fLastTime = time(NULL); 82 fSeconds = fMinute = fHour = 0; 83 fCurrentTimeStr[0] = 0; 84 fCurrentDateStr[0] = 0; 85 fLastTimeStr[0] = 0; 86 fLastDateStr[0] = 0; 87 fNeedToUpdate = true; 88 UpdateTimeFormat(); 89 } 90 91 92 #ifdef AS_REPLICANT 93 TTimeView::TTimeView(BMessage* data) 94 : BView(data), 95 fTimeFormat(NULL), 96 fDateFormat(NULL) 97 { 98 fCurrentTime = fLastTime = time(NULL); 99 data->FindBool("seconds", &fShowSeconds); 100 101 UpdateTimeFormat(); 102 } 103 #endif 104 105 106 TTimeView::~TTimeView() 107 { 108 delete fTimeFormat; 109 delete fDateFormat; 110 } 111 112 113 #ifdef AS_REPLICANT 114 BArchivable* 115 TTimeView::Instantiate(BMessage* data) 116 { 117 if (!validate_instantiation(data, "TTimeView")) 118 return NULL; 119 120 return new TTimeView(data); 121 } 122 123 124 status_t 125 TTimeView::Archive(BMessage* data, bool deep) const 126 { 127 BView::Archive(data, deep); 128 data->AddBool("orientation", fOrientation); 129 data->AddInt16("showLevel", fShowLevel); 130 data->AddBool("showSeconds", fShowSeconds); 131 data->AddBool("showDayOfWeek", fShowDayOfWeek); 132 data->AddBool("showTimeZone", fShowTimeZone); 133 data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT); 134 135 return B_OK; 136 } 137 #endif 138 139 140 void 141 TTimeView::AttachedToWindow() 142 { 143 fCurrentTime = time(NULL); 144 145 SetFont(be_plain_font); 146 if (Parent()) { 147 fParent = Parent(); 148 AdoptParentColors(); 149 } else 150 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 151 152 CalculateTextPlacement(); 153 ResizeToPreferred(); 154 } 155 156 157 void 158 TTimeView::Draw(BRect /*updateRect*/) 159 { 160 PushState(); 161 162 SetHighColor(ViewColor()); 163 SetLowColor(ViewColor()); 164 FillRect(Bounds()); 165 SetHighUIColor(B_MENU_ITEM_TEXT_COLOR); 166 167 DrawString(fCurrentTimeStr, fTimeLocation); 168 169 PopState(); 170 } 171 172 173 void 174 TTimeView::FrameMoved(BPoint) 175 { 176 Update(); 177 } 178 179 180 void 181 TTimeView::GetPreferredSize(float* width, float* height) 182 { 183 *height = fHeight; 184 185 GetCurrentTime(); 186 187 float timeWidth = StringWidth(fCurrentTimeStr); 188 189 // TODO: SetOrientation never gets called, fix that when in vertical mode, 190 // we want to limit the width so that it can't overlap the bevels in the 191 // parent view. 192 *width = fOrientation ? std::min(fMaxWidth - kHMargin, timeWidth) 193 : 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 CalculateTextPlacement(); 316 Invalidate(); 317 } 318 319 320 bool 321 TTimeView::ShowSeconds() const 322 { 323 return fShowSeconds; 324 } 325 326 327 void 328 TTimeView::SetShowSeconds(bool show) 329 { 330 fShowSeconds = show; 331 UpdateTimeFormat(); 332 Update(); 333 } 334 335 336 bool 337 TTimeView::ShowDayOfWeek() const 338 { 339 return fShowDayOfWeek; 340 } 341 342 343 void 344 TTimeView::SetShowDayOfWeek(bool show) 345 { 346 fShowDayOfWeek = show; 347 UpdateTimeFormat(); 348 Update(); 349 } 350 351 352 bool 353 TTimeView::ShowTimeZone() const 354 { 355 return fShowTimeZone; 356 } 357 358 359 void 360 TTimeView::SetShowTimeZone(bool show) 361 { 362 fShowTimeZone = show; 363 UpdateTimeFormat(); 364 Update(); 365 } 366 367 368 void 369 TTimeView::ShowCalendar(BPoint where) 370 { 371 if (fCalendarWindow.IsValid()) { 372 // If the calendar is already shown, just activate it 373 BMessage activate(B_SET_PROPERTY); 374 activate.AddSpecifier("Active"); 375 activate.AddBool("data", true); 376 377 if (fCalendarWindow.SendMessage(&activate) == B_OK) 378 return; 379 } 380 381 where.y = Bounds().bottom + 4.0; 382 ConvertToScreen(&where); 383 384 if (where.y >= BScreen().Frame().bottom) 385 where.y -= (Bounds().Height() + 4.0); 386 387 CalendarMenuWindow* window = new CalendarMenuWindow(where); 388 fCalendarWindow = BMessenger(window); 389 390 window->Show(); 391 } 392 393 394 // # pragma mark - Private methods 395 396 397 void 398 TTimeView::UpdateTimeFormat() 399 { 400 int32 fields = B_DATE_ELEMENT_HOUR | B_DATE_ELEMENT_MINUTE; 401 if (fShowSeconds) 402 fields |= B_DATE_ELEMENT_SECOND; 403 if (fShowDayOfWeek) 404 fields |= B_DATE_ELEMENT_WEEKDAY; 405 if (fShowTimeZone) 406 fields |= B_DATE_ELEMENT_TIMEZONE; 407 408 delete fTimeFormat; 409 fTimeFormat = new BDateTimeFormat(BLocale::Default()); 410 fTimeFormat->SetDateTimeFormat(B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT, fields); 411 412 delete fDateFormat; 413 fDateFormat = new BDateFormat(BLocale::Default()); 414 } 415 416 void 417 TTimeView::GetCurrentTime() 418 { 419 fTimeFormat->Format(fCurrentTimeStr, sizeof(fCurrentTimeStr), fCurrentTime, 420 B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT); 421 } 422 423 424 void 425 TTimeView::GetCurrentDate() 426 { 427 char tmp[sizeof(fCurrentDateStr)]; 428 429 fDateFormat->Format(tmp, sizeof(fCurrentDateStr), fCurrentTime, 430 B_FULL_DATE_FORMAT); 431 432 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 433 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 434 const char* str = tmp; 435 if (str[0] == '0') 436 str++; 437 438 strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr)); 439 } 440 441 442 void 443 TTimeView::CalculateTextPlacement() 444 { 445 BRect bounds(Bounds()); 446 447 fDateLocation.x = 0.0; 448 fTimeLocation.x = 0.0; 449 450 BFont font; 451 GetFont(&font); 452 453 const char* stringArray[1]; 454 stringArray[0] = fCurrentTimeStr; 455 BRect rectArray[1]; 456 escapement_delta delta = { 0.0, 0.0 }; 457 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 458 rectArray); 459 460 fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height() 461 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 462 } 463 464 465 void 466 TTimeView::ShowTimeOptions(BPoint point) 467 { 468 BPopUpMenu* menu = new BPopUpMenu("", false, false); 469 menu->SetFont(be_plain_font); 470 BMenuItem* item; 471 472 item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS), 473 new BMessage(kChangeTime)); 474 menu->AddItem(item); 475 476 item = new BMenuItem(B_TRANSLATE("Hide clock"), 477 new BMessage(kShowHideTime)); 478 menu->AddItem(item); 479 480 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 481 new BMessage(kShowCalendar)); 482 menu->AddItem(item); 483 484 menu->SetTargetForItems(this); 485 // Changed to accept screen coord system point; 486 // not constrained to this view now 487 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 488 point.x + 4, point.y +4), true); 489 } 490 491 492 void 493 TTimeView::Update() 494 { 495 GetCurrentTime(); 496 GetCurrentDate(); 497 SetToolTip(fCurrentDateStr); 498 499 CalculateTextPlacement(); 500 ResizeToPreferred(); 501 502 if (fParent != NULL) 503 fParent->Invalidate(); 504 } 505