1 /*
2 * Copyright 2001-2015, Haiku, Inc. All Rights Reserved.
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 * Stephan Aßmus <superstippi@gmx.de>
9 * Joseph Groover <looncraz@looncraz.net>
10 */
11
12 /*! BStatusBar displays a "percentage-of-completion" gauge. */
13 #include <StatusBar.h>
14
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include <ControlLook.h>
20 #include <Layout.h>
21 #include <LayoutUtils.h>
22 #include <Message.h>
23 #include <Region.h>
24
25 #include <binary_compatibility/Interface.h>
26
27 enum internalFlags {
28 kCustomBarColor = 1
29 };
30
31
BStatusBar(BRect frame,const char * name,const char * label,const char * trailingLabel)32 BStatusBar::BStatusBar(BRect frame, const char *name, const char *label,
33 const char *trailingLabel)
34 :
35 BView(frame, name, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW),
36 fLabel(label),
37 fTrailingLabel(trailingLabel)
38 {
39 _InitObject();
40 }
41
42
BStatusBar(const char * name,const char * label,const char * trailingLabel)43 BStatusBar::BStatusBar(const char *name, const char *label,
44 const char *trailingLabel)
45 :
46 BView(BRect(0, 0, -1, -1), name, B_FOLLOW_LEFT | B_FOLLOW_TOP,
47 B_WILL_DRAW | B_SUPPORTS_LAYOUT),
48 fLabel(label),
49 fTrailingLabel(trailingLabel)
50 {
51 _InitObject();
52 }
53
54
BStatusBar(BMessage * archive)55 BStatusBar::BStatusBar(BMessage *archive)
56 :
57 BView(archive)
58 {
59 _InitObject();
60
61 archive->FindString("_label", &fLabel);
62 archive->FindString("_tlabel", &fTrailingLabel);
63
64 archive->FindString("_text", &fText);
65 archive->FindString("_ttext", &fTrailingText);
66
67 float floatValue;
68 if (archive->FindFloat("_high", &floatValue) == B_OK) {
69 fBarHeight = floatValue;
70 fCustomBarHeight = true;
71 }
72
73 int32 color;
74 if (archive->FindInt32("_bcolor", (int32 *)&color) == B_OK) {
75 fBarColor = *(rgb_color *)&color;
76 fInternalFlags |= kCustomBarColor;
77 }
78
79 if (archive->FindFloat("_val", &floatValue) == B_OK)
80 fCurrent = floatValue;
81 if (archive->FindFloat("_max", &floatValue) == B_OK)
82 fMax = floatValue;
83 }
84
85
~BStatusBar()86 BStatusBar::~BStatusBar()
87 {
88 }
89
90
91 BArchivable *
Instantiate(BMessage * archive)92 BStatusBar::Instantiate(BMessage *archive)
93 {
94 if (validate_instantiation(archive, "BStatusBar"))
95 return new BStatusBar(archive);
96
97 return NULL;
98 }
99
100
101 status_t
Archive(BMessage * archive,bool deep) const102 BStatusBar::Archive(BMessage *archive, bool deep) const
103 {
104 status_t err = BView::Archive(archive, deep);
105 if (err < B_OK)
106 return err;
107
108 if (fCustomBarHeight)
109 err = archive->AddFloat("_high", fBarHeight);
110
111 if (err == B_OK && fInternalFlags & kCustomBarColor)
112 err = archive->AddInt32("_bcolor", (const uint32 &)fBarColor);
113
114 if (err == B_OK && fCurrent != 0)
115 err = archive->AddFloat("_val", fCurrent);
116 if (err == B_OK && fMax != 100 )
117 err = archive->AddFloat("_max", fMax);
118
119 if (err == B_OK && fText.Length())
120 err = archive->AddString("_text", fText);
121 if (err == B_OK && fTrailingText.Length())
122 err = archive->AddString("_ttext", fTrailingText);
123
124 if (err == B_OK && fLabel.Length())
125 err = archive->AddString("_label", fLabel);
126 if (err == B_OK && fTrailingLabel.Length())
127 err = archive->AddString ("_tlabel", fTrailingLabel);
128
129 return err;
130 }
131
132
133 // #pragma mark -
134
135
136 void
AttachedToWindow()137 BStatusBar::AttachedToWindow()
138 {
139 // resize so that the height fits
140 float width, height;
141 GetPreferredSize(&width, &height);
142 ResizeTo(Bounds().Width(), height);
143
144 SetViewColor(B_TRANSPARENT_COLOR);
145
146 AdoptParentColors();
147
148 fTextDivider = Bounds().Width();
149
150 if ((fInternalFlags & kCustomBarColor) == 0)
151 fBarColor = ui_color(B_STATUS_BAR_COLOR);
152 }
153
154
155 void
DetachedFromWindow()156 BStatusBar::DetachedFromWindow()
157 {
158 BView::DetachedFromWindow();
159 }
160
161
162 void
AllAttached()163 BStatusBar::AllAttached()
164 {
165 BView::AllAttached();
166 }
167
168
169 void
AllDetached()170 BStatusBar::AllDetached()
171 {
172 BView::AllDetached();
173 }
174
175
176 // #pragma mark -
177
178
179 void
WindowActivated(bool state)180 BStatusBar::WindowActivated(bool state)
181 {
182 BView::WindowActivated(state);
183 }
184
185
186 void
MakeFocus(bool state)187 BStatusBar::MakeFocus(bool state)
188 {
189 BView::MakeFocus(state);
190 }
191
192
193 // #pragma mark -
194
195
196 void
GetPreferredSize(float * _width,float * _height)197 BStatusBar::GetPreferredSize(float* _width, float* _height)
198 {
199 if (_width) {
200 // AttachedToWindow() might not have been called yet
201 *_width = ceilf(StringWidth(fLabel.String()))
202 + ceilf(StringWidth(fTrailingLabel.String()))
203 + ceilf(StringWidth(fText.String()))
204 + ceilf(StringWidth(fTrailingText.String()))
205 + 5;
206 }
207
208 if (_height) {
209 float labelHeight = 0;
210 if (_HasText()) {
211 font_height fontHeight;
212 GetFontHeight(&fontHeight);
213 labelHeight = ceilf(fontHeight.ascent + fontHeight.descent) + 6;
214 }
215
216 *_height = labelHeight + BarHeight();
217 }
218 }
219
220
221 BSize
MinSize()222 BStatusBar::MinSize()
223 {
224 float width, height;
225 GetPreferredSize(&width, &height);
226
227 return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(width, height));
228 }
229
230
231 BSize
MaxSize()232 BStatusBar::MaxSize()
233 {
234 float width, height;
235 GetPreferredSize(&width, &height);
236
237 return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
238 BSize(B_SIZE_UNLIMITED, height));
239 }
240
241
242 BSize
PreferredSize()243 BStatusBar::PreferredSize()
244 {
245 float width, height;
246 GetPreferredSize(&width, &height);
247
248 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
249 BSize(width, height));
250 }
251
252
253 void
ResizeToPreferred()254 BStatusBar::ResizeToPreferred()
255 {
256 BView::ResizeToPreferred();
257 }
258
259
260 void
FrameMoved(BPoint newPosition)261 BStatusBar::FrameMoved(BPoint newPosition)
262 {
263 BView::FrameMoved(newPosition);
264 }
265
266
267 void
FrameResized(float newWidth,float newHeight)268 BStatusBar::FrameResized(float newWidth, float newHeight)
269 {
270 BView::FrameResized(newWidth, newHeight);
271 Invalidate();
272 }
273
274
275 // #pragma mark -
276
277
278 void
Draw(BRect updateRect)279 BStatusBar::Draw(BRect updateRect)
280 {
281 rgb_color backgroundColor = LowColor();
282
283 font_height fontHeight;
284 GetFontHeight(&fontHeight);
285 BRect barFrame = _BarFrame(&fontHeight);
286 BRect outerFrame = barFrame.InsetByCopy(-2, -2);
287
288 BRegion background(updateRect);
289 background.Exclude(outerFrame);
290 FillRegion(&background, B_SOLID_LOW);
291
292 // Draw labels/texts
293
294 BRect rect = outerFrame;
295 rect.top = 0;
296 rect.bottom = outerFrame.top - 1;
297
298 if (updateRect.Intersects(rect)) {
299 // update labels
300 BString leftText;
301 leftText << fLabel << fText;
302
303 BString rightText;
304 rightText << fTrailingText << fTrailingLabel;
305
306 float baseLine = ceilf(fontHeight.ascent) + 1;
307 fTextDivider = rect.right;
308
309 BFont font;
310 GetFont(&font);
311
312 if (rightText.Length()) {
313 font.TruncateString(&rightText, B_TRUNCATE_BEGINNING,
314 rect.Width());
315 fTextDivider -= StringWidth(rightText.String());
316 }
317
318 if (leftText.Length()) {
319 float width = max_c(0.0, fTextDivider - rect.left);
320 font.TruncateString(&leftText, B_TRUNCATE_END, width);
321 }
322
323 rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
324
325 if (backgroundColor != ui_color(B_PANEL_BACKGROUND_COLOR)) {
326 if (backgroundColor.IsLight())
327 textColor = make_color(0, 0, 0, 255);
328 else
329 textColor = make_color(255, 255, 255, 255);
330 }
331
332 SetHighColor(textColor);
333
334 if (leftText.Length())
335 DrawString(leftText.String(), BPoint(rect.left, baseLine));
336
337 if (rightText.Length())
338 DrawString(rightText.String(), BPoint(fTextDivider, baseLine));
339 }
340
341 // Draw bar
342
343 if (!updateRect.Intersects(outerFrame))
344 return;
345
346 rect = outerFrame;
347
348 be_control_look->DrawStatusBar(this, rect, updateRect,
349 backgroundColor, fBarColor, _BarPosition(barFrame));
350 }
351
352
353 void
MessageReceived(BMessage * message)354 BStatusBar::MessageReceived(BMessage *message)
355 {
356 switch(message->what) {
357 case B_UPDATE_STATUS_BAR:
358 {
359 float delta;
360 const char *text = NULL, *trailing_text = NULL;
361
362 message->FindFloat("delta", &delta);
363 message->FindString("text", &text);
364 message->FindString("trailing_text", &trailing_text);
365
366 Update(delta, text, trailing_text);
367
368 break;
369 }
370
371 case B_RESET_STATUS_BAR:
372 {
373 const char *label = NULL, *trailing_label = NULL;
374
375 message->FindString("label", &label);
376 message->FindString("trailing_label", &trailing_label);
377
378 Reset(label, trailing_label);
379
380 break;
381 }
382
383 case B_COLORS_UPDATED:
384 {
385 // Change the bar color IF we don't have an application-set color.
386 if ((fInternalFlags & kCustomBarColor) == 0) {
387 message->FindColor(ui_color_name(B_STATUS_BAR_COLOR),
388 &fBarColor);
389 }
390
391 break;
392 }
393
394 default:
395 BView::MessageReceived(message);
396 break;
397 }
398 }
399
400
401 void
MouseDown(BPoint point)402 BStatusBar::MouseDown(BPoint point)
403 {
404 BView::MouseDown(point);
405 }
406
407
408 void
MouseUp(BPoint point)409 BStatusBar::MouseUp(BPoint point)
410 {
411 BView::MouseUp(point);
412 }
413
414
415 void
MouseMoved(BPoint point,uint32 transit,const BMessage * message)416 BStatusBar::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
417 {
418 BView::MouseMoved(point, transit, message);
419 }
420
421
422 // #pragma mark -
423
424
425 void
SetBarColor(rgb_color color)426 BStatusBar::SetBarColor(rgb_color color)
427 {
428 fInternalFlags |= kCustomBarColor;
429 fBarColor = color;
430
431 Invalidate();
432 }
433
434
435 void
SetBarHeight(float barHeight)436 BStatusBar::SetBarHeight(float barHeight)
437 {
438 float oldHeight = BarHeight();
439
440 fCustomBarHeight = true;
441 fBarHeight = barHeight;
442
443 if (barHeight == oldHeight)
444 return;
445
446 // resize so that the height fits
447 if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
448 InvalidateLayout();
449 else {
450 float width, height;
451 GetPreferredSize(&width, &height);
452 ResizeTo(Bounds().Width(), height);
453 }
454 }
455
456
457 void
SetText(const char * string)458 BStatusBar::SetText(const char* string)
459 {
460 _SetTextData(fText, string, fLabel, false);
461 }
462
463
464 void
SetTrailingText(const char * string)465 BStatusBar::SetTrailingText(const char* string)
466 {
467 _SetTextData(fTrailingText, string, fTrailingLabel, true);
468 }
469
470
471 void
SetMaxValue(float max)472 BStatusBar::SetMaxValue(float max)
473 {
474 // R5 and/or Zeta's SetMaxValue does not trigger an invalidate here.
475 // this is probably not ideal behavior, but it does break apps in some cases
476 // as observed with SpaceMonitor.
477 // TODO: revisit this when we break binary compatibility
478 fMax = max;
479 }
480
481
482 void
Update(float delta,const char * text,const char * trailingText)483 BStatusBar::Update(float delta, const char* text, const char* trailingText)
484 {
485 // If any of these are NULL, the existing text remains (BeBook)
486 if (text == NULL)
487 text = fText.String();
488 if (trailingText == NULL)
489 trailingText = fTrailingText.String();
490 BStatusBar::SetTo(fCurrent + delta, text, trailingText);
491 }
492
493
494 void
Reset(const char * label,const char * trailingLabel)495 BStatusBar::Reset(const char *label, const char *trailingLabel)
496 {
497 // Reset replaces the label and trailing label with copies of the
498 // strings passed as arguments. If either argument is NULL, the
499 // label or trailing label will be deleted and erased.
500 fLabel = label ? label : "";
501 fTrailingLabel = trailingLabel ? trailingLabel : "";
502
503 // Reset deletes and erases any text or trailing text
504 fText = "";
505 fTrailingText = "";
506
507 fCurrent = 0;
508 fMax = 100;
509
510 Invalidate();
511 }
512
513
514 void
SetTo(float value,const char * text,const char * trailingText)515 BStatusBar::SetTo(float value, const char* text, const char* trailingText)
516 {
517 SetText(text);
518 SetTrailingText(trailingText);
519
520 if (value > fMax)
521 value = fMax;
522 else if (value < 0)
523 value = 0;
524 if (value == fCurrent)
525 return;
526
527 BRect barFrame = _BarFrame();
528 float oldPosition = _BarPosition(barFrame);
529
530 fCurrent = value;
531
532 float newPosition = _BarPosition(barFrame);
533 if (oldPosition == newPosition)
534 return;
535
536 // update only the part of the status bar with actual changes
537 BRect update = barFrame;
538 if (oldPosition < newPosition) {
539 update.left = floorf(max_c(oldPosition - 1, update.left));
540 update.right = ceilf(newPosition);
541 } else {
542 update.left = floorf(max_c(newPosition - 1, update.left));
543 update.right = ceilf(oldPosition);
544 }
545
546 // TODO: Ask the BControlLook in the first place about dirty rect.
547 update.InsetBy(-1, -1);
548
549 Invalidate(update);
550 }
551
552
553 float
CurrentValue() const554 BStatusBar::CurrentValue() const
555 {
556 return fCurrent;
557 }
558
559
560 float
MaxValue() const561 BStatusBar::MaxValue() const
562 {
563 return fMax;
564 }
565
566
567 rgb_color
BarColor() const568 BStatusBar::BarColor() const
569 {
570 return fBarColor;
571 }
572
573
574 float
BarHeight() const575 BStatusBar::BarHeight() const
576 {
577 if (!fCustomBarHeight && fBarHeight == -1) {
578 // the default bar height is as height as the label
579 font_height fontHeight;
580 GetFontHeight(&fontHeight);
581 const_cast<BStatusBar *>(this)->fBarHeight = fontHeight.ascent
582 + fontHeight.descent + 5;
583 }
584
585 return ceilf(fBarHeight);
586 }
587
588
589 const char *
Text() const590 BStatusBar::Text() const
591 {
592 return fText.String();
593 }
594
595
596 const char *
TrailingText() const597 BStatusBar::TrailingText() const
598 {
599 return fTrailingText.String();
600 }
601
602
603 const char *
Label() const604 BStatusBar::Label() const
605 {
606 return fLabel.String();
607 }
608
609
610 const char *
TrailingLabel() const611 BStatusBar::TrailingLabel() const
612 {
613 return fTrailingLabel.String();
614 }
615
616
617 // #pragma mark -
618
619
620 BHandler *
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 what,const char * property)621 BStatusBar::ResolveSpecifier(BMessage* message, int32 index,
622 BMessage* specifier, int32 what, const char *property)
623 {
624 return BView::ResolveSpecifier(message, index, specifier, what, property);
625 }
626
627
628 status_t
GetSupportedSuites(BMessage * data)629 BStatusBar::GetSupportedSuites(BMessage* data)
630 {
631 return BView::GetSupportedSuites(data);
632 }
633
634
635 status_t
Perform(perform_code code,void * _data)636 BStatusBar::Perform(perform_code code, void* _data)
637 {
638 switch (code) {
639 case PERFORM_CODE_MIN_SIZE:
640 ((perform_data_min_size*)_data)->return_value
641 = BStatusBar::MinSize();
642 return B_OK;
643 case PERFORM_CODE_MAX_SIZE:
644 ((perform_data_max_size*)_data)->return_value
645 = BStatusBar::MaxSize();
646 return B_OK;
647 case PERFORM_CODE_PREFERRED_SIZE:
648 ((perform_data_preferred_size*)_data)->return_value
649 = BStatusBar::PreferredSize();
650 return B_OK;
651 case PERFORM_CODE_LAYOUT_ALIGNMENT:
652 ((perform_data_layout_alignment*)_data)->return_value
653 = BStatusBar::LayoutAlignment();
654 return B_OK;
655 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
656 ((perform_data_has_height_for_width*)_data)->return_value
657 = BStatusBar::HasHeightForWidth();
658 return B_OK;
659 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
660 {
661 perform_data_get_height_for_width* data
662 = (perform_data_get_height_for_width*)_data;
663 BStatusBar::GetHeightForWidth(data->width, &data->min, &data->max,
664 &data->preferred);
665 return B_OK;
666 }
667 case PERFORM_CODE_SET_LAYOUT:
668 {
669 perform_data_set_layout* data = (perform_data_set_layout*)_data;
670 BStatusBar::SetLayout(data->layout);
671 return B_OK;
672 }
673 case PERFORM_CODE_LAYOUT_INVALIDATED:
674 {
675 perform_data_layout_invalidated* data
676 = (perform_data_layout_invalidated*)_data;
677 BStatusBar::LayoutInvalidated(data->descendants);
678 return B_OK;
679 }
680 case PERFORM_CODE_DO_LAYOUT:
681 {
682 BStatusBar::DoLayout();
683 return B_OK;
684 }
685 }
686
687 return BView::Perform(code, _data);
688 }
689
690
691 // #pragma mark -
692
693
694 extern "C" void
_ReservedStatusBar1__10BStatusBar(BStatusBar * self,float value,const char * text,const char * trailingText)695 _ReservedStatusBar1__10BStatusBar(BStatusBar* self, float value,
696 const char* text, const char* trailingText)
697 {
698 self->BStatusBar::SetTo(value, text, trailingText);
699 }
700
701
_ReservedStatusBar2()702 void BStatusBar::_ReservedStatusBar2() {}
_ReservedStatusBar3()703 void BStatusBar::_ReservedStatusBar3() {}
_ReservedStatusBar4()704 void BStatusBar::_ReservedStatusBar4() {}
705
706
707 BStatusBar &
operator =(const BStatusBar & other)708 BStatusBar::operator=(const BStatusBar& other)
709 {
710 return *this;
711 }
712
713
714 // #pragma mark -
715
716
717 void
_InitObject()718 BStatusBar::_InitObject()
719 {
720 fMax = 100.0;
721 fCurrent = 0.0;
722
723 fBarHeight = -1.0;
724 fTextDivider = Bounds().Width();
725
726 fCustomBarHeight = false;
727 fInternalFlags = 0;
728
729 SetFlags(Flags() | B_FRAME_EVENTS);
730 }
731
732
733 void
_SetTextData(BString & text,const char * source,const BString & combineWith,bool rightAligned)734 BStatusBar::_SetTextData(BString& text, const char* source,
735 const BString& combineWith, bool rightAligned)
736 {
737 if (source == NULL)
738 source = "";
739
740 // If there were no changes, we don't have to do anything
741 if (text == source)
742 return;
743
744 bool oldHasText = _HasText();
745 text = source;
746
747 BString newString;
748 if (rightAligned)
749 newString << text << combineWith;
750 else
751 newString << combineWith << text;
752
753 if (oldHasText != _HasText())
754 InvalidateLayout();
755
756 font_height fontHeight;
757 GetFontHeight(&fontHeight);
758
759 // Invalidate(BRect(position, 0, position + invalidateWidth,
760 // ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)));
761 // TODO: redrawing the entire area takes care of the edge case
762 // where the left side string changes because of truncation and
763 // part of it needs to be redrawn as well.
764 Invalidate(BRect(0, 0, Bounds().right,
765 ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)));
766 }
767
768
769 /*!
770 Returns the inner bar frame without the surrounding bevel.
771 */
772 BRect
_BarFrame(const font_height * fontHeight) const773 BStatusBar::_BarFrame(const font_height* fontHeight) const
774 {
775 float top = 2;
776 if (_HasText()) {
777 if (fontHeight == NULL) {
778 font_height height;
779 GetFontHeight(&height);
780 top = ceilf(height.ascent + height.descent) + 6;
781 } else
782 top = ceilf(fontHeight->ascent + fontHeight->descent) + 6;
783 }
784
785 return BRect(2, top, Bounds().right - 2, top + BarHeight() - 4);
786 }
787
788
789 float
_BarPosition(const BRect & barFrame) const790 BStatusBar::_BarPosition(const BRect& barFrame) const
791 {
792 if (fCurrent == 0)
793 return barFrame.left - 1;
794
795 return roundf(barFrame.left - 1
796 + (fCurrent * (barFrame.Width() + 3) / fMax));
797 }
798
799
800 bool
_HasText() const801 BStatusBar::_HasText() const
802 {
803 // Force BeOS behavior where the size of the BStatusBar always included
804 // room for labels, even when there weren't any.
805 if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
806 return true;
807 return fLabel.Length() > 0 || fTrailingLabel.Length() > 0
808 || fTrailingText.Length() > 0 || fText.Length() > 0;
809 }
810