xref: /haiku/src/apps/deskbar/TimeView.cpp (revision 95c9effd68127df2dce202d5e254a7c86560010a)
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 "CalendarMenuWindow.h"
55 
56 
57 static const char* const kMinString = "99:99 AM";
58 static const float kHMargin = 2.0;
59 
60 
61 #undef B_TRANSLATION_CONTEXT
62 #define B_TRANSLATION_CONTEXT "TimeView"
63 
64 
65 TTimeView::TTimeView(float maxWidth, float height)
66 	:
67 	BView(BRect(-100, -100, -90, -90), "_deskbar_tv_",
68 		B_FOLLOW_RIGHT | B_FOLLOW_TOP,
69 		B_WILL_DRAW | B_PULSE_NEEDED | B_FRAME_EVENTS),
70 	fParent(NULL),
71 	fMaxWidth(maxWidth),
72 	fHeight(height),
73 	fOrientation(true),
74 	fShowLevel(0),
75 	fShowSeconds(false),
76 	fShowDayOfWeek(false),
77 	fShowTimeZone(false),
78 	fTimeFormat(NULL),
79 	fDateFormat(NULL)
80 {
81 	fCurrentTime = fLastTime = time(NULL);
82 	fSeconds = fMinute = fHour = 0;
83 	fCurrentTimeStr[0] = 0;
84 	fCurrentDateStr[0] = 0;
85 	fLastTimeStr[0] = 0;
86 	fLastDateStr[0] = 0;
87 	fNeedToUpdate = true;
88 	UpdateTimeFormat();
89 }
90 
91 
92 #ifdef AS_REPLICANT
93 TTimeView::TTimeView(BMessage* data)
94 	: BView(data),
95 	fTimeFormat(NULL),
96 	fDateFormat(NULL)
97 {
98 	fCurrentTime = fLastTime = time(NULL);
99 	data->FindBool("seconds", &fShowSeconds);
100 
101 	UpdateTimeFormat();
102 }
103 #endif
104 
105 
106 TTimeView::~TTimeView()
107 {
108 	delete fTimeFormat;
109 	delete fDateFormat;
110 }
111 
112 
113 #ifdef AS_REPLICANT
114 BArchivable*
115 TTimeView::Instantiate(BMessage* data)
116 {
117 	if (!validate_instantiation(data, "TTimeView"))
118 		return NULL;
119 
120 	return new TTimeView(data);
121 }
122 
123 
124 status_t
125 TTimeView::Archive(BMessage* data, bool deep) const
126 {
127 	BView::Archive(data, deep);
128 	data->AddBool("orientation", fOrientation);
129 	data->AddInt16("showLevel", fShowLevel);
130 	data->AddBool("showSeconds", fShowSeconds);
131 	data->AddBool("showDayOfWeek", fShowDayOfWeek);
132 	data->AddBool("showTimeZone", fShowTimeZone);
133 	data->AddInt32("deskbar:private_align", B_ALIGN_RIGHT);
134 
135 	return B_OK;
136 }
137 #endif
138 
139 
140 void
141 TTimeView::AttachedToWindow()
142 {
143 	fCurrentTime = time(NULL);
144 
145 	SetFont(be_plain_font);
146 	if (Parent()) {
147 		fParent = Parent();
148 		AdoptParentColors();
149 	} else
150 		SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
151 
152 	CalculateTextPlacement();
153 	ResizeToPreferred();
154 }
155 
156 
157 void
158 TTimeView::Draw(BRect /*updateRect*/)
159 {
160 	PushState();
161 
162 	SetHighColor(ViewColor());
163 	SetLowColor(ViewColor());
164 	FillRect(Bounds());
165 	SetHighUIColor(B_MENU_ITEM_TEXT_COLOR);
166 
167 	DrawString(fCurrentTimeStr, fTimeLocation);
168 
169 	PopState();
170 }
171 
172 
173 void
174 TTimeView::FrameMoved(BPoint)
175 {
176 	Update();
177 }
178 
179 
180 void
181 TTimeView::GetPreferredSize(float* width, float* height)
182 {
183 	*height = fHeight;
184 
185 	GetCurrentTime();
186 
187 	float timeWidth = StringWidth(fCurrentTimeStr);
188 
189 	// TODO: SetOrientation never gets called, fix that when in vertical mode,
190 	// we want to limit the width so that it can't overlap the bevels in the
191 	// parent view.
192 	*width = fOrientation ? std::min(fMaxWidth - kHMargin, timeWidth)
193 		: 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 	CalculateTextPlacement();
316 	Invalidate();
317 }
318 
319 
320 bool
321 TTimeView::ShowSeconds() const
322 {
323 	return fShowSeconds;
324 }
325 
326 
327 void
328 TTimeView::SetShowSeconds(bool show)
329 {
330 	fShowSeconds = show;
331 	UpdateTimeFormat();
332 	Update();
333 }
334 
335 
336 bool
337 TTimeView::ShowDayOfWeek() const
338 {
339 	return fShowDayOfWeek;
340 }
341 
342 
343 void
344 TTimeView::SetShowDayOfWeek(bool show)
345 {
346 	fShowDayOfWeek = show;
347 	UpdateTimeFormat();
348 	Update();
349 }
350 
351 
352 bool
353 TTimeView::ShowTimeZone() const
354 {
355 	return fShowTimeZone;
356 }
357 
358 
359 void
360 TTimeView::SetShowTimeZone(bool show)
361 {
362 	fShowTimeZone = show;
363 	UpdateTimeFormat();
364 	Update();
365 }
366 
367 
368 void
369 TTimeView::ShowCalendar(BPoint where)
370 {
371 	if (fCalendarWindow.IsValid()) {
372 		// If the calendar is already shown, just activate it
373 		BMessage activate(B_SET_PROPERTY);
374 		activate.AddSpecifier("Active");
375 		activate.AddBool("data", true);
376 
377 		if (fCalendarWindow.SendMessage(&activate) == B_OK)
378 			return;
379 	}
380 
381 	where.y = Bounds().bottom + 4.0;
382 	ConvertToScreen(&where);
383 
384 	if (where.y >= BScreen().Frame().bottom)
385 		where.y -= (Bounds().Height() + 4.0);
386 
387 	CalendarMenuWindow* window = new CalendarMenuWindow(where);
388 	fCalendarWindow = BMessenger(window);
389 
390 	window->Show();
391 }
392 
393 
394 //	# pragma mark - Private methods
395 
396 
397 void
398 TTimeView::UpdateTimeFormat()
399 {
400 	int32 fields = B_DATE_ELEMENT_HOUR | B_DATE_ELEMENT_MINUTE;
401 	if (fShowSeconds)
402 		fields |= B_DATE_ELEMENT_SECOND;
403 	if (fShowDayOfWeek)
404 		fields |= B_DATE_ELEMENT_WEEKDAY;
405 	if (fShowTimeZone)
406 		fields |= B_DATE_ELEMENT_TIMEZONE;
407 
408 	delete fTimeFormat;
409 	fTimeFormat = new BDateTimeFormat(BLocale::Default());
410 	fTimeFormat->SetDateTimeFormat(B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT, fields);
411 
412 	delete fDateFormat;
413 	fDateFormat = new BDateFormat(BLocale::Default());
414 }
415 
416 void
417 TTimeView::GetCurrentTime()
418 {
419 	fTimeFormat->Format(fCurrentTimeStr, sizeof(fCurrentTimeStr), fCurrentTime,
420 		B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT);
421 }
422 
423 
424 void
425 TTimeView::GetCurrentDate()
426 {
427 	char tmp[sizeof(fCurrentDateStr)];
428 
429 	fDateFormat->Format(tmp, sizeof(fCurrentDateStr), fCurrentTime,
430 		B_FULL_DATE_FORMAT);
431 
432 	// remove leading 0 from date when month is less than 10 (MM/DD/YY)
433 	// or remove leading 0 from date when day is less than 10 (DD/MM/YY)
434 	const char* str = tmp;
435 	if (str[0] == '0')
436 		str++;
437 
438 	strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr));
439 }
440 
441 
442 void
443 TTimeView::CalculateTextPlacement()
444 {
445 	BRect bounds(Bounds());
446 
447 	fDateLocation.x = 0.0;
448 	fTimeLocation.x = 0.0;
449 
450 	BFont font;
451 	GetFont(&font);
452 
453 	const char* stringArray[1];
454 	stringArray[0] = fCurrentTimeStr;
455 	BRect rectArray[1];
456 	escapement_delta delta = { 0.0, 0.0 };
457 	font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta,
458 		rectArray);
459 
460 	fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height()
461 		- rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top);
462 }
463 
464 
465 void
466 TTimeView::ShowTimeOptions(BPoint point)
467 {
468 	BPopUpMenu* menu = new BPopUpMenu("", false, false);
469 	menu->SetFont(be_plain_font);
470 	BMenuItem* item;
471 
472 	item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS),
473 		new BMessage(kChangeTime));
474 	menu->AddItem(item);
475 
476 	item = new BMenuItem(B_TRANSLATE("Hide clock"),
477 		new BMessage(kShowHideTime));
478 	menu->AddItem(item);
479 
480 	item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS),
481 		new BMessage(kShowCalendar));
482 	menu->AddItem(item);
483 
484 	menu->SetTargetForItems(this);
485 	// Changed to accept screen coord system point;
486 	// not constrained to this view now
487 	menu->Go(point, true, true, BRect(point.x - 4, point.y - 4,
488 		point.x + 4, point.y +4), true);
489 }
490 
491 
492 void
493 TTimeView::Update()
494 {
495 	GetCurrentTime();
496 	GetCurrentDate();
497 	SetToolTip(fCurrentDateStr);
498 
499 	CalculateTextPlacement();
500 	ResizeToPreferred();
501 
502 	if (fParent != NULL)
503 		fParent->Invalidate();
504 }
505