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 <Country.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 fLastTimeStr[0] = 0; 85 fLastDateStr[0] = 0; 86 fNeedToUpdate = true; 87 88 fLocale = *be_locale; 89 } 90 91 92 #ifdef AS_REPLICANT 93 TTimeView::TTimeView(BMessage* data) 94 : BView(data) 95 { 96 fTime = fLastTime = time(NULL); 97 data->FindBool("seconds", &fShowSeconds); 98 data->FindBool("interval", &fInterval); 99 100 fLocale = *be_locale; 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->AddBool("interval", fInterval); 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 159 // When in vertical mode, we want to limit the width so that it can't 160 // overlap the bevels in the 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 } 222 } 223 224 225 void 226 TTimeView::ShowCalendar(BPoint where) 227 { 228 if (fCalendarWindow.IsValid()) { 229 // If the calendar is already shown, just activate it 230 BMessage activate(B_SET_PROPERTY); 231 activate.AddSpecifier("Active"); 232 activate.AddBool("data", true); 233 234 if (fCalendarWindow.SendMessage(&activate) == B_OK) 235 return; 236 } 237 238 where.y = Bounds().bottom + 4.0; 239 ConvertToScreen(&where); 240 241 if (where.y >= BScreen().Frame().bottom) 242 where.y -= (Bounds().Height() + 4.0); 243 244 CalendarMenuWindow* window = new CalendarMenuWindow(where); 245 fCalendarWindow = BMessenger(window); 246 247 window->Show(); 248 } 249 250 251 void 252 TTimeView::GetCurrentTime() 253 { 254 fLocale.FormatTime(fTimeStr, 64, fTime, fShowSeconds); 255 } 256 257 258 void 259 TTimeView::GetCurrentDate() 260 { 261 char tmp[64]; 262 263 fLocale.FormatDate(tmp, 64, fTime, true); 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 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