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