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