xref: /haiku/src/apps/deskbar/TimeView.cpp (revision 56187df6ebd9d1fb16bf954fce8df0999e9e3048)
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 trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 
36 #include "TimeView.h"
37 
38 #include <string.h>
39 
40 #include <Catalog.h>
41 #include <Country.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 	fLastTimeStr[0] = 0;
85 	fLastDateStr[0] = 0;
86 	fNeedToUpdate = true;
87 
88 	fLocale = *be_locale;
89 }
90 
91 
92 #ifdef AS_REPLICANT
93 TTimeView::TTimeView(BMessage* data)
94 	: BView(data)
95 {
96 	fTime = fLastTime = time(NULL);
97 	data->FindBool("seconds", &fShowSeconds);
98 	data->FindBool("interval", &fInterval);
99 
100 	fLocale = *be_locale;
101 }
102 #endif
103 
104 
105 TTimeView::~TTimeView()
106 {
107 }
108 
109 
110 #ifdef AS_REPLICANT
111 BArchivable*
112 TTimeView::Instantiate(BMessage* data)
113 {
114 	if (!validate_instantiation(data, "TTimeView"))
115 		return NULL;
116 
117 	return new TTimeView(data);
118 }
119 
120 
121 status_t
122 TTimeView::Archive(BMessage* data, bool deep) const
123 {
124 	BView::Archive(data, deep);
125 	data->AddBool("seconds", fShowSeconds);
126 	data->AddBool("interval", fInterval);
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
159 	// When in vertical mode, we want to limit the width so that it can't
160 	// overlap the bevels in the 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 	}
222 }
223 
224 
225 void
226 TTimeView::ShowCalendar(BPoint where)
227 {
228 	if (fCalendarWindow.IsValid()) {
229 		// If the calendar is already shown, just activate it
230 		BMessage activate(B_SET_PROPERTY);
231 		activate.AddSpecifier("Active");
232 		activate.AddBool("data", true);
233 
234 		if (fCalendarWindow.SendMessage(&activate) == B_OK)
235 			return;
236 	}
237 
238 	where.y = Bounds().bottom + 4.0;
239 	ConvertToScreen(&where);
240 
241 	if (where.y >= BScreen().Frame().bottom)
242 		where.y -= (Bounds().Height() + 4.0);
243 
244 	CalendarMenuWindow* window = new CalendarMenuWindow(where);
245 	fCalendarWindow = BMessenger(window);
246 
247 	window->Show();
248 }
249 
250 
251 void
252 TTimeView::GetCurrentTime()
253 {
254 	fLocale.FormatTime(fTimeStr, 64, fTime, fShowSeconds);
255 }
256 
257 
258 void
259 TTimeView::GetCurrentDate()
260 {
261 	char tmp[64];
262 
263 	fLocale.FormatDate(tmp, 64, fTime, true);
264 
265 	//	remove leading 0 from date when month is less than 10 (MM/DD/YY)
266 	//  or remove leading 0 from date when day is less than 10 (DD/MM/YY)
267 	const char* str = tmp;
268 	if (str[0] == '0')
269 		str++;
270 
271 	strcpy(fDateStr, str);
272 }
273 
274 
275 void
276 TTimeView::Draw(BRect /*updateRect*/)
277 {
278 	PushState();
279 
280 	SetHighColor(ViewColor());
281 	SetLowColor(ViewColor());
282 	FillRect(Bounds());
283 	SetHighColor(0, 0, 0, 255);
284 
285 	DrawString(fTimeStr, fTimeLocation);
286 
287 	PopState();
288 }
289 
290 
291 void
292 TTimeView::MouseDown(BPoint point)
293 {
294 	uint32 buttons;
295 
296 	Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
297 	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
298 		ShowClockOptions(ConvertToScreen(point));
299 		return;
300 	} else if (buttons == B_PRIMARY_MOUSE_BUTTON)
301 		ShowCalendar(point);
302 
303 	// invalidate last time/date strings and call the pulse
304 	// method directly to change the display instantly
305 	fLastDateStr[0] = '\0';
306 	fLastTimeStr[0] = '\0';
307 	Pulse();
308 }
309 
310 
311 void
312 TTimeView::Pulse()
313 {
314 	time_t curTime = time(NULL);
315 	tm* ct = localtime(&curTime);
316 	if (ct == NULL)
317 		return;
318 
319 	fTime = curTime;
320 
321 	GetCurrentTime();
322 	GetCurrentDate();
323 	if (strcmp(fTimeStr, fLastTimeStr) != 0) {
324 		// Update bounds when the size of the strings has changed
325 		// For dates, Update() could be called two times in a row,
326 		// but that should only happen very rarely
327 		if ((fLastTimeStr[1] != fTimeStr[1]
328 				&& (fLastTimeStr[1] == ':' || fTimeStr[1] == ':'))
329 			|| !fLastTimeStr[0])
330 			Update();
331 
332 		strcpy(fLastTimeStr, fTimeStr);
333 		strcpy(fLastDateStr, fDateStr);
334 		fNeedToUpdate = true;
335 	}
336 
337 	if (fNeedToUpdate) {
338 		fSeconds = ct->tm_sec;
339 		fMinute = ct->tm_min;
340 		fHour = ct->tm_hour;
341 		fInterval = ct->tm_hour >= 12;
342 
343 		Draw(Bounds());
344 		fNeedToUpdate = false;
345 	}
346 }
347 
348 
349 void
350 TTimeView::ShowSeconds(bool on)
351 {
352 	fShowSeconds = on;
353 	Update();
354 }
355 
356 
357 void
358 TTimeView::Update()
359 {
360 	fLocale = *be_locale;
361 	GetCurrentTime();
362 	GetCurrentDate();
363 
364 	SetToolTip(fDateStr);
365 
366 	ResizeToPreferred();
367 	CalculateTextPlacement();
368 
369 	if (fParent) {
370 		BMessage reformat('Trfm');
371 		fParent->MessageReceived(&reformat);
372 			// time string format realign
373 		fParent->Invalidate();
374 	}
375 }
376 
377 
378 void
379 TTimeView::SetOrientation(bool o)
380 {
381 	fOrientation = o;
382 	CalculateTextPlacement();
383 	Invalidate();
384 }
385 
386 
387 void
388 TTimeView::CalculateTextPlacement()
389 {
390 	BRect bounds(Bounds());
391 
392 	fDateLocation.x = 0.0;
393 	fTimeLocation.x = 0.0;
394 
395 	BFont font;
396 	GetFont(&font);
397 	const char* stringArray[1];
398 	stringArray[0] = fTimeStr;
399 	BRect rectArray[1];
400 	escapement_delta delta = { 0.0, 0.0 };
401 	font.GetBoundingBoxesForStrings(stringArray, 1, B_SCREEN_METRIC, &delta,
402 		rectArray);
403 
404 	fTimeLocation.y = fDateLocation.y = ceilf((bounds.Height()
405 		- rectArray[0].Height() + 1.0) / 2.0 - rectArray[0].top);
406 }
407 
408 
409 void
410 TTimeView::ShowClockOptions(BPoint point)
411 {
412 	BPopUpMenu* menu = new BPopUpMenu("", false, false);
413 	menu->SetFont(be_plain_font);
414 	BMenuItem* item;
415 
416 	item = new BMenuItem(B_TRANSLATE("Change time" B_UTF8_ELLIPSIS),
417 		new BMessage(kChangeClock));
418 	menu->AddItem(item);
419 
420 	item = new BMenuItem(B_TRANSLATE("Hide time"), new BMessage('time'));
421 	menu->AddItem(item);
422 
423 	item = new BMenuItem(B_TRANSLATE("Show calendar" B_UTF8_ELLIPSIS),
424 		new BMessage(kShowCalendar));
425 	menu->AddItem(item);
426 
427 	menu->SetTargetForItems(this);
428 	// Changed to accept screen coord system point;
429 	// not constrained to this view now
430 	menu->Go(point, true, true, BRect(point.x - 4, point.y - 4,
431 		point.x + 4, point.y +4), true);
432 }
433 
434