* Copyright 2001-2015, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Marc Flerackers (mflerackers@androme.be)
* Axel Dörfler, axeld@pinc-software.de
* Stephan Aßmus <superstippi@gmx.de>
* Joseph Groover <looncraz@looncraz.net>
*/
#include <StatusBar.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ControlLook.h>
#include <Layout.h>
#include <LayoutUtils.h>
#include <Message.h>
#include <Region.h>
#include <binary_compatibility/Interface.h>
enum internalFlags {
kCustomBarColor = 1
};
BStatusBar::BStatusBar(BRect frame, const char *name, const char *label,
const char *trailingLabel)
:
BView(frame, name, B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW),
fLabel(label),
fTrailingLabel(trailingLabel)
{
_InitObject();
}
BStatusBar::BStatusBar(const char *name, const char *label,
const char *trailingLabel)
:
BView(BRect(0, 0, -1, -1), name, B_FOLLOW_LEFT | B_FOLLOW_TOP,
B_WILL_DRAW | B_SUPPORTS_LAYOUT),
fLabel(label),
fTrailingLabel(trailingLabel)
{
_InitObject();
}
BStatusBar::BStatusBar(BMessage *archive)
:
BView(archive)
{
_InitObject();
archive->FindString("_label", &fLabel);
archive->FindString("_tlabel", &fTrailingLabel);
archive->FindString("_text", &fText);
archive->FindString("_ttext", &fTrailingText);
float floatValue;
if (archive->FindFloat("_high", &floatValue) == B_OK) {
fBarHeight = floatValue;
fCustomBarHeight = true;
}
int32 color;
if (archive->FindInt32("_bcolor", (int32 *)&color) == B_OK) {
fBarColor = *(rgb_color *)&color;
fInternalFlags |= kCustomBarColor;
}
if (archive->FindFloat("_val", &floatValue) == B_OK)
fCurrent = floatValue;
if (archive->FindFloat("_max", &floatValue) == B_OK)
fMax = floatValue;
}
BStatusBar::~BStatusBar()
{
}
BArchivable *
BStatusBar::Instantiate(BMessage *archive)
{
if (validate_instantiation(archive, "BStatusBar"))
return new BStatusBar(archive);
return NULL;
}
status_t
BStatusBar::Archive(BMessage *archive, bool deep) const
{
status_t err = BView::Archive(archive, deep);
if (err < B_OK)
return err;
if (fCustomBarHeight)
err = archive->AddFloat("_high", fBarHeight);
if (err == B_OK && fInternalFlags & kCustomBarColor)
err = archive->AddInt32("_bcolor", (const uint32 &)fBarColor);
if (err == B_OK && fCurrent != 0)
err = archive->AddFloat("_val", fCurrent);
if (err == B_OK && fMax != 100 )
err = archive->AddFloat("_max", fMax);
if (err == B_OK && fText.Length())
err = archive->AddString("_text", fText);
if (err == B_OK && fTrailingText.Length())
err = archive->AddString("_ttext", fTrailingText);
if (err == B_OK && fLabel.Length())
err = archive->AddString("_label", fLabel);
if (err == B_OK && fTrailingLabel.Length())
err = archive->AddString ("_tlabel", fTrailingLabel);
return err;
}
void
BStatusBar::AttachedToWindow()
{
float width, height;
GetPreferredSize(&width, &height);
ResizeTo(Bounds().Width(), height);
SetViewColor(B_TRANSPARENT_COLOR);
AdoptParentColors();
fTextDivider = Bounds().Width();
if ((fInternalFlags & kCustomBarColor) == 0)
fBarColor = ui_color(B_STATUS_BAR_COLOR);
}
void
BStatusBar::DetachedFromWindow()
{
BView::DetachedFromWindow();
}
void
BStatusBar::AllAttached()
{
BView::AllAttached();
}
void
BStatusBar::AllDetached()
{
BView::AllDetached();
}
void
BStatusBar::WindowActivated(bool state)
{
BView::WindowActivated(state);
}
void
BStatusBar::MakeFocus(bool state)
{
BView::MakeFocus(state);
}
void
BStatusBar::GetPreferredSize(float* _width, float* _height)
{
if (_width) {
*_width = ceilf(StringWidth(fLabel.String()))
+ ceilf(StringWidth(fTrailingLabel.String()))
+ ceilf(StringWidth(fText.String()))
+ ceilf(StringWidth(fTrailingText.String()))
+ 5;
}
if (_height) {
float labelHeight = 0;
if (_HasText()) {
font_height fontHeight;
GetFontHeight(&fontHeight);
labelHeight = ceilf(fontHeight.ascent + fontHeight.descent) + 6;
}
*_height = labelHeight + BarHeight();
}
}
BSize
BStatusBar::MinSize()
{
float width, height;
GetPreferredSize(&width, &height);
return BLayoutUtils::ComposeSize(ExplicitMinSize(), BSize(width, height));
}
BSize
BStatusBar::MaxSize()
{
float width, height;
GetPreferredSize(&width, &height);
return BLayoutUtils::ComposeSize(ExplicitMaxSize(),
BSize(B_SIZE_UNLIMITED, height));
}
BSize
BStatusBar::PreferredSize()
{
float width, height;
GetPreferredSize(&width, &height);
return BLayoutUtils::ComposeSize(ExplicitPreferredSize(),
BSize(width, height));
}
void
BStatusBar::ResizeToPreferred()
{
BView::ResizeToPreferred();
}
void
BStatusBar::FrameMoved(BPoint newPosition)
{
BView::FrameMoved(newPosition);
}
void
BStatusBar::FrameResized(float newWidth, float newHeight)
{
BView::FrameResized(newWidth, newHeight);
Invalidate();
}
void
BStatusBar::Draw(BRect updateRect)
{
rgb_color backgroundColor = LowColor();
font_height fontHeight;
GetFontHeight(&fontHeight);
BRect barFrame = _BarFrame(&fontHeight);
BRect outerFrame = barFrame.InsetByCopy(-2, -2);
BRegion background(updateRect);
background.Exclude(outerFrame);
FillRegion(&background, B_SOLID_LOW);
BRect rect = outerFrame;
rect.top = 0;
rect.bottom = outerFrame.top - 1;
if (updateRect.Intersects(rect)) {
BString leftText;
leftText << fLabel << fText;
BString rightText;
rightText << fTrailingText << fTrailingLabel;
float baseLine = ceilf(fontHeight.ascent) + 1;
fTextDivider = rect.right;
BFont font;
GetFont(&font);
if (rightText.Length()) {
font.TruncateString(&rightText, B_TRUNCATE_BEGINNING,
rect.Width());
fTextDivider -= StringWidth(rightText.String());
}
if (leftText.Length()) {
float width = max_c(0.0, fTextDivider - rect.left);
font.TruncateString(&leftText, B_TRUNCATE_END, width);
}
rgb_color textColor = ui_color(B_PANEL_TEXT_COLOR);
if (backgroundColor != ui_color(B_PANEL_BACKGROUND_COLOR)) {
if (backgroundColor.IsLight())
textColor = make_color(0, 0, 0, 255);
else
textColor = make_color(255, 255, 255, 255);
}
SetHighColor(textColor);
if (leftText.Length())
DrawString(leftText.String(), BPoint(rect.left, baseLine));
if (rightText.Length())
DrawString(rightText.String(), BPoint(fTextDivider, baseLine));
}
if (!updateRect.Intersects(outerFrame))
return;
rect = outerFrame;
be_control_look->DrawStatusBar(this, rect, updateRect,
backgroundColor, fBarColor, _BarPosition(barFrame));
}
void
BStatusBar::MessageReceived(BMessage *message)
{
switch(message->what) {
case B_UPDATE_STATUS_BAR:
{
float delta;
const char *text = NULL, *trailing_text = NULL;
message->FindFloat("delta", &delta);
message->FindString("text", &text);
message->FindString("trailing_text", &trailing_text);
Update(delta, text, trailing_text);
break;
}
case B_RESET_STATUS_BAR:
{
const char *label = NULL, *trailing_label = NULL;
message->FindString("label", &label);
message->FindString("trailing_label", &trailing_label);
Reset(label, trailing_label);
break;
}
case B_COLORS_UPDATED:
{
rgb_color color;
if (message->FindColor(ui_color_name(B_STATUS_BAR_COLOR), &color) == B_OK) {
if ((fInternalFlags & kCustomBarColor) == 0)
fBarColor = color;
}
break;
}
default:
BView::MessageReceived(message);
break;
}
}
void
BStatusBar::MouseDown(BPoint point)
{
BView::MouseDown(point);
}
void
BStatusBar::MouseUp(BPoint point)
{
BView::MouseUp(point);
}
void
BStatusBar::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
{
BView::MouseMoved(point, transit, message);
}
void
BStatusBar::SetBarColor(rgb_color color)
{
fInternalFlags |= kCustomBarColor;
fBarColor = color;
Invalidate();
}
void
BStatusBar::SetBarHeight(float barHeight)
{
float oldHeight = BarHeight();
fCustomBarHeight = true;
fBarHeight = barHeight;
if (barHeight == oldHeight)
return;
if ((Flags() & B_SUPPORTS_LAYOUT) != 0)
InvalidateLayout();
else {
float width, height;
GetPreferredSize(&width, &height);
ResizeTo(Bounds().Width(), height);
}
}
void
BStatusBar::SetText(const char* string)
{
_SetTextData(fText, string, fLabel, false);
}
void
BStatusBar::SetTrailingText(const char* string)
{
_SetTextData(fTrailingText, string, fTrailingLabel, true);
}
void
BStatusBar::SetMaxValue(float max)
{
fMax = max;
}
void
BStatusBar::Update(float delta, const char* text, const char* trailingText)
{
if (text == NULL)
text = fText.String();
if (trailingText == NULL)
trailingText = fTrailingText.String();
BStatusBar::SetTo(fCurrent + delta, text, trailingText);
}
void
BStatusBar::Reset(const char *label, const char *trailingLabel)
{
fLabel = label ? label : "";
fTrailingLabel = trailingLabel ? trailingLabel : "";
fText = "";
fTrailingText = "";
fCurrent = 0;
fMax = 100;
Invalidate();
}
void
BStatusBar::SetTo(float value, const char* text, const char* trailingText)
{
SetText(text);
SetTrailingText(trailingText);
if (value > fMax)
value = fMax;
else if (value < 0)
value = 0;
if (value == fCurrent)
return;
BRect barFrame = _BarFrame();
float oldPosition = _BarPosition(barFrame);
fCurrent = value;
float newPosition = _BarPosition(barFrame);
if (oldPosition == newPosition)
return;
BRect update = barFrame;
if (oldPosition < newPosition) {
update.left = floorf(max_c(oldPosition - 1, update.left));
update.right = ceilf(newPosition);
} else {
update.left = floorf(max_c(newPosition - 1, update.left));
update.right = ceilf(oldPosition);
}
update.InsetBy(-1, -1);
Invalidate(update);
}
float
BStatusBar::CurrentValue() const
{
return fCurrent;
}
float
BStatusBar::MaxValue() const
{
return fMax;
}
rgb_color
BStatusBar::BarColor() const
{
return fBarColor;
}
float
BStatusBar::BarHeight() const
{
if (!fCustomBarHeight && fBarHeight == -1) {
font_height fontHeight;
GetFontHeight(&fontHeight);
const_cast<BStatusBar *>(this)->fBarHeight = fontHeight.ascent
+ fontHeight.descent + 5;
}
return ceilf(fBarHeight);
}
const char *
BStatusBar::Text() const
{
return fText.String();
}
const char *
BStatusBar::TrailingText() const
{
return fTrailingText.String();
}
const char *
BStatusBar::Label() const
{
return fLabel.String();
}
const char *
BStatusBar::TrailingLabel() const
{
return fTrailingLabel.String();
}
BHandler *
BStatusBar::ResolveSpecifier(BMessage* message, int32 index,
BMessage* specifier, int32 what, const char *property)
{
return BView::ResolveSpecifier(message, index, specifier, what, property);
}
status_t
BStatusBar::GetSupportedSuites(BMessage* data)
{
return BView::GetSupportedSuites(data);
}
status_t
BStatusBar::Perform(perform_code code, void* _data)
{
switch (code) {
case PERFORM_CODE_MIN_SIZE:
((perform_data_min_size*)_data)->return_value
= BStatusBar::MinSize();
return B_OK;
case PERFORM_CODE_MAX_SIZE:
((perform_data_max_size*)_data)->return_value
= BStatusBar::MaxSize();
return B_OK;
case PERFORM_CODE_PREFERRED_SIZE:
((perform_data_preferred_size*)_data)->return_value
= BStatusBar::PreferredSize();
return B_OK;
case PERFORM_CODE_LAYOUT_ALIGNMENT:
((perform_data_layout_alignment*)_data)->return_value
= BStatusBar::LayoutAlignment();
return B_OK;
case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
((perform_data_has_height_for_width*)_data)->return_value
= BStatusBar::HasHeightForWidth();
return B_OK;
case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
{
perform_data_get_height_for_width* data
= (perform_data_get_height_for_width*)_data;
BStatusBar::GetHeightForWidth(data->width, &data->min, &data->max,
&data->preferred);
return B_OK;
}
case PERFORM_CODE_SET_LAYOUT:
{
perform_data_set_layout* data = (perform_data_set_layout*)_data;
BStatusBar::SetLayout(data->layout);
return B_OK;
}
case PERFORM_CODE_LAYOUT_INVALIDATED:
{
perform_data_layout_invalidated* data
= (perform_data_layout_invalidated*)_data;
BStatusBar::LayoutInvalidated(data->descendants);
return B_OK;
}
case PERFORM_CODE_DO_LAYOUT:
{
BStatusBar::DoLayout();
return B_OK;
}
}
return BView::Perform(code, _data);
}
extern "C" void
_ReservedStatusBar1__10BStatusBar(BStatusBar* self, float value,
const char* text, const char* trailingText)
{
self->BStatusBar::SetTo(value, text, trailingText);
}
void BStatusBar::_ReservedStatusBar2() {}
void BStatusBar::_ReservedStatusBar3() {}
void BStatusBar::_ReservedStatusBar4() {}
BStatusBar &
BStatusBar::operator=(const BStatusBar& other)
{
return *this;
}
void
BStatusBar::_InitObject()
{
fMax = 100.0;
fCurrent = 0.0;
fBarHeight = -1.0;
fTextDivider = Bounds().Width();
fCustomBarHeight = false;
fInternalFlags = 0;
SetFlags(Flags() | B_FRAME_EVENTS);
}
void
BStatusBar::_SetTextData(BString& text, const char* source,
const BString& combineWith, bool rightAligned)
{
if (source == NULL)
source = "";
if (text == source)
return;
bool oldHasText = _HasText();
text = source;
BString newString;
if (rightAligned)
newString << text << combineWith;
else
newString << combineWith << text;
if (oldHasText != _HasText())
InvalidateLayout();
font_height fontHeight;
GetFontHeight(&fontHeight);
Invalidate(BRect(0, 0, Bounds().right,
ceilf(fontHeight.ascent) + ceilf(fontHeight.descent)));
}
Returns the inner bar frame without the surrounding bevel.
*/
BRect
BStatusBar::_BarFrame(const font_height* fontHeight) const
{
float top = 2;
if (_HasText()) {
if (fontHeight == NULL) {
font_height height;
GetFontHeight(&height);
top = ceilf(height.ascent + height.descent) + 6;
} else
top = ceilf(fontHeight->ascent + fontHeight->descent) + 6;
}
return BRect(2, top, Bounds().right - 2, top + BarHeight() - 4);
}
float
BStatusBar::_BarPosition(const BRect& barFrame) const
{
if (fCurrent == 0)
return barFrame.left - 1;
return roundf(barFrame.left - 1
+ (fCurrent * (barFrame.Width() + 3) / fMax));
}
bool
BStatusBar::_HasText() const
{
if ((Flags() & B_SUPPORTS_LAYOUT) == 0)
return true;
return fLabel.Length() > 0 || fTrailingLabel.Length() > 0
|| fTrailingText.Length() > 0 || fText.Length() > 0;
}