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