1/*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions, and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions, and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32// DormantNodeView.cpp
33
34#include "DormantNodeView.h"
35// DormantNodeView
36#include "DormantNodeWindow.h"
37#include "DormantNodeListItem.h"
38// InfoWindow
39#include "InfoWindowManager.h"
40
41// Interface Kit
42#include <Deskbar.h>
43#include <Region.h>
44#include <Screen.h>
45#include <ScrollBar.h>
46// Media Kit
47#include <MediaRoster.h>
48// Storage Kit
49#include <Mime.h>
50
51__USE_CORTEX_NAMESPACE
52
53#include <Debug.h>
54#define D_ALLOC(x) //PRINT (x)		// ctor/dtor
55#define D_HOOK(x) //PRINT (x)		// BListView impl.
56#define D_MESSAGE(x) //PRINT (x)	// MessageReceived()
57#define D_INTERNAL(x) //PRINT (x)	// internal operations
58
59// -------------------------------------------------------- //
60// ctor/dtor (public)
61// -------------------------------------------------------- //
62
63DormantNodeView::DormantNodeView(
64	BRect frame,
65	const char *name,
66	uint32 resizeMode)
67	: BListView(frame, name, B_SINGLE_SELECTION_LIST, resizeMode),
68	  m_lastItemUnder(0) {
69	D_ALLOC(("DormantNodeView::DormantNodeView()\n"));
70
71}
72
73DormantNodeView::~DormantNodeView() {
74	D_ALLOC(("DormantNodeView::~DormantNodeView()\n"));
75
76}
77
78// -------------------------------------------------------- //
79// BListView impl. (public)
80// -------------------------------------------------------- //
81
82void DormantNodeView::AttachedToWindow() {
83	D_HOOK(("DormantNodeView::AttachedToWindow()\n"));
84
85	// populate the list
86	_populateList();
87
88	// Start watching the MediaRoster for flavor changes
89	BMediaRoster *roster = BMediaRoster::CurrentRoster();
90	if (roster) {
91		BMessenger messenger(this, Window());
92		roster->StartWatching(messenger, B_MEDIA_FLAVORS_CHANGED);
93	}
94}
95
96void DormantNodeView::DetachedFromWindow() {
97	D_HOOK(("DormantNodeView::DetachedFromWindow()\n"));
98
99	// delete the lists contents
100	_freeList();
101
102	// Stop watching the MediaRoster for flavor changes
103	BMediaRoster *roster = BMediaRoster::CurrentRoster();
104	if (roster) {
105		BMessenger messenger(this, Window());
106		roster->StopWatching(messenger, B_MEDIA_FLAVORS_CHANGED);
107	}
108}
109
110void DormantNodeView::GetPreferredSize(
111	float* width,
112	float* height) {
113	D_HOOK(("DormantNodeView::GetPreferredSize()\n"));
114
115	// calculate the accumulated size of all list items
116	*width = 0;
117	*height = 0;
118	for (int32 i = 0; i < CountItems(); i++) {
119		DormantNodeListItem *item;
120		item = dynamic_cast<DormantNodeListItem *>(ItemAt(i));
121		if (item) {
122			BRect r = item->getRealFrame(be_plain_font);
123			if (r.Width() > *width) {
124				*width = r.Width();
125			}
126			*height += r.Height() + 1.0;
127		}
128	}
129}
130
131void DormantNodeView::MessageReceived(
132	BMessage *message) {
133	D_MESSAGE(("DormantNodeView::MessageReceived()\n"));
134
135	switch (message->what) {
136		case B_MEDIA_FLAVORS_CHANGED: {
137			D_MESSAGE((" -> B_MEDIA_FLAVORS_CHANGED\n"));
138
139			// init & re-populate the list
140			int32 addOnID = 0;
141			if (message->FindInt32("be:addon_id", &addOnID) != B_OK) {
142				D_MESSAGE((" -> messages doesn't contain 'be:addon_id'!\n"));
143				return;
144			}
145			_updateList(addOnID);
146			break;
147		}
148		case InfoWindowManager::M_INFO_WINDOW_REQUESTED: {
149			D_MESSAGE((" -> InfoWindowManager::M_INFO_WINDOW_REQUESTED)\n"));
150
151			DormantNodeListItem *item;
152			item = dynamic_cast<DormantNodeListItem *>(ItemAt(CurrentSelection()));
153			if (item) {
154				InfoWindowManager *manager = InfoWindowManager::Instance();
155				if (manager && manager->Lock()) {
156					manager->openWindowFor(item->info());
157					manager->Unlock();
158				}
159			}
160			break;
161		}
162		default: {
163			_inherited::MessageReceived(message);
164		}
165	}
166}
167
168void DormantNodeView::MouseDown(
169	BPoint point) {
170	D_HOOK(("DormantNodeView::MouseDown()\n"));
171
172	BMessage* message = Window()->CurrentMessage();
173	int32 buttons = message->FindInt32("buttons");
174	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
175		int32 index;
176		if ((index = IndexOf(point)) >= 0) {
177			DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(index));
178			if (item) {
179				Select(index);
180				BRect r = item->getRealFrame(be_plain_font);
181				if (r.Contains(point)) {
182					item->showContextMenu(point, this);
183				}
184			}
185		}
186	}
187
188	_inherited::MouseDown(point);
189}
190
191void DormantNodeView::MouseMoved(
192	BPoint point,
193	uint32 transit,
194	const BMessage *message) {
195	D_HOOK(("DormantNodeView::MouseMoved()\n"));
196
197	int32 index;
198	if (!message && ((index = IndexOf(point)) >= 0)) {
199		DormantNodeListItem *item =
200			dynamic_cast<DormantNodeListItem *>(ItemAt(index));
201		DormantNodeListItem *last =
202			dynamic_cast<DormantNodeListItem *>(m_lastItemUnder);
203		if (item != NULL) {
204			BRect r = item->getRealFrame(be_plain_font);
205			if (r.Contains(point)) {
206				if (item != last) {
207					if (last != NULL)
208						last->MouseOver(this, point, B_EXITED_VIEW);
209					item->MouseOver(this, point, B_ENTERED_VIEW);
210					m_lastItemUnder = item;
211				}
212				else {
213					item->MouseOver(this, point, B_INSIDE_VIEW);
214				}
215			}
216		}
217		else if (last != NULL) {
218			last->MouseOver(this, point, B_EXITED_VIEW);
219		}
220	}
221
222	_inherited::MouseMoved(point, transit, message);
223}
224
225bool DormantNodeView::InitiateDrag(
226	BPoint point,
227	int32 index,
228	bool wasSelected) {
229	D_HOOK(("DormantNodeView::InitiateDrag()\n"));
230
231	DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(CurrentSelection()));
232	if (item) {
233		BMessage dragMsg(M_INSTANTIATE_NODE);
234		dragMsg.AddData("which", B_RAW_TYPE,
235						reinterpret_cast<const void *>(&item->info()),
236						sizeof(item->info()));
237		point -= ItemFrame(index).LeftTop();
238		DragMessage(&dragMsg, item->getDragBitmap(), B_OP_ALPHA, point);
239		return true;
240	}
241	return false;
242}
243
244// -------------------------------------------------------- //
245// internal operations (private)
246// -------------------------------------------------------- //
247
248void DormantNodeView::_populateList() {
249	D_INTERNAL(("DormantNodeView::_populateList()\n"));
250
251	// init the resizable node-info buffer
252	BMediaRoster *roster = BMediaRoster::CurrentRoster();
253	const int32 bufferInc = 64;
254	int32 bufferSize = bufferInc;
255	dormant_node_info *infoBuffer = new dormant_node_info[bufferSize];
256	int32 numNodes;
257
258	// fill the buffer
259	while (true) {
260		numNodes = bufferSize;
261		status_t error = roster->GetDormantNodes(infoBuffer, &numNodes);
262		if (error) {
263			return;
264		}
265		if (numNodes < bufferSize) {
266			break;
267		}
268
269		// reallocate buffer & try again
270		delete [] infoBuffer;
271		bufferSize += bufferInc;
272		infoBuffer = new dormant_node_info[bufferSize];
273	}
274
275	// populate the list
276	for (int32 i = 0; i < numNodes; i++) {
277		DormantNodeListItem *item = new DormantNodeListItem(infoBuffer[i]);
278		AddItem(item);
279	}
280	SortItems(compareName);
281}
282
283void DormantNodeView::_freeList() {
284	D_HOOK(("DormantNodeView::_freeList()\n"));
285
286	// remove and delete all items in the list
287	while (CountItems() > 0) {
288		BListItem *item = ItemAt(0);
289		if (RemoveItem(item)) {
290			delete item;
291		}
292	}
293}
294
295void DormantNodeView::_updateList(
296	int32 addOnID) {
297	D_INTERNAL(("DormantNodeView::_updateList(%ld)\n", addOnID));
298
299	// init the resizable node-info buffer
300	BMediaRoster *roster = BMediaRoster::CurrentRoster();
301	const int32 bufferInc = 64;
302	int32 bufferSize = bufferInc;
303	dormant_node_info *infoBuffer = new dormant_node_info[bufferSize];
304	int32 numNodes;
305
306	// fill the buffer
307	while (true) {
308		numNodes = bufferSize;
309		status_t error = roster->GetDormantNodes(infoBuffer, &numNodes);
310		if (error) {
311			return;
312		}
313		if (numNodes < bufferSize) {
314			break;
315		}
316
317		// reallocate buffer & try again
318		delete [] infoBuffer;
319		bufferSize += bufferInc;
320		infoBuffer = new dormant_node_info[bufferSize];
321	}
322
323	// sort the list by add-on id to avoid multiple searches through
324	// the list
325	SortItems(compareAddOnID);
326
327	// Remove all nodes managed by this add-on
328	int32 start;
329	for (start = 0; start < CountItems(); start++) {
330		DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(start));
331		if (item && (item->info().addon == addOnID)) {
332			break;
333		}
334	}
335	int32 count = 0;
336	for (int32 i = start; start < CountItems(); i++) {
337		DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(i));
338		if (!item || (item->info().addon != addOnID)) {
339			break;
340		}
341		count++;
342	}
343	RemoveItems(start, count);
344
345	// add the items
346	for (int32 i = 0; i < numNodes; i++) {
347		if (infoBuffer[i].addon != addOnID) {
348			continue;
349		}
350		AddItem(new DormantNodeListItem(infoBuffer[i]));
351	}
352
353	SortItems(compareName);
354}
355
356// END -- DormantNodeView.cpp --
357