* Copyright (c) 1999-2000, Eric Moon.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions, and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "StatusView.h"
#include "cortex_ui.h"
#include "RouteAppNodeManager.h"
#include "TipManager.h"
#include <Message.h>
#include <MessageRunner.h>
#include <Bitmap.h>
#include <Font.h>
#include <ScrollBar.h>
#include <Window.h>
#include <Beep.h>
__USE_CORTEX_NAMESPACE
#include <Debug.h>
#define D_ALLOC(x)
#define D_HOOK(x)
#define D_MESSAGE(x)
#define D_OPERATION(x)
const bigtime_t TICK_PERIOD = 50000;
const bigtime_t TEXT_DECAY_DELAY = 10 * 1000 * 1000;
const bigtime_t TEXT_DECAY_TIME = 3 * 1000 * 1000;
const unsigned char ERROR_ICON_BITS [] = {
0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,
0xff,0x00,0xfa,0xfa,0x00,0xff,0xff,0xff,
0x00,0x3f,0x3f,0xfa,0xfa,0x00,0xff,0xff,
0x00,0xf9,0xf9,0x3f,0x5d,0x00,0xff,0xff,
0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff,
0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff,
0x00,0x00,0xf9,0x5d,0x00,0x00,0xff,0xff,
0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,
0x00,0xf9,0x00,0x00,0x5d,0x00,0xff,0xff,
0x00,0xf9,0xf9,0x5d,0x5d,0x00,0xff,0xff,
0x00,0x00,0xf9,0x5d,0x00,0x00,0x0f,0xff,
0xff,0xff,0x00,0x00,0x00,0x0f,0x0f,0x0f,
};
const unsigned char INFO_ICON_BITS [] = {
0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,
0xff,0x00,0x21,0x21,0x21,0x00,0xff,0xff,
0xff,0x00,0x21,0x92,0x25,0x00,0xff,0xff,
0xff,0x00,0x21,0x25,0x25,0x00,0xff,0xff,
0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,
0xff,0x00,0x92,0x92,0x25,0x00,0xff,0xff,
0xff,0x00,0x21,0x21,0x25,0x00,0xff,0xff,
0xff,0x00,0x21,0x21,0x25,0x00,0xff,0xff,
0x00,0x00,0x21,0x21,0x25,0x00,0x00,0xff,
0x00,0xbe,0x21,0x21,0x92,0xbe,0x25,0x00,
0x00,0x00,0x21,0x21,0x21,0x25,0x00,0x0f,
0xff,0xff,0x00,0x00,0x00,0x00,0x0f,0x0f,
};
StatusView::StatusView(
BRect frame,
RouteAppNodeManager *manager,
BScrollBar *scrollBar)
: BStringView(frame, "StatusView", "", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM,
B_FRAME_EVENTS | B_WILL_DRAW),
m_scrollBar(scrollBar),
m_icon(0),
m_opacity(1.0),
m_clock(0),
m_dragging(false),
m_backBitmap(0),
m_backView(0),
m_dirty(true),
m_manager(manager) {
D_ALLOC(("StatusView::StatusView()\n"));
SetViewColor(B_TRANSPARENT_COLOR);
SetFont(be_plain_font);
allocBackBitmap(frame.Width(), frame.Height());
}
StatusView::~StatusView() {
D_ALLOC(("StatusView::~ParameterContainerView()\n"));
TipManager *manager = TipManager::Instance();
manager->removeAll(this);
delete m_clock;
freeBackBitmap();
}
void StatusView::AttachedToWindow() {
D_HOOK(("StatusView::AttachedToWindow()\n"));
if (m_manager) {
m_manager->setLogTarget(BMessenger(this, Window()));
}
allocBackBitmap(Bounds().Width(), Bounds().Height());
}
void StatusView::Draw(
BRect updateRect) {
D_HOOK(("StatusView::Draw()\n"));
if(!m_backView) {
drawInto(this, updateRect);
} else {
if(m_dirty) {
m_backBitmap->Lock();
drawInto(m_backView, updateRect);
m_backView->Sync();
m_backBitmap->Unlock();
m_dirty = false;
}
SetDrawingMode(B_OP_COPY);
DrawBitmap(m_backBitmap, updateRect, updateRect);
}
}
void StatusView::FrameResized(
float width,
float height) {
D_HOOK(("StatusView::FrameResized()\n"));
allocBackBitmap(width, height);
TipManager *manager = TipManager::Instance();
manager->removeAll(this);
BString text = m_fullText;
if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) {
be_plain_font->TruncateString(&text, B_TRUNCATE_END,
Bounds().Width() - 25.0);
manager->setTip(m_fullText.String(), this);
}
BStringView::SetText(text.String());
float minWidth, maxWidth, minHeight, maxHeight;
Window()->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
minWidth = width + 6 * B_V_SCROLL_BAR_WIDTH;
Window()->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
}
void StatusView::MessageReceived(
BMessage *message) {
D_MESSAGE(("StatusView::MessageReceived()\n"));
switch (message->what) {
case 'Tick':
fadeTick();
break;
case RouteAppNodeManager::M_LOG: {
D_MESSAGE((" -> RouteAppNodeManager::M_LOG\n"));
BString title;
if (message->FindString("title", &title) != B_OK) {
return;
}
BString details, line;
for (int32 i = 0; message->FindString("line", i, &line) == B_OK; i++) {
if (details.CountChars() > 0) {
details << "\n";
}
details << line;
}
status_t error = B_OK;
message->FindInt32("error", &error);
setMessage(title, details, error);
break;
}
default: {
BStringView::MessageReceived(message);
}
}
}
void StatusView::MouseDown(
BPoint point) {
D_HOOK(("StatusView::MouseDown()\n"));
int32 buttons;
if (Window()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) {
buttons = B_PRIMARY_MOUSE_BUTTON;
}
if (buttons == B_PRIMARY_MOUSE_BUTTON) {
BRect dragRect(Bounds());
dragRect.left = dragRect.right - 10.0;
if (dragRect.Contains(point)) {
m_dragging = true;
SetMouseEventMask(B_POINTER_EVENTS,
B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
}
}
}
void StatusView::MouseMoved(
BPoint point,
uint32 transit,
const BMessage *message) {
D_HOOK(("StatusView::MouseMoved()\n"));
if (m_dragging) {
float x = point.x - (Bounds().right - 5.0);
if ((Bounds().Width() + x) <= 16.0) {
return;
}
if (m_scrollBar
&& ((m_scrollBar->Bounds().Width() - x) <= (6 * B_V_SCROLL_BAR_WIDTH))) {
return;
}
ResizeBy(x, 0.0);
BRect r(Bounds());
r.left = r.right - 5.0;
if (x > 0)
r.left -= x;
m_dirty = true;
Invalidate(r);
if (m_scrollBar) {
m_scrollBar->ResizeBy(-x, 0.0);
m_scrollBar->MoveBy(x, 0.0);
}
}
}
void StatusView::MouseUp(
BPoint point) {
D_HOOK(("StatusView::MouseUp()\n"));
m_dragging = false;
}
void
StatusView::drawInto(BView *v, BRect updateRect)
{
BRect r(Bounds());
D_OPERATION(("StatusView::drawInto(%.1f, %.1f)\n", r.Width(), r.Height()));
v->SetDrawingMode(B_OP_COPY);
v->BeginLineArray(8);
v->AddLine(r.LeftTop(), r.RightTop(), M_MED_GRAY_COLOR);
BPoint rtop = r.RightTop();
rtop.y++;
v->AddLine(rtop, r.RightBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT));
v->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR);
v->AddLine(r.LeftBottom(), r.LeftTop(), M_MED_GRAY_COLOR);
r.InsetBy(1.0, 1.0);
v->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR);
rtop.y++;
rtop.x--;
v->AddLine(rtop, r.RightBottom(), M_GRAY_COLOR);
v->AddLine(r.RightBottom(), r.LeftBottom(), tint_color(M_MED_GRAY_COLOR, B_LIGHTEN_1_TINT));
v->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR);
v->EndLineArray();
r.InsetBy(1.0, 1.0);
v->SetLowColor(M_GRAY_COLOR);
v->FillRect(r, B_SOLID_LOW);
r.InsetBy(2.0, 0.0);
v->SetDrawingMode(B_OP_ALPHA);
v->SetHighColor(0, 0, 0, uchar(255 * m_opacity));
if (m_icon) {
v->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_OVERLAY);
BPoint p = r.LeftTop();
p.y--;
v->DrawBitmap(m_icon, p);
}
r.left += 10.0;
font_height fh;
be_plain_font->GetHeight(&fh);
r.bottom = Bounds().bottom - fh.descent
- (Bounds().Height() - fh.ascent - fh.descent) / 2;
v->MovePenTo(r.LeftBottom());
v->DrawString(Text());
v->SetDrawingMode(B_OP_OVER);
r = Bounds();
r.right -= 2.0;
r.left = r.right - 2.0;
r.InsetBy(0.0, 3.0);
r.top += 1.0;
for (int32 i = 0; i < r.IntegerHeight(); i += 3) {
BPoint p = r.LeftTop() + BPoint(0.0, i);
v->SetHighColor(M_MED_GRAY_COLOR);
v->StrokeLine(p, p, B_SOLID_HIGH);
p += BPoint(1.0, 1.0);
v->SetHighColor(M_WHITE_COLOR);
v->StrokeLine(p, p, B_SOLID_HIGH);
}
}
void StatusView::setMessage(
BString &title,
BString &details,
status_t error) {
D_OPERATION(("StatusView::setMessage(%s)\n", title.String()));
TipManager *manager = TipManager::Instance();
manager->removeAll(this);
if (error) {
title << " (" << strerror(error) << ")";
}
bool truncated = false;
m_fullText = title;
if (be_plain_font->StringWidth(title.String()) > Bounds().Width() - 25.0) {
be_plain_font->TruncateString(&title, B_TRUNCATE_END,
Bounds().Width() - 25.0);
truncated = true;
}
BStringView::SetText(title.String());
if (truncated || details.CountChars() > 0) {
BString tip = m_fullText;
if (details.CountChars() > 0) {
tip << "\n" << details;
}
manager->setTip(tip.String(), this);
}
if (error) {
beep();
if (m_icon) {
delete m_icon;
m_icon = 0;
}
BRect iconRect(0.0, 0.0, 7.0, 11.0);
m_icon = new BBitmap(iconRect, B_CMAP8);
m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8);
}
else {
if (m_icon) {
delete m_icon;
m_icon = 0;
}
BRect iconRect(0.0, 0.0, 7.0, 11.0);
m_icon = new BBitmap(iconRect, B_CMAP8);
m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8);
}
m_dirty = true;
startFade();
Invalidate();
}
void StatusView::setErrorMessage(
BString text,
bool error) {
D_OPERATION(("StatusView::setErrorMessage(%s)\n",
text.String()));
TipManager *manager = TipManager::Instance();
manager->removeAll(this);
m_fullText = text;
if (be_plain_font->StringWidth(text.String()) > Bounds().Width() - 25.0) {
be_plain_font->TruncateString(&text, B_TRUNCATE_END,
Bounds().Width() - 25.0);
manager->setTip(m_fullText.String(), this);
}
BStringView::SetText(text.String());
if (error) {
beep();
if (m_icon) {
delete m_icon;
m_icon = 0;
}
BRect iconRect(0.0, 0.0, 7.0, 11.0);
m_icon = new BBitmap(iconRect, B_CMAP8);
m_icon->SetBits(ERROR_ICON_BITS, 96, 0, B_CMAP8);
}
else {
if (m_icon) {
delete m_icon;
m_icon = 0;
}
BRect iconRect(0.0, 0.0, 7.0, 11.0);
m_icon = new BBitmap(iconRect, B_CMAP8);
m_icon->SetBits(INFO_ICON_BITS, 96, 0, B_CMAP8);
}
m_dirty = true;
startFade();
Invalidate();
}
void StatusView::startFade() {
D_OPERATION(("StatusView::startFade()\n"));
m_opacity = 1.0;
m_decayDelay = TEXT_DECAY_DELAY;
if(!m_clock) {
m_clock = new BMessageRunner(
BMessenger(this),
new BMessage('Tick'),
TICK_PERIOD);
}
}
void StatusView::fadeTick() {
D_HOOK(("StatusView::fadeTick()\n"));
if (m_opacity > 0.0) {
if(m_decayDelay > 0) {
m_decayDelay -= TICK_PERIOD;
return;
}
float steps = static_cast<float>(TEXT_DECAY_TIME)
/ static_cast<float>(TICK_PERIOD);
m_opacity -= (1.0 / steps);
if (m_opacity < 0.001) {
m_opacity = 0.0;
}
m_dirty = true;
Invalidate();
}
else if (m_clock) {
delete m_clock;
m_clock = 0;
TipManager *manager = TipManager::Instance();
manager->removeAll(this);
}
}
void StatusView::allocBackBitmap(float width, float height) {
D_OPERATION(("StatusView::allocBackBitmap(%.1f, %.1f)\n", width, height));
if(width <= 0.0 || height <= 0.0)
return;
if(m_backBitmap) {
BRect b = m_backBitmap->Bounds();
if(b.Width() >= width && b.Height() >= height)
return;
freeBackBitmap();
}
BRect b(0.0, 0.0, width, height);
m_backBitmap = new BBitmap(b, B_RGB32, true);
if(!m_backBitmap) {
D_OPERATION(("StatusView::allocBackBitmap(): failed to allocate\n"));
return;
}
m_backView = new BView(b, 0, B_FOLLOW_NONE, B_WILL_DRAW);
m_backBitmap->AddChild(m_backView);
m_dirty = true;
}
void StatusView::freeBackBitmap() {
if(m_backBitmap) {
delete m_backBitmap;
m_backBitmap = 0;
m_backView = 0;
}
}