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 fLastTimeStr[0] = 0; 84 fLastDateStr[0] = 0; 85 fNeedToUpdate = true; 86 87 fLocale = *be_locale; 88 } 89 90 91 #ifdef AS_REPLICANT 92 TTimeView::TTimeView(BMessage* data) 93 : BView(data) 94 { 95 fTime = fLastTime = time(NULL); 96 data->FindBool("seconds", &fShowSeconds); 97 data->FindBool("interval", &fInterval); 98 99 fLocale = *be_locale; 100 } 101 #endif 102 103 104 TTimeView::~TTimeView() 105 { 106 } 107 108 109 #ifdef AS_REPLICANT 110 BArchivable* 111 TTimeView::Instantiate(BMessage* data) 112 { 113 if (!validate_instantiation(data, "TTimeView")) 114 return NULL; 115 116 return new TTimeView(data); 117 } 118 119 120 status_t 121 TTimeView::Archive(BMessage* data, bool deep) const 122 { 123 BView::Archive(data, deep); 124 data->AddBool("seconds", fShowSeconds); 125 data->AddBool("interval", fInterval); 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 strcpy(fDateStr, str); 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 strcpy(fLastTimeStr, fTimeStr); 333 strcpy(fLastDateStr, fDateStr); 334 fNeedToUpdate = true; 335 } 336 337 if (fNeedToUpdate) { 338 fSeconds = ct->tm_sec; 339 fMinute = ct->tm_min; 340 fHour = ct->tm_hour; 341 fInterval = ct->tm_hour >= 12; 342 343 Draw(Bounds()); 344 fNeedToUpdate = false; 345 } 346 } 347 348 349 void 350 TTimeView::ShowSeconds(bool on) 351 { 352 fShowSeconds = on; 353 Update(); 354 } 355 356 357 void 358 TTimeView::Update() 359 { 360 fLocale = *be_locale; 361 GetCurrentTime(); 362 GetCurrentDate(); 363 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