/* * 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 #include #include #include #include #include "AppGroupView.h" #include "NotificationWindow.h" #include "NotificationView.h" 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(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 float buttonSize = CloseButtonSize(); float arrowButtonSize = buttonSize * 1.5; // make arrow button a bit larger fCollapseRect.top = (fHeaderSize - arrowButtonSize) / 2; fCollapseRect.left = buttonSize + 1; // button left padding fCollapseRect.right = fCollapseRect.left + arrowButtonSize; fCollapseRect.bottom = fCollapseRect.top + arrowButtonSize; fCloseRect = bounds; fCloseRect.top = (fHeaderSize - buttonSize) / 2; fCloseRect.right -= buttonSize - 1; fCloseRect.left = fCloseRect.right - buttonSize; fCloseRect.bottom = fCloseRect.top + buttonSize; SetPenSize(1); // Draw the arrow button uint32 direction = fCollapsed ? BControlLook::B_DOWN_ARROW : BControlLook::B_UP_ARROW; be_control_look->DrawArrowShape(this, fCollapseRect, fCollapseRect, LowColor(), direction, 0, B_DARKEN_3_TINT); // 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); float x = fCollapseRect.right + buttonSize * 1.5; DrawString(label.String(), BPoint(x, 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(); } float AppGroupView::CloseButtonSize() const { return be_control_look->DefaultLabelSpacing() + 1; } 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]; if(view->ProgressPercent() != oldView->ProgressPercent() && view->ProgressPercent() >= 0) { system_beep("Notification progress"); } 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) { if(view->ProgressPercent() >= 0) system_beep("Notification progress"); 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(); }