xref: /haiku/src/servers/notification/AppGroupView.cpp (revision 1b6bc2675fe3691538c8764ab016593f3b06ca53)
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 = 20;
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 {
36 	SetFlags(Flags() | B_WILL_DRAW);
37 
38 	static_cast<BGroupLayout*>(GetLayout())->SetInsets(0, kHeaderSize, 0, 0);
39 }
40 
41 
42 void
43 AppGroupView::Draw(BRect updateRect)
44 {
45 	rgb_color menuColor = ViewColor();
46 	BRect bounds = Bounds();
47 	rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
48 	rgb_color dark = tint_color(menuColor, B_DARKEN_2_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 	if (be_control_look != NULL) {
54 		SetHighColor(tint_color(menuColor, 1.22));
55 		SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
56 		StrokeLine(bounds.LeftTop(), bounds.LeftBottom());
57 		uint32 borders = BControlLook::B_TOP_BORDER
58 			| BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER;
59 
60 		be_control_look->DrawButtonBackground(this, bounds, bounds, menuColor,
61 			0, borders);
62 	} else {
63 		SetHighColor(vlight);
64 		StrokeLine(bounds.LeftTop(), bounds.RightTop());
65 		StrokeLine(BPoint(bounds.left, bounds.top + 1), bounds.LeftBottom());
66 		SetHighColor(hilite);
67 		StrokeLine(BPoint(bounds.left + 1, bounds.bottom),
68 			bounds.RightBottom());
69 	}
70 
71 	// Draw the buttons
72 	fCollapseRect.top = (kHeaderSize - kExpandSize) / 2;
73 	fCollapseRect.left = kEdgePadding * 2;
74 	fCollapseRect.right = fCollapseRect.left + 1.5 * kExpandSize;
75 	fCollapseRect.bottom = fCollapseRect.top + kExpandSize;
76 
77 	fCloseRect = bounds;
78 	fCloseRect.top = (kHeaderSize - kExpandSize) / 2;
79 	fCloseRect.right -= kEdgePadding * 2;
80 	fCloseRect.left = fCloseRect.right - kCloseSize;
81 	fCloseRect.bottom = fCloseRect.top + kCloseSize;
82 
83 	if (be_control_look != NULL) {
84 		uint32 arrowDirection = fCollapsed
85 			? BControlLook::B_DOWN_ARROW : BControlLook::B_UP_ARROW;
86 		be_control_look->DrawArrowShape(this, fCollapseRect, fCollapseRect,
87 			LowColor(), arrowDirection, 0, B_DARKEN_3_TINT);
88 	} else {
89 		rgb_color outlineColor = {80, 80, 80, 255};
90 		rgb_color middleColor = {200, 200, 200, 255};
91 
92 		SetDrawingMode(B_OP_OVER);
93 
94 		if (fCollapsed) {
95 			BeginLineArray(6);
96 
97 			AddLine(BPoint(fCollapseRect.left + 3, fCollapseRect.top + 1),
98 					BPoint(fCollapseRect.left + 3, fCollapseRect.bottom - 1), outlineColor);
99 			AddLine(BPoint(fCollapseRect.left + 3, fCollapseRect.top + 1),
100 					BPoint(fCollapseRect.left + 7, fCollapseRect.top + 5), outlineColor);
101 			AddLine(BPoint(fCollapseRect.left + 7, fCollapseRect.top + 5),
102 					BPoint(fCollapseRect.left + 3, fCollapseRect.bottom - 1), outlineColor);
103 
104 			AddLine(BPoint(fCollapseRect.left + 4, fCollapseRect.top + 3),
105 					BPoint(fCollapseRect.left + 4, fCollapseRect.bottom - 3), middleColor);
106 			AddLine(BPoint(fCollapseRect.left + 5, fCollapseRect.top + 4),
107 					BPoint(fCollapseRect.left + 5, fCollapseRect.bottom - 4), middleColor);
108 			AddLine(BPoint(fCollapseRect.left + 5, fCollapseRect.top + 5),
109 					BPoint(fCollapseRect.left + 6, fCollapseRect.top + 5), middleColor);
110 			EndLineArray();
111 		} else {
112 			// expanded state
113 
114 			BeginLineArray(6);
115 			AddLine(BPoint(fCollapseRect.left + 1, fCollapseRect.top + 3),
116 					BPoint(fCollapseRect.right - 3, fCollapseRect.top + 3), outlineColor);
117 			AddLine(BPoint(fCollapseRect.left + 1, fCollapseRect.top + 3),
118 					BPoint(fCollapseRect.left + 5, fCollapseRect.top + 7), outlineColor);
119 			AddLine(BPoint(fCollapseRect.left + 5, fCollapseRect.top + 7),
120 					BPoint(fCollapseRect.right - 3, fCollapseRect.top + 3), outlineColor);
121 
122 			AddLine(BPoint(fCollapseRect.left + 3, fCollapseRect.top + 4),
123 					BPoint(fCollapseRect.right - 5, fCollapseRect.top + 4), middleColor);
124 			AddLine(BPoint(fCollapseRect.left + 4, fCollapseRect.top + 5),
125 					BPoint(fCollapseRect.right - 6, fCollapseRect.top + 5), middleColor);
126 			AddLine(BPoint(fCollapseRect.left + 5, fCollapseRect.top + 5),
127 					BPoint(fCollapseRect.left + 5, fCollapseRect.top + 6), middleColor);
128 			EndLineArray();
129 		}
130 	}
131 
132 	SetPenSize(kPenSize);
133 
134 	// Draw the dismiss widget
135 	BRect closeCross = fCloseRect;
136 	closeCross.InsetBy(kSmallPadding, kSmallPadding);
137 	rgb_color detailCol = ui_color(B_CONTROL_BORDER_COLOR);
138 	detailCol = tint_color(detailCol, B_LIGHTEN_2_TINT);
139 
140 	StrokeRoundRect(fCloseRect, kSmallPadding, kSmallPadding);
141 	StrokeLine(closeCross.LeftTop(), closeCross.RightBottom());
142 	StrokeLine(closeCross.RightTop(), closeCross.LeftBottom());
143 
144 	// Draw the label
145 	SetHighColor(ui_color(B_PANEL_TEXT_COLOR));
146 	BString label = fLabel;
147 	if (fCollapsed)
148 		label << " (" << fInfo.size() << ")";
149 
150 	SetFont(be_bold_font);
151 
152 	DrawString(label.String(), BPoint(fCollapseRect.right + 2 * kEdgePadding,
153 				fCloseRect.bottom));
154 }
155 
156 
157 	void
158 AppGroupView::MouseDown(BPoint point)
159 {
160 	bool changed = false;
161 	if (fCloseRect.Contains(point)) {
162 		changed = true;
163 
164 		int32 children = fInfo.size();
165 		for (int32 i = 0; i < children; i++) {
166 			GetLayout()->RemoveView(fInfo[i]);
167 			delete fInfo[i];
168 		}
169 
170 		fInfo.clear();
171 
172 		// Remove ourselves from the parent view
173 		BMessage message(kRemoveView);
174 		message.AddPointer("view", this);
175 		fParent->PostMessage(&message);
176 	}
177 
178 	if (fCollapseRect.Contains(point)) {
179 		fCollapsed = !fCollapsed;
180 		int32 children = fInfo.size();
181 		if (fCollapsed) {
182 			for (int32 i = 0; i < children; i++) {
183 				if (!fInfo[i]->IsHidden())
184 					fInfo[i]->Hide();
185 			}
186 		} else {
187 			for (int32 i = 0; i < children; i++) {
188 				if (fInfo[i]->IsHidden())
189 					fInfo[i]->Show();
190 			}
191 		}
192 		changed = true;
193 		Invalidate(); // Need to redraw the collapse indicator and title
194 
195 		BMessage message(kRemoveView);
196 			// Do not actually remive anything, but update size
197 		fParent->PostMessage(&message);
198 	}
199 
200 	if (changed) {
201 		_ResizeViews();
202 	}
203 }
204 
205 
206 void
207 AppGroupView::MessageReceived(BMessage* msg)
208 {
209 	switch (msg->what) {
210 		case kRemoveView:
211 		{
212 			NotificationView* view = NULL;
213 			if (msg->FindPointer("view", (void**)&view) != B_OK)
214 				return;
215 
216 			infoview_t::iterator vIt = find(fInfo.begin(), fInfo.end(), view);
217 
218 			if (vIt != fInfo.end()) {
219 				fInfo.erase(vIt);
220 				GetLayout()->RemoveView(view);
221 				delete view;
222 			}
223 
224 			_ResizeViews();
225 
226 			if (Window() != NULL)
227 				Window()->PostMessage(msg);
228 			break;
229 		}
230 		default:
231 			BView::MessageReceived(msg);
232 	}
233 }
234 
235 
236 void
237 AppGroupView::AddInfo(NotificationView* view)
238 {
239 	BString id = view->MessageID();
240 	bool found = false;
241 
242 	if (id.Length() > 0) {
243 		int32 children = fInfo.size();
244 
245 		for (int32 i = 0; i < children; i++) {
246 			if (id == fInfo[i]->MessageID()) {
247 				GetLayout()->RemoveView(fInfo[i]);
248 				delete fInfo[i];
249 
250 				fInfo[i] = view;
251 				found = true;
252 
253 				break;
254 			}
255 		}
256 	}
257 
258 	if (!found) {
259 		fInfo.push_back(view);
260 	}
261 	GetLayout()->AddView(view);
262 
263 	if (IsHidden())
264 		Show();
265 	if (view->IsHidden() && !fCollapsed)
266 		view->Show();
267 }
268 
269 
270 void
271 AppGroupView::_ResizeViews()
272 {
273 	int32 children = fInfo.size();
274 
275 	if (!fCollapsed) {
276 		for (int32 i = 0; i < children; i++) {
277 			if (fInfo[i]->IsHidden())
278 				fInfo[i]->Show();
279 		}
280 	} else {
281 		for (int32 i = 0; i < children; i++)
282 			if (!fInfo[i]->IsHidden())
283 				fInfo[i]->Hide();
284 	}
285 
286 	if (!IsHidden() && !HasChildren())
287 		Hide();
288 	fParent->Layout(true);
289 }
290 
291 
292 bool
293 AppGroupView::HasChildren()
294 {
295 	return !fInfo.empty();
296 }
297