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