xref: /haiku/src/apps/deskbar/TimeView.cpp (revision cfc3fa87da824bdf593eb8b817a83b6376e77935)
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 #ifdef _SHOW_CALENDAR_MENU_ITEM
39 #	include "CalendarMenuItem.h"
40 #endif
41 
42 #include <Debug.h>
43 #include <MenuItem.h>
44 #include <PopUpMenu.h>
45 #include <Roster.h>
46 #include <Window.h>
47 #include <Screen.h>
48 
49 #include <string.h>
50 
51 
52 const char *kShortDateFormat = "%m/%d/%y";
53 const char *kShortEuroDateFormat = "%d/%m/%y";
54 const char *kLongDateFormat = "%a, %B %d, %Y";
55 const char *kLongEuroDateFormat = "%a, %d %B, %Y";
56 
57 static const char * const kMinString = "99:99 AM";
58 
59 
60 static float
61 FontHeight(BView *target, bool full)
62 {
63 	font_height fontInfo;
64 	target->GetFontHeight(&fontInfo);
65 	float h = fontInfo.ascent + fontInfo.descent;
66 
67 	if (full)
68 		h += fontInfo.leading;
69 
70 	return h;
71 }
72 
73 
74 enum {
75 	kMsgShowClock,
76 	kMsgChangeClock,
77 	kMsgHide
78 };
79 
80 
81 TTimeView::TTimeView(bool showSeconds, bool milTime, bool fullDate, bool euroDate, bool)
82 	: 	BView(BRect(-100,-100,-90,-90), "_deskbar_tv_",
83 	B_FOLLOW_RIGHT | B_FOLLOW_TOP,
84 	B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS),
85 	fParent(NULL),
86 	fShowInterval(true), // ToDo: defaulting this to true until UI is in place
87 	fShowSeconds(showSeconds),
88 	fMilTime(milTime),
89 	fFullDate(fullDate),
90 	fCanShowFullDate(false),
91 	fEuroDate(euroDate),
92 	fOrientation(false)
93 {
94 	fShowingDate = false;
95 	fTime = fLastTime = time(NULL);
96 	fSeconds = fMinute = fHour = 0;
97 	fLastTimeStr[0] = 0;
98 	fLastDateStr[0] = 0;
99 	fNeedToUpdate = true;
100 }
101 
102 
103 #ifdef AS_REPLICANT
104 TTimeView::TTimeView(BMessage *data)
105 	: BView(data)
106 {
107 	fTime = fLastTime = time(NULL);
108 	data->FindBool("seconds", &fShowSeconds);
109 	data->FindBool("miltime", &fMilTime);
110 	data->FindBool("fulldate", &fFullDate);
111 	data->FindBool("eurodate", &fEuroDate);
112 	data->FindBool("interval", &fInterval);
113 	fShowingDate = false;
114 }
115 #endif
116 
117 
118 TTimeView::~TTimeView()
119 {
120 }
121 
122 
123 #ifdef AS_REPLICANT
124 BArchivable*
125 TTimeView::Instantiate(BMessage *data)
126 {
127 	if (!validate_instantiation(data, "TTimeView"))
128 		return NULL;
129 
130 	return new TTimeView(data);
131 }
132 
133 
134 status_t
135 TTimeView::Archive(BMessage *data, bool deep) const
136 {
137 	BView::Archive(data, deep);
138 	data->AddBool("seconds", fShowSeconds);
139 	data->AddBool("miltime", fMilTime);
140 	data->AddBool("fulldate", fFullDate);
141 	data->AddBool("eurodate", fEuroDate);
142 	data->AddBool("interval", fInterval);
143 	data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT);
144 
145 	return B_OK;
146 }
147 #endif
148 
149 
150 void
151 TTimeView::AttachedToWindow()
152 {
153 	fTime = time(NULL);
154 
155 	SetFont(be_plain_font);
156 	if (Parent()) {
157 		fParent = Parent();
158 		SetViewColor(Parent()->ViewColor());
159 	} else
160 		SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
161 
162 	fFontHeight = FontHeight(this, true);
163 	ResizeToPreferred();
164 	CalculateTextPlacement();
165 }
166 
167 
168 void
169 TTimeView::GetPreferredSize(float *width, float *height)
170 {
171 	*height = fFontHeight;
172 
173 	GetCurrentTime();
174 	GetCurrentDate();
175 
176 	if (ShowingDate())
177 		*width = 6 + StringWidth(fDateStr);
178 	else {
179 		*width = 6 + StringWidth(fTimeStr);
180 		// Changed this from 10 to 6 so even with interval + seconds, there still
181 		// is room for two replicants in the default tray.
182 	}
183 }
184 
185 
186 void
187 TTimeView::ResizeToPreferred()
188 {
189 	float width, height;
190 	float oldWidth = Bounds().Width(), oldHeight = Bounds().Height();
191 
192 	GetPreferredSize(&width, &height);
193 	if (height != oldHeight || width != oldWidth) {
194 		ResizeTo(width, height);
195 		MoveBy(oldWidth - width, 0);
196 		fNeedToUpdate = true;
197 	}
198 }
199 
200 
201 void
202 TTimeView::FrameMoved(BPoint)
203 {
204 	Update();
205 }
206 
207 
208 void
209 TTimeView::MessageReceived(BMessage* message)
210 {
211 	switch (message->what) {
212 		case kMsgFullDate:
213 			ShowFullDate(!ShowingFullDate());
214 			break;
215 
216 		case kMsgShowSeconds:
217 			ShowSeconds(!ShowingSeconds());
218 			break;
219 
220 		case kMsgMilTime:
221 			ShowMilTime(!ShowingMilTime());
222 			break;
223 
224 		case kMsgEuroDate:
225 			ShowEuroDate(!ShowingEuroDate());
226 			break;
227 
228 		case kMsgChangeClock:
229 			// launch the time prefs app
230 			be_roster->Launch("application/x-vnd.Haiku-Time");
231 			break;
232 
233 		case 'time':
234 			Window()->PostMessage(message, Parent());
235 			break;
236 
237 		default:
238 			BView::MessageReceived(message);
239 	}
240 }
241 
242 
243 void
244 TTimeView::GetCurrentTime()
245 {
246 	char tmp[64];
247 	tm time = *localtime(&fTime);
248 
249 	if (fMilTime) {
250 		strftime(tmp, 64, fShowSeconds ? "%H:%M:%S" : "%H:%M", &time);
251 	} else {
252 		if (fShowInterval)
253 			strftime(tmp, 64, fShowSeconds ? "%I:%M:%S %p" : "%I:%M %p", &time);
254 		else
255 			strftime(tmp, 64, fShowSeconds ? "%I:%M:%S" : "%I:%M", &time);
256 	}
257 
258 	//	remove leading 0 from time when hour is less than 10
259 	const char *str = tmp;
260 	if (str[0] == '0')
261 		str++;
262 
263 	strcpy(fTimeStr, str);
264 
265 	fSeconds = time.tm_sec;
266 	fMinute = time.tm_min;
267 	fHour = time.tm_hour;
268 }
269 
270 
271 void
272 TTimeView::GetCurrentDate()
273 {
274 	char tmp[64];
275 	tm time = *localtime(&fTime);
276 
277 	if (fFullDate && CanShowFullDate())
278 		strftime(tmp, 64, fEuroDate ? kLongEuroDateFormat : kLongDateFormat, &time);
279 	else
280 		strftime(tmp, 64, fEuroDate ? kShortEuroDateFormat : kShortDateFormat, &time);
281 
282 	//	remove leading 0 from date when month is less than 10 (MM/DD/YY)
283 	//  or remove leading 0 from date when day is less than 10 (DD/MM/YY)
284 	const char* str = tmp;
285 	if (str[0] == '0')
286 		str++;
287 
288 	strcpy(fDateStr, str);
289 }
290 
291 
292 void
293 TTimeView::Draw(BRect /*updateRect*/)
294 {
295 	PushState();
296 
297 	SetHighColor(ViewColor());
298 	SetLowColor(ViewColor());
299 	FillRect(Bounds());
300 	SetHighColor(0, 0, 0, 255);
301 
302 	if (fShowingDate)
303 		DrawString(fDateStr, fDateLocation);
304 	else
305 		DrawString(fTimeStr, fTimeLocation);
306 
307 	PopState();
308 }
309 
310 
311 void
312 TTimeView::MouseDown(BPoint point)
313 {
314 	uint32 buttons;
315 
316 	Window()->CurrentMessage()->FindInt32("buttons", (int32 *)&buttons);
317 	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
318 		ShowClockOptions(ConvertToScreen(point));
319 		return;
320 	}
321 
322 	//	flip to/from showing date or time
323 	fShowingDate = !fShowingDate;
324 	if (fShowingDate)
325 		fLastTime = time(NULL);
326 
327 	// invalidate last time/date strings and call the pulse
328 	// method directly to change the display instantly
329 	fLastDateStr[0] = '\0';
330 	fLastTimeStr[0] = '\0';
331 	Pulse();
332 
333 #ifdef _SHOW_CALENDAR_MENU_ITEM
334 	// see if the user holds down the button long enough to show him the calendar
335 
336 	bigtime_t startTime = system_time();
337 
338 	// use the doubleClickSpeed as a treshold
339 	bigtime_t doubleClickSpeed;
340 	get_click_speed(&doubleClickSpeed);
341 
342 	while (buttons) {
343 		BPoint where;
344 		GetMouse(&where, &buttons, false);
345 
346 		if ((system_time() - startTime) > doubleClickSpeed) {
347 			BPopUpMenu *menu = new BPopUpMenu("", false, false);
348 			menu->SetFont(be_plain_font);
349 
350 			menu->AddItem(new CalendarMenuItem());
351 			menu->ResizeToPreferred();
352 
353 			point = where;
354 			BScreen screen;
355 			where.y = Bounds().bottom + 4;
356 
357 			// make sure the menu is visible at doesn't hide the date
358 			ConvertToScreen(&where);
359 			if (where.y + menu->Bounds().Height() > screen.Frame().bottom)
360 				where.y -= menu->Bounds().Height() + 2 * Bounds().Height();
361 
362 			ConvertToScreen(&point);
363 			menu->Go(where, true, true, BRect(point.x - 4, point.y - 4,
364 				point.x + 4, point.y + 4), true);
365 			return;
366 		}
367 
368 		snooze(15000);
369 	}
370 #endif
371 }
372 
373 
374 void
375 TTimeView::Pulse()
376 {
377 	time_t curTime = time(NULL);
378 	tm	ct = *localtime(&curTime);
379 	fTime = curTime;
380 
381 	GetCurrentTime();
382 	GetCurrentDate();
383 	if ((!fShowingDate && strcmp(fTimeStr, fLastTimeStr) != 0)
384 		|| 	(fShowingDate && strcmp(fDateStr, fLastDateStr) != 0)) {
385 		// Update bounds when the size of the strings has changed
386 		// For dates, Update() could be called two times in a row,
387 		// but that should only happen very rarely
388 		if ((!fShowingDate && fLastTimeStr[1] != fTimeStr[1]
389 				&& (fLastTimeStr[1] == ':' || fTimeStr[1] == ':'))
390 			|| (fShowingDate && strlen(fDateStr) != strlen(fLastDateStr))
391 			|| !fLastTimeStr[0])
392 			Update();
393 
394 		strcpy(fLastTimeStr, fTimeStr);
395 		strcpy(fLastDateStr, fDateStr);
396 		fNeedToUpdate = true;
397 	}
398 
399 	if (fShowingDate && (fLastTime + 5 <= time(NULL))) {
400 		fShowingDate = false;
401 		Update();	// Needs to happen since size can change here
402 	}
403 
404 	if (fNeedToUpdate) {
405 		fSeconds = ct.tm_sec;
406 		fMinute = ct.tm_min;
407 		fHour = ct.tm_hour;
408 		fInterval = ct.tm_hour >= 12;
409 
410 		Draw(Bounds());
411 		fNeedToUpdate = false;
412 	}
413 }
414 
415 
416 void
417 TTimeView::ShowSeconds(bool on)
418 {
419 	fShowSeconds = on;
420 	Update();
421 }
422 
423 
424 void
425 TTimeView::ShowMilTime(bool on)
426 {
427 	fMilTime = on;
428 	Update();
429 }
430 
431 
432 void
433 TTimeView::ShowDate(bool on)
434 {
435 	fShowingDate = on;
436 	Update();
437 }
438 
439 
440 void
441 TTimeView::ShowFullDate(bool on)
442 {
443 	fFullDate = on;
444 	Update();
445 }
446 
447 
448 void
449 TTimeView::ShowEuroDate(bool on)
450 {
451 	fEuroDate = on;
452 	Update();
453 }
454 
455 
456 void
457 TTimeView::AllowFullDate(bool allow)
458 {
459 	fCanShowFullDate = allow;
460 
461 	if (allow != ShowingFullDate())
462 		Update();
463 }
464 
465 
466 void
467 TTimeView::Update()
468 {
469 	GetCurrentTime();
470 	GetCurrentDate();
471 
472 	ResizeToPreferred();
473 	CalculateTextPlacement();
474 
475 	if (fParent) {
476 		BMessage reformat('Trfm');
477 		fParent->MessageReceived(&reformat);
478 			// time string format realign
479 		fParent->Invalidate();
480 	}
481 }
482 
483 
484 void
485 TTimeView::SetOrientation(bool o)
486 {
487 	fOrientation = o;
488 	CalculateTextPlacement();
489 	Invalidate();
490 }
491 
492 
493 void
494 TTimeView::CalculateTextPlacement()
495 {
496 	BRect bounds(Bounds());
497 
498 	if (fOrientation) {		// vertical mode
499 		fDateLocation.x = bounds.Width()/2 - StringWidth(fDateStr)/2;
500 		fTimeLocation.x = bounds.Width()/2 - StringWidth(fTimeStr)/2;
501 	} else {
502 		fTimeLocation.x = bounds.Width() - StringWidth(fTimeStr) - 5;
503 		fDateLocation.x = bounds.Width() - StringWidth(fDateStr) - 5;
504 	}
505 	//	center vertically
506 	fDateLocation.y = fTimeLocation.y = bounds.Height()/2 + fFontHeight/2;
507 }
508 
509 
510 void
511 TTimeView::ShowClockOptions(BPoint point)
512 {
513 	BPopUpMenu *menu = new BPopUpMenu("", false, false);
514 	menu->SetFont(be_plain_font);
515 	BMenuItem *item;
516 
517 	item = new BMenuItem("Change Time" B_UTF8_ELLIPSIS, new BMessage(kMsgChangeClock));
518 	menu->AddItem(item);
519 
520 	item = new BMenuItem("Hide Time", new BMessage('time'));
521 	menu->AddItem(item);
522 
523 	menu->SetTargetForItems(this);
524 	// Changed to accept screen coord system point;
525 	// not constrained to this view now
526 	menu->Go(point, true, true, BRect(point.x - 4, point.y - 4,
527 		point.x + 4, point.y +4), true);
528 }
529