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