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
24static const uint32 kMsgActivateWindow = 'AcWn';
25
26
27class WindowModel : public GroupListModel {
28public:
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 void* GroupAt(int32 index)
100	{
101		return (void*)(_NthSetBit(index, fWorkspaces) + 1);
102	}
103
104	virtual void* GroupForItemAt(int32 index)
105	{
106		client_window_info* info = fWindows.ItemAt(index);
107		return (void*)(_NthSetBit(0, info->workspaces) + 1);
108	}
109
110private:
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	int32 _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
136private:
137	BObjectList<client_window_info>	fWindows;
138	uint32							fWorkspaces;
139	int32							fWorkspaceCount;
140};
141
142
143class StringItemRenderer : public ListItemRenderer {
144public:
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
212private:
213	BString	fText;
214	int32	fWidth;
215	int32	fHeight;
216	int32	fBaselineOffset;
217};
218
219
220class WorkspaceRenderer : public StringItemRenderer {
221public:
222	virtual void SetTo(BView* owner, void* item)
223	{
224		fWorkspace = (uint32)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
241private:
242	uint32	fWorkspace;
243};
244
245
246class WindowRenderer : public StringItemRenderer {
247public:
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
266private:
267	client_window_info*	fInfo;
268};
269
270
271// #pragma mark -
272
273
274WindowsView::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
313WindowsView::~WindowsView()
314{
315}
316
317
318void
319WindowsView::AttachedToWindow()
320{
321	fListView->SetSelectionMessage(new BMessage(kMsgActivateWindow), this);
322}
323
324
325void
326WindowsView::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
351orientation
352WindowsView::_Orientation(uint32 location)
353{
354	return (location & (kTopEdge | kBottomEdge)) != 0
355		? B_HORIZONTAL : B_VERTICAL;
356}
357