xref: /haiku/src/apps/switcher/WindowsView.cpp (revision 2141d2fe3a5df2f55f3590f67660573b50d1d1d3)
1 /*
2  * Copyright 2011, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "WindowsView.h"
8 
9 #include <stdlib.h>
10 
11 #include <LayoutBuilder.h>
12 #include <ObjectList.h>
13 #include <StringView.h>
14 
15 #include <MessengerPrivate.h>
16 #include <WindowInfo.h>
17 #include <WindowPrivate.h>
18 
19 #include "GroupListView.h"
20 #include "LaunchButton.h"
21 #include "Switcher.h"
22 
23 
24 static const uint32 kMsgActivateWindow = 'AcWn';
25 
26 
27 class WindowModel : public GroupListModel {
28 public:
29 	WindowModel(team_id team)
30 		:
31 		fWindows(true),
32 		fWorkspaces(0),
33 		fWorkspaceCount(0)
34 	{
35 		// TODO: more than one team via signature!
36 		int32 count;
37 		int32* tokens = get_token_list(team, &count);
38 
39 		for (int32 i = 0; i < count; i++) {
40 			client_window_info* info = get_window_info(tokens[i]);
41 			if (!_WindowShouldBeListed(info)) {
42 				free(info);
43 				continue;
44 			}
45 
46 			fWorkspaces |= info->workspaces;
47 			fWindows.AddItem(info);
48 		}
49 		free(tokens);
50 
51 		for (uint32 i = 0; i < 32; i++) {
52 			if ((fWorkspaces & (1UL << i)) != 0)
53 				fWorkspaceCount++;
54 		}
55 
56 		fWindows.SortItems(&_CompareWindowInfo);
57 	}
58 
59 	virtual ~WindowModel()
60 	{
61 	}
62 
63 	void BringToFront(int32 index)
64 	{
65 		client_window_info* info = fWindows.ItemAt(index);
66 		if (info == NULL)
67 			return;
68 
69 		do_window_action(info->server_token, B_BRING_TO_FRONT, BRect(), false);
70 	}
71 
72 	void Close(int32 index)
73 	{
74 		client_window_info* info = fWindows.ItemAt(index);
75 		if (info == NULL)
76 			return;
77 
78 		BMessenger window;
79 		BMessenger::Private(window).SetTo(info->team, info->client_port,
80 			info->client_token);
81 		window.SendMessage(B_QUIT_REQUESTED);
82 	}
83 
84 	virtual	int32 CountItems()
85 	{
86 		return fWindows.CountItems();
87 	}
88 
89 	virtual void* ItemAt(int32 index)
90 	{
91 		return fWindows.ItemAt(index);
92 	}
93 
94 	virtual int32 CountGroups()
95 	{
96 		return fWorkspaceCount;
97 	}
98 
99 	virtual addr_t GroupAt(int32 index)
100 	{
101 		return _NthSetBit(index, fWorkspaces) + 1;
102 	}
103 
104 	virtual addr_t GroupForItemAt(int32 index)
105 	{
106 		client_window_info* info = fWindows.ItemAt(index);
107 		return _NthSetBit(0, info->workspaces) + 1;
108 	}
109 
110 private:
111 	bool _WindowShouldBeListed(client_window_info* info)
112 	{
113 		return info != NULL
114 			&& (info->feel == B_NORMAL_WINDOW_FEEL
115 				|| info->feel == kWindowScreenFeel)
116 			&& (info->show_hide_level <= 0 || info->is_mini);
117 	}
118 
119 	uint32 _NthSetBit(int32 index, uint32 mask)
120 	{
121 		for (uint32 i = 0; i < 32; i++) {
122 			if ((mask & (1UL << i)) != 0) {
123 				if (index-- == 0)
124 					return i;
125 			}
126 		}
127 		return 0;
128 	}
129 
130 	static int _CompareWindowInfo(const client_window_info* a,
131 		const client_window_info* b)
132 	{
133 		return strcasecmp(a->name, b->name);
134 	}
135 
136 private:
137 	BObjectList<client_window_info>	fWindows;
138 	uint32							fWorkspaces;
139 	int32							fWorkspaceCount;
140 };
141 
142 
143 class StringItemRenderer : public ListItemRenderer {
144 public:
145 	StringItemRenderer()
146 	{
147 	}
148 
149 	virtual ~StringItemRenderer()
150 	{
151 	}
152 
153 	void SetText(BView* owner, const BString& text)
154 	{
155 		fText = text;
156 		owner->TruncateString(&fText, B_TRUNCATE_MIDDLE, 200);
157 		SetWidth((int32)ceilf(owner->StringWidth(fText.String())));
158 
159 		font_height fontHeight;
160 		owner->GetFontHeight(&fontHeight);
161 
162 		SetBaselineOffset(
163 			2 + (int32)ceilf(fontHeight.ascent + fontHeight.leading / 2));
164 
165 		SetHeight((int32)ceilf(fontHeight.ascent)
166 			+ (int32)ceilf(fontHeight.descent)
167 			+ (int32)ceilf(fontHeight.leading) + 4);
168 	}
169 
170 	virtual void SetWidth(int32 width)
171 	{
172 		fWidth = width;
173 	}
174 
175 	virtual void SetHeight(int32 height)
176 	{
177 		fHeight = height;
178 	}
179 
180 	virtual void SetBaselineOffset(int32 offset)
181 	{
182 		fBaselineOffset = offset;
183 	}
184 
185 	const BString& Text() const
186 	{
187 		return fText;
188 	}
189 
190 	virtual BSize MinSize()
191 	{
192 		return BSize(fWidth, fHeight);
193 	}
194 
195 	virtual BSize MaxSize()
196 	{
197 		return BSize(B_SIZE_UNLIMITED, fHeight);
198 	}
199 
200 	virtual BSize PreferredSize()
201 	{
202 		return BSize(fWidth, fHeight);
203 	}
204 
205 	virtual void Draw(BView* owner, BRect frame, int32 index, bool selected)
206 	{
207 		owner->SetLowColor(owner->ViewColor());
208 		owner->MovePenTo(frame.left, frame.top + fBaselineOffset);
209 		owner->DrawString(fText);
210 	}
211 
212 private:
213 	BString	fText;
214 	int32	fWidth;
215 	int32	fHeight;
216 	int32	fBaselineOffset;
217 };
218 
219 
220 class WorkspaceRenderer : public StringItemRenderer {
221 public:
222 	virtual void SetTo(BView* owner, void* item)
223 	{
224 		fWorkspace = (uintptr_t)item;
225 
226 		if ((uint32)current_workspace() == fWorkspace - 1)
227 			SetText(owner, "Current workspace");
228 		else {
229 			BString text("Workspace ");
230 			text << fWorkspace;
231 			SetText(owner, text);
232 		}
233 	}
234 
235 	virtual void Draw(BView* owner, BRect frame, int32 index, bool selected)
236 	{
237 		owner->SetHighColor(tint_color(owner->ViewColor(), B_DARKEN_2_TINT));
238 		StringItemRenderer::Draw(owner, frame, index, false);
239 	}
240 
241 private:
242 	uint32	fWorkspace;
243 };
244 
245 
246 class WindowRenderer : public StringItemRenderer {
247 public:
248 	virtual void SetTo(BView* owner, void* item)
249 	{
250 		fInfo = (client_window_info*)item;
251 		SetText(owner, fInfo->name);
252 	}
253 
254 	virtual void SetWidth(int32 width)
255 	{
256 		StringItemRenderer::SetWidth(width + 20);
257 	}
258 
259 	virtual void Draw(BView* owner, BRect frame, int32 index, bool selected)
260 	{
261 		owner->SetHighColor(0, 0, 0);
262 		frame.left += 20;
263 		StringItemRenderer::Draw(owner, frame, index, selected);
264 	}
265 
266 private:
267 	client_window_info*	fInfo;
268 };
269 
270 
271 // #pragma mark -
272 
273 
274 WindowsView::WindowsView(team_id team, uint32 location)
275 	:
276 	BGridView("windows")
277 {
278 	app_info info;
279 	be_roster->GetRunningAppInfo(team, &info);
280 
281 	LaunchButton* launchButton = new LaunchButton(info.signature, NULL, NULL,
282 		this);
283 	launchButton->SetTo(&info.ref);
284 
285 	BStringView* nameView = new BStringView("name", info.ref.name);
286 	BFont font(be_plain_font);
287 	font.SetSize(font.Size() * 2);
288 	font.SetFace(B_BOLD_FACE);
289 	nameView->SetFont(&font);
290 
291 	fListView = new GroupListView("list", new WindowModel(team),
292 		_Orientation(location));
293 	fListView->SetItemRenderer(new WindowRenderer());
294 	fListView->SetGroupRenderer(new WorkspaceRenderer());
295 
296 	GridLayout()->SetInsets(B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING,
297 		B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING);
298 
299 	if (_Orientation(location) == B_HORIZONTAL) {
300 		BLayoutBuilder::Grid<>(this)
301 			.Add(launchButton, 0, 0)
302 			.Add(nameView, 1, 0)
303 			.Add(fListView, 2, 0);
304 	} else {
305 		BLayoutBuilder::Grid<>(this)
306 			.Add(launchButton, 0, 0)
307 			.Add(nameView, 1, 0)
308 			.Add(fListView, 0, 1, 2);
309 	}
310 }
311 
312 
313 WindowsView::~WindowsView()
314 {
315 }
316 
317 
318 void
319 WindowsView::AttachedToWindow()
320 {
321 	fListView->SetSelectionMessage(new BMessage(kMsgActivateWindow), this);
322 }
323 
324 
325 void
326 WindowsView::MessageReceived(BMessage* message)
327 {
328 	switch (message->what) {
329 		case kMsgActivateWindow:
330 		{
331 			int32 index;
332 			if (message->FindInt32("index", &index) == B_OK
333 				&& message->HasPointer("item")) {
334 				WindowModel* model = (WindowModel*)fListView->Model();
335 
336 				if (message->FindInt32("buttons") == B_SECONDARY_MOUSE_BUTTON)
337 					model->Close(index);
338 				else
339 					model->BringToFront(index);
340 			}
341 			break;
342 		}
343 
344 		default:
345 			BGridView::MessageReceived(message);
346 			break;
347 	}
348 }
349 
350 
351 orientation
352 WindowsView::_Orientation(uint32 location)
353 {
354 	return (location & (kTopEdge | kBottomEdge)) != 0
355 		? B_HORIZONTAL : B_VERTICAL;
356 }
357