xref: /haiku/src/kits/interface/StatusBar.cpp (revision 1acbe440b8dd798953bec31d18ee589aa3f71b73)
1 /*
2  * Copyright 2001-2006, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 /*! BStatusBar displays a "percentage-of-completion" gauge. */
11 
12 #include <Message.h>
13 #include <Region.h>
14 #include <StatusBar.h>
15 
16 #include <stdlib.h>
17 #include <string.h>
18 
19 
20 static const rgb_color kDefaultBarColor = {50, 150, 255, 255};
21 
22 
23 BStatusBar::BStatusBar(BRect frame, const char *name, const char *label,
24 					   const char *trailingLabel)
25 	: BView(frame, name, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW),
26 	fLabel(label),
27 	fTrailingLabel(trailingLabel)
28 {
29 	_InitObject();
30 }
31 
32 
33 BStatusBar::BStatusBar(BMessage *archive)
34 	: BView(archive)
35 {
36 	_InitObject();
37 
38 	archive->FindString("_label", &fLabel);
39 	archive->FindString("_tlabel", &fTrailingLabel);
40 
41 	archive->FindString("_text", &fText);
42 	archive->FindString("_ttext", &fTrailingText);
43 
44 	float floatValue;
45 	if (archive->FindFloat("_high", &floatValue) == B_OK) {
46 		fBarHeight = floatValue;
47 		fCustomBarHeight = true;
48 	}
49 
50 	int32 color;
51 	if (archive->FindInt32("_bcolor", (int32 *)&color) == B_OK)
52 		fBarColor = *(rgb_color *)&color;
53 
54 	if (archive->FindFloat("_val", &floatValue) == B_OK)
55 		fCurrent = floatValue;
56 	if (archive->FindFloat("_max", &floatValue) == B_OK)
57 		fMax = floatValue;
58 }
59 
60 
61 BStatusBar::~BStatusBar()
62 {
63 }
64 
65 
66 void
67 BStatusBar::_InitObject()
68 {
69 	fMax = 100.0f;
70 	fCurrent = 0.0f;
71 
72 	fBarHeight = -1.0f;
73 	fTextWidth = 0.0f;
74 	fLabelWidth = 0.0f;
75 	fTrailingTextWidth = 0.0f;
76 	fTrailingLabelWidth = 0.0f;
77 
78 	fBarColor = kDefaultBarColor;
79 	fCustomBarHeight = false;
80 }
81 
82 
83 BArchivable *
84 BStatusBar::Instantiate(BMessage *archive)
85 {
86 	if (validate_instantiation(archive, "BStatusBar"))
87 		return new BStatusBar(archive);
88 
89 	return NULL;
90 }
91 
92 
93 status_t
94 BStatusBar::Archive(BMessage *archive, bool deep) const
95 {
96 	status_t err = BView::Archive(archive, deep);
97 	if (err < B_OK)
98 		return err;
99 
100 	if (fCustomBarHeight)
101 		err = archive->AddFloat("_high", fBarHeight);
102 
103 	if (err == B_OK && fBarColor != kDefaultBarColor)
104 		err = archive->AddInt32("_bcolor", (const uint32 &)fBarColor);
105 
106 	if (err == B_OK && fCurrent != 0.0f)
107 		err = archive->AddFloat("_val", fCurrent);
108 	if (err == B_OK && fMax != 100.0f )
109 		err = archive->AddFloat("_max", fMax);
110 
111 	if (err == B_OK && fText.Length())
112 		err = archive->AddString("_text", fText);
113 	if (err == B_OK && fTrailingText.Length())
114 		err = archive->AddString("_ttext", fTrailingText);
115 
116 	if (err == B_OK && fLabel.Length())
117 		err = archive->AddString("_label", fLabel);
118 	if (err == B_OK && fTrailingLabel.Length())
119 		err = archive->AddString ("_tlabel", fTrailingLabel);
120 
121 	return err;
122 }
123 
124 
125 void
126 BStatusBar::AttachedToWindow()
127 {
128 	// resize so that the height fits
129 	float width, height;
130 	GetPreferredSize(&width, &height);
131 	ResizeTo(Bounds().Width(), height);
132 
133 	SetViewColor(B_TRANSPARENT_COLOR);
134 
135 	if (Parent())
136 		SetLowColor(Parent()->ViewColor());
137 
138 	fLabelWidth = ceilf(StringWidth(fLabel.String()));
139 	fTrailingLabelWidth = ceilf(StringWidth(fTrailingLabel.String()));
140 	fTextWidth = ceilf(StringWidth(fText.String()));
141 	fTrailingTextWidth = ceilf(StringWidth(fTrailingText.String()));
142 }
143 
144 
145 void
146 BStatusBar::MessageReceived(BMessage *message)
147 {
148 	switch(message->what) {
149 		case B_UPDATE_STATUS_BAR:
150 		{
151 			float delta;
152 			const char *text = NULL, *trailing_text = NULL;
153 
154 			message->FindFloat("delta", &delta);
155 			message->FindString("text", &text);
156 			message->FindString("trailing_text", &trailing_text);
157 
158 			Update(delta, text, trailing_text);
159 
160 			break;
161 		}
162 
163 		case B_RESET_STATUS_BAR:
164 		{
165 			const char *label = NULL, *trailing_label = NULL;
166 
167 			message->FindString("label", &label);
168 			message->FindString("trailing_label", &trailing_label);
169 
170 			Reset(label, trailing_label);
171 
172 			break;
173 		}
174 
175 		default:
176 			BView::MessageReceived(message);
177 			break;
178 	}
179 }
180 
181 
182 void
183 BStatusBar::Draw(BRect updateRect)
184 {
185 	rgb_color backgroundColor;
186 	if (Parent())
187 		backgroundColor = Parent()->ViewColor();
188 	else
189 		backgroundColor = ui_color(B_PANEL_BACKGROUND_COLOR);
190 
191 	font_height fontHeight;
192 	GetFontHeight(&fontHeight);
193 	BRect barFrame = _BarFrame(&fontHeight);
194 	BRect outerFrame = barFrame.InsetByCopy(-2.0f, -2.0f);
195 
196 	BRegion background(updateRect);
197 	background.Exclude(outerFrame);
198 	FillRegion(&background, B_SOLID_LOW);
199 
200 	// Draw labels/texts
201 
202 	BRect rect = outerFrame;
203 	rect.top = 0.0f;
204 	rect.bottom = outerFrame.top - 1.0f;
205 
206 	if (updateRect.Intersects(rect)) {
207 		// update labels
208 		SetHighColor(ui_color(B_CONTROL_TEXT_COLOR));
209 		MovePenTo(0.0f, ceilf(fontHeight.ascent) + 1.0f);
210 
211 		if (fLabel.Length())
212 			DrawString(fLabel.String());
213 		if (fText.Length())
214 			DrawString(fText.String());
215 
216 		if (fTrailingText.Length() || fTrailingLabel.Length()) {
217 			MovePenTo(rect.right - fTrailingTextWidth - fTrailingLabelWidth,
218 				ceilf(fontHeight.ascent) + 1.0f);
219 
220 			if (fTrailingText.Length())
221 				DrawString(fTrailingText.String());
222 
223 			if (fTrailingLabel.Length())
224 				DrawString(fTrailingLabel.String());
225 		}
226 	}
227 
228 	// Draw bar
229 
230 	if (!updateRect.Intersects(outerFrame))
231 		return;
232 
233 	rect = outerFrame;
234 
235 	// First bevel
236 	SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_1_TINT));
237 	StrokeLine(rect.LeftBottom(), rect.LeftTop());
238 	StrokeLine(rect.RightTop());
239 
240 	SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_2_TINT));
241 	StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
242 	StrokeLine(BPoint(rect.right, rect.top + 1.0f));
243 
244 	rect.InsetBy(1.0f, 1.0f);
245 
246 	// Second bevel
247 	SetHighColor(tint_color(ui_color ( B_PANEL_BACKGROUND_COLOR ), B_DARKEN_4_TINT));
248 	StrokeLine(rect.LeftBottom(), rect.LeftTop());
249 	StrokeLine(rect.RightTop());
250 
251 	SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR));
252 	StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
253 	StrokeLine(BPoint(rect.right, rect.top + 1.0f));
254 
255 	rect = barFrame;
256 	rect.right = _BarPosition(barFrame);
257 
258 	// draw bar itself
259 
260 	if (rect.right >= rect.left) {
261 		// Bevel
262 		SetHighColor(tint_color(fBarColor, B_LIGHTEN_2_TINT));
263 		StrokeLine(rect.LeftBottom(), rect.LeftTop());
264 		StrokeLine(rect.RightTop());
265 
266 		SetHighColor(tint_color(fBarColor, B_DARKEN_2_TINT));
267 		StrokeLine(BPoint(rect.left + 1.0f, rect.bottom), rect.RightBottom());
268 		StrokeLine(BPoint(rect.right, rect.top + 1.0f));
269 
270 		// filling
271 		SetHighColor(fBarColor);
272 		FillRect(rect.InsetByCopy(1.0f, 1.0f));
273 	}
274 
275 	if (rect.right < barFrame.right) {
276 		// empty space
277 		rect.left = rect.right + 1.0f;
278 		rect.right = barFrame.right;
279 		SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_LIGHTEN_MAX_TINT));
280 		FillRect(rect);
281 	}
282 }
283 
284 
285 void
286 BStatusBar::SetBarColor(rgb_color color)
287 {
288 	fBarColor = color;
289 
290 	Invalidate();
291 }
292 
293 
294 void
295 BStatusBar::SetBarHeight(float barHeight)
296 {
297 	float oldHeight = BarHeight();
298 
299 	fCustomBarHeight = true;
300 	fBarHeight = barHeight;
301 
302 	if (barHeight == oldHeight)
303 		return;
304 
305 	// resize so that the height fits
306 	float width, height;
307 	GetPreferredSize(&width, &height);
308 	ResizeTo(Bounds().Width(), height);
309 }
310 
311 
312 void
313 BStatusBar::SetText(const char* string)
314 {
315 	_SetTextData(fText, fTextWidth, string, fLabelWidth, false);
316 }
317 
318 
319 void
320 BStatusBar::SetTrailingText(const char* string)
321 {
322 	_SetTextData(fTrailingText, fTrailingTextWidth, string,
323 		fTrailingLabelWidth, true);
324 }
325 
326 
327 void
328 BStatusBar::SetMaxValue(float max)
329 {
330 	fMax = max;
331 	Invalidate(_BarFrame());
332 }
333 
334 
335 void
336 BStatusBar::Update(float delta, const char* text, const char* trailingText)
337 {
338 	BStatusBar::SetTo(fCurrent + delta, text, trailingText);
339 }
340 
341 
342 void
343 BStatusBar::SetTo(float value, const char* text, const char* trailingText)
344 {
345 	if (text != NULL)
346 		_SetTextData(fText, fTextWidth, text, fLabelWidth, false);
347 	if (trailingText != NULL) {
348 		_SetTextData(fTrailingText, fTrailingTextWidth, trailingText,
349 			fTrailingLabelWidth, true);
350 	}
351 
352 	if (value > fMax)
353 		value = fMax;
354 	else if (value < 0.0f)
355 		value = 0.0f;
356 	if (value == fCurrent)
357 		return;
358 
359 	BRect barFrame = _BarFrame();
360 	float oldPosition = _BarPosition(barFrame);
361 
362 	fCurrent = value;
363 	float newPosition = _BarPosition(barFrame);
364 
365 	// update only the part of the status bar with actual changes
366 
367 	if (oldPosition == newPosition)
368 		return;
369 
370 	BRect update = barFrame;
371 	update.InsetBy(-1, -1);
372 		// TODO: this shouldn't be necessary, investigate - app_server bug?!
373 
374 	if (oldPosition < newPosition) {
375 		update.left = max_c(oldPosition - 1, update.left);
376 		update.right = newPosition;
377 	} else {
378 		update.left = max_c(newPosition - 1, update.left);
379 		update.right = oldPosition;
380 	}
381 
382 	Invalidate(update);
383 }
384 
385 
386 void
387 BStatusBar::Reset(const char *label, const char *trailingLabel)
388 {
389 	// Reset replaces the label and trailing label with copies of the
390 	// strings passed as arguments. If either argument is NULL, the
391 	// label or trailing label will be deleted and erased.
392 	_SetTextData(fLabel, fLabelWidth, label, 0.0f, false);
393 	_SetTextData(fTrailingLabel, fTrailingLabelWidth, trailingLabel, 0.0f, true);
394 
395 	// Reset deletes and erases any text or trailing text
396 	_SetTextData(fText, fTextWidth, NULL, fLabelWidth, false);
397 	_SetTextData(fTrailingText, fTrailingTextWidth, NULL, fTrailingLabelWidth, true);
398 
399 	fCurrent = 0.0f;
400 	fMax = 100.0f;
401 
402 	Invalidate();
403 }
404 
405 
406 float
407 BStatusBar::CurrentValue() const
408 {
409 	return fCurrent;
410 }
411 
412 
413 float
414 BStatusBar::MaxValue() const
415 {
416 	return fMax;
417 }
418 
419 
420 rgb_color
421 BStatusBar::BarColor() const
422 {
423 	return fBarColor;
424 }
425 
426 
427 float
428 BStatusBar::BarHeight() const
429 {
430 	if (!fCustomBarHeight && fBarHeight == -1.0f) {
431 		// the default bar height is as height as the label
432 		font_height fontHeight;
433 		GetFontHeight(&fontHeight);
434 		const_cast<BStatusBar *>(this)->fBarHeight = fontHeight.ascent
435 			+ fontHeight.descent + 5.0f;
436 	}
437 
438 	return fBarHeight;
439 }
440 
441 
442 const char *
443 BStatusBar::Text() const
444 {
445 	return fText.String();
446 }
447 
448 
449 const char *
450 BStatusBar::TrailingText() const
451 {
452 	return fTrailingText.String();
453 }
454 
455 
456 const char *
457 BStatusBar::Label() const
458 {
459 	return fLabel.String();
460 }
461 
462 
463 const char *
464 BStatusBar::TrailingLabel() const
465 {
466 	return fTrailingLabel.String();
467 }
468 
469 
470 void
471 BStatusBar::MouseDown(BPoint point)
472 {
473 	BView::MouseDown(point);
474 }
475 
476 
477 void
478 BStatusBar::MouseUp(BPoint point)
479 {
480 	BView::MouseUp(point);
481 }
482 
483 
484 void
485 BStatusBar::WindowActivated(bool state)
486 {
487 	BView::WindowActivated(state);
488 }
489 
490 
491 void
492 BStatusBar::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
493 {
494 	BView::MouseMoved(point, transit, message);
495 }
496 
497 
498 void
499 BStatusBar::DetachedFromWindow()
500 {
501 	BView::DetachedFromWindow();
502 }
503 
504 
505 void
506 BStatusBar::FrameMoved(BPoint newPosition)
507 {
508 	BView::FrameMoved(newPosition);
509 }
510 
511 
512 void
513 BStatusBar::FrameResized(float newWidth, float newHeight)
514 {
515 	BView::FrameResized(newWidth, newHeight);
516 }
517 
518 
519 BHandler *
520 BStatusBar::ResolveSpecifier(BMessage* message, int32 index,
521 	BMessage* specifier, int32 what, const char *property)
522 {
523 	return BView::ResolveSpecifier(message, index, specifier, what, property);
524 }
525 
526 
527 void
528 BStatusBar::ResizeToPreferred()
529 {
530 	BView::ResizeToPreferred();
531 }
532 
533 
534 void
535 BStatusBar::GetPreferredSize(float* _width, float* _height)
536 {
537 	if (_width) {
538 		// AttachedToWindow() might not have been called yet
539 		*_width = ceilf(StringWidth(fLabel.String()))
540 			+ ceilf(StringWidth(fTrailingLabel.String()))
541 			+ ceilf(StringWidth(fText.String()))
542 			+ ceilf(StringWidth(fTrailingText.String()))
543 			+ 5.0f;
544 	}
545 
546 	if (_height) {
547 		font_height fontHeight;
548 		GetFontHeight(&fontHeight);
549 
550 		*_height = ceilf(fontHeight.ascent + fontHeight.descent) + 6.0f + BarHeight();
551 	}
552 }
553 
554 
555 void
556 BStatusBar::MakeFocus(bool state)
557 {
558 	BView::MakeFocus(state);
559 }
560 
561 
562 void
563 BStatusBar::AllAttached()
564 {
565 	BView::AllAttached();
566 }
567 
568 
569 void
570 BStatusBar::AllDetached()
571 {
572 	BView::AllDetached();
573 }
574 
575 
576 status_t
577 BStatusBar::GetSupportedSuites(BMessage* data)
578 {
579 	return BView::GetSupportedSuites(data);
580 }
581 
582 
583 status_t
584 BStatusBar::Perform(perform_code d, void* arg)
585 {
586 	return BView::Perform(d, arg);
587 }
588 
589 
590 void
591 _ReservedStatusBar1__10BStatusBar(BStatusBar* self, float value,
592 	const char* text, const char* trailingText)
593 {
594 	self->BStatusBar::SetTo(value, text, trailingText);
595 }
596 
597 
598 void BStatusBar::_ReservedStatusBar2() {}
599 void BStatusBar::_ReservedStatusBar3() {}
600 void BStatusBar::_ReservedStatusBar4() {}
601 
602 
603 BStatusBar &
604 BStatusBar::operator=(const BStatusBar& other)
605 {
606 	return *this;
607 }
608 
609 
610 void
611 BStatusBar::_SetTextData(BString& text, float& width, const char* source,
612 	float position, bool rightAligned)
613 {
614 	// If there were no changes, we don't have to do anything
615 	if (text == source)
616 		return;
617 
618 	float oldWidth = width;
619 	text = source;
620 	width = ceilf(StringWidth(text.String()));
621 
622 	// determine which part of the view needs an update
623 
624 	// if a label changed, we also need to update the texts
625 	float invalidateWidth = max_c(width, oldWidth);
626 	if (&text == &fLabel)
627 		invalidateWidth += fTextWidth;
628 	if (&text == &fTrailingLabel)
629 		invalidateWidth += fTrailingTextWidth;
630 
631 	if (rightAligned)
632 		position = Bounds().Width() - position - invalidateWidth;
633 
634 	font_height fontHeight;
635 	GetFontHeight(&fontHeight);
636 
637 	Invalidate(BRect(position, 1.0f, position + invalidateWidth,
638 		ceilf(fontHeight.ascent + fontHeight.descent)));
639 }
640 
641 
642 /*!
643 	Returns the inner bar frame without the surrounding bevel.
644 */
645 BRect
646 BStatusBar::_BarFrame(const font_height* fontHeight) const
647 {
648 	float top;
649 	if (fontHeight == NULL) {
650 		font_height height;
651 		GetFontHeight(&height);
652 		top = ceilf(height.ascent + height.descent) + 6.0f;
653 	} else
654 		top = ceilf(fontHeight->ascent + fontHeight->descent) + 6.0f;
655 
656 	return BRect(2.0f, top, Bounds().right - 2.0f, top + BarHeight() - 4.0f);
657 }
658 
659 
660 float
661 BStatusBar::_BarPosition(const BRect& barFrame) const
662 {
663 	if (fCurrent == 0.0f)
664 		return barFrame.left - 1.0f;
665 
666 	return roundf(barFrame.left + ceilf(fCurrent * barFrame.Width() / fMax));
667 }
668 
669