xref: /haiku/src/servers/notification/AppGroupView.cpp (revision 45bd7bb3db9d9e4dcb02b89a3e7c2bf382c0a88c)
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