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