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