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 "GroupListView.h"
8
9#include <map>
10
11#include <Window.h>
12
13
14class RendererLayoutItem : public BAbstractLayout {
15public:
16	RendererLayoutItem(BView* owner, int32 index, void* item,
17		ListItemRenderer*& renderer)
18		:
19		fOwner(owner),
20		fIndex(index),
21		fItem(item),
22		fRenderer(renderer)
23	{
24	}
25
26	ListItemRenderer* Renderer() const
27	{
28		fRenderer->SetTo(fOwner, fItem);
29		return fRenderer;
30	}
31
32	int32 Index() const
33	{
34		return fIndex;
35	}
36
37	void* Item() const
38	{
39		return fItem;
40	}
41
42	virtual	BSize BaseMinSize()
43	{
44		fRenderer->SetTo(fOwner, fItem);
45		return fRenderer->MinSize();
46	}
47
48	virtual	BSize BasePreferredSize()
49	{
50		fRenderer->SetTo(fOwner, fItem);
51		return fRenderer->PreferredSize();
52	}
53
54protected:
55	virtual	void DoLayout()
56	{
57		// shouldn't ever be called?
58	}
59
60private:
61	BView*				fOwner;
62	int32				fIndex;
63	void*				fItem;
64	ListItemRenderer*&	fRenderer;
65};
66
67
68GroupListView::GroupListView(const char* name, GroupListModel* model,
69	enum orientation orientation, float spacing)
70	:
71	BView(NULL, B_WILL_DRAW, new BGroupLayout(orientation, spacing)),
72	fModel(NULL),
73	fItemRenderer(NULL),
74	fGroupRenderer(NULL),
75	fSelectionMessage(NULL)
76{
77	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
78	SetModel(model);
79}
80
81
82GroupListView::~GroupListView()
83{
84	delete fModel;
85	delete fItemRenderer;
86	delete fGroupRenderer;
87	delete fSelectionMessage;
88}
89
90
91void
92GroupListView::SetModel(GroupListModel* model)
93{
94	// TODO: remove all previous
95	// TODO: add change mechanism
96	// TODO: use a "virtual" BGroupLayout (ie. one that create its layout items
97	//		on the fly).
98	fModel = model;
99
100	std::map<addr_t, BGroupLayout*> groupMap;
101
102	int32 groupCount = model->CountGroups();
103	for (int groupIndex = 0; groupIndex < groupCount; groupIndex++) {
104		BGroupLayout* groupItem = new BGroupLayout(B_VERTICAL, 0);
105		groupItem->SetVisible(false);
106		AddChild(groupItem);
107
108		void* group = model->GroupAt(groupIndex);
109		groupMap[(addr_t)group] = groupItem;
110		groupItem->AddItem(new RendererLayoutItem(this, groupIndex, group,
111			fGroupRenderer));
112	}
113
114	int32 itemCount = model->CountItems();
115	for (int itemIndex = 0; itemIndex < itemCount; itemIndex++) {
116		void* group = model->GroupForItemAt(itemIndex);
117		if (group == NULL)
118			continue;
119
120		BGroupLayout* groupItem = groupMap[(addr_t)group];
121		if (groupItem == NULL)
122			continue;
123
124		groupItem->SetVisible(true);
125
126		RendererLayoutItem* rendererItem = new RendererLayoutItem(this,
127			itemIndex, model->ItemAt(itemIndex), fItemRenderer);
128		groupItem->AddItem(rendererItem);
129	}
130}
131
132
133void
134GroupListView::SetItemRenderer(ListItemRenderer* renderer)
135{
136	fItemRenderer = renderer;
137	InvalidateLayout();
138}
139
140
141void
142GroupListView::SetGroupRenderer(ListItemRenderer* renderer)
143{
144	fGroupRenderer = renderer;
145	InvalidateLayout();
146}
147
148
149void
150GroupListView::SetSelectionMessage(BMessage* message, BMessenger target)
151{
152	fSelectionMessage = message;
153	fSelectionTarget = target;
154}
155
156
157void
158GroupListView::AttachedToWindow()
159{
160}
161
162
163void
164GroupListView::MessageReceived(BMessage* message)
165{
166	switch (message->what) {
167		default:
168			BView::MessageReceived(message);
169			break;
170	}
171}
172
173
174void
175GroupListView::MouseDown(BPoint point)
176{
177	if (fSelectionMessage == NULL)
178		return;
179
180	BLayoutItem* item = _ItemAt(GetLayout(), point);
181
182	if (RendererLayoutItem* rendererItem
183			= dynamic_cast<RendererLayoutItem*>(item)) {
184		BMessage message(*fSelectionMessage);
185
186		int32 buttons = 0;
187		if (Window()->CurrentMessage() != NULL)
188			buttons = Window()->CurrentMessage()->FindInt32("buttons");
189		if (buttons != 0)
190			message.AddInt32("buttons", buttons);
191		message.AddInt32("index", rendererItem->Index());
192		message.AddPointer(rendererItem->Renderer() == fGroupRenderer
193			? "group" : "item", rendererItem->Item());
194
195		fSelectionTarget.SendMessage(&message);
196	}
197}
198
199
200void
201GroupListView::Draw(BRect updateRect)
202{
203	_Draw(GetLayout(), updateRect);
204}
205
206
207void
208GroupListView::_Draw(BLayoutItem* item, BRect updateRect)
209{
210	if (RendererLayoutItem* rendererItem
211			= dynamic_cast<RendererLayoutItem*>(item)) {
212		ListItemRenderer* renderer = rendererItem->Renderer();
213		renderer->Draw(this, rendererItem->Frame(), rendererItem->Index(),
214			false);
215	} else if (BLayout* layout = dynamic_cast<BLayout*>(item)) {
216		for (int i = 0; i < layout->CountItems(); i++) {
217			item = layout->ItemAt(i);
218			if (!item->IsVisible() || !item->Frame().Intersects(updateRect))
219				continue;
220
221			_Draw(item, updateRect);
222		}
223	}
224}
225
226
227BLayoutItem*
228GroupListView::_ItemAt(BLayoutItem* item, BPoint point)
229{
230	if (RendererLayoutItem* rendererItem
231			= dynamic_cast<RendererLayoutItem*>(item))
232		return rendererItem;
233
234	if (BLayout* layout = dynamic_cast<BLayout*>(item)) {
235		for (int i = 0; i < layout->CountItems(); i++) {
236			item = layout->ItemAt(i);
237			if (!item->IsVisible() || !item->Frame().Contains(point))
238				continue;
239
240			return _ItemAt(item, point);
241		}
242	}
243
244	return item;
245}
246