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