haiku/src/servers/notification/AppGroupView.cpp

284 lines
6.4 KiB
C++

/*
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include <algorithm>
#include <ControlLook.h>
#include <GroupLayout.h>
#include <GroupView.h>
#include "AppGroupView.h"
#include "NotificationWindow.h"
#include "NotificationView.h"
const float kCloseSize = 6;
const float kEdgePadding = 2;
AppGroupView::AppGroupView(const BMessenger& messenger, const char* label)
:
BGroupView("appGroup", B_VERTICAL, 0),
fLabel(label),
fMessenger(messenger),
fCollapsed(false),
fCloseClicked(false),
fPreviewModeOn(false)
{
SetFlags(Flags() | B_WILL_DRAW);
fHeaderSize = be_bold_font->Size()
+ be_control_look->ComposeSpacing(B_USE_ITEM_SPACING);
static_cast<BGroupLayout*>(GetLayout())->SetInsets(0, fHeaderSize, 0, 0);
}
void
AppGroupView::Draw(BRect updateRect)
{
rgb_color menuColor = ViewColor();
BRect bounds = Bounds();
bounds.bottom = bounds.top + fHeaderSize;
// Draw the header background
SetHighColor(tint_color(menuColor, 1.22));
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
StrokeLine(bounds.LeftTop(), bounds.LeftBottom());
uint32 borders = BControlLook::B_TOP_BORDER
| BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER;
be_control_look->DrawButtonBackground(this, bounds, bounds, menuColor,
0, borders);
// Draw the buttons
fCollapseRect.top = (fHeaderSize - kExpandSize) / 2;
fCollapseRect.left = kEdgePadding * 3;
fCollapseRect.right = fCollapseRect.left + 1.5 * kExpandSize;
fCollapseRect.bottom = fCollapseRect.top + kExpandSize;
fCloseRect = bounds;
fCloseRect.top = (fHeaderSize - kCloseSize) / 2;
// Take off the 1 to line this up with the close button on the
// notification view
fCloseRect.right -= kEdgePadding * 3 - 1;
fCloseRect.left = fCloseRect.right - kCloseSize;
fCloseRect.bottom = fCloseRect.top + kCloseSize;
uint32 arrowDirection = fCollapsed
? BControlLook::B_DOWN_ARROW : BControlLook::B_UP_ARROW;
be_control_look->DrawArrowShape(this, fCollapseRect, fCollapseRect,
LowColor(), arrowDirection, 0, B_DARKEN_3_TINT);
SetPenSize(kPenSize);
// Draw the dismiss widget
_DrawCloseButton(updateRect);
// Draw the label
SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
BString label = fLabel;
if (fCollapsed)
label << " (" << fInfo.size() << ")";
SetFont(be_bold_font);
font_height fontHeight;
GetFontHeight(&fontHeight);
float y = (bounds.top + bounds.bottom - ceilf(fontHeight.ascent)
- ceilf(fontHeight.descent)) / 2.0 + ceilf(fontHeight.ascent);
DrawString(label.String(),
BPoint(fCollapseRect.right + 4 * kEdgePadding, y));
}
void
AppGroupView::_DrawCloseButton(const BRect& updateRect)
{
PushState();
BRect closeRect = fCloseRect;
rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR);
float tint = B_DARKEN_2_TINT;
if (fCloseClicked) {
BRect buttonRect(closeRect.InsetByCopy(-4, -4));
be_control_look->DrawButtonFrame(this, buttonRect, updateRect,
base, base,
BControlLook::B_ACTIVATED | BControlLook::B_BLEND_FRAME);
be_control_look->DrawButtonBackground(this, buttonRect, updateRect,
base, BControlLook::B_ACTIVATED);
tint *= 1.2;
closeRect.OffsetBy(1, 1);
}
base = tint_color(base, tint);
SetHighColor(base);
SetPenSize(2);
StrokeLine(closeRect.LeftTop(), closeRect.RightBottom());
StrokeLine(closeRect.LeftBottom(), closeRect.RightTop());
PopState();
}
void
AppGroupView::MouseDown(BPoint point)
{
// Preview Mode ignores any mouse clicks
if (fPreviewModeOn)
return;
if (BRect(fCloseRect).InsetBySelf(-5, -5).Contains(point)) {
int32 children = fInfo.size();
for (int32 i = 0; i < children; i++) {
fInfo[i]->RemoveSelf();
delete fInfo[i];
}
fInfo.clear();
// Remove ourselves from the parent view
BMessage message(kRemoveGroupView);
message.AddPointer("view", this);
fMessenger.SendMessage(&message);
} else if (BRect(fCollapseRect).InsetBySelf(-5, -5).Contains(point)) {
fCollapsed = !fCollapsed;
int32 children = fInfo.size();
if (fCollapsed) {
for (int32 i = 0; i < children; i++) {
if (!fInfo[i]->IsHidden())
fInfo[i]->Hide();
}
GetLayout()->SetExplicitMaxSize(GetLayout()->MinSize());
} else {
for (int32 i = 0; i < children; i++) {
if (fInfo[i]->IsHidden())
fInfo[i]->Show();
}
GetLayout()->SetExplicitMaxSize(BSize(B_SIZE_UNSET, B_SIZE_UNSET));
}
InvalidateLayout();
Invalidate(); // Need to redraw the collapse indicator and title
}
}
void
AppGroupView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kRemoveView:
{
NotificationView* view = NULL;
if (msg->FindPointer("view", (void**)&view) != B_OK)
return;
infoview_t::iterator vIt = find(fInfo.begin(), fInfo.end(), view);
if (vIt == fInfo.end())
break;
fInfo.erase(vIt);
view->RemoveSelf();
delete view;
fMessenger.SendMessage(msg);
if (!this->HasChildren()) {
Hide();
BMessage removeSelfMessage(kRemoveGroupView);
removeSelfMessage.AddPointer("view", this);
fMessenger.SendMessage(&removeSelfMessage);
}
break;
}
default:
BView::MessageReceived(msg);
}
}
void
AppGroupView::AddInfo(NotificationView* view)
{
BString id = view->MessageID();
bool found = false;
if (id.Length() > 0) {
int32 children = fInfo.size();
for (int32 i = 0; i < children; i++) {
if (id == fInfo[i]->MessageID()) {
NotificationView* oldView = fInfo[i];
oldView->RemoveSelf();
delete oldView;
fInfo[i] = view;
found = true;
break;
}
}
}
// Invalidate all children to show or hide the close buttons in the
// notification view
int32 children = fInfo.size();
for (int32 i = 0; i < children; i++) {
fInfo[i]->Invalidate();
}
if (!found) {
fInfo.push_back(view);
}
GetLayout()->AddView(view);
if (IsHidden())
Show();
if (view->IsHidden(view) && !fCollapsed)
view->Show();
}
void
AppGroupView::SetPreviewModeOn(bool enabled)
{
fPreviewModeOn = enabled;
}
const BString&
AppGroupView::Group() const
{
return fLabel;
}
void
AppGroupView::SetGroup(const char* group)
{
fLabel.SetTo(group);
Invalidate();
}
bool
AppGroupView::HasChildren()
{
return !fInfo.empty();
}
int32
AppGroupView::ChildrenCount()
{
return fInfo.size();
}