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