xref: /haiku/src/apps/deskbar/TimeView.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
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 	int32 fields = B_DATE_ELEMENT_HOUR | B_DATE_ELEMENT_MINUTE;
387 	if (fShowSeconds)
388 		fields |= B_DATE_ELEMENT_SECOND;
389 	if (fShowDayOfWeek)
390 		fields |= B_DATE_ELEMENT_WEEKDAY;
391 	if (fShowTimeZone)
392 		fields |= B_DATE_ELEMENT_TIMEZONE;
393 
394 	BDateTimeFormat format(&fLocale);
395 	format.SetDateTimeFormat(B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT, fields);
396 
397 	format.Format(fCurrentTimeStr, sizeof(fCurrentTimeStr), fCurrentTime,
398 		B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT);
399 }
400 
401 
402 void
403 TTimeView::GetCurrentDate()
404 {
405 	char tmp[sizeof(fCurrentDateStr)];
406 
407 	BDateFormat format(&fLocale);
408 	format.Format(tmp, sizeof(fCurrentDateStr), fCurrentTime,
409 		B_FULL_DATE_FORMAT);
410 
411 	// remove leading 0 from date when month is less than 10 (MM/DD/YY)
412 	// or remove leading 0 from date when day is less than 10 (DD/MM/YY)
413 	const char* str = tmp;
414 	if (str[0] == '0')
415 		str++;
416 
417 	strlcpy(fCurrentDateStr, str, sizeof(fCurrentDateStr));
418 }
419 
420 
421 void
422 TTimeView::CalculateTextPlacement()
423 {
424 	BRect bounds(Bounds());
425 
426 	fDateLocation.x = 0.0;
427 	fTimeLocation.x = 0.0;
428 
429 	BFont font;
430 	GetFont(&font);
431 
432 	const char* stringArray[1];
433 	stringArray[0] = fCurrentTimeStr;
434 	BRect rectArray[1];
435 	escapement_delta delta = { 0.0, 0.0 };
436 	font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta,
437 		rectArray);
438 
439 	fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height()
440 		- rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top);
441 }
442 
443 
444 void
445 TTimeView::ShowTimeOptions(BPoint point)
446 {
447 	BPopUpMenu* menu = new BPopUpMenu("", false, false);
448 	menu->SetFont(be_plain_font);
449 	BMenuItem* item;
450 
451 	item = new BMenuItem(B_TRANSLATE("Time preferences" B_UTF8_ELLIPSIS),
452 		new BMessage(kChangeTime));
453 	menu->AddItem(item);
454 
455 	item = new BMenuItem(B_TRANSLATE("Hide clock"),
456 		new BMessage(kShowHideTime));
457 	menu->AddItem(item);
458 
459 	item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS),
460 		new BMessage(kShowCalendar));
461 	menu->AddItem(item);
462 
463 	menu->SetTargetForItems(this);
464 	// Changed to accept screen coord system point;
465 	// not constrained to this view now
466 	menu->Go(point, true, true, BRect(point.x - 4, point.y - 4,
467 		point.x + 4, point.y +4), true);
468 }
469 
470 
471 void
472 TTimeView::Update()
473 {
474 	fLocale = *BLocale::Default();
475 
476 	GetCurrentTime();
477 	GetCurrentDate();
478 	SetToolTip(fCurrentDateStr);
479 
480 	CalculateTextPlacement();
481 	ResizeToPreferred();
482 
483 	if (fParent != NULL)
484 		fParent->Invalidate();
485 }
486