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 "AppGroupView.h" 17 18 #include "NotificationWindow.h" 19 #include "NotificationView.h" 20 21 22 AppGroupView::AppGroupView(NotificationWindow* win, const char* label) 23 : 24 BView(BRect(0, 0, win->ViewWidth(), 1), label, B_FOLLOW_LEFT_RIGHT, 25 B_WILL_DRAW|B_FULL_UPDATE_ON_RESIZE|B_FRAME_EVENTS), 26 fLabel(label), 27 fParent(win), 28 fCollapsed(false) 29 { 30 Show(); 31 } 32 33 34 AppGroupView::~AppGroupView() 35 { 36 } 37 38 39 void 40 AppGroupView::AttachedToWindow() 41 { 42 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 43 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 44 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 45 } 46 47 48 void 49 AppGroupView::Draw(BRect updateRect) 50 { 51 FillRect(Bounds(), B_SOLID_LOW); 52 53 BString label = fLabel; 54 if (fCollapsed) 55 label << " (" << fInfo.size() << ")"; 56 57 font_height fh; 58 be_bold_font->GetHeight(&fh); 59 float labelOffset = fh.ascent + fh.leading; 60 61 62 BRect textRect = Bounds(); 63 //textRect.left = kEdgePadding * 2; 64 //textRect.right = textRect.left + be_bold_font->StringWidth(label.String()) 65 // + (kEdgePadding * 3); 66 textRect.bottom = 2 * labelOffset; 67 68 BRect borderRect = Bounds().InsetByCopy(kEdgePadding, kEdgePadding); 69 borderRect.top = 2 * labelOffset; 70 71 BRect closeCross = fCloseRect; 72 closeCross.InsetBy(kSmallPadding, kSmallPadding); 73 74 rgb_color detailCol = ui_color(B_CONTROL_BORDER_COLOR); 75 detailCol = tint_color(detailCol, B_LIGHTEN_2_TINT); 76 // detailCol = tint_color(detailCol, B_LIGHTEN_1_TINT); 77 78 if (fCollapsed) { 79 PushState(); 80 SetFont(be_bold_font); 81 SetPenSize(kPenSize); 82 float linePos = textRect.top + textRect.Height() / 2; 83 84 // Draw the line to the expand widget 85 PushState(); 86 SetHighColor(detailCol); 87 StrokeLine(BPoint(kEdgePadding, linePos), BPoint(fCollapseRect.left, linePos)); 88 PopState(); 89 90 // Draw the expand widget 91 PushState(); 92 SetHighColor(detailCol); 93 StrokeRoundRect(fCollapseRect, kSmallPadding, kSmallPadding); 94 95 BPoint expandHorStart(fCollapseRect.left + kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top); 96 BPoint expandHorEnd(fCollapseRect.right - kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top); 97 StrokeLine(expandHorStart, expandHorEnd); 98 99 BPoint expandVerStart(fCollapseRect.Width() / 2 + fCollapseRect.left, fCollapseRect.top + kSmallPadding); 100 BPoint expandVerEnd(fCollapseRect.Width() / 2 + fCollapseRect.left, fCollapseRect.bottom - kSmallPadding); 101 StrokeLine(expandVerStart, expandVerEnd); 102 PopState(); 103 104 // Draw the app title 105 DrawString(label.String(), BPoint(fCollapseRect.right + kEdgePadding, labelOffset + kEdgePadding)); 106 107 // Draw the line from the label to the close widget 108 PushState(); 109 SetHighColor(detailCol); 110 111 BPoint lineSeg2Start(textRect.right + kSmallPadding / 2, linePos); 112 BPoint lineSeg2End(fCloseRect.left, linePos); 113 StrokeLine(lineSeg2Start, lineSeg2End); 114 PopState(); 115 116 // Draw the dismiss widget 117 PushState(); 118 SetHighColor(detailCol); 119 120 StrokeRoundRect(fCloseRect, kSmallPadding, kSmallPadding); 121 122 StrokeLine(closeCross.LeftTop(), closeCross.RightBottom()); 123 StrokeLine(closeCross.RightTop(), closeCross.LeftBottom()); 124 PopState(); 125 126 // Draw the line from the dismiss widget 127 PushState(); 128 SetHighColor(detailCol); 129 130 BPoint lineSeg3Start(fCloseRect.right, linePos); 131 BPoint lineSeg3End(borderRect.right, linePos); 132 StrokeLine(lineSeg3Start, lineSeg3End); 133 PopState(); 134 135 PopState(); 136 } else { 137 PushState(); 138 SetFont(be_bold_font); 139 SetPenSize(kPenSize); 140 141 SetLowColor(tint_color(ViewColor(), B_DARKEN_1_TINT)); 142 FillRect(textRect, B_SOLID_LOW); 143 144 SetHighColor(ui_color(B_PANEL_TEXT_COLOR)); 145 146 // Draw the collapse widget 147 StrokeRoundRect(fCollapseRect, kSmallPadding, kSmallPadding); 148 149 BPoint expandHorStart(fCollapseRect.left + kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top); 150 BPoint expandHorEnd(fCollapseRect.right - kSmallPadding, fCollapseRect.Height() / 2 + fCollapseRect.top); 151 152 StrokeLine(expandHorStart, expandHorEnd); 153 154 // Draw the dismiss widget 155 StrokeRoundRect(fCloseRect, kSmallPadding, kSmallPadding); 156 157 StrokeLine(closeCross.LeftTop(), closeCross.RightBottom()); 158 StrokeLine(closeCross.RightTop(), closeCross.LeftBottom()); 159 160 // Draw the label 161 DrawString(label.String(), BPoint(fCollapseRect.right + kEdgePadding, labelOffset + kEdgePadding)); 162 PopState(); 163 } 164 165 Sync(); 166 } 167 168 169 void 170 AppGroupView::MouseDown(BPoint point) 171 { 172 bool changed = false; 173 if (fCloseRect.Contains(point)) { 174 changed = true; 175 176 int32 children = fInfo.size(); 177 for (int32 i = 0; i < children; i++) { 178 fInfo[i]->RemoveSelf(); 179 delete fInfo[i]; 180 } 181 fInfo.clear(); 182 } 183 184 if (fCollapseRect.Contains(point)) { 185 fCollapsed = !fCollapsed; 186 changed = true; 187 } 188 189 if (changed) { 190 ResizeViews(); 191 Invalidate(); 192 } 193 } 194 195 196 void 197 AppGroupView::GetPreferredSize(float* width, float* height) 198 { 199 font_height fh; 200 be_bold_font->GetHeight(&fh); 201 202 float h = fh.ascent + fh.leading + fh.leading; 203 h += kEdgePadding * 2; // Padding between top and bottom of label 204 205 if (!fCollapsed) { 206 int32 children = fInfo.size(); 207 208 for (int32 i = 0; i < children; i++) { 209 float childHeight = 0; 210 float childWidth = 0; 211 212 fInfo[i]->GetPreferredSize(&childWidth, &childHeight); 213 214 h += childHeight; 215 } 216 } 217 218 h += kEdgePadding; 219 220 *width = fParent->ViewWidth(); 221 *height = h; 222 } 223 224 225 void 226 AppGroupView::MessageReceived(BMessage* msg) 227 { 228 switch (msg->what) { 229 case kRemoveView: 230 { 231 NotificationView* view = NULL; 232 if (msg->FindPointer("view", (void**)&view) != B_OK) 233 return; 234 235 infoview_t::iterator vIt = find(fInfo.begin(), fInfo.end(), view); 236 237 if (vIt != fInfo.end()) { 238 fInfo.erase(vIt); 239 view->RemoveSelf(); 240 delete view; 241 } 242 243 ResizeViews(); 244 Invalidate(); 245 246 // When all the views are destroy, save app filters 247 if (fInfo.size() == 0) 248 dynamic_cast<NotificationWindow*>(Window())->SaveAppFilters(); 249 break; 250 } 251 default: 252 BView::MessageReceived(msg); 253 } 254 } 255 256 257 void 258 AppGroupView::AddInfo(NotificationView* view) 259 { 260 BString id = view->MessageID(); 261 if (id.Length() > 0) { 262 int32 children = fInfo.size(); 263 bool found = false; 264 265 for (int32 i = 0; i < children; i++) { 266 if (fInfo[i]->HasMessageID(id.String())) { 267 fInfo[i]->RemoveSelf(); 268 delete fInfo[i]; 269 270 fInfo[i] = view; 271 found = true; 272 273 break; 274 } 275 } 276 277 if (!found) 278 fInfo.push_back(view); 279 } else 280 fInfo.push_back(view); 281 282 if (fParent->IsHidden()) 283 fParent->Show(); 284 if (IsHidden()) 285 Show(); 286 if (view->IsHidden()) 287 view->Show(); 288 289 AddChild(view); 290 291 ResizeViews(); 292 Invalidate(); 293 } 294 295 296 void 297 AppGroupView::ResizeViews() 298 { 299 font_height fh; 300 be_bold_font->GetHeight(&fh); 301 302 float offset = 2 * kEdgePadding + fh.ascent + fh.leading + fh.descent; 303 int32 children = fInfo.size(); 304 305 if (!fCollapsed) { 306 offset += kEdgePadding + kPenSize; 307 308 for (int32 i = 0; i < children; i++) { 309 fInfo[i]->ResizeToPreferred(); 310 fInfo[i]->MoveTo(0, offset); 311 312 offset += fInfo[i]->Bounds().Height(); 313 if (fInfo[i]->IsHidden()) 314 fInfo[i]->Show(); 315 fInfo[i]->SetPosition(false, false); 316 }; 317 } else { 318 for (int32 i = 0; i < children; i++) 319 if (!fInfo[i]->IsHidden()) 320 fInfo[i]->Hide(); 321 } 322 323 if (children == 1) 324 fInfo[0]->SetPosition(true, true); 325 else if (children > 1) { 326 fInfo[0]->SetPosition(true, false); 327 fInfo[children - 1]->SetPosition(false, true); 328 } 329 330 ResizeTo(fParent->ViewWidth(), offset); 331 float labelOffset = fh.ascent + fh.leading; 332 333 BRect borderRect = Bounds().InsetByCopy(kEdgePadding, kEdgePadding); 334 borderRect.top = 2*labelOffset; 335 336 fCollapseRect = borderRect; 337 fCollapseRect.right = fCollapseRect.left + kExpandSize; 338 fCollapseRect.bottom = fCollapseRect.top + kExpandSize; 339 fCollapseRect.OffsetTo(kEdgePadding * 2, kEdgePadding * 1.5); 340 341 fCloseRect = borderRect; 342 fCloseRect.right -= kEdgePadding * 4; 343 fCloseRect.left = fCloseRect.right - kCloseSize; 344 fCloseRect.bottom = fCloseRect.top + kCloseSize; 345 fCloseRect.OffsetTo(fCloseRect.left, kEdgePadding * 1.5); 346 347 fParent->ResizeAll(); 348 } 349 350 351 bool 352 AppGroupView::HasChildren() 353 { 354 return !fInfo.empty(); 355 } 356