xref: /haiku/src/apps/deskbar/TimeView.cpp (revision e688bf23d48bfd1216a0cacbdbda5e35a1bcd779)
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_TRANSLATION_CONTEXT
67 #define B_TRANSLATION_CONTEXT "TimeView"
68 
69 
70 TTimeView::TTimeView(float maxWidth, float height, bool use24HourClock,
71 	bool showSeconds, bool showDayOfWeek)
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 	fMaxWidth(maxWidth),
78 	fHeight(height),
79 	fOrientation(true),
80 	fUse24HourClock(use24HourClock),
81 	fShowSeconds(showSeconds),
82 	fShowDayOfWeek(showDayOfWeek)
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(ui_color(B_MENU_ITEM_TEXT_COLOR));
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 kChangeTime:
198 			// launch the time prefs app
199 			be_roster->Launch("application/x-vnd.Haiku-Time");
200 			break;
201 
202 		case kShowHideTime:
203 			Window()->PostMessage(message, Parent());
204 			break;
205 
206 		case kShowCalendar:
207 		{
208 			BRect bounds(Bounds());
209 			BPoint center(bounds.LeftTop());
210 			center += BPoint(bounds.Width() / 2, bounds.Height() / 2);
211 			ShowCalendar(center);
212 			break;
213 		}
214 
215 		default:
216 			BView::MessageReceived(message);
217 			break;
218 	}
219 }
220 
221 
222 void
223 TTimeView::MouseDown(BPoint point)
224 {
225 	uint32 buttons;
226 
227 	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
228 	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
229 		ShowTimeOptions(ConvertToScreen(point));
230 		return;
231 	} else if (buttons == B_PRIMARY_MOUSE_BUTTON)
232 		ShowCalendar(point);
233 
234 	// invalidate last time/date strings and call the pulse
235 	// method directly to change the display instantly
236 	fLastDateStr[0] = '\0';
237 	fLastTimeStr[0] = '\0';
238 	Pulse();
239 }
240 
241 
242 void
243 TTimeView::Pulse()
244 {
245 	time_t curTime = time(NULL);
246 	tm* ct = localtime(&curTime);
247 	if (ct == NULL)
248 		return;
249 
250 	fCurrentTime = curTime;
251 
252 	GetCurrentTime();
253 	GetCurrentDate();
254 	if (strcmp(fCurrentTimeStr, fLastTimeStr) != 0) {
255 		// Update bounds when the size of the strings has changed
256 		// For dates, Update() could be called two times in a row,
257 		// but that should only happen very rarely
258 		if ((fLastTimeStr[1] != fCurrentTimeStr[1]
259 			&& (fLastTimeStr[1] == ':' || fCurrentTimeStr[1] == ':'))
260 			|| !fLastTimeStr[0])
261 			Update();
262 
263 		strlcpy(fLastTimeStr, fCurrentTimeStr, sizeof(fLastTimeStr));
264 		fNeedToUpdate = true;
265 	}
266 
267 	// Update the tooltip if the date has changed
268 	if (strcmp(fCurrentDateStr, fLastDateStr) != 0) {
269 		strlcpy(fLastDateStr, fCurrentDateStr, sizeof(fLastDateStr));
270 		SetToolTip(fCurrentDateStr);
271 	}
272 
273 	if (fNeedToUpdate) {
274 		fSeconds = ct->tm_sec;
275 		fMinute = ct->tm_min;
276 		fHour = ct->tm_hour;
277 
278 		Draw(Bounds());
279 		fNeedToUpdate = false;
280 	}
281 }
282 
283 
284 void
285 TTimeView::ResizeToPreferred()
286 {
287 	float width, height;
288 	float oldWidth = Bounds().Width(), oldHeight = Bounds().Height();
289 
290 	GetPreferredSize(&width, &height);
291 	if (height != oldHeight || width != oldWidth) {
292 		ResizeTo(width, height);
293 		MoveBy(oldWidth - width, 0);
294 		fNeedToUpdate = true;
295 	}
296 }
297 
298 
299 //	# pragma mark - Public methods
300 
301 
302 void
303 TTimeView::SetOrientation(bool orientation)
304 {
305 	fOrientation = orientation;
306 	CalculateTextPlacement();
307 	Invalidate();
308 }
309 
310 
311 bool
312 TTimeView::Use24HourClock() const
313 {
314 	return fUse24HourClock;
315 }
316 
317 
318 void
319 TTimeView::SetUse24HourClock(bool use24HourClock)
320 {
321 	fUse24HourClock = use24HourClock;
322 	Update();
323 }
324 
325 
326 bool
327 TTimeView::ShowSeconds() const
328 {
329 	return fShowSeconds;
330 }
331 
332 
333 void
334 TTimeView::SetShowSeconds(bool show)
335 {
336 	fShowSeconds = show;
337 	Update();
338 }
339 
340 
341 bool
342 TTimeView::ShowDayOfWeek() const
343 {
344 	return fShowDayOfWeek;
345 }
346 
347 
348 void
349 TTimeView::SetShowDayOfWeek(bool show)
350 {
351 	fShowDayOfWeek = show;
352 	Update();
353 }
354 
355 
356 void
357 TTimeView::ShowCalendar(BPoint where)
358 {
359 	if (fCalendarWindow.IsValid()) {
360 		// If the calendar is already shown, just activate it
361 		BMessage activate(B_SET_PROPERTY);
362 		activate.AddSpecifier("Active");
363 		activate.AddBool("data", true);
364 
365 		if (fCalendarWindow.SendMessage(&activate) == B_OK)
366 			return;
367 	}
368 
369 	where.y = Bounds().bottom + 4.0;
370 	ConvertToScreen(&where);
371 
372 	if (where.y >= BScreen().Frame().bottom)
373 		where.y -= (Bounds().Height() + 4.0);
374 
375 	CalendarMenuWindow* window = new CalendarMenuWindow(where);
376 	fCalendarWindow = BMessenger(window);
377 
378 	window->Show();
379 }
380 
381 
382 //	# pragma mark - Private methods
383 
384 
385 void
386 TTimeView::GetCurrentTime()
387 {
388 	fLocale.FormatTime(fCurrentTimeStr, 64, fCurrentTime, fTimeFormat);
389 }
390 
391 
392 void
393 TTimeView::GetCurrentDate()
394 {
395 	char tmp[64];
396 
397 	fLocale.FormatDate(tmp, 64, fCurrentTime, B_FULL_DATE_FORMAT);
398 
399 	// remove leading 0 from date when month is less than 10 (MM/DD/YY)
400 	// or remove leading 0 from date when day is less than 10 (DD/MM/YY)
401 	const char* str = tmp;
402 	if (str[0] == '0')
403 		str++;
404 
405 	strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr));
406 }
407 
408 
409 void
410 TTimeView::CalculateTextPlacement()
411 {
412 	BRect bounds(Bounds());
413 
414 	fDateLocation.x = 0.0;
415 	fTimeLocation.x = 0.0;
416 
417 	BFont font;
418 	GetFont(&font);
419 
420 	const char* stringArray[1];
421 	stringArray[0] = fCurrentTimeStr;
422 	BRect rectArray[1];
423 	escapement_delta delta = { 0.0, 0.0 };
424 	font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta,
425 		rectArray);
426 
427 	fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height()
428 		- rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top);
429 }
430 
431 
432 void
433 TTimeView::ShowTimeOptions(BPoint point)
434 {
435 	BPopUpMenu* menu = new BPopUpMenu("", false, false);
436 	menu->SetFont(be_plain_font);
437 	BMenuItem* item;
438 
439 	item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS),
440 		new BMessage(kChangeTime));
441 	menu->AddItem(item);
442 
443 	item = new BMenuItem(B_TRANSLATE("Hide time"),
444 		new BMessage(kShowHideTime));
445 	menu->AddItem(item);
446 
447 	item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS),
448 		new BMessage(kShowCalendar));
449 	menu->AddItem(item);
450 
451 	menu->SetTargetForItems(this);
452 	// Changed to accept screen coord system point;
453 	// not constrained to this view now
454 	menu->Go(point, true, true, BRect(point.x - 4, point.y - 4,
455 		point.x + 4, point.y +4), true);
456 }
457 
458 
459 void
460 TTimeView::Update()
461 {
462 	fLocale = *BLocale::Default();
463 	UpdateTimeFormat();
464 
465 	GetCurrentTime();
466 	GetCurrentDate();
467 	SetToolTip(fCurrentDateStr);
468 
469 	CalculateTextPlacement();
470 	ResizeToPreferred();
471 
472 	if (fParent != NULL)
473 		fParent->Invalidate();
474 }
475 
476 
477 void
478 TTimeView::UpdateTimeFormat()
479 {
480 	BString timeFormat;
481 
482 	if (fShowDayOfWeek)
483 		timeFormat.Append("eee ");
484 
485 	if (fUse24HourClock)
486 		timeFormat.Append("H:mm");
487 	else
488 		timeFormat.Append("h:mm");
489 
490 	if (fShowSeconds)
491 		timeFormat.Append(":ss");
492 
493 	if (!fUse24HourClock)
494 		timeFormat.Append(" a");
495 
496 	fTimeFormat = timeFormat;
497 }
498