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