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