1 /* 2 * Copyright 2010, Haiku, Inc. All Rights Reserved. 3 * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved. 4 * Copyright 2004-2008, Michael Davidson. All Rights Reserved. 5 * Copyright 2004-2007, Mikael Eiman. All Rights Reserved. 6 * Distributed under the terms of the MIT License. 7 * 8 * Authors: 9 * Michael Davidson, slaad@bong.com.au 10 * Mikael Eiman, mikael@eiman.tv 11 * Pier Luigi Fiorini, pierluigi.fiorini@gmail.com 12 */ 13 14 #include <algorithm> 15 16 #include <ControlLook.h> 17 #include <GroupLayout.h> 18 #include <GroupView.h> 19 20 #include "AppGroupView.h" 21 22 #include "NotificationWindow.h" 23 #include "NotificationView.h" 24 25 26 static const int kHeaderSize = 23; 27 28 29 AppGroupView::AppGroupView(NotificationWindow* win, const char* label) 30 : 31 BGroupView("appGroup", B_VERTICAL, 0), 32 fLabel(label), 33 fParent(win), 34 fCollapsed(false), 35 fCloseClicked(false) 36 { 37 SetFlags(Flags() | B_WILL_DRAW); 38 39 static_cast<BGroupLayout*>(GetLayout())->SetInsets(0, kHeaderSize, 0, 0); 40 } 41 42 43 void 44 AppGroupView::Draw(BRect updateRect) 45 { 46 rgb_color menuColor = ViewColor(); 47 BRect bounds = Bounds(); 48 rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT); 49 rgb_color vlight = tint_color(menuColor, B_LIGHTEN_2_TINT); 50 bounds.bottom = bounds.top + kHeaderSize; 51 52 // Draw the header background 53 SetHighColor(tint_color(menuColor, 1.22)); 54 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 55 StrokeLine(bounds.LeftTop(), bounds.LeftBottom()); 56 uint32 borders = BControlLook::B_TOP_BORDER 57 | BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER; 58 59 be_control_look->DrawButtonBackground(this, bounds, bounds, menuColor, 60 0, borders); 61 62 // Draw the buttons 63 fCollapseRect.top = (kHeaderSize - kExpandSize) / 2; 64 fCollapseRect.left = kEdgePadding * 3; 65 fCollapseRect.right = fCollapseRect.left + 1.5 * kExpandSize; 66 fCollapseRect.bottom = fCollapseRect.top + kExpandSize; 67 68 fCloseRect = bounds; 69 fCloseRect.top = (kHeaderSize - kCloseSize) / 2; 70 // Take off the 1 to line this up with the close button on the 71 // notification view 72 fCloseRect.right -= kEdgePadding * 3 - 1; 73 fCloseRect.left = fCloseRect.right - kCloseSize; 74 fCloseRect.bottom = fCloseRect.top + kCloseSize; 75 76 uint32 arrowDirection = fCollapsed 77 ? BControlLook::B_DOWN_ARROW : BControlLook::B_UP_ARROW; 78 be_control_look->DrawArrowShape(this, fCollapseRect, fCollapseRect, 79 LowColor(), arrowDirection, 0, B_DARKEN_3_TINT); 80 81 SetPenSize(kPenSize); 82 83 // Draw the dismiss widget 84 _DrawCloseButton(updateRect); 85 86 // Draw the label 87 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 88 BString label = fLabel; 89 if (fCollapsed) 90 label << " (" << fInfo.size() << ")"; 91 92 SetFont(be_bold_font); 93 font_height fontHeight; 94 GetFontHeight(&fontHeight); 95 float y = (bounds.top + bounds.bottom - ceilf(fontHeight.ascent) 96 - ceilf(fontHeight.descent)) / 2.0 + ceilf(fontHeight.ascent); 97 98 DrawString(label.String(), 99 BPoint(fCollapseRect.right + 4 * kEdgePadding, y)); 100 } 101 102 103 void 104 AppGroupView::_DrawCloseButton(const BRect& updateRect) 105 { 106 PushState(); 107 BRect closeRect = fCloseRect; 108 109 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 110 float tint = B_DARKEN_2_TINT; 111 112 if (fCloseClicked) { 113 BRect buttonRect(closeRect.InsetByCopy(-4, -4)); 114 be_control_look->DrawButtonFrame(this, buttonRect, updateRect, 115 base, base, 116 BControlLook::B_ACTIVATED | BControlLook::B_BLEND_FRAME); 117 be_control_look->DrawButtonBackground(this, buttonRect, updateRect, 118 base, BControlLook::B_ACTIVATED); 119 tint *= 1.2; 120 closeRect.OffsetBy(1, 1); 121 } 122 123 base = tint_color(base, tint); 124 SetHighColor(base); 125 SetPenSize(2); 126 StrokeLine(closeRect.LeftTop(), closeRect.RightBottom()); 127 StrokeLine(closeRect.LeftBottom(), closeRect.RightTop()); 128 PopState(); 129 } 130 131 132 void 133 AppGroupView::MouseDown(BPoint point) 134 { 135 if (BRect(fCloseRect).InsetBySelf(-5, -5).Contains(point)) { 136 int32 children = fInfo.size(); 137 for (int32 i = 0; i < children; i++) { 138 fInfo[i]->RemoveSelf(); 139 delete fInfo[i]; 140 } 141 142 fInfo.clear(); 143 144 // Remove ourselves from the parent view 145 BMessage message(kRemoveGroupView); 146 message.AddPointer("view", this); 147 fParent->PostMessage(&message); 148 } else if (BRect(fCollapseRect).InsetBySelf(-5, -5).Contains(point)) { 149 fCollapsed = !fCollapsed; 150 int32 children = fInfo.size(); 151 if (fCollapsed) { 152 for (int32 i = 0; i < children; i++) { 153 if (!fInfo[i]->IsHidden()) 154 fInfo[i]->Hide(); 155 } 156 GetLayout()->SetExplicitMaxSize(GetLayout()->MinSize()); 157 } else { 158 for (int32 i = 0; i < children; i++) { 159 if (fInfo[i]->IsHidden()) 160 fInfo[i]->Show(); 161 } 162 GetLayout()->SetExplicitMaxSize(BSize(B_SIZE_UNSET, B_SIZE_UNSET)); 163 } 164 165 InvalidateLayout(); 166 Invalidate(); // Need to redraw the collapse indicator and title 167 } 168 } 169 170 171 void 172 AppGroupView::MessageReceived(BMessage* msg) 173 { 174 switch (msg->what) { 175 case kRemoveView: 176 { 177 NotificationView* view = NULL; 178 if (msg->FindPointer("view", (void**)&view) != B_OK) 179 return; 180 181 infoview_t::iterator vIt = find(fInfo.begin(), fInfo.end(), view); 182 if (vIt == fInfo.end()) 183 break; 184 185 fInfo.erase(vIt); 186 view->RemoveSelf(); 187 delete view; 188 189 fParent->PostMessage(msg); 190 191 if (!this->HasChildren()) { 192 Hide(); 193 BMessage removeSelfMessage(kRemoveGroupView); 194 removeSelfMessage.AddPointer("view", this); 195 fParent->PostMessage(&removeSelfMessage); 196 } 197 198 break; 199 } 200 default: 201 BView::MessageReceived(msg); 202 } 203 } 204 205 206 void 207 AppGroupView::AddInfo(NotificationView* view) 208 { 209 BString id = view->MessageID(); 210 bool found = false; 211 212 if (id.Length() > 0) { 213 int32 children = fInfo.size(); 214 215 for (int32 i = 0; i < children; i++) { 216 if (id == fInfo[i]->MessageID()) { 217 NotificationView* oldView = fInfo[i]; 218 fParent->NotificationViewSwapped(oldView, view); 219 oldView->RemoveSelf(); 220 delete oldView; 221 222 fInfo[i] = view; 223 found = true; 224 225 break; 226 } 227 } 228 } 229 230 // Invalidate all children to show or hide the close buttons in the 231 // notification view 232 int32 children = fInfo.size(); 233 for (int32 i = 0; i < children; i++) { 234 fInfo[i]->Invalidate(); 235 } 236 237 if (!found) { 238 fInfo.push_back(view); 239 } 240 GetLayout()->AddView(view); 241 242 if (IsHidden()) 243 Show(); 244 if (view->IsHidden(view) && !fCollapsed) 245 view->Show(); 246 } 247 248 249 const BString& 250 AppGroupView::Group() const 251 { 252 return fLabel; 253 } 254 255 256 bool 257 AppGroupView::HasChildren() 258 { 259 return !fInfo.empty(); 260 } 261 262 263 int32 264 AppGroupView::ChildrenCount() 265 { 266 return fInfo.size(); 267 } 268