xref: /haiku/src/apps/mail/QueryMenu.cpp (revision 0995b563847c884054e912abbd2c5bc08d614f59)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2001, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All 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 
48 BLooper *QueryMenu::fQueryLooper = NULL;
49 int32 QueryMenu::fMenuCount = 0;
50 
51 
52 // ***
53 // QHandler
54 // ***
55 
56 
57 class QHandler : public BHandler
58 {
59 	public:
60 		QHandler(QueryMenu *queryMenu);
61 		virtual void MessageReceived(BMessage *msg);
62 
63 		QueryMenu *fQueryMenu;
64 };
65 
66 
QHandler(QueryMenu * queryMenu)67 QHandler::QHandler(QueryMenu *queryMenu)
68 	:	BHandler((const char *)NULL),
69 	fQueryMenu(queryMenu)
70 {
71 }
72 
73 
MessageReceived(BMessage * msg)74 void 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 
QueryMenu(const char * title,bool popUp,bool radioMode,bool autoRename)94 QueryMenu::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 
~QueryMenu(void)115 QueryMenu::~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 
DoQueryMessage(BMessage * msg)137 void 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 
SetPredicate(const char * expr,BVolume * volume)166 status_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 
RemoveEntries()231 void 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 
query_thread(void * data)242 int32 QueryMenu::query_thread(void *data)
243 {
244 	return ((QueryMenu *)(data))->QueryThread();
245 }
246 
247 
QueryThread()248 int32 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 
SetTargetForItems(BHandler * handler)285 status_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 
SetTargetForItems(BMessenger messenger)294 status_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 
EntryCreated(const entry_ref & ref,ino_t node)306 void 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 
EntryRemoved(ino_t node)322 void 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 
ScreenLocation()340 BPoint QueryMenu::ScreenLocation()
341 {
342 	if (fPopUp)
343 		return BPopUpMenu::ScreenLocation();
344 
345 	return BMenu::ScreenLocation();
346 }
347 
348