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