1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2001, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All rights reserved.
33*/
34
35#include "QueryMenu.h"
36#include <Query.h>
37#include <Autolock.h>
38#include <MenuItem.h>
39#include <NodeMonitor.h>
40#include <VolumeRoster.h>
41#include <Looper.h>
42#include <Node.h>
43#include <stdio.h>
44#include <string.h>
45#include <PopUpMenu.h>
46
47
48BLooper *QueryMenu::fQueryLooper = NULL;
49int32 QueryMenu::fMenuCount = 0;
50
51
52// ***
53// QHandler
54// ***
55
56
57class QHandler : public BHandler
58{
59	public:
60		QHandler(QueryMenu *queryMenu);
61		virtual void MessageReceived(BMessage *msg);
62
63		QueryMenu *fQueryMenu;
64};
65
66
67QHandler::QHandler(QueryMenu *queryMenu)
68	:	BHandler((const char *)NULL),
69	fQueryMenu(queryMenu)
70{
71}
72
73
74void QHandler::MessageReceived(BMessage *msg)
75{
76	switch (msg->what)
77	{
78		case B_QUERY_UPDATE:
79			fQueryMenu->DoQueryMessage(msg);
80			break;
81
82		default:
83			BHandler::MessageReceived(msg);
84			break;
85	}
86}
87
88
89// ***
90// QueryMenu
91// ***
92
93
94QueryMenu::QueryMenu(const char *title, bool popUp, bool radioMode, bool autoRename)
95	:	BPopUpMenu(title, radioMode, autoRename),
96	fTargetHandler(NULL),
97	fPopUp(popUp)
98{
99	if (atomic_add(&fMenuCount, 1) == 0)
100	{
101		fQueryLooper = new BLooper("Query Watcher");
102		fQueryLooper->Run();
103	}
104	fQueryHandler = new QHandler(this);
105	fQueryLooper->Lock();
106	fQueryLooper->AddHandler(fQueryHandler);
107	fQueryLooper->Unlock();
108
109//	BMessenger mercury(fQueryHandler, fQueryLooper);
110//	fQuery = new BQuery();
111//	fQuery->SetTarget(mercury);
112}
113
114
115QueryMenu::~QueryMenu(void)
116{
117	fCancelQuery = true;
118	fQueryLock.Lock();
119
120	int32 queries = fQueries.size();
121	for (int i = 0; i < queries; i++) {
122		fQueries[i]->Clear();
123		delete fQueries[i];
124	};
125	fQueryLock.Unlock();
126
127	fQueryLooper->Lock();
128	fQueryLooper->RemoveHandler(fQueryHandler);
129	delete fQueryHandler;
130	if (atomic_add(&fMenuCount, -1) == 1)
131		fQueryLooper->Quit();
132	else
133		fQueryLooper->Unlock();
134}
135
136
137void QueryMenu::DoQueryMessage(BMessage *msg)
138{
139	int32 opcode;
140	int64 directory;
141	int32 device;
142	int64 node;
143	if (msg->FindInt32("opcode", &opcode) == B_OK
144		&& msg->FindInt64("directory", &directory) == B_OK
145		&& msg->FindInt32("device", &device) == B_OK
146		&& msg->FindInt64("node", &node) == B_OK)
147	{
148		const char *name;
149		if (opcode == B_ENTRY_CREATED && msg->FindString("name", &name) == B_OK)
150		{
151			entry_ref ref(device, directory, name);
152			EntryCreated(ref, node);
153			return;
154		}
155		else if (opcode == B_ENTRY_REMOVED)
156		{
157			BAutolock lock(fQueryLock);
158			if (!lock.IsLocked())
159				return;
160			EntryRemoved(node);
161		}
162	}
163}
164
165
166status_t QueryMenu::SetPredicate(const char *expr, BVolume *volume)
167{
168//	status_t status;
169
170	if (volume == NULL) {
171		BVolumeRoster roster;
172		BVolume volume;
173		BMessenger mercury(fQueryHandler, fQueryLooper);
174
175		roster.Rewind();
176		while (roster.GetNextVolume(&volume) == B_NO_ERROR) {
177			if ((volume.KnowsQuery() == true) && (volume.KnowsAttr() == true) &&
178				(volume.KnowsMime() == true)) {
179
180				BQuery *query = new BQuery();
181				if (query->SetVolume(&volume) != B_OK) {
182					delete query;
183					continue;
184				};
185				if (query->SetPredicate(expr) != B_OK) {
186					delete query;
187					continue;
188				};
189				if (query->SetTarget(mercury) != B_OK) {
190					delete query;
191					continue;
192				};
193
194				fQueries.push_back(query);
195			};
196		};
197	} else {
198	};
199
200	// Set the volume
201//	if (volume == NULL)
202//	{
203//		BVolume bootVolume;
204//		BVolumeRoster().GetBootVolume(&bootVolume);
205//
206//		if ( (status = fQuery->SetVolume(&bootVolume)) != B_OK)
207//			return status;
208//	}
209//	else if ((status = fQuery->SetVolume(volume)) != B_OK)
210//		return status;
211//
212//	if ((status = fQuery->SetPredicate(expr)) < B_OK)
213//		return status;
214
215	// Force query thread to exit if still running
216	fCancelQuery = true;
217	fQueryLock.Lock();
218
219	// Remove all existing menu items (if any... )
220	RemoveEntries();
221	fQueryLock.Unlock();
222
223	// Resolve Query/Build Menu in seperate thread
224	thread_id thread;
225	thread = spawn_thread(query_thread, "query menu thread", B_NORMAL_PRIORITY, this);
226
227	return resume_thread(thread);
228}
229
230
231void QueryMenu::RemoveEntries()
232{
233	int64 node;
234	for (int32 i = CountItems() - 1;i >= 0;i--)
235	{
236		if (ItemAt(i)->Message()->FindInt64("node", &node) == B_OK)
237			RemoveItem(i);
238	}
239}
240
241
242int32 QueryMenu::query_thread(void *data)
243{
244	return ((QueryMenu *)(data))->QueryThread();
245}
246
247
248int32 QueryMenu::QueryThread()
249{
250	BAutolock lock(fQueryLock);
251
252	if (!lock.IsLocked())
253		return B_ERROR;
254
255	// Begin resolving query
256	fCancelQuery = false;
257	int32 queries = fQueries.size();
258	for (int i = 0; i < queries; i++) {
259		BQuery *query = fQueries[i];
260
261		query->Fetch();
262
263		// Build Menu
264		entry_ref ref;
265		node_ref node;
266		while (query->GetNextRef(&ref) == B_OK && !fCancelQuery)
267		{
268			BEntry entry(&ref);
269			entry.GetNodeRef(&node);
270			EntryCreated(ref, node.node);
271		}
272	};
273
274	// Remove the group separator if there are no groups or no items without groups
275	BMenuItem *item;
276	if (dynamic_cast<BSeparatorItem *>(item = ItemAt(0)) != NULL)
277		RemoveItem(item);
278	else if (dynamic_cast<BSeparatorItem *>(item = ItemAt(CountItems() - 1)) != NULL)
279		RemoveItem(item);
280
281	return B_OK;
282}
283
284
285status_t QueryMenu::SetTargetForItems(BHandler *handler)
286{
287	fTargetHandler = handler;
288	return BMenu::SetTargetForItems(handler);
289}
290
291// Include the following version of SetTargetForItems() to eliminate
292// hidden polymorphism warning. Should be correct, but is unused and untested.
293
294status_t QueryMenu::SetTargetForItems(BMessenger messenger)
295{
296	if (messenger.IsTargetLocal())
297	{
298		BLooper *ignore; // don't care what value this gets
299		fTargetHandler = messenger.Target(&ignore);
300		return BMenu::SetTargetForItems(messenger);
301	}
302	return B_ERROR;
303}
304
305
306void QueryMenu::EntryCreated(const entry_ref &ref, ino_t node)
307{
308	BMessage 		*msg;
309	BMenuItem 		*item;
310
311	msg = new BMessage(B_REFS_RECEIVED);
312	msg->AddRef("refs", &ref);
313	msg->AddInt64("node", node);
314	item = new BMenuItem(ref.name, msg);
315	if (fTargetHandler) {
316		item->SetTarget(fTargetHandler);
317	};
318	AddItem(item);
319}
320
321
322void QueryMenu::EntryRemoved(ino_t node)
323{
324	// Search for item in menu
325	BMenuItem  *item;
326	for (int32 i = 0;(item = ItemAt(i)) != NULL;i++)
327	{
328		// Is it our item?
329		int64 inode;
330		if ((item->Message())->FindInt64("node", &inode) == B_OK
331			&& inode == node)
332		{
333			RemoveItem(i);
334			return;
335		}
336	}
337}
338
339
340BPoint QueryMenu::ScreenLocation()
341{
342	if (fPopUp)
343		return BPopUpMenu::ScreenLocation();
344
345	return BMenu::ScreenLocation();
346}
347
348