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