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 <algorithm>
40
41 #include <string.h>
42
43 #include <Application.h>
44 #include <Catalog.h>
45 #include <Debug.h>
46 #include <Locale.h>
47 #include <MenuItem.h>
48 #include <MessageRunner.h>
49 #include <PopUpMenu.h>
50 #include <Roster.h>
51 #include <Screen.h>
52 #include <Window.h>
53
54 #include "BarApp.h"
55 #include "BarView.h"
56 #include "BarWindow.h"
57 #include "StatusView.h"
58 #include "CalendarMenuWindow.h"
59 #include "StatusView.h"
60
61
62 static const float kHMargin = 2.0;
63
64
65 #undef B_TRANSLATION_CONTEXT
66 #define B_TRANSLATION_CONTEXT "TimeView"
67
68
TTimeView(float maxWidth,float height,TBarView * barView)69 TTimeView::TTimeView(float maxWidth, float height, TBarView* barView)
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 fBarView(barView),
75 fParent(NULL),
76 fMaxWidth(maxWidth),
77 fHeight(height),
78 fShowLevel(0),
79 fShowSeconds(false),
80 fShowDayOfWeek(false),
81 fShowTimeZone(false),
82 fCalendarWindow(NULL),
83 fTimeFormat(NULL),
84 fDateFormat(NULL)
85 {
86 fCurrentTime = fLastTime = time(NULL);
87 fSeconds = fMinute = fHour = 0;
88 fCurrentTimeStr[0] = 0;
89 fCurrentDateStr[0] = 0;
90 fLastTimeStr[0] = 0;
91 fLastDateStr[0] = 0;
92 fNeedToUpdate = true;
93 UpdateTimeFormat();
94 }
95
96
97 #ifdef AS_REPLICANT
TTimeView(BMessage * data)98 TTimeView::TTimeView(BMessage* data)
99 : BView(data),
100 fTimeFormat(NULL),
101 fDateFormat(NULL)
102 {
103 fCurrentTime = fLastTime = time(NULL);
104 data->FindBool("seconds", &fShowSeconds);
105
106 UpdateTimeFormat();
107 }
108 #endif
109
110
~TTimeView()111 TTimeView::~TTimeView()
112 {
113 if (fCalendarWindowMessenger.IsValid())
114 fCalendarWindowMessenger.SendMessage(B_QUIT_REQUESTED);
115
116 delete fTimeFormat;
117 delete fDateFormat;
118 }
119
120
121 #ifdef AS_REPLICANT
122 BArchivable*
Instantiate(BMessage * data)123 TTimeView::Instantiate(BMessage* data)
124 {
125 if (!validate_instantiation(data, "TTimeView"))
126 return NULL;
127
128 return new TTimeView(data);
129 }
130
131
132 status_t
Archive(BMessage * data,bool deep) const133 TTimeView::Archive(BMessage* data, bool deep) const
134 {
135 BView::Archive(data, deep);
136 data->AddBool("orientation", Vertical());
137 data->AddInt16("showLevel", fShowLevel);
138 data->AddBool("showSeconds", fShowSeconds);
139 data->AddBool("showDayOfWeek", fShowDayOfWeek);
140 data->AddBool("showTimeZone", fShowTimeZone);
141 data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT);
142
143 return B_OK;
144 }
145 #endif
146
147
148 void
AttachedToWindow()149 TTimeView::AttachedToWindow()
150 {
151 fCurrentTime = time(NULL);
152
153 SetFont(be_plain_font);
154 if (Parent()) {
155 fParent = Parent();
156 AdoptParentColors();
157 } else
158 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
159
160 ResizeToPreferred();
161 CalculateTextPlacement();
162 }
163
164
165 void
Draw(BRect)166 TTimeView::Draw(BRect /*updateRect*/)
167 {
168 PushState();
169
170 SetHighColor(ViewColor());
171 SetLowColor(ViewColor());
172 FillRect(Bounds());
173 SetHighUIColor(B_MENU_ITEM_TEXT_COLOR);
174
175 DrawString(fCurrentTimeStr, fTimeLocation);
176
177 PopState();
178 }
179
180
181 void
FrameMoved(BPoint)182 TTimeView::FrameMoved(BPoint)
183 {
184 Update();
185 }
186
187
188 void
GetPreferredSize(float * width,float * height)189 TTimeView::GetPreferredSize(float* width, float* height)
190 {
191 float timeWidth = StringWidth(fCurrentTimeStr);
192
193 // set the height based on the font size
194 font_height fontHeight;
195 GetFontHeight(&fontHeight);
196 fHeight = fontHeight.ascent + fontHeight.descent - 2;
197 // reduce height by 2px so that clock doesn't draw on top of border
198
199 if (Vertical()) {
200 float appWidth = static_cast<TBarApp*>(be_app)->Settings()->width;
201 *width = fMaxWidth
202 = std::min(appWidth - (gDragRegionWidth + kHMargin) * 2, timeWidth);
203 } else
204 *width = fMaxWidth = timeWidth;
205
206 *height = fHeight;
207 }
208
209
210 void
MessageReceived(BMessage * message)211 TTimeView::MessageReceived(BMessage* message)
212 {
213 switch (message->what) {
214 case kChangeTime:
215 {
216 // launch the time prefs app
217 be_roster->Launch("application/x-vnd.Haiku-Time");
218 // tell Time preflet to switch to the clock tab
219 BMessenger messenger("application/x-vnd.Haiku-Time");
220 BMessage switchToClock('SlCk');
221 messenger.SendMessage(&switchToClock);
222 break;
223 }
224
225 case kShowHideTime:
226 {
227 be_app->MessageReceived(message);
228 break;
229 }
230
231 case kShowCalendar:
232 {
233 BRect bounds(Bounds());
234 BPoint center(bounds.LeftTop());
235 center += BPoint(bounds.Width() / 2, bounds.Height() / 2);
236 ShowCalendar(center);
237 break;
238 }
239
240 default:
241 BView::MessageReceived(message);
242 break;
243 }
244 }
245
246
247 void
MouseDown(BPoint point)248 TTimeView::MouseDown(BPoint point)
249 {
250 uint32 buttons;
251
252 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
253 if (buttons == B_SECONDARY_MOUSE_BUTTON) {
254 ShowTimeOptions(ConvertToScreen(point));
255 return;
256 } else if (buttons == B_PRIMARY_MOUSE_BUTTON)
257 ShowCalendar(point);
258
259 // invalidate last time/date strings and call the pulse
260 // method directly to change the display instantly
261 fLastDateStr[0] = '\0';
262 fLastTimeStr[0] = '\0';
263 Pulse();
264 }
265
266
267 void
Pulse()268 TTimeView::Pulse()
269 {
270 time_t curTime = time(NULL);
271 tm* ct = localtime(&curTime);
272 if (ct == NULL)
273 return;
274
275 fCurrentTime = curTime;
276
277 GetCurrentTime();
278 GetCurrentDate();
279 if (strcmp(fCurrentTimeStr, fLastTimeStr) != 0) {
280 // Update bounds when the size of the strings has changed
281 Update();
282
283 strlcpy(fLastTimeStr, fCurrentTimeStr, sizeof(fLastTimeStr));
284 fNeedToUpdate = true;
285 }
286
287 // Update the tooltip if the date has changed
288 if (strcmp(fCurrentDateStr, fLastDateStr) != 0) {
289 strlcpy(fLastDateStr, fCurrentDateStr, sizeof(fLastDateStr));
290 SetToolTip(fCurrentDateStr);
291 }
292
293 if (fNeedToUpdate) {
294 fSeconds = ct->tm_sec;
295 fMinute = ct->tm_min;
296 fHour = ct->tm_hour;
297
298 Draw(Bounds());
299 fNeedToUpdate = false;
300 }
301 }
302
303
304 void
ResizeToPreferred()305 TTimeView::ResizeToPreferred()
306 {
307 float width;
308 float height;
309 float oldWidth = Bounds().Width();
310 float oldHeight = Bounds().Height();
311
312 GetPreferredSize(&width, &height);
313 if (height != oldHeight || width != oldWidth) {
314 ResizeTo(width, height);
315 MoveBy(oldWidth - width, 0);
316 fNeedToUpdate = true;
317 }
318 }
319
320
321 // # pragma mark - Public methods
322
323
324 bool
ShowSeconds() const325 TTimeView::ShowSeconds() const
326 {
327 return fShowSeconds;
328 }
329
330
331 void
SetShowSeconds(bool show)332 TTimeView::SetShowSeconds(bool show)
333 {
334 fShowSeconds = show;
335 UpdateTimeFormat();
336 Update();
337 }
338
339
340 bool
ShowDayOfWeek() const341 TTimeView::ShowDayOfWeek() const
342 {
343 return fShowDayOfWeek;
344 }
345
346
347 void
SetShowDayOfWeek(bool show)348 TTimeView::SetShowDayOfWeek(bool show)
349 {
350 fShowDayOfWeek = show;
351 UpdateTimeFormat();
352 Update();
353 }
354
355
356 bool
ShowTimeZone() const357 TTimeView::ShowTimeZone() const
358 {
359 return fShowTimeZone;
360 }
361
362
363 void
SetShowTimeZone(bool show)364 TTimeView::SetShowTimeZone(bool show)
365 {
366 fShowTimeZone = show;
367 UpdateTimeFormat();
368 Update();
369 }
370
371
372 void
ShowCalendar(BPoint where)373 TTimeView::ShowCalendar(BPoint where)
374 {
375 if (fCalendarWindowMessenger.IsValid()) {
376 // If the calendar is already shown, just activate it
377 BMessage activate(B_SET_PROPERTY);
378 activate.AddSpecifier("Active");
379 activate.AddBool("data", true);
380
381 if (fCalendarWindowMessenger.SendMessage(&activate) == B_OK)
382 return;
383 }
384
385 where.y = Bounds().bottom + 4.0;
386 ConvertToScreen(&where);
387
388 if (where.y >= BScreen().Frame().bottom)
389 where.y -= (Bounds().Height() + 4.0);
390
391 fCalendarWindow = new CalendarMenuWindow(where);
392 fCalendarWindowMessenger = BMessenger(fCalendarWindow);
393 fCalendarWindow->Show();
394 }
395
396
397 bool
IsShowingCalendar()398 TTimeView::IsShowingCalendar()
399 {
400 return fCalendarWindow != NULL && !fCalendarWindow->IsHidden();
401 }
402
403
404 // # pragma mark - Private methods
405
406
407 void
UpdateTimeFormat()408 TTimeView::UpdateTimeFormat()
409 {
410 int32 fields = B_DATE_ELEMENT_HOUR | B_DATE_ELEMENT_MINUTE;
411 if (fShowSeconds)
412 fields |= B_DATE_ELEMENT_SECOND;
413 if (fShowDayOfWeek)
414 fields |= B_DATE_ELEMENT_WEEKDAY;
415 if (fShowTimeZone)
416 fields |= B_DATE_ELEMENT_TIMEZONE;
417
418 delete fTimeFormat;
419 fTimeFormat = new BDateTimeFormat(BLocale::Default());
420 fTimeFormat->SetDateTimeFormat(B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT,
421 fields);
422
423 delete fDateFormat;
424 fDateFormat = new BDateFormat(BLocale::Default());
425 }
426
427
428 void
GetCurrentTime()429 TTimeView::GetCurrentTime()
430 {
431 fTimeFormat->Format(fCurrentTimeStr, sizeof(fCurrentTimeStr), fCurrentTime,
432 B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT);
433 }
434
435
436 void
GetCurrentDate()437 TTimeView::GetCurrentDate()
438 {
439 char tmp[sizeof(fCurrentDateStr)];
440
441 fDateFormat->Format(tmp, sizeof(fCurrentDateStr), fCurrentTime,
442 B_FULL_DATE_FORMAT);
443
444 // remove leading 0 from date when month is less than 10 (MM/DD/YY)
445 // or remove leading 0 from date when day is less than 10 (DD/MM/YY)
446 const char* str = tmp;
447 if (str[0] == '0')
448 str++;
449
450 strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr));
451 }
452
453
454 void
CalculateTextPlacement()455 TTimeView::CalculateTextPlacement()
456 {
457 fDateLocation.x = 0.0;
458 fTimeLocation.x = 0.0;
459
460 BFont font;
461 GetFont(&font);
462
463 const char* stringArray[1];
464 stringArray[0] = fCurrentTimeStr;
465 BRect rectArray[1];
466 escapement_delta delta = { 0.0, 0.0 };
467 font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta,
468 rectArray);
469
470 // center vertically
471 fTimeLocation.y = fDateLocation.y = ceilf((Bounds().Height()
472 - rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top);
473
474 if (Vertical()) {
475 float timeWidth = StringWidth(fCurrentTimeStr);
476 if (timeWidth > fMaxWidth) {
477 // time does not fit, push it over to truncate the left side
478 // to see the entire time string you must make the window wider
479 float difference = timeWidth - fMaxWidth;
480 fDateLocation.x -= difference;
481 fTimeLocation.x -= difference;
482 }
483 }
484 }
485
486
487 void
ShowTimeOptions(BPoint point)488 TTimeView::ShowTimeOptions(BPoint point)
489 {
490 BPopUpMenu* menu = new BPopUpMenu("", false, false);
491 menu->SetFont(be_plain_font);
492 BMenuItem* item;
493
494 item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS),
495 new BMessage(kChangeTime));
496 menu->AddItem(item);
497
498 item = new BMenuItem(B_TRANSLATE("Hide clock"),
499 new BMessage(kShowHideTime));
500 menu->AddItem(item);
501
502 item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS),
503 new BMessage(kShowCalendar));
504 menu->AddItem(item);
505
506 menu->SetTargetForItems(this);
507 // Changed to accept screen coord system point;
508 // not constrained to this view now
509 menu->Go(point, true, true, BRect(point.x - 4, point.y - 4,
510 point.x + 4, point.y +4), true);
511 }
512
513
514 void
Update()515 TTimeView::Update()
516 {
517 GetCurrentTime();
518 GetCurrentDate();
519 SetToolTip(fCurrentDateStr);
520
521 ResizeToPreferred();
522 CalculateTextPlacement();
523
524 if (fParent != NULL)
525 fParent->Invalidate();
526 }
527
528
529 bool
Vertical()530 TTimeView::Vertical()
531 {
532 if (fBarView == NULL)
533 return true;
534
535 return fBarView->Vertical();
536 }
537