xref: /haiku/src/apps/deskbar/TimeView.cpp (revision 675ffabd70492a962f8c0288a32208c22ce5de18)
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 	kLongClick,
63 	kShowCalendar
64 };
65 
66 
67 #undef B_TRANSLATE_CONTEXT
68 #define B_TRANSLATE_CONTEXT "TimeView"
69 
70 TTimeView::TTimeView(float maxWidth, float height, bool showSeconds,
71 	bool fullDate, bool)
72 	:
73 	BView(BRect(-100, -100, -90, -90), "_deskbar_tv_",
74 	B_FOLLOW_RIGHT | B_FOLLOW_TOP,
75 	B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS),
76 	fParent(NULL),
77 	fShowInterval(true), // ToDo: defaulting this to true until UI is in place
78 	fShowSeconds(showSeconds),
79 	fFullDate(fullDate),
80 	fCanShowFullDate(false),
81 	fMaxWidth(maxWidth),
82 	fHeight(height),
83 	fOrientation(true),
84 	fLongClickMessageRunner(NULL)
85 {
86 	fShowingDate = false;
87 	fTime = fLastTime = time(NULL);
88 	fSeconds = fMinute = fHour = 0;
89 	fLastTimeStr[0] = 0;
90 	fLastDateStr[0] = 0;
91 	fNeedToUpdate = true;
92 
93 	fLocale = *be_locale;
94 }
95 
96 
97 #ifdef AS_REPLICANT
98 TTimeView::TTimeView(BMessage* data)
99 	: BView(data)
100 {
101 	fTime = fLastTime = time(NULL);
102 	data->FindBool("seconds", &fShowSeconds);
103 	data->FindBool("fulldate", &fFullDate);
104 	data->FindBool("interval", &fInterval);
105 	fShowingDate = false;
106 
107 	fLocale = *be_locale;
108 }
109 #endif
110 
111 
112 TTimeView::~TTimeView()
113 {
114 	StopLongClickNotifier();
115 }
116 
117 
118 #ifdef AS_REPLICANT
119 BArchivable*
120 TTimeView::Instantiate(BMessage* data)
121 {
122 	if (!validate_instantiation(data, "TTimeView"))
123 		return NULL;
124 
125 	return new TTimeView(data);
126 }
127 
128 
129 status_t
130 TTimeView::Archive(BMessage* data, bool deep) const
131 {
132 	BView::Archive(data, deep);
133 	data->AddBool("seconds", fShowSeconds);
134 	data->AddBool("fulldate", fFullDate);
135 	data->AddBool("interval", fInterval);
136 	data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT);
137 
138 	return B_OK;
139 }
140 #endif
141 
142 
143 void
144 TTimeView::AttachedToWindow()
145 {
146 	fTime = time(NULL);
147 
148 	SetFont(be_plain_font);
149 	if (Parent()) {
150 		fParent = Parent();
151 		SetViewColor(Parent()->ViewColor());
152 	} else
153 		SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
154 
155 	ResizeToPreferred();
156 	CalculateTextPlacement();
157 }
158 
159 
160 void
161 TTimeView::GetPreferredSize(float* width, float* height)
162 {
163 	*height = fHeight;
164 
165 	GetCurrentTime();
166 	GetCurrentDate();
167 
168 	// TODO: SetOrientation never gets called, fix that
169 	// When in vertical mode, we want to limit the width so that it can't
170 	// overlap the bevels in the parent view.
171 	if (ShowingDate())
172 		*width = fOrientation ?
173 			min_c(fMaxWidth - kHMargin, kHMargin + StringWidth(fDateStr))
174 			: kHMargin + StringWidth(fDateStr);
175 	else {
176 		*width = fOrientation ?
177 			min_c(fMaxWidth - kHMargin, kHMargin + StringWidth(fTimeStr))
178 			: kHMargin + StringWidth(fTimeStr);
179 	}
180 }
181 
182 
183 void
184 TTimeView::ResizeToPreferred()
185 {
186 	float width, height;
187 	float oldWidth = Bounds().Width(), oldHeight = Bounds().Height();
188 
189 	GetPreferredSize(&width, &height);
190 	if (height != oldHeight || width != oldWidth) {
191 		ResizeTo(width, height);
192 		MoveBy(oldWidth - width, 0);
193 		fNeedToUpdate = true;
194 	}
195 }
196 
197 
198 void
199 TTimeView::FrameMoved(BPoint)
200 {
201 	Update();
202 }
203 
204 
205 void
206 TTimeView::MessageReceived(BMessage* message)
207 {
208 	switch (message->what) {
209 		case kFullDate:
210 			ShowFullDate(!ShowingFullDate());
211 			break;
212 
213 		case kShowSeconds:
214 			ShowSeconds(!ShowingSeconds());
215 			break;
216 
217 		case B_LOCALE_CHANGED:
218 			Update();
219 			break;
220 
221 		case kChangeClock:
222 			// launch the time prefs app
223 			be_roster->Launch("application/x-vnd.Haiku-Time");
224 			break;
225 
226 		case 'time':
227 			Window()->PostMessage(message, Parent());
228 			break;
229 
230 		case kLongClick:
231 		{
232 			StopLongClickNotifier();
233 			BPoint where;
234 			message->FindPoint("where", &where);
235 			ShowCalendar(where);
236 			break;
237 		}
238 
239 		case kShowCalendar:
240 		{
241 			BRect bounds(Bounds());
242 			BPoint center(bounds.LeftTop());
243 			center += BPoint(bounds.Width() / 2, bounds.Height() / 2);
244 			ShowCalendar(center);
245 			break;
246 		}
247 
248 		default:
249 			BView::MessageReceived(message);
250 	}
251 }
252 
253 
254 void
255 TTimeView::ShowCalendar(BPoint where)
256 {
257 	if (fCalendarWindow.IsValid()) {
258 		// If the calendar is already shown, just activate it
259 		BMessage activate(B_SET_PROPERTY);
260 		activate.AddSpecifier("Active");
261 		activate.AddBool("data", true);
262 
263 		if (fCalendarWindow.SendMessage(&activate) == B_OK)
264 			return;
265 	}
266 
267 	where.y = Bounds().bottom + 4.0;
268 	ConvertToScreen(&where);
269 
270 	if (where.y >= BScreen().Frame().bottom)
271 		where.y -= (Bounds().Height() + 4.0);
272 
273 	CalendarMenuWindow* window = new CalendarMenuWindow(where);
274 	fCalendarWindow = BMessenger(window);
275 
276 	window->Show();
277 }
278 
279 
280 void
281 TTimeView::StartLongClickNotifier(BPoint where)
282 {
283 	StopLongClickNotifier();
284 
285 	BMessage longClickMessage(kLongClick);
286 	longClickMessage.AddPoint("where", where);
287 
288 	bigtime_t longClickThreshold;
289 	get_click_speed(&longClickThreshold);
290 		// use the doubleClickSpeed as a threshold
291 
292 	fLongClickMessageRunner = new BMessageRunner(BMessenger(this),
293 		&longClickMessage, longClickThreshold, 1);
294 }
295 
296 
297 void
298 TTimeView::StopLongClickNotifier()
299 {
300 	delete fLongClickMessageRunner;
301 	fLongClickMessageRunner = NULL;
302 }
303 
304 
305 void
306 TTimeView::GetCurrentTime()
307 {
308 	fLocale.FormatTime(fTimeStr, 64, fTime, fShowSeconds);
309 }
310 
311 
312 void
313 TTimeView::GetCurrentDate()
314 {
315 	char tmp[64];
316 
317 	fLocale.FormatDate(tmp, 64, fTime, fFullDate && CanShowFullDate());
318 
319 	//	remove leading 0 from date when month is less than 10 (MM/DD/YY)
320 	//  or remove leading 0 from date when day is less than 10 (DD/MM/YY)
321 	const char* str = tmp;
322 	if (str[0] == '0')
323 		str++;
324 
325 	strcpy(fDateStr, str);
326 }
327 
328 
329 void
330 TTimeView::Draw(BRect /*updateRect*/)
331 {
332 	PushState();
333 
334 	SetHighColor(ViewColor());
335 	SetLowColor(ViewColor());
336 	FillRect(Bounds());
337 	SetHighColor(0, 0, 0, 255);
338 
339 	if (fShowingDate)
340 		DrawString(fDateStr, fDateLocation);
341 	else
342 		DrawString(fTimeStr, fTimeLocation);
343 
344 	PopState();
345 }
346 
347 
348 void
349 TTimeView::MouseDown(BPoint point)
350 {
351 	uint32 buttons;
352 
353 	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
354 	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
355 		ShowClockOptions(ConvertToScreen(point));
356 		return;
357 	} else if (buttons == B_PRIMARY_MOUSE_BUTTON) {
358 		StartLongClickNotifier(point);
359 	}
360 
361 	//	flip to/from showing date or time
362 	fShowingDate = !fShowingDate;
363 	if (fShowingDate)
364 		fLastTime = time(NULL);
365 
366 	// invalidate last time/date strings and call the pulse
367 	// method directly to change the display instantly
368 	fLastDateStr[0] = '\0';
369 	fLastTimeStr[0] = '\0';
370 	Pulse();
371 }
372 
373 
374 void
375 TTimeView::MouseUp(BPoint point)
376 {
377 	StopLongClickNotifier();
378 }
379 
380 
381 void
382 TTimeView::Pulse()
383 {
384 	time_t curTime = time(NULL);
385 	tm* ct = localtime(&curTime);
386 	if (ct == NULL)
387 		return;
388 
389 	fTime = curTime;
390 
391 	GetCurrentTime();
392 	GetCurrentDate();
393 	if ((!fShowingDate && strcmp(fTimeStr, fLastTimeStr) != 0)
394 		|| 	(fShowingDate && strcmp(fDateStr, fLastDateStr) != 0)) {
395 		// Update bounds when the size of the strings has changed
396 		// For dates, Update() could be called two times in a row,
397 		// but that should only happen very rarely
398 		if ((!fShowingDate && fLastTimeStr[1] != fTimeStr[1]
399 				&& (fLastTimeStr[1] == ':' || fTimeStr[1] == ':'))
400 			|| (fShowingDate && strlen(fDateStr) != strlen(fLastDateStr))
401 			|| !fLastTimeStr[0])
402 			Update();
403 
404 		strcpy(fLastTimeStr, fTimeStr);
405 		strcpy(fLastDateStr, fDateStr);
406 		fNeedToUpdate = true;
407 	}
408 
409 	if (fShowingDate && (fLastTime + 5 <= time(NULL))) {
410 		fShowingDate = false;
411 		Update();	// Needs to happen since size can change here
412 	}
413 
414 	if (fNeedToUpdate) {
415 		fSeconds = ct->tm_sec;
416 		fMinute = ct->tm_min;
417 		fHour = ct->tm_hour;
418 		fInterval = ct->tm_hour >= 12;
419 
420 		Draw(Bounds());
421 		fNeedToUpdate = false;
422 	}
423 }
424 
425 
426 void
427 TTimeView::ShowSeconds(bool on)
428 {
429 	fShowSeconds = on;
430 	Update();
431 }
432 
433 
434 void
435 TTimeView::ShowDate(bool on)
436 {
437 	fShowingDate = on;
438 	Update();
439 }
440 
441 
442 void
443 TTimeView::ShowFullDate(bool on)
444 {
445 	fFullDate = on;
446 	Update();
447 }
448 
449 
450 void
451 TTimeView::AllowFullDate(bool allow)
452 {
453 	fCanShowFullDate = allow;
454 
455 	if (allow != ShowingFullDate())
456 		Update();
457 }
458 
459 
460 void
461 TTimeView::Update()
462 {
463 	fLocale = *be_locale;
464 	GetCurrentTime();
465 	GetCurrentDate();
466 
467 	ResizeToPreferred();
468 	CalculateTextPlacement();
469 
470 	if (fParent) {
471 		BMessage reformat('Trfm');
472 		fParent->MessageReceived(&reformat);
473 			// time string format realign
474 		fParent->Invalidate();
475 	}
476 }
477 
478 
479 void
480 TTimeView::SetOrientation(bool o)
481 {
482 	fOrientation = o;
483 	CalculateTextPlacement();
484 	Invalidate();
485 }
486 
487 
488 void
489 TTimeView::CalculateTextPlacement()
490 {
491 	BRect bounds(Bounds());
492 
493 	fDateLocation.x = 0.0;
494 	fTimeLocation.x = 0.0;
495 
496 	BFont font;
497 	GetFont(&font);
498 	const char* stringArray[1];
499 	stringArray[0] = fTimeStr;
500 	BRect rectArray[1];
501 	escapement_delta delta = { 0.0, 0.0 };
502 	font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta,
503 		rectArray);
504 
505 	fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height()
506 		- rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top);
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(B_TRANSLATE("Change time" B_UTF8_ELLIPSIS),
518 		new BMessage(kChangeClock));
519 	menu->AddItem(item);
520 
521 	item = new BMenuItem(B_TRANSLATE("Hide time"), new BMessage('time'));
522 	menu->AddItem(item);
523 
524 	item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS),
525 		new BMessage(kShowCalendar));
526 	menu->AddItem(item);
527 
528 	menu->SetTargetForItems(this);
529 	// Changed to accept screen coord system point;
530 	// not constrained to this view now
531 	menu->Go(point, true, true, BRect(point.x - 4, point.y - 4,
532 		point.x + 4, point.y +4), true);
533 }
534 
535