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 kShowClock, 60 kChangeClock, 61 kHide, 62 kShowCalendar 63 }; 64 65 66 #undef B_TRANSLATE_CONTEXT 67 #define B_TRANSLATE_CONTEXT "TimeView" 68 69 TTimeView::TTimeView(float maxWidth, float height, bool showSeconds, 70 bool) 71 : 72 BView(BRect(-100, -100, -90, -90), "_deskbar_tv_", 73 B_FOLLOW_RIGHT | B_FOLLOW_TOP, 74 B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS), 75 fParent(NULL), 76 fShowInterval(true), // ToDo: defaulting this to true until UI is in place 77 fShowSeconds(showSeconds), 78 fMaxWidth(maxWidth), 79 fHeight(height), 80 fOrientation(true) 81 { 82 fTime = fLastTime = time(NULL); 83 fSeconds = fMinute = fHour = 0; 84 fTimeStr[0] = 0; 85 fDateStr[0] = 0; 86 fLastTimeStr[0] = 0; 87 fLastDateStr[0] = 0; 88 fNeedToUpdate = true; 89 90 fLocale = *BLocale::Default(); 91 } 92 93 94 #ifdef AS_REPLICANT 95 TTimeView::TTimeView(BMessage* data) 96 : BView(data) 97 { 98 fTime = fLastTime = time(NULL); 99 data->FindBool("seconds", &fShowSeconds); 100 101 fLocale = *BLocale::Default(); 102 } 103 #endif 104 105 106 TTimeView::~TTimeView() 107 { 108 } 109 110 111 #ifdef AS_REPLICANT 112 BArchivable* 113 TTimeView::Instantiate(BMessage* data) 114 { 115 if (!validate_instantiation(data, "TTimeView")) 116 return NULL; 117 118 return new TTimeView(data); 119 } 120 121 122 status_t 123 TTimeView::Archive(BMessage* data, bool deep) const 124 { 125 BView::Archive(data, deep); 126 data->AddBool("seconds", fShowSeconds); 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 fTime = time(NULL); 138 139 SetFont(be_plain_font); 140 if (Parent()) { 141 fParent = Parent(); 142 SetViewColor(Parent()->ViewColor()); 143 } else 144 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 145 146 ResizeToPreferred(); 147 CalculateTextPlacement(); 148 } 149 150 151 void 152 TTimeView::GetPreferredSize(float* width, float* height) 153 { 154 *height = fHeight; 155 156 GetCurrentTime(); 157 158 // TODO: SetOrientation never gets called, fix that when in vertical mode, 159 // we want to limit the width so that it can't overlap the bevels in the 160 // parent view. 161 *width = fOrientation ? 162 min_c(fMaxWidth - kHMargin, kHMargin + StringWidth(fTimeStr)) 163 : kHMargin + StringWidth(fTimeStr); 164 } 165 166 167 void 168 TTimeView::ResizeToPreferred() 169 { 170 float width, height; 171 float oldWidth = Bounds().Width(), oldHeight = Bounds().Height(); 172 173 GetPreferredSize(&width, &height); 174 if (height != oldHeight || width != oldWidth) { 175 ResizeTo(width, height); 176 MoveBy(oldWidth - width, 0); 177 fNeedToUpdate = true; 178 } 179 } 180 181 182 void 183 TTimeView::FrameMoved(BPoint) 184 { 185 Update(); 186 } 187 188 189 void 190 TTimeView::MessageReceived(BMessage* message) 191 { 192 switch (message->what) { 193 case kShowSeconds: 194 ShowSeconds(!ShowingSeconds()); 195 break; 196 197 case B_LOCALE_CHANGED: 198 Update(); 199 break; 200 201 case kChangeClock: 202 // launch the time prefs app 203 be_roster->Launch("application/x-vnd.Haiku-Time"); 204 break; 205 206 case 'time': 207 Window()->PostMessage(message, Parent()); 208 break; 209 210 case kShowCalendar: 211 { 212 BRect bounds(Bounds()); 213 BPoint center(bounds.LeftTop()); 214 center += BPoint(bounds.Width() / 2, bounds.Height() / 2); 215 ShowCalendar(center); 216 break; 217 } 218 219 default: 220 BView::MessageReceived(message); 221 break; 222 } 223 } 224 225 226 void 227 TTimeView::ShowCalendar(BPoint where) 228 { 229 if (fCalendarWindow.IsValid()) { 230 // If the calendar is already shown, just activate it 231 BMessage activate(B_SET_PROPERTY); 232 activate.AddSpecifier("Active"); 233 activate.AddBool("data", true); 234 235 if (fCalendarWindow.SendMessage(&activate) == B_OK) 236 return; 237 } 238 239 where.y = Bounds().bottom + 4.0; 240 ConvertToScreen(&where); 241 242 if (where.y >= BScreen().Frame().bottom) 243 where.y -= (Bounds().Height() + 4.0); 244 245 CalendarMenuWindow* window = new CalendarMenuWindow(where); 246 fCalendarWindow = BMessenger(window); 247 248 window->Show(); 249 } 250 251 252 void 253 TTimeView::GetCurrentTime() 254 { 255 fLocale.FormatTime(fTimeStr, 64, fTime, 256 fShowSeconds ? B_MEDIUM_TIME_FORMAT : B_SHORT_TIME_FORMAT); 257 } 258 259 260 void 261 TTimeView::GetCurrentDate() 262 { 263 char tmp[64]; 264 265 fLocale.FormatDate(tmp, 64, fTime, B_FULL_DATE_FORMAT); 266 267 // remove leading 0 from date when month is less than 10 (MM/DD/YY) 268 // or remove leading 0 from date when day is less than 10 (DD/MM/YY) 269 const char* str = tmp; 270 if (str[0] == '0') 271 str++; 272 273 strlcpy(fDateStr, str, sizeof(fDateStr)); 274 } 275 276 277 void 278 TTimeView::Draw(BRect /*updateRect*/) 279 { 280 PushState(); 281 282 SetHighColor(ViewColor()); 283 SetLowColor(ViewColor()); 284 FillRect(Bounds()); 285 SetHighColor(0, 0, 0, 255); 286 287 DrawString(fTimeStr, fTimeLocation); 288 289 PopState(); 290 } 291 292 293 void 294 TTimeView::MouseDown(BPoint point) 295 { 296 uint32 buttons; 297 298 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 299 if (buttons == B_SECONDARY_MOUSE_BUTTON) { 300 ShowClockOptions(ConvertToScreen(point)); 301 return; 302 } else if (buttons == B_PRIMARY_MOUSE_BUTTON) 303 ShowCalendar(point); 304 305 // invalidate last time/date strings and call the pulse 306 // method directly to change the display instantly 307 fLastDateStr[0] = '\0'; 308 fLastTimeStr[0] = '\0'; 309 Pulse(); 310 } 311 312 313 void 314 TTimeView::Pulse() 315 { 316 time_t curTime = time(NULL); 317 tm* ct = localtime(&curTime); 318 if (ct == NULL) 319 return; 320 321 fTime = curTime; 322 323 GetCurrentTime(); 324 GetCurrentDate(); 325 if (strcmp(fTimeStr, fLastTimeStr) != 0) { 326 // Update bounds when the size of the strings has changed 327 // For dates, Update() could be called two times in a row, 328 // but that should only happen very rarely 329 if ((fLastTimeStr[1] != fTimeStr[1] 330 && (fLastTimeStr[1] == ':' || fTimeStr[1] == ':')) 331 || !fLastTimeStr[0]) 332 Update(); 333 334 strlcpy(fLastTimeStr, fTimeStr, sizeof(fLastTimeStr)); 335 fNeedToUpdate = true; 336 } 337 338 // Update the tooltip if the date has changed 339 if (strcmp(fDateStr, fLastDateStr) != 0) { 340 strlcpy(fLastDateStr, fDateStr, sizeof(fLastDateStr)); 341 SetToolTip(fDateStr); 342 } 343 344 if (fNeedToUpdate) { 345 fSeconds = ct->tm_sec; 346 fMinute = ct->tm_min; 347 fHour = ct->tm_hour; 348 349 Draw(Bounds()); 350 fNeedToUpdate = false; 351 } 352 } 353 354 355 void 356 TTimeView::ShowSeconds(bool on) 357 { 358 fShowSeconds = on; 359 Update(); 360 } 361 362 363 void 364 TTimeView::Update() 365 { 366 fLocale = *BLocale::Default(); 367 GetCurrentTime(); 368 GetCurrentDate(); 369 370 SetToolTip(fDateStr); 371 372 ResizeToPreferred(); 373 CalculateTextPlacement(); 374 375 if (fParent) { 376 BMessage reformat('Trfm'); 377 fParent->MessageReceived(&reformat); 378 // time string format realign 379 fParent->Invalidate(); 380 } 381 } 382 383 384 void 385 TTimeView::SetOrientation(bool o) 386 { 387 fOrientation = o; 388 CalculateTextPlacement(); 389 Invalidate(); 390 } 391 392 393 void 394 TTimeView::CalculateTextPlacement() 395 { 396 BRect bounds(Bounds()); 397 398 fDateLocation.x = 0.0; 399 fTimeLocation.x = 0.0; 400 401 BFont font; 402 GetFont(&font); 403 const char* stringArray[1]; 404 stringArray[0] = fTimeStr; 405 BRect rectArray[1]; 406 escapement_delta delta = { 0.0, 0.0 }; 407 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 408 rectArray); 409 410 fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height() 411 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 412 } 413 414 415 void 416 TTimeView::ShowClockOptions(BPoint point) 417 { 418 BPopUpMenu* menu = new BPopUpMenu("", false, false); 419 menu->SetFont(be_plain_font); 420 BMenuItem* item; 421 422 item = new BMenuItem(B_TRANSLATE("Change time" B_UTF8_ELLIPSIS), 423 new BMessage(kChangeClock)); 424 menu->AddItem(item); 425 426 item = new BMenuItem(B_TRANSLATE("Hide time"), new BMessage('time')); 427 menu->AddItem(item); 428 429 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 430 new BMessage(kShowCalendar)); 431 menu->AddItem(item); 432 433 menu->SetTargetForItems(this); 434 // Changed to accept screen coord system point; 435 // not constrained to this view now 436 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 437 point.x + 4, point.y +4), true); 438 } 439 440