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 strlcpy(fLastDateStr, fDateStr, sizeof(fLastDateStr)); 336 fNeedToUpdate = true; 337 } 338 339 if (fNeedToUpdate) { 340 fSeconds = ct->tm_sec; 341 fMinute = ct->tm_min; 342 fHour = ct->tm_hour; 343 344 Draw(Bounds()); 345 fNeedToUpdate = false; 346 } 347 } 348 349 350 void 351 TTimeView::ShowSeconds(bool on) 352 { 353 fShowSeconds = on; 354 Update(); 355 } 356 357 358 void 359 TTimeView::Update() 360 { 361 fLocale = *BLocale::Default(); 362 GetCurrentTime(); 363 GetCurrentDate(); 364 365 SetToolTip(fDateStr); 366 367 ResizeToPreferred(); 368 CalculateTextPlacement(); 369 370 if (fParent) { 371 BMessage reformat('Trfm'); 372 fParent->MessageReceived(&reformat); 373 // time string format realign 374 fParent->Invalidate(); 375 } 376 } 377 378 379 void 380 TTimeView::SetOrientation(bool o) 381 { 382 fOrientation = o; 383 CalculateTextPlacement(); 384 Invalidate(); 385 } 386 387 388 void 389 TTimeView::CalculateTextPlacement() 390 { 391 BRect bounds(Bounds()); 392 393 fDateLocation.x = 0.0; 394 fTimeLocation.x = 0.0; 395 396 BFont font; 397 GetFont(&font); 398 const char* stringArray[1]; 399 stringArray[0] = fTimeStr; 400 BRect rectArray[1]; 401 escapement_delta delta = { 0.0, 0.0 }; 402 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta, 403 rectArray); 404 405 fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height() 406 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top); 407 } 408 409 410 void 411 TTimeView::ShowClockOptions(BPoint point) 412 { 413 BPopUpMenu* menu = new BPopUpMenu("", false, false); 414 menu->SetFont(be_plain_font); 415 BMenuItem* item; 416 417 item = new BMenuItem(B_TRANSLATE("Change time" B_UTF8_ELLIPSIS), 418 new BMessage(kChangeClock)); 419 menu->AddItem(item); 420 421 item = new BMenuItem(B_TRANSLATE("Hide time"), new BMessage('time')); 422 menu->AddItem(item); 423 424 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS), 425 new BMessage(kShowCalendar)); 426 menu->AddItem(item); 427 428 menu->SetTargetForItems(this); 429 // Changed to accept screen coord system point; 430 // not constrained to this view now 431 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4, 432 point.x + 4, point.y +4), true); 433 } 434 435