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