xref: /haiku/src/apps/deskbar/TimeView.cpp (revision 830f67ef991407f287dbc1238aa5f5906d90c991)
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 
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
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 
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*
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
133 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
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
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
182 TTimeView::FrameMoved(BPoint)
183 {
184 	Update();
185 }
186 
187 
188 void
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 - (kDragRegionWidth + kHMargin) * 2, timeWidth);
203 	} else
204 		*width = fMaxWidth = timeWidth;
205 
206 	*height = fHeight;
207 }
208 
209 
210 void
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
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
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
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
325 TTimeView::ShowSeconds() const
326 {
327 	return fShowSeconds;
328 }
329 
330 
331 void
332 TTimeView::SetShowSeconds(bool show)
333 {
334 	fShowSeconds = show;
335 	UpdateTimeFormat();
336 	Update();
337 }
338 
339 
340 bool
341 TTimeView::ShowDayOfWeek() const
342 {
343 	return fShowDayOfWeek;
344 }
345 
346 
347 void
348 TTimeView::SetShowDayOfWeek(bool show)
349 {
350 	fShowDayOfWeek = show;
351 	UpdateTimeFormat();
352 	Update();
353 }
354 
355 
356 bool
357 TTimeView::ShowTimeZone() const
358 {
359 	return fShowTimeZone;
360 }
361 
362 
363 void
364 TTimeView::SetShowTimeZone(bool show)
365 {
366 	fShowTimeZone = show;
367 	UpdateTimeFormat();
368 	Update();
369 }
370 
371 
372 void
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
398 TTimeView::IsShowingCalendar()
399 {
400 	return fCalendarWindow != NULL && !fCalendarWindow->IsHidden();
401 }
402 
403 
404 //	# pragma mark - Private methods
405 
406 
407 void
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
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
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
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
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
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
530 TTimeView::Vertical()
531 {
532 	if (fBarView == NULL)
533 		return true;
534 
535 	return fBarView->Vertical();
536 }
537