xref: /haiku/src/kits/tracker/FindPanel.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, 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 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 
36 #include "FindPanel.h"
37 
38 #include <Application.h>
39 #include <Box.h>
40 #include <Button.h>
41 #include <Catalog.h>
42 #include <CheckBox.h>
43 #include <ControlLook.h>
44 #include <Debug.h>
45 #include <Directory.h>
46 #include <FindDirectory.h>
47 #include <File.h>
48 #include <FilePanel.h>
49 #include <GridLayoutBuilder.h>
50 #include <InterfaceDefs.h>
51 #include <LayoutBuilder.h>
52 #include <Locale.h>
53 #include <MenuField.h>
54 #include <MenuItem.h>
55 #include <Mime.h>
56 #include <NodeInfo.h>
57 #include <PopUpMenu.h>
58 #include <Path.h>
59 #include <Query.h>
60 #include <SeparatorView.h>
61 #include <Size.h>
62 #include <SpaceLayoutItem.h>
63 #include <TextControl.h>
64 #include <TextView.h>
65 #include <View.h>
66 #include <Volume.h>
67 #include <VolumeRoster.h>
68 
69 #include <fs_attr.h>
70 #include <parsedate.h>
71 #include <stdlib.h>
72 #include <string.h>
73 #include <utility>
74 
75 #include "Attributes.h"
76 #include "AutoLock.h"
77 #include "Commands.h"
78 #include "ContainerWindow.h"
79 #include "FSUtils.h"
80 #include "FunctionObject.h"
81 #include "IconMenuItem.h"
82 #include "MimeTypes.h"
83 #include "Tracker.h"
84 #include "Utilities.h"
85 
86 
87 #undef B_TRANSLATION_CONTEXT
88 #define B_TRANSLATION_CONTEXT "FindPanel"
89 
90 
91 const char* kAllMimeTypes = "mime/ALLTYPES";
92 
93 const BRect kInitialRect(0, 0, 0, 0);
94 const int32 kInitialAttrModeWindowHeight = 140;
95 const int32 kIncrementPerAttribute = 30;
96 const float kMoreOptionsDelta = 20;
97 
98 const uint32 kMoreOptionsMessage = 'mrop';
99 const uint32 kNameModifiedMessage = 'nmmd';
100 const uint32 kSwitchToQueryTemplate = 'swqt';
101 const uint32 kRunSaveAsTemplatePanel = 'svtm';
102 const uint32 kLatchChanged = 'ltch';
103 
104 const char* kDragNDropTypes[] = {
105 	B_QUERY_MIMETYPE,
106 	B_QUERY_TEMPLATE_MIMETYPE
107 };
108 static const char* kDragNDropActionSpecifiers[] = {
109 	B_TRANSLATE_MARK("Create a Query"),
110 	B_TRANSLATE_MARK("Create a Query template")
111 };
112 
113 const uint32 kAttachFile = 'attf';
114 
115 const int32 operators[] = {
116 	B_CONTAINS,
117 	B_EQ,
118 	B_NE,
119 	B_BEGINS_WITH,
120 	B_ENDS_WITH,
121 	B_GE,
122 	B_LE
123 };
124 
125 static const char* operatorLabels[] = {
126 	B_TRANSLATE_MARK("contains"),
127 	B_TRANSLATE_MARK("is"),
128 	B_TRANSLATE_MARK("is not"),
129 	B_TRANSLATE_MARK("starts with"),
130 	B_TRANSLATE_MARK("ends with"),
131 	B_TRANSLATE_MARK("greater than"),
132 	B_TRANSLATE_MARK("less than"),
133 	B_TRANSLATE_MARK("before"),
134 	B_TRANSLATE_MARK("after")
135 };
136 
137 
138 namespace BPrivate {
139 
140 class MostUsedNames {
141 	public:
142 		MostUsedNames(const char* fileName, const char* directory,
143 			int32 maxCount = 5);
144 		~MostUsedNames();
145 
146 		bool ObtainList(BList* list);
147 		void ReleaseList();
148 
149 		void AddName(const char*);
150 
151 	protected:
152 		struct list_entry {
153 			char* name;
154 			int32 count;
155 		};
156 
157 		static int CompareNames(const void* a, const void* b);
158 		void LoadList();
159 		void UpdateList();
160 
161 		const char*	fFileName;
162 		const char*	fDirectory;
163 		bool		fLoaded;
164 		mutable Benaphore fLock;
165 		BList		fList;
166 		int32		fCount;
167 };
168 
169 
170 MostUsedNames gMostUsedMimeTypes("MostUsedMimeTypes", "Tracker");
171 
172 
173 //	#pragma mark - MoreOptionsStruct
174 
175 
176 void
177 MoreOptionsStruct::EndianSwap(void*)
178 {
179 	// noop for now
180 }
181 
182 
183 void
184 MoreOptionsStruct::SetQueryTemporary(BNode* node, bool on)
185 {
186 	MoreOptionsStruct saveMoreOptions;
187 
188 	ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign,
189 		B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct),
190 		&MoreOptionsStruct::EndianSwap);
191 	saveMoreOptions.temporary = on;
192 	node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions,
193 		sizeof(saveMoreOptions));
194 }
195 
196 
197 bool
198 MoreOptionsStruct::QueryTemporary(const BNode* node)
199 {
200 	MoreOptionsStruct saveMoreOptions;
201 
202 	if (ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign,
203 		B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct),
204 		&MoreOptionsStruct::EndianSwap) == kReadAttrFailed) {
205 		return false;
206 	}
207 
208 	return saveMoreOptions.temporary;
209 }
210 
211 
212 //	#pragma mark - FindWindow
213 
214 
215 FindWindow::FindWindow(const entry_ref* newRef, bool editIfTemplateOnly)
216 	:
217 	BWindow(kInitialRect, B_TRANSLATE("Find"), B_TITLED_WINDOW,
218 		B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_CLOSE_ON_ESCAPE
219 			| B_AUTO_UPDATE_SIZE_LIMITS),
220 	fFile(TryOpening(newRef)),
221 	fFromTemplate(false),
222 	fEditTemplateOnly(false),
223 	fSaveAsTemplatePanel(NULL)
224 {
225 	if (fFile != NULL) {
226 		fRef = *newRef;
227 		if (editIfTemplateOnly) {
228 			char type[B_MIME_TYPE_LENGTH];
229 			if (BNodeInfo(fFile).GetType(type) == B_OK
230 				&& strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) {
231 				fEditTemplateOnly = true;
232 				SetTitle(B_TRANSLATE("Edit Query template"));
233 			}
234 		}
235 	} else {
236 		// no initial query, fall back on the default query template
237 		BEntry entry;
238 		GetDefaultQuery(entry);
239 		entry.GetRef(&fRef);
240 
241 		if (entry.Exists())
242 			fFile = TryOpening(&fRef);
243 		else {
244 			// no default query template yet
245 			fFile = new BFile(&entry, O_RDWR | O_CREAT);
246 			if (fFile->InitCheck() < B_OK) {
247 				delete fFile;
248 				fFile = NULL;
249 			} else
250 				SaveQueryAttributes(fFile, true);
251 		}
252 	}
253 
254 	fFromTemplate = IsQueryTemplate(fFile);
255 
256 	fBackground = new FindPanel(fFile, this, fFromTemplate,
257 		fEditTemplateOnly);
258 	SetLayout(new BGroupLayout(B_VERTICAL));
259 	GetLayout()->AddView(fBackground);
260 	CenterOnScreen();
261 }
262 
263 
264 FindWindow::~FindWindow()
265 {
266 	delete fFile;
267 	delete fSaveAsTemplatePanel;
268 }
269 
270 
271 BFile*
272 FindWindow::TryOpening(const entry_ref* ref)
273 {
274 	if (!ref)
275 		return NULL;
276 
277 	BFile* result = new BFile(ref, O_RDWR);
278 	if (result->InitCheck() != B_OK) {
279 		delete result;
280 		result = NULL;
281 	}
282 	return result;
283 }
284 
285 
286 void
287 FindWindow::GetDefaultQuery(BEntry &entry)
288 {
289 	BPath path;
290 	find_directory(B_USER_DIRECTORY, &path, true);
291 	path.Append("queries");
292 	mkdir(path.Path(), 0777);
293 	BDirectory directory(path.Path());
294 
295 	entry.SetTo(&directory, "default");
296 }
297 
298 
299 bool
300 FindWindow::IsQueryTemplate(BNode* file)
301 {
302 	char type[B_MIME_TYPE_LENGTH];
303 	if (BNodeInfo(file).GetType(type) != B_OK)
304 		return false;
305 
306 	return strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0;
307 }
308 
309 
310 void
311 FindWindow::SwitchToTemplate(const entry_ref* ref)
312 {
313 	try {
314 		BEntry entry(ref, true);
315 		BFile templateFile(&entry, O_RDONLY);
316 
317 		ThrowOnInitCheckError(&templateFile);
318 		fBackground->SwitchToTemplate(&templateFile);
319 	} catch (...) {
320 		;
321 	}
322 }
323 
324 
325 const char*
326 FindWindow::QueryName() const
327 {
328 	if (fFromTemplate) {
329 		if (!fQueryNameFromTemplate.Length()) {
330 			fFile->ReadAttrString(kAttrQueryTemplateName,
331 				&fQueryNameFromTemplate);
332 		}
333 
334 		return fQueryNameFromTemplate.String();
335 	}
336 	if (!fFile)
337 		return "";
338 
339 	return fRef.name;
340 }
341 
342 
343 static const char*
344 MakeValidFilename(BString &string)
345 {
346 	// make a file name that is legal under bfs and hfs - possibly could
347 	// add code here to accomodate FAT32 etc. too
348 	if (string.Length() > B_FILE_NAME_LENGTH - 1) {
349 		string.Truncate(B_FILE_NAME_LENGTH - 4);
350 		string += B_UTF8_ELLIPSIS;
351 	}
352 
353 	// replace slashes
354 	int32 length = string.Length();
355 	char* buf = string.LockBuffer(length);
356 	for (int32 index = length; index-- > 0;)
357 		if (buf[index] == '/' /*|| buf[index] == ':'*/)
358 			buf[index] = '_';
359 	string.UnlockBuffer(length);
360 
361 	return string.String();
362 }
363 
364 
365 void
366 FindWindow::GetPredicateString(BString &predicate, bool &dynamicDate)
367 {
368 	BQuery query;
369 	BTextControl* textControl
370 		= dynamic_cast<BTextControl*>(FindView("TextControl"));
371 	switch (fBackground->Mode()) {
372 		case kByNameItem:
373 			fBackground->GetByNamePredicate(&query);
374 			query.GetPredicate(&predicate);
375 			break;
376 
377 		case kByFormulaItem:
378 			predicate.SetTo(textControl->TextView()->Text(), 1023);
379 			break;
380 
381 		case kByAttributeItem:
382 			fBackground->GetByAttrPredicate(&query, dynamicDate);
383 			query.GetPredicate(&predicate);
384 			break;
385 	}
386 }
387 
388 
389 void
390 FindWindow::GetDefaultName(BString &result)
391 {
392 	fBackground->GetDefaultName(result);
393 
394 	time_t timeValue = time(0);
395 	char namebuf[B_FILE_NAME_LENGTH];
396 
397 	tm timeData;
398 	localtime_r(&timeValue, &timeData);
399 
400 	strftime(namebuf, 32, " - %b %d, %I:%M:%S %p", &timeData);
401 	result << namebuf;
402 
403 	MakeValidFilename(result);
404 }
405 
406 
407 void
408 FindWindow::SaveQueryAttributes(BNode* file, bool queryTemplate)
409 {
410 	ThrowOnError(BNodeInfo(file).SetType(
411 		queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE));
412 
413 	// save date/time info for recent query support and transient query killer
414 	int32 currentTime = (int32)time(0);
415 	file->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, &currentTime,
416 		sizeof(int32));
417 	int32 tmp = 1;
418 	file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32));
419 }
420 
421 
422 status_t
423 FindWindow::SaveQueryAsAttributes(BNode* file, BEntry* entry,
424 	bool queryTemplate, const BMessage* oldAttributes,
425 	const BPoint* oldLocation)
426 {
427 	if (oldAttributes != NULL) {
428 		// revive old window settings
429 		BContainerWindow::SetLayoutState(file, oldAttributes);
430 	}
431 
432 	if (oldLocation != NULL) {
433 		// and the file's location
434 		FSSetPoseLocation(entry, *oldLocation);
435 	}
436 
437 	BNodeInfo(file).SetType(queryTemplate
438 		? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE);
439 
440 	BString predicate;
441 	bool dynamicDate;
442 	GetPredicateString(predicate, dynamicDate);
443 	file->WriteAttrString(kAttrQueryString, &predicate);
444 
445 	if (dynamicDate) {
446 		file->WriteAttr(kAttrDynamicDateQuery, B_BOOL_TYPE, 0, &dynamicDate,
447 			sizeof(dynamicDate));
448 	}
449 
450 	int32 tmp = 1;
451 	file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32));
452 
453 	// write some useful info to help locate the volume to query
454 	BMenuItem* item = fBackground->VolMenu()->FindMarked();
455 	if (item != NULL) {
456 		dev_t dev;
457 		BMessage message;
458 		uint32 count = 0;
459 
460 		int32 itemCount = fBackground->VolMenu()->CountItems();
461 		for (int32 index = 2; index < itemCount; index++) {
462 			BMenuItem* item = fBackground->VolMenu()->ItemAt(index);
463 
464 			if (!item->IsMarked())
465 				continue;
466 
467 			if (item->Message()->FindInt32("device", &dev) != B_OK)
468 				continue;
469 
470 			count++;
471 			BVolume volume(dev);
472 			EmbedUniqueVolumeInfo(&message, &volume);
473 		}
474 
475 		if (count > 0) {
476 			// do we need to embed any volumes
477 			ssize_t size = message.FlattenedSize();
478 			BString buffer;
479 			status_t result = message.Flatten(buffer.LockBuffer(size), size);
480 			if (result == B_OK) {
481 				if (file->WriteAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0,
482 					buffer.String(), (size_t)size) != size) {
483 					return B_IO_ERROR;
484 				}
485 			}
486 			buffer.UnlockBuffer();
487 		}
488 		// default to query for everything
489 	}
490 
491 	fBackground->SaveWindowState(file, fEditTemplateOnly);
492 		// write out all the dialog items as attributes so that the query can
493 		// be reopened and edited later
494 
495 	BView* focusedItem = CurrentFocus();
496 	if (focusedItem != NULL) {
497 		// text controls never get the focus, their internal text views do
498 		BView* parent = focusedItem->Parent();
499 		if (dynamic_cast<BTextControl*>(parent))
500 			focusedItem = parent;
501 
502 		// write out the current focus and, if text control, selection
503 		BString name(focusedItem->Name());
504 		file->WriteAttrString("_trk/focusedView", &name);
505 		BTextControl* textControl = dynamic_cast<BTextControl*>(focusedItem);
506 		if (textControl) {
507 			int32 selStart, selEnd;
508 			textControl->TextView()->GetSelection(&selStart, &selEnd);
509 			file->WriteAttr("_trk/focusedSelStart", B_INT32_TYPE, 0,
510 				&selStart, sizeof(selStart));
511 			file->WriteAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0,
512 				&selEnd, sizeof(selEnd));
513 		}
514 	}
515 
516 	return B_OK;
517 }
518 
519 
520 void
521 FindWindow::Save()
522 {
523 	FindSaveCommon(false);
524 
525 	// close the find panel
526 	PostMessage(B_QUIT_REQUESTED);
527 }
528 
529 
530 void
531 FindWindow::Find()
532 {
533 	if (!FindSaveCommon(true)) {
534 		// have to wait for the node monitor to force old query to close
535 		// to avoid a race condition
536 		TTracker* tracker = dynamic_cast<TTracker*>(be_app);
537 		ASSERT(tracker);
538 		for (int32 timeOut = 0; ; timeOut++) {
539 			if (!tracker->EntryHasWindowOpen(&fRef)) {
540 				// window quit, we can post refs received to open a
541 				// new copy
542 				break;
543 			}
544 
545 			// PRINT(("waiting for query window to quit, %d\n", timeOut));
546 			if (timeOut == 5000) {
547 				// the old query window would not quit for some reason
548 				TRESPASS();
549 				PostMessage(B_QUIT_REQUESTED);
550 				return;
551 			}
552 			snooze(1000);
553 		}
554 	}
555 
556 	int32 currentTime = (int32)time(0);
557 	fFile->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, &currentTime,
558 		sizeof(int32));
559 
560 	// tell the tracker about it
561 	BMessage message(B_REFS_RECEIVED);
562 	message.AddRef("refs", &fRef);
563 	be_app->PostMessage(&message);
564 
565 	// close the find panel
566 	PostMessage(B_QUIT_REQUESTED);
567 }
568 
569 
570 bool
571 FindWindow::FindSaveCommon(bool find)
572 {
573 	// figure out what we need to do
574 	bool readFromOldFile = fFile != NULL;
575 	bool replaceOriginal = fFile && (!fFromTemplate || fEditTemplateOnly);
576 	bool keepPoseLocation = replaceOriginal;
577 	bool newFile = !fFile || (fFromTemplate && !fEditTemplateOnly);
578 
579 	BEntry entry;
580 	BMessage oldAttributes;
581 	BPoint location;
582 	bool hadLocation = false;
583 	const char* userSpecifiedName = fBackground->UserSpecifiedName();
584 
585 	if (readFromOldFile) {
586 		entry.SetTo(&fRef);
587 		BContainerWindow::GetLayoutState(fFile, &oldAttributes);
588 		hadLocation = FSGetPoseLocation(fFile, &location);
589 	}
590 
591 	if (replaceOriginal) {
592 		fFile->Unset();
593 		entry.Remove();
594 			// remove the current entry - need to do this to quit the
595 			// running query and to close the corresponding window
596 
597 		if (userSpecifiedName && !fEditTemplateOnly) {
598 			// change the name of the old query per users request
599 			fRef.set_name(userSpecifiedName);
600 			entry.SetTo(&fRef);
601 		}
602 	}
603 
604 	if (newFile) {
605 		// create query file in the user's directory
606 		BPath path;
607 		find_directory(B_USER_DIRECTORY, &path, true);
608 		path.Append("queries");
609 		// there might be no queries folder yet, create one
610 		mkdir(path.Path(), 0777);
611 
612 		// either use the user specified name, or go with the name
613 		// generated from the predicate, etc.
614 		if (!userSpecifiedName) {
615 			BString text;
616 			GetDefaultName(text);
617 			path.Append(text.String());
618 		} else
619 			path.Append(userSpecifiedName);
620 
621 		entry.SetTo(path.Path());
622 		entry.Remove();
623 		entry.GetRef(&fRef);
624 	}
625 
626 	fFile = new BFile(&entry, O_RDWR | O_CREAT);
627 	ASSERT(fFile->InitCheck() == B_OK);
628 
629 	SaveQueryAsAttributes(fFile, &entry, !find, newFile ? 0 : &oldAttributes,
630 		(hadLocation && keepPoseLocation) ? &location : 0);
631 
632 	return newFile;
633 }
634 
635 
636 void
637 FindWindow::MessageReceived(BMessage* message)
638 {
639 	switch (message->what) {
640 		case kFindButton:
641 			Find();
642 			break;
643 
644 		case kSaveButton:
645 			Save();
646 			break;
647 
648 		case kAttachFile:
649 			{
650 				entry_ref dir;
651 				const char* name;
652 				bool queryTemplate;
653 				if (message->FindString("name", &name) == B_OK
654 					&& message->FindRef("directory", &dir) == B_OK
655 					&& message->FindBool("template", &queryTemplate)
656 						== B_OK) {
657 					delete fFile;
658 					fFile = NULL;
659 					BDirectory directory(&dir);
660 					BEntry entry(&directory, name);
661 					entry_ref tmpRef;
662 					entry.GetRef(&tmpRef);
663 					fFile = TryOpening(&tmpRef);
664 					if (fFile != NULL) {
665 						fRef = tmpRef;
666 						SaveQueryAsAttributes(fFile, &entry, queryTemplate,
667 							0, 0);
668 							// try to save whatever state we aleady have
669 							// to the new query so that if the user
670 							// opens it before runing it from the find panel,
671 							// something reasonable happens
672 					}
673 				}
674 			}
675 			break;
676 
677 		case kSwitchToQueryTemplate:
678 		{
679 			entry_ref ref;
680 			if (message->FindRef("refs", &ref) == B_OK)
681 				SwitchToTemplate(&ref);
682 
683 			break;
684 		}
685 
686 		case kRunSaveAsTemplatePanel:
687 			if (fSaveAsTemplatePanel != NULL)
688 				fSaveAsTemplatePanel->Show();
689 			else {
690 				BMessenger panel(BackgroundView());
691 				fSaveAsTemplatePanel = new BFilePanel(B_SAVE_PANEL, &panel);
692 				fSaveAsTemplatePanel->SetSaveText(
693 					B_TRANSLATE("Query template"));
694 				fSaveAsTemplatePanel->Window()->SetTitle(
695 					B_TRANSLATE("Save as Query template:"));
696 				fSaveAsTemplatePanel->Show();
697 			}
698 			break;
699 
700 		default:
701 			_inherited::MessageReceived(message);
702 			break;
703 	}
704 }
705 
706 
707 //	#pragma mark - FindPanel
708 
709 
710 FindPanel::FindPanel(BFile* node, FindWindow* parent, bool fromTemplate,
711 	bool editTemplateOnly)
712 	:
713 	BView("MainView", B_WILL_DRAW),
714 	fMode(kByNameItem),
715 	fAttrGrid(NULL),
716 	fDraggableIcon(NULL)
717 {
718 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
719 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
720 
721 	uint32 initialMode = InitialMode(node);
722 
723 	BMessenger self(this);
724 	fRecentQueries = new BPopUpMenu(B_TRANSLATE("Recent queries"), false,
725 		false);
726 	AddRecentQueries(fRecentQueries, true, &self, kSwitchToQueryTemplate);
727 
728 	// add popup for mime types
729 	fMimeTypeMenu = new BPopUpMenu("MimeTypeMenu");
730 	fMimeTypeMenu->SetRadioMode(false);
731 	AddMimeTypesToMenu();
732 
733 	fMimeTypeField = new BMenuField("MimeTypeMenu", "", fMimeTypeMenu);
734 	fMimeTypeField->SetDivider(0.0f);
735 	fMimeTypeField->MenuItem()->SetLabel(B_TRANSLATE("All files and folders"));
736 	// add popup for search criteria
737 	fSearchModeMenu = new BPopUpMenu("searchMode");
738 	fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by name"),
739 		new BMessage(kByNameItem)));
740 	fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by attribute"),
741 		new BMessage(kByAttributeItem)));
742 	fSearchModeMenu->AddItem(new BMenuItem(B_TRANSLATE("by formula"),
743 		new BMessage(kByFormulaItem)));
744 
745 	fSearchModeMenu->ItemAt(initialMode == kByNameItem ? 0 :
746 		(initialMode == kByAttributeItem ? 1 : 2))->SetMarked(true);
747 		// mark the appropriate mode
748 	BMenuField* searchModeField = new BMenuField("", "", fSearchModeMenu);
749 	searchModeField->SetDivider(0.0f);
750 
751 	// add popup for volume list
752 	fVolMenu = new BPopUpMenu("", false, false);
753 	BMenuField* volumeField = new BMenuField("", B_TRANSLATE("On"), fVolMenu);
754 	volumeField->SetDivider(volumeField->StringWidth(volumeField->Label()) + 8);
755 	AddVolumes(fVolMenu);
756 
757 	if (!editTemplateOnly) {
758 		BPoint draggableIconOrigin(0, 0);
759 		BMessage dragNDropMessage(B_SIMPLE_DATA);
760 		dragNDropMessage.AddInt32("be:actions", B_COPY_TARGET);
761 		dragNDropMessage.AddString("be:types", B_FILE_MIME_TYPE);
762 		dragNDropMessage.AddString("be:filetypes", kDragNDropTypes[0]);
763 		dragNDropMessage.AddString("be:filetypes", kDragNDropTypes[1]);
764 		dragNDropMessage.AddString("be:actionspecifier",
765 			B_TRANSLATE_NOCOLLECT(kDragNDropActionSpecifiers[0]));
766 		dragNDropMessage.AddString("be:actionspecifier",
767 			B_TRANSLATE_NOCOLLECT(kDragNDropActionSpecifiers[1]));
768 
769 		BMessenger self(this);
770 		BRect draggableRect = DraggableIcon::PreferredRect(draggableIconOrigin,
771 			B_LARGE_ICON);
772 		fDraggableIcon = new DraggableQueryIcon(draggableRect,
773 			"saveHere", &dragNDropMessage, self,
774 			B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
775 		fDraggableIcon->SetExplicitMaxSize(
776 			BSize(draggableRect.right - draggableRect.left,
777 				draggableRect.bottom - draggableRect.top));
778 	}
779 
780 	fQueryName = new BTextControl("query name", B_TRANSLATE("Query name:"),
781 		"", NULL, B_WILL_DRAW | B_NAVIGABLE | B_NAVIGABLE_JUMP);
782 	FillCurrentQueryName(fQueryName, parent);
783 	fSearchTrashCheck = new BCheckBox("searchTrash",
784 		B_TRANSLATE("Include trash"), NULL);
785 	fTemporaryCheck = new BCheckBox("temporary",
786 		B_TRANSLATE("Temporary"), NULL);
787 	fTemporaryCheck->SetValue(B_CONTROL_ON);
788 
789 	BView* checkboxGroup = BLayoutBuilder::Group<>(B_HORIZONTAL)
790 		.Add(fSearchTrashCheck)
791 		.Add(fTemporaryCheck)
792 		.View();
793 
794 	// add the more options collapsible pane
795 	fMoreOptions = new BBox(B_NO_BORDER, BLayoutBuilder::Group<>()
796 		.AddGrid(B_USE_SMALL_SPACING, B_USE_SMALL_SPACING)
797 			.Add(fQueryName->CreateLabelLayoutItem(), 0, 0)
798 			.Add(fQueryName->CreateTextViewLayoutItem(), 1, 0)
799 			.Add(BSpaceLayoutItem::CreateHorizontalStrut(0), 0, 1)
800 			.Add(checkboxGroup, 1, 1)
801 			.End()
802 		.View());
803 
804 	fLatch = new PaneSwitch("optionsLatch", true, B_WILL_DRAW);
805 	fLatch->SetLabels(B_TRANSLATE("Fewer options"), B_TRANSLATE("More options"));
806 	fLatch->SetValue(0);
807 	fLatch->SetMessage(new BMessage(kLatchChanged));
808 	fLatch->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
809 		B_ALIGN_VERTICAL_CENTER));
810 	fMoreOptions->Hide();
811 
812 	// add Search button
813 	BButton* button;
814 	if (editTemplateOnly) {
815 		button = new BButton("save", B_TRANSLATE("Save"),
816 			new BMessage(kSaveButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
817 	} else {
818 		button = new BButton("find", B_TRANSLATE("Search"),
819 			new BMessage(kFindButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
820 	}
821 	button->MakeDefault(true);
822 
823 	BView* icon = fDraggableIcon;
824 	if (icon == NULL) {
825 		icon = new BBox("no draggable icon", B_WILL_DRAW, B_NO_BORDER);
826 		icon->SetExplicitMaxSize(BSize(0, 0));
827 	}
828 
829 	BView* mimeTypeFieldSpacer = new BBox("MimeTypeMenuSpacer", B_WILL_DRAW,
830 		B_NO_BORDER);
831 	mimeTypeFieldSpacer->SetExplicitMaxSize(BSize(0, 0));
832 
833 	BBox* queryControls = new BBox("Box");
834 	queryControls->SetBorder(B_NO_BORDER);
835 
836 	BBox* queryBox = new BBox("Outer Controls");
837 	queryBox->SetLabel(new BMenuField("RecentQueries", NULL, fRecentQueries));
838 
839 	BGroupView* queryBoxView = new BGroupView(B_VERTICAL,
840 		B_USE_DEFAULT_SPACING);
841 	queryBoxView->GroupLayout()->SetInsets(B_USE_DEFAULT_SPACING);
842 	queryBox->AddChild(queryBoxView);
843 
844 	icon->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_BOTTOM));
845 	button->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM));
846 
847 	BLayoutBuilder::Group<>(queryBoxView, B_VERTICAL, B_USE_DEFAULT_SPACING)
848 		.SetInsets(B_USE_DEFAULT_SPACING)
849 		.AddGroup(B_HORIZONTAL, B_USE_SMALL_SPACING)
850 			.Add(fMimeTypeField)
851 			.Add(mimeTypeFieldSpacer)
852 			.Add(searchModeField)
853 			.AddStrut(B_USE_DEFAULT_SPACING)
854 			.Add(volumeField)
855 			.End()
856 		.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
857 		.Add(queryControls);
858 	BLayoutBuilder::Group<>(this, B_VERTICAL, B_USE_DEFAULT_SPACING)
859 		.SetInsets(B_USE_DEFAULT_SPACING)
860 		.Add(queryBox)
861 		.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
862 			.Add(icon)
863 			.AddGroup(B_VERTICAL)
864 				.AddGroup(B_HORIZONTAL)
865 					.Add(fLatch)
866 					.Add(new BSeparatorView(B_HORIZONTAL, B_PLAIN_BORDER))
867 					.End()
868 				.Add(fMoreOptions)
869 				.End()
870 			.Add(button)
871 			.End();
872 
873 	if (initialMode != kByAttributeItem)
874 		AddByNameOrFormulaItems();
875 	else
876 		AddByAttributeItems(node);
877 
878 	ResizeMenuField(fMimeTypeField);
879 	ResizeMenuField(searchModeField);
880 	ResizeMenuField(volumeField);
881 }
882 
883 
884 FindPanel::~FindPanel()
885 {
886 }
887 
888 
889 void
890 FindPanel::AttachedToWindow()
891 {
892 	BNode* node = dynamic_cast<FindWindow*>(Window())->QueryNode();
893 	fSearchModeMenu->SetTargetForItems(this);
894 	fQueryName->SetTarget(this);
895 	fLatch->SetTarget(this);
896 	RestoreMimeTypeMenuSelection(node);
897 		// preselect the mime we used the last time have to do it here
898 		// because AddByAttributeItems will build different menus based
899 		// on which mime type is preselected
900 	RestoreWindowState(node);
901 
902 	if (!Window()->CurrentFocus()) {
903 		// try to pick a good focus if we restore to one already
904 		BTextControl* textControl
905 			= dynamic_cast<BTextControl*>(FindView("TextControl"));
906 		if (textControl == NULL) {
907 			// pick the last text control in the attribute view
908 			BString title("TextEntry");
909 			title << (fAttrGrid->CountRows() - 1);
910 			textControl
911 				= dynamic_cast<BTextControl*>(FindView(title.String()));
912 		}
913 		if (textControl)
914 			textControl->MakeFocus();
915 	}
916 
917 	BButton* button = dynamic_cast<BButton*>(FindView("remove button"));
918 	if (button != NULL)
919 		button->SetTarget(this);
920 
921 	button = dynamic_cast<BButton*>(FindView("add button"));
922 	if (button != NULL)
923 		button->SetTarget(this);
924 
925 	fVolMenu->SetTargetForItems(this);
926 
927 	// set target for MIME type items
928 	for (int32 index = MimeTypeMenu()->CountItems();index-- > 2;) {
929 		BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu();
930 		if (submenu != NULL)
931 				submenu->SetTargetForItems(this);
932 	}
933 	fMimeTypeMenu->SetTargetForItems(this);
934 
935 	if (fDraggableIcon != NULL)
936 		fDraggableIcon->SetTarget(BMessenger(this));
937 
938 	fRecentQueries->SetTargetForItems(Window());
939 }
940 
941 
942 void
943 FindPanel::ResizeMenuField(BMenuField* menuField)
944 {
945 	BSize size;
946 	menuField->GetPreferredSize(&size.width, &size.height);
947 
948 	BMenu* menu = menuField->Menu();
949 
950 	float padding = 0.0f;
951 	float width = 0.0f;
952 
953 	BMenuItem* markedItem = menu->FindMarked();
954 	if (markedItem != NULL) {
955 		if (markedItem->Submenu() != NULL) {
956 			BMenuItem* markedSubItem = markedItem->Submenu()->FindMarked();
957 			if (markedSubItem != NULL && markedSubItem->Label() != NULL) {
958 				float labelWidth
959 					= menuField->StringWidth(markedSubItem->Label());
960 				padding = size.width - labelWidth;
961 			}
962 		} else if (markedItem->Label() != NULL) {
963 			float labelWidth = menuField->StringWidth(markedItem->Label());
964 			padding = size.width - labelWidth;
965 		}
966 	}
967 
968 	for (int32 index = menu->CountItems(); index-- > 0; ) {
969 		BMenuItem* item = menu->ItemAt(index);
970 		if (item->Label() != NULL)
971 			width = std::max(width, menuField->StringWidth(item->Label()));
972 
973 		BMenu* submenu = item->Submenu();
974 		if (submenu != NULL) {
975 			for (int32 subIndex = submenu->CountItems(); subIndex-- > 0; ) {
976 				BMenuItem* subItem = submenu->ItemAt(subIndex);
977 				if (subItem->Label() == NULL)
978 					continue;
979 
980 				width = std::max(width,
981 					menuField->StringWidth(subItem->Label()));
982 			}
983 		}
984 	}
985 
986 	float maxWidth = be_control_look->DefaultItemSpacing() * 20;
987 	size.width = std::min(width + padding, maxWidth);
988 	menuField->SetExplicitSize(size);
989 }
990 
991 static void
992 PopUpMenuSetTitle(BMenu* menu, const char* title)
993 {
994 	// This should really be in BMenuField
995 	BMenu* bar = menu->Supermenu();
996 
997 	ASSERT(bar);
998 	ASSERT(bar->ItemAt(0));
999 	if (bar == NULL || !bar->ItemAt(0))
1000 		return;
1001 
1002 	bar->ItemAt(0)->SetLabel(title);
1003 }
1004 
1005 
1006 void
1007 FindPanel::ShowVolumeMenuLabel()
1008 {
1009 	if (fVolMenu->ItemAt(0)->IsMarked()) {
1010 		// "all disks" selected
1011 		PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label());
1012 		return;
1013 	}
1014 
1015 	// find out if more than one items are marked
1016 	int32 count = fVolMenu->CountItems();
1017 	int32 countSelected = 0;
1018 	BMenuItem* tmpItem = NULL;
1019 	for (int32 index = 2; index < count; index++) {
1020 		BMenuItem* item = fVolMenu->ItemAt(index);
1021 		if (item->IsMarked()) {
1022 			countSelected++;
1023 			tmpItem = item;
1024 		}
1025 	}
1026 
1027 	if (countSelected == 0) {
1028 		// no disk selected, for now revert to search all disks
1029 		// ToDo:
1030 		// show no disks here and add a check that will not let the
1031 		// query go if the user doesn't pick at least one
1032 		fVolMenu->ItemAt(0)->SetMarked(true);
1033 		PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label());
1034 	} else if (countSelected > 1)
1035 		// if more than two disks selected, don't use the disk name
1036 		// as a label
1037 		PopUpMenuSetTitle(fVolMenu,	B_TRANSLATE("multiple disks"));
1038 	else {
1039 		ASSERT(tmpItem);
1040 		PopUpMenuSetTitle(fVolMenu, tmpItem->Label());
1041 	}
1042 }
1043 
1044 
1045 void
1046 FindPanel::Draw(BRect)
1047 {
1048 	if (fAttrGrid == NULL)
1049 		return;
1050 
1051 	for (int32 index = 0; index < fAttrGrid->CountRows(); index++) {
1052 		BMenuField* menuField
1053 			= dynamic_cast<BMenuField*>(FindAttrView("MenuField", index));
1054 		if (menuField == NULL)
1055 			continue;
1056 
1057 		BLayoutItem* stringView = fAttrGrid->ItemAt(1, index);
1058 		BMenuItem* item = menuField->Menu()->FindMarked();
1059 		if (item == NULL || item->Submenu() == NULL
1060 			|| item->Submenu()->FindMarked() == NULL) {
1061 			continue;
1062 		}
1063 
1064 		if (stringView == NULL) {
1065 			stringView = fAttrGrid->AddView(new BStringView("",
1066 				item->Submenu()->FindMarked()->Label()), 1, index);
1067 			stringView->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
1068 				B_ALIGN_VERTICAL_UNSET));
1069 		} else {
1070 			dynamic_cast<BStringView*>(stringView->View())->SetText(
1071 				item->Submenu()->FindMarked()->Label());
1072 		}
1073 	}
1074 }
1075 
1076 
1077 void
1078 FindPanel::MessageReceived(BMessage* message)
1079 {
1080 	entry_ref dir;
1081 	const char* name;
1082 	BMenuItem* item;
1083 
1084 	switch (message->what) {
1085 		case kVolumeItem:
1086 		{
1087 			// volume changed
1088 			BMenuItem* invokedItem;
1089 			dev_t dev;
1090 			if (message->FindPointer("source", (void**)&invokedItem) != B_OK)
1091 				return;
1092 
1093 			if (message->FindInt32("device", &dev) != B_OK)
1094 				break;
1095 
1096 			BMenu* menu = invokedItem->Menu();
1097 			ASSERT(menu);
1098 
1099 			if (dev == -1) {
1100 				// all disks selected, uncheck everything else
1101 				int32 count = menu->CountItems();
1102 				for (int32 index = 2; index < count; index++)
1103 					menu->ItemAt(index)->SetMarked(false);
1104 
1105 				// make all disks the title and check it
1106 				PopUpMenuSetTitle(menu, menu->ItemAt(0)->Label());
1107 				menu->ItemAt(0)->SetMarked(true);
1108 			} else {
1109 				// a specific volume selected, unmark "all disks"
1110 				menu->ItemAt(0)->SetMarked(false);
1111 
1112 				// toggle mark on invoked item
1113 				int32 count = menu->CountItems();
1114 				for (int32 index = 2; index < count; index++) {
1115 					BMenuItem* item = menu->ItemAt(index);
1116 
1117 					if (invokedItem == item) {
1118 						// we just selected this
1119 						bool wasMarked = item->IsMarked();
1120 						item->SetMarked(!wasMarked);
1121 					}
1122 				}
1123 			}
1124 			// make sure the right label is showing
1125 			ShowVolumeMenuLabel();
1126 
1127 			break;
1128 		}
1129 
1130 		case kByAttributeItem:
1131 		case kByNameItem:
1132 		case kByFormulaItem:
1133 			SwitchMode(message->what);
1134 			break;
1135 
1136 		case kAddItem:
1137 			AddAttrRow();
1138 			break;
1139 
1140 		case kRemoveItem:
1141 			RemoveAttrRow();
1142 			break;
1143 
1144 		case kMIMETypeItem:
1145 		{
1146 			if (fMode == kByAttributeItem) {
1147 				// the attributes for this type may be different
1148 				RemoveAttrViewItems(false);
1149 				AddAttrRow();
1150 			}
1151 
1152 			BMenuItem* item;
1153 			if (message->FindPointer("source", (void**)&item) == B_OK) {
1154 				// don't add the "All files and folders" to the list
1155 				if (fMimeTypeMenu->IndexOf(item) != 0)
1156 					gMostUsedMimeTypes.AddName(item->Label());
1157 
1158 				SetCurrentMimeType(item);
1159 			}
1160 
1161 			break;
1162 		}
1163 
1164 		case kNameModifiedMessage:
1165 			// the query name was edited, make the query permanent
1166 			fTemporaryCheck->SetValue(0);
1167 			break;
1168 
1169 		case kAttributeItem:
1170 			if (message->FindPointer("source", (void**)&item) != B_OK)
1171 				return;
1172 
1173 			item->Menu()->Superitem()->SetMarked(true);
1174 			Invalidate();
1175 			break;
1176 
1177 		case kAttributeItemMain:
1178 			// in case someone selected just an attribute without the
1179 			// comparator
1180 			if (message->FindPointer("source", (void**)&item) != B_OK)
1181 				return;
1182 
1183 			if (item->Submenu()->ItemAt(0) != NULL)
1184 				item->Submenu()->ItemAt(0)->SetMarked(true);
1185 
1186 			Invalidate();
1187 			break;
1188 
1189 		case kLatchChanged:
1190 		{
1191 			int32 value;
1192 			if (message->FindInt32("be:value", &value) != B_OK)
1193 				break;
1194 
1195 			if (value == 0 && !fMoreOptions->IsHidden(this))
1196 				fMoreOptions->Hide();
1197 			else if (value == 1 && fMoreOptions->IsHidden(this))
1198 				fMoreOptions->Show();
1199 
1200 			break;
1201 		}
1202 
1203 		case B_SAVE_REQUESTED:
1204 		{
1205 			// finish saving query template from a SaveAs panel
1206 			entry_ref ref;
1207 			status_t error = message->FindRef("refs", &ref);
1208 
1209 			if (error == B_OK) {
1210 				// direct entry selected, convert to parent dir and name
1211 				BEntry entry(&ref);
1212 				error = entry.GetParent(&entry);
1213 				if (error == B_OK) {
1214 					entry.GetRef(&dir);
1215 					name = ref.name;
1216 				}
1217 			} else {
1218 				// parent dir and name selected
1219 				error = message->FindRef("directory", &dir);
1220 				if (error == B_OK)
1221 					error = message->FindString("name", &name);
1222 			}
1223 
1224 			if (error == B_OK)
1225 				SaveAsQueryOrTemplate(&dir, name, true);
1226 
1227 			break;
1228 		}
1229 
1230 		case B_COPY_TARGET:
1231 		{
1232 			// finish drag&drop
1233 			const char* str;
1234 			const char* mimeType = NULL;
1235 			const char* actionSpecifier = NULL;
1236 
1237 			if (message->FindString("be:types", &str) == B_OK
1238 				&& strcasecmp(str, B_FILE_MIME_TYPE) == 0
1239 				&& (message->FindString("be:actionspecifier",
1240 						&actionSpecifier) == B_OK
1241 					|| message->FindString("be:filetypes", &mimeType) == B_OK)
1242 				&& message->FindString("name", &name) == B_OK
1243 				&& message->FindRef("directory", &dir) == B_OK) {
1244 
1245 				bool query = false;
1246 				bool queryTemplate = false;
1247 
1248 				if (actionSpecifier
1249 					&& strcasecmp(actionSpecifier,
1250 						B_TRANSLATE_NOCOLLECT(
1251 							kDragNDropActionSpecifiers[0])) == 0) {
1252 					query = true;
1253 				} else if (actionSpecifier
1254 					&& strcasecmp(actionSpecifier,
1255 						B_TRANSLATE_NOCOLLECT(
1256 							kDragNDropActionSpecifiers[1])) == 0) {
1257 					queryTemplate = true;
1258 				} else if (mimeType && strcasecmp(mimeType,
1259 						kDragNDropTypes[0]) == 0) {
1260 					query = true;
1261 				} else if (mimeType && strcasecmp(mimeType,
1262 					kDragNDropTypes[1]) == 0) {
1263 					queryTemplate = true;
1264 				}
1265 
1266 				if (query || queryTemplate)
1267 					SaveAsQueryOrTemplate(&dir, name, queryTemplate);
1268 			}
1269 
1270 			break;
1271 		}
1272 
1273 		default:
1274 			_inherited::MessageReceived(message);
1275 			break;
1276 	}
1277 }
1278 
1279 
1280 void
1281 FindPanel::SaveAsQueryOrTemplate(const entry_ref* dir, const char* name,
1282 	bool queryTemplate)
1283 {
1284 	BDirectory directory(dir);
1285 	BFile file(&directory, name, O_RDWR | O_CREAT | O_TRUNC);
1286 	BNodeInfo(&file).SetType(queryTemplate
1287 		? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE);
1288 
1289 	BMessage attach(kAttachFile);
1290 	attach.AddRef("directory", dir);
1291 	attach.AddString("name", name);
1292 	attach.AddBool("template", queryTemplate);
1293 	Window()->PostMessage(&attach, 0);
1294 }
1295 
1296 
1297 BView*
1298 FindPanel::FindAttrView(const char* name, int row) const
1299 {
1300 	for (int32 index = 0; index < fAttrGrid->CountColumns(); index++) {
1301 
1302 		BLayoutItem* item = fAttrGrid->ItemAt(index, row);
1303 		if (item == NULL)
1304 			continue;
1305 
1306 		BView* view = item->View();
1307 		if (view == NULL)
1308 			continue;
1309 
1310 		view = view->FindView(name);
1311 		if (view != NULL)
1312 			return view;
1313 
1314 	}
1315 
1316 	return NULL;
1317 }
1318 
1319 void
1320 FindPanel::BuildAttrQuery(BQuery* query, bool &dynamicDate) const
1321 {
1322 	dynamicDate = false;
1323 
1324 	// go through each attrview and add the attr and comparison info
1325 	for (int32 index = 0; index < fAttrGrid->CountRows(); index++) {
1326 
1327 		BString title;
1328 		title << "TextEntry" << index;
1329 
1330 		BTextControl* textControl = dynamic_cast<BTextControl*>
1331 			(FindAttrView(title, index));
1332 		if (textControl == NULL)
1333 			return;
1334 
1335 		BMenuField* menuField
1336 			= dynamic_cast<BMenuField*>(FindAttrView("MenuField", index));
1337 		if (menuField == NULL)
1338 			return;
1339 
1340 		BMenuItem* item = menuField->Menu()->FindMarked();
1341 		if (item == NULL)
1342 			continue;
1343 
1344 		BMessage* message = item->Message();
1345 		int32 type;
1346 		if (message->FindInt32("type", &type) == B_OK) {
1347 
1348 			const char* str;
1349 			if (message->FindString("name", &str) == B_OK)
1350 				query->PushAttr(str);
1351 			else
1352 				query->PushAttr(item->Label());
1353 
1354 			switch (type) {
1355 				case B_STRING_TYPE:
1356 					query->PushString(textControl->TextView()->Text(), true);
1357 					break;
1358 
1359 				case B_TIME_TYPE:
1360 				{
1361 					int flags = 0;
1362 					DEBUG_ONLY(time_t result =)
1363 					parsedate_etc(textControl->TextView()->Text(), -1,
1364 						&flags);
1365 					dynamicDate = (flags & PARSEDATE_RELATIVE_TIME) != 0;
1366 					PRINT(("parsedate_etc - date is %srelative, %"
1367 						B_PRIdTIME "\n",
1368 						dynamicDate ? "" : "not ", result));
1369 
1370 					query->PushDate(textControl->TextView()->Text());
1371 					break;
1372 				}
1373 
1374 				case B_BOOL_TYPE:
1375 				{
1376 					uint32 value;
1377 					if (strcasecmp(textControl->TextView()->Text(),
1378 							"true") == 0) {
1379 						value = 1;
1380 					} else if (strcasecmp(textControl->TextView()->Text(),
1381 							"true") == 0) {
1382 						value = 1;
1383 					} else
1384 						value = (uint32)atoi(textControl->TextView()->Text());
1385 
1386 					value %= 2;
1387 					query->PushUInt32(value);
1388 					break;
1389 				}
1390 
1391 				case B_UINT8_TYPE:
1392 				case B_UINT16_TYPE:
1393 				case B_UINT32_TYPE:
1394 					query->PushUInt32((uint32)StringToScalar(
1395 						textControl->TextView()->Text()));
1396 					break;
1397 
1398 				case B_INT8_TYPE:
1399 				case B_INT16_TYPE:
1400 				case B_INT32_TYPE:
1401 					query->PushInt32((int32)StringToScalar(
1402 						textControl->TextView()->Text()));
1403 					break;
1404 
1405 				case B_UINT64_TYPE:
1406 					query->PushUInt64((uint64)StringToScalar(
1407 						textControl->TextView()->Text()));
1408 					break;
1409 
1410 				case B_OFF_T_TYPE:
1411 				case B_INT64_TYPE:
1412 					query->PushInt64(StringToScalar(
1413 						textControl->TextView()->Text()));
1414 					break;
1415 
1416 				case B_FLOAT_TYPE:
1417 				{
1418 					float floatVal;
1419 					sscanf(textControl->TextView()->Text(), "%f",
1420 						&floatVal);
1421 					query->PushFloat(floatVal);
1422 					break;
1423 				}
1424 
1425 				case B_DOUBLE_TYPE:
1426 				{
1427 					double doubleVal;
1428 					sscanf(textControl->TextView()->Text(), "%lf",
1429 						&doubleVal);
1430 					query->PushDouble(doubleVal);
1431 					break;
1432 				}
1433 			}
1434 		}
1435 
1436 		query_op theOperator;
1437 		BMenuItem* operatorItem = item->Submenu()->FindMarked();
1438 		if (operatorItem && operatorItem->Message() != NULL) {
1439 			operatorItem->Message()->FindInt32("operator",
1440 				(int32*)&theOperator);
1441 			query->PushOp(theOperator);
1442 		} else
1443 			query->PushOp(B_EQ);
1444 
1445 		// add logic based on selection in Logic menufield
1446 		if (index > 0) {
1447 			menuField
1448 				= dynamic_cast<BMenuField*>(FindAttrView("Logic", index - 1));
1449 			if (menuField) {
1450 				item = menuField->Menu()->FindMarked();
1451 				if (item) {
1452 					message = item->Message();
1453 					message->FindInt32("combine", (int32*)&theOperator);
1454 					query->PushOp(theOperator);
1455 				}
1456 			} else
1457 				query->PushOp(B_AND);
1458 		}
1459 	}
1460 }
1461 
1462 
1463 void
1464 FindPanel::PushMimeType(BQuery* query) const
1465 {
1466 	const char* type;
1467 	if (CurrentMimeType(&type) == NULL)
1468 		return;
1469 
1470 	if (strcmp(kAllMimeTypes, type)) {
1471 		// add an asterisk if we are searching for a supertype
1472 		char buffer[B_FILE_NAME_LENGTH];
1473 		if (strchr(type, '/') == NULL) {
1474 			strlcpy(buffer, type, sizeof(buffer));
1475 			strlcat(buffer, "/*", sizeof(buffer));
1476 			type = buffer;
1477 		}
1478 
1479 		query->PushAttr(kAttrMIMEType);
1480 		query->PushString(type);
1481 		query->PushOp(B_EQ);
1482 		query->PushOp(B_AND);
1483 	}
1484 }
1485 
1486 
1487 void
1488 FindPanel::GetByAttrPredicate(BQuery* query, bool &dynamicDate) const
1489 {
1490 	ASSERT(Mode() == (int32)kByAttributeItem);
1491 	BuildAttrQuery(query, dynamicDate);
1492 	PushMimeType(query);
1493 }
1494 
1495 
1496 void
1497 FindPanel::GetDefaultName(BString &result) const
1498 {
1499 	BTextControl* textControl
1500 		= dynamic_cast<BTextControl*>(FindView("TextControl"));
1501 	switch (Mode()) {
1502 		case kByNameItem:
1503 			result.SetTo(B_TRANSLATE_COMMENT("Name = %name",
1504 				"FindResultTitle"));
1505 			result.ReplaceFirst("%name", textControl->TextView()->Text());
1506 			break;
1507 
1508 		case kByFormulaItem:
1509 			result.SetTo(B_TRANSLATE_COMMENT("Formula %formula",
1510 				"FindResultTitle"));
1511 			result.ReplaceFirst("%formula", textControl->TextView()->Text());
1512 			break;
1513 
1514 		case kByAttributeItem:
1515 		{
1516 			BMenuItem* item = fMimeTypeMenu->FindMarked();
1517 			if (item != NULL)
1518 				result << item->Label() << ": ";
1519 			for (int32 i = 0; i < fAttrGrid->CountRows(); i++) {
1520 				GetDefaultAttrName(result, i);
1521 				if (i + 1 < fAttrGrid->CountRows())
1522 					result << ", ";
1523 			}
1524 			break;
1525 		}
1526 	}
1527 }
1528 
1529 
1530 const char*
1531 FindPanel::UserSpecifiedName() const
1532 {
1533 	if (fQueryName->Text()[0] == '\0')
1534 		return NULL;
1535 
1536 	return fQueryName->Text();
1537 }
1538 
1539 
1540 void
1541 FindPanel::GetByNamePredicate(BQuery* query) const
1542 {
1543 	ASSERT(Mode() == (int32)kByNameItem);
1544 	BTextControl* textControl
1545 		= dynamic_cast<BTextControl*>(FindView("TextControl"));
1546 	ASSERT(textControl);
1547 
1548 	query->PushAttr("name");
1549 	query->PushString(textControl->TextView()->Text(), true);
1550 
1551 	if (strstr(textControl->TextView()->Text(), "*")) {
1552 		// assume pattern is a regular expression, try doing an exact match
1553 		query->PushOp(B_EQ);
1554 	} else
1555 		query->PushOp(B_CONTAINS);
1556 
1557 	PushMimeType(query);
1558 }
1559 
1560 
1561 void
1562 FindPanel::SwitchMode(uint32 mode)
1563 {
1564 	if (fMode == mode)
1565 		// no work, bail
1566 		return;
1567 
1568 	uint32 oldMode = fMode;
1569 	BString buffer;
1570 
1571 	switch (mode) {
1572 		case kByFormulaItem:
1573 		{
1574 			if (oldMode == kByAttributeItem || oldMode == kByNameItem) {
1575 				BQuery query;
1576 				if (oldMode == kByAttributeItem) {
1577 					bool dummy;
1578 					GetByAttrPredicate(&query, dummy);
1579 				} else
1580 					GetByNamePredicate(&query);
1581 
1582 				query.GetPredicate(&buffer);
1583 			}
1584 		} // fall thru
1585 		case kByNameItem:
1586 		{
1587 			fMode = mode;
1588 			RemoveByAttributeItems();
1589 			ShowOrHideMimeTypeMenu();
1590 			AddByNameOrFormulaItems();
1591 
1592 			if (buffer.Length()) {
1593 				ASSERT(mode == kByFormulaItem
1594 					|| oldMode == kByAttributeItem);
1595 				BTextControl* textControl
1596 					= dynamic_cast<BTextControl*>(FindView("TextControl"));
1597 				textControl->SetText(buffer.String());
1598 			}
1599 			break;
1600 		}
1601 
1602 		case kByAttributeItem:
1603 		{
1604 			fMode = mode;
1605 			BTextControl* textControl
1606 				= dynamic_cast<BTextControl*>(FindView("TextControl"));
1607 			if (textControl) {
1608 				textControl->RemoveSelf();
1609 				delete textControl;
1610 			}
1611 
1612 			ShowOrHideMimeTypeMenu();
1613 			AddAttrRow();
1614 			break;
1615 		}
1616 	}
1617 }
1618 
1619 
1620 BMenuItem*
1621 FindPanel::CurrentMimeType(const char** type) const
1622 {
1623 	// search for marked item in the list
1624 	BMenuItem* item = MimeTypeMenu()->FindMarked();
1625 
1626 	if (item != NULL && MimeTypeMenu()->IndexOf(item) != 0
1627 		&& item->Submenu() == NULL) {
1628 		// if it's one of the most used items, ignore it
1629 		item = NULL;
1630 	}
1631 
1632 	if (item == NULL) {
1633 		for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) {
1634 			BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu();
1635 			if (submenu != NULL && (item = submenu->FindMarked()) != NULL)
1636 				break;
1637 		}
1638 	}
1639 
1640 	if (type != NULL && item != NULL) {
1641 		BMessage* message = item->Message();
1642 		if (message == NULL)
1643 			return NULL;
1644 
1645 		if (message->FindString("mimetype", type) != B_OK)
1646 			return NULL;
1647 	}
1648 	return item;
1649 }
1650 
1651 
1652 status_t
1653 FindPanel::SetCurrentMimeType(BMenuItem* item)
1654 {
1655 	// unmark old MIME type (in most used list, and the tree)
1656 
1657 	BMenuItem* marked = CurrentMimeType();
1658 	if (marked != NULL) {
1659 		marked->SetMarked(false);
1660 
1661 		if ((marked = MimeTypeMenu()->FindMarked()) != NULL)
1662 			marked->SetMarked(false);
1663 	}
1664 
1665 	// mark new MIME type (in most used list, and the tree)
1666 
1667 	if (item != NULL) {
1668 		item->SetMarked(true);
1669 		fMimeTypeField->MenuItem()->SetLabel(item->Label());
1670 
1671 		BMenuItem* search;
1672 		for (int32 i = 2; (search = MimeTypeMenu()->ItemAt(i)) != NULL; i++) {
1673 			if (item == search || search->Label() == NULL)
1674 				continue;
1675 
1676 			if (strcmp(item->Label(), search->Label()) == 0) {
1677 				search->SetMarked(true);
1678 				break;
1679 			}
1680 
1681 			BMenu* submenu = search->Submenu();
1682 			if (submenu == NULL)
1683 				continue;
1684 
1685 			for (int32 j = submenu->CountItems(); j-- > 0;) {
1686 				BMenuItem* sub = submenu->ItemAt(j);
1687 				if (strcmp(item->Label(), sub->Label()) == 0) {
1688 					sub->SetMarked(true);
1689 					break;
1690 				}
1691 			}
1692 		}
1693 	}
1694 
1695 	return B_OK;
1696 }
1697 
1698 
1699 status_t
1700 FindPanel::SetCurrentMimeType(const char* label)
1701 {
1702 	// unmark old MIME type (in most used list, and the tree)
1703 
1704 	BMenuItem* marked = CurrentMimeType();
1705 	if (marked != NULL) {
1706 		marked->SetMarked(false);
1707 
1708 		if ((marked = MimeTypeMenu()->FindMarked()) != NULL)
1709 			marked->SetMarked(false);
1710 	}
1711 
1712 	// mark new MIME type (in most used list, and the tree)
1713 
1714 	fMimeTypeField->MenuItem()->SetLabel(label);
1715 	bool found = false;
1716 
1717 	for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) {
1718 		BMenuItem* item = MimeTypeMenu()->ItemAt(index);
1719 		BMenu* submenu = item->Submenu();
1720 		if (submenu != NULL && !found) {
1721 			for (int32 subIndex = submenu->CountItems(); subIndex-- > 0;) {
1722 				BMenuItem* subItem = submenu->ItemAt(subIndex);
1723 				if (subItem->Label() != NULL
1724 					&& strcmp(label, subItem->Label()) == 0) {
1725 					subItem->SetMarked(true);
1726 					found = true;
1727 				}
1728 			}
1729 		}
1730 
1731 		if (item->Label() != NULL && strcmp(label, item->Label()) == 0) {
1732 			item->SetMarked(true);
1733 			return B_OK;
1734 		}
1735 	}
1736 
1737 	return found ? B_OK : B_ENTRY_NOT_FOUND;
1738 }
1739 
1740 
1741 bool
1742 FindPanel::AddOneMimeTypeToMenu(const ShortMimeInfo* info, void* castToMenu)
1743 {
1744 	BPopUpMenu* menu = static_cast<BPopUpMenu*>(castToMenu);
1745 
1746 	BMimeType type(info->InternalName());
1747 	BMimeType super;
1748 	type.GetSupertype(&super);
1749 	if (super.InitCheck() < B_OK)
1750 		return false;
1751 
1752 	BMenuItem* superItem = menu->FindItem(super.Type());
1753 	if (superItem != NULL) {
1754 		BMessage* msg = new BMessage(kMIMETypeItem);
1755 		msg->AddString("mimetype", info->InternalName());
1756 
1757 		superItem->Submenu()->AddItem(new IconMenuItem(
1758 			info->ShortDescription(), msg, info->InternalName(),
1759 			B_MINI_ICON));
1760 	}
1761 
1762 	return false;
1763 }
1764 
1765 
1766 void
1767 FindPanel::AddMimeTypesToMenu()
1768 {
1769 	BMessage* itemMessage = new BMessage(kMIMETypeItem);
1770 	itemMessage->AddString("mimetype", kAllMimeTypes);
1771 	MimeTypeMenu()->AddItem(
1772 		new BMenuItem(B_TRANSLATE("All files and folders"), itemMessage));
1773 	MimeTypeMenu()->AddSeparatorItem();
1774 	MimeTypeMenu()->ItemAt(0)->SetMarked(true);
1775 
1776 	// add recent MIME types
1777 
1778 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
1779 
1780 	BList list;
1781 	if (gMostUsedMimeTypes.ObtainList(&list) && tracker) {
1782 		int32 count = 0;
1783 		for (int32 index = 0; index < list.CountItems(); index++) {
1784 			const char* name = (const char*)list.ItemAt(index);
1785 
1786 			const ShortMimeInfo* info;
1787 			if ((info = tracker->MimeTypes()->FindMimeType(name)) == NULL)
1788 				continue;
1789 
1790 			BMessage* message = new BMessage(kMIMETypeItem);
1791 			message->AddString("mimetype", info->InternalName());
1792 
1793 			MimeTypeMenu()->AddItem(new BMenuItem(name, message));
1794 			count++;
1795 		}
1796 		if (count != 0)
1797 			MimeTypeMenu()->AddSeparatorItem();
1798 
1799 		gMostUsedMimeTypes.ReleaseList();
1800 	}
1801 
1802 	// add MIME type tree list
1803 
1804 	BMessage types;
1805 	if (BMimeType::GetInstalledSupertypes(&types) == B_OK) {
1806 		const char* superType;
1807 		int32 index = 0;
1808 
1809 		while (types.FindString("super_types", index++, &superType) == B_OK) {
1810 			BMenu* superMenu = new BMenu(superType);
1811 
1812 			BMessage* message = new BMessage(kMIMETypeItem);
1813 			message->AddString("mimetype", superType);
1814 
1815 			MimeTypeMenu()->AddItem(new IconMenuItem(superMenu, message,
1816 				superType, B_MINI_ICON));
1817 
1818 			// the MimeTypeMenu's font is not correct at this time
1819 			superMenu->SetFont(be_plain_font);
1820 		}
1821 	}
1822 
1823 	if (tracker != NULL) {
1824 		tracker->MimeTypes()->EachCommonType(&FindPanel::AddOneMimeTypeToMenu,
1825 			MimeTypeMenu());
1826 	}
1827 
1828 	// remove empty super type menus (and set target)
1829 
1830 	for (int32 index = MimeTypeMenu()->CountItems(); index-- > 2;) {
1831 		BMenuItem* item = MimeTypeMenu()->ItemAt(index);
1832 		BMenu* submenu = item->Submenu();
1833 		if (submenu == NULL)
1834 			continue;
1835 
1836 		if (submenu->CountItems() == 0) {
1837 			MimeTypeMenu()->RemoveItem(item);
1838 			delete item;
1839 		} else
1840 			submenu->SetTargetForItems(this);
1841 	}
1842 
1843 	MimeTypeMenu()->SetTargetForItems(this);
1844 }
1845 
1846 
1847 void
1848 FindPanel::AddVolumes(BMenu* menu)
1849 {
1850 // ToDo: add calls to this to rebuild the menu when a volume gets mounted
1851 
1852 	BMessage* message = new BMessage(kVolumeItem);
1853 	message->AddInt32("device", -1);
1854 	menu->AddItem(new BMenuItem(B_TRANSLATE("All disks"), message));
1855 	menu->AddSeparatorItem();
1856 	PopUpMenuSetTitle(menu, B_TRANSLATE("All disks"));
1857 
1858 	BVolumeRoster roster;
1859 	BVolume volume;
1860 	roster.Rewind();
1861 	while (roster.GetNextVolume(&volume) == B_OK) {
1862 		if (volume.IsPersistent() && volume.KnowsQuery()) {
1863 			BDirectory root;
1864 			if (volume.GetRootDirectory(&root) != B_OK)
1865 				continue;
1866 
1867 			BEntry entry;
1868 			root.GetEntry(&entry);
1869 
1870 			Model model(&entry, true);
1871 			if (model.InitCheck() != B_OK)
1872 				continue;
1873 
1874 			message = new BMessage(kVolumeItem);
1875 			message->AddInt32("device", volume.Device());
1876 			menu->AddItem(new ModelMenuItem(&model, model.Name(), message));
1877 		}
1878 	}
1879 
1880 	if (menu->ItemAt(0))
1881 		menu->ItemAt(0)->SetMarked(true);
1882 
1883 	menu->SetTargetForItems(this);
1884 }
1885 
1886 
1887 typedef std::pair<entry_ref, uint32> EntryWithDate;
1888 
1889 static int
1890 SortByDatePredicate(const EntryWithDate* entry1, const EntryWithDate* entry2)
1891 {
1892 	return entry1->second > entry2->second ?
1893 		-1 : (entry1->second == entry2->second ? 0 : 1);
1894 }
1895 
1896 struct AddOneRecentParams {
1897 	BMenu* menu;
1898 	const BMessenger* target;
1899 	uint32 what;
1900 };
1901 
1902 static const entry_ref*
1903 AddOneRecentItem(const entry_ref* ref, void* castToParams)
1904 {
1905 	AddOneRecentParams* params = (AddOneRecentParams*)castToParams;
1906 
1907 	BMessage* message = new BMessage(params->what);
1908 	message->AddRef("refs", ref);
1909 
1910 	char type[B_MIME_TYPE_LENGTH];
1911 	BNode node(ref);
1912 	BNodeInfo(&node).GetType(type);
1913 	BMenuItem* item = new IconMenuItem(ref->name, message, type, B_MINI_ICON);
1914 	item->SetTarget(*params->target);
1915 	params->menu->AddItem(item);
1916 
1917 	return NULL;
1918 }
1919 
1920 
1921 void
1922 FindPanel::AddRecentQueries(BMenu* menu, bool addSaveAsItem,
1923 	const BMessenger* target, uint32 what)
1924 {
1925 	BObjectList<entry_ref> templates(10, true);
1926 	BObjectList<EntryWithDate> recentQueries(10, true);
1927 
1928 	// find all the queries on all volumes
1929 	BVolumeRoster roster;
1930 	BVolume volume;
1931 	roster.Rewind();
1932 	while (roster.GetNextVolume(&volume) == B_OK) {
1933 		if (volume.IsPersistent() && volume.KnowsQuery()
1934 			&& volume.KnowsAttr()) {
1935 			BQuery query;
1936 			query.SetVolume(&volume);
1937 			query.SetPredicate("_trk/recentQuery == 1");
1938 			if (query.Fetch() != B_OK)
1939 				continue;
1940 
1941 			entry_ref ref;
1942 			while (query.GetNextRef(&ref) == B_OK) {
1943 				// ignore queries in the Trash
1944 				if (FSInTrashDir(&ref))
1945 					continue;
1946 
1947 				char type[B_MIME_TYPE_LENGTH];
1948 				BNode node(&ref);
1949 				BNodeInfo(&node).GetType(type);
1950 
1951 				if (strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0)
1952 					templates.AddItem(new entry_ref(ref));
1953 				else {
1954 					uint32 changeTime;
1955 					if (node.ReadAttr(kAttrQueryLastChange, B_INT32_TYPE, 0,
1956 						&changeTime, sizeof(uint32)) != sizeof(uint32))
1957 						continue;
1958 
1959 					recentQueries.AddItem(new EntryWithDate(ref, changeTime));
1960 				}
1961 			}
1962 		}
1963 	}
1964 
1965 	// we are only adding last ten queries
1966 	recentQueries.SortItems(SortByDatePredicate);
1967 
1968 	// but all templates
1969 	AddOneRecentParams params;
1970 	params.menu = menu;
1971 	params.target = target;
1972 	params.what = what;
1973 	templates.EachElement(AddOneRecentItem, &params);
1974 
1975 	int32 count = recentQueries.CountItems();
1976 	if (count > 10) {
1977 		// show only up to 10 recent queries
1978 		count = 10;
1979 	} else if (count < 0)
1980 		count = 0;
1981 
1982 	if (templates.CountItems() > 0 && count > 0)
1983 		menu->AddSeparatorItem();
1984 
1985 	for (int32 index = 0; index < count; index++)
1986 		AddOneRecentItem(&recentQueries.ItemAt(index)->first, &params);
1987 
1988 	if (addSaveAsItem) {
1989 		// add a Save as template item
1990 		if (count > 0 || templates.CountItems() > 0)
1991 			menu->AddSeparatorItem();
1992 
1993 		BMessage* message = new BMessage(kRunSaveAsTemplatePanel);
1994 		BMenuItem* item = new BMenuItem(
1995 			B_TRANSLATE("Save query as template" B_UTF8_ELLIPSIS), message);
1996 		menu->AddItem(item);
1997 	}
1998 }
1999 
2000 
2001 void
2002 FindPanel::SetUpAddRemoveButtons(BBox* box)
2003 {
2004 	BButton* removeButton = dynamic_cast<BButton*>(box->FindView("remove button"));
2005 	if (removeButton != NULL) {
2006 		removeButton->SetEnabled(fAttrGrid->CountRows() > 1);
2007 		return;
2008 	}
2009 
2010 	removeButton = new BButton("remove button", B_TRANSLATE("Remove"),
2011 		new BMessage(kRemoveItem));
2012 	removeButton->SetEnabled(false);
2013 	removeButton->SetTarget(this);
2014 
2015 	BButton* addButton = new BButton("add button", B_TRANSLATE("Add"),
2016 		new BMessage(kAddItem));
2017 	addButton->SetTarget(this);
2018 
2019 	BGroupLayout* layout = dynamic_cast<BGroupLayout*>(box->GetLayout());
2020 	BLayoutBuilder::Group<>(layout)
2021 		.AddGroup(B_HORIZONTAL)
2022 			.AddGlue()
2023 			.Add(removeButton)
2024 			.Add(addButton)
2025 			.End();
2026 }
2027 
2028 
2029 void
2030 FindPanel::FillCurrentQueryName(BTextControl* queryName, FindWindow* window)
2031 {
2032 	ASSERT(window);
2033 	queryName->SetText(window->QueryName());
2034 }
2035 
2036 
2037 void
2038 FindPanel::AddAttrRow()
2039 {
2040 	BBox* box = dynamic_cast<BBox*>(FindView("Box"));
2041 	BGridView* grid = dynamic_cast<BGridView*>(box->FindView("AttrFields"));
2042 
2043 	if (grid == NULL) {
2044 		// reset layout
2045 		BLayoutBuilder::Group<>(box, B_VERTICAL);
2046 
2047 		grid = new BGridView("AttrFields");
2048 		box->AddChild(grid);
2049 	}
2050 
2051 	fAttrGrid = grid->GridLayout();
2052 
2053 	AddAttributeControls(fAttrGrid->CountRows());
2054 
2055 	// add logic to previous attrview
2056 	if (fAttrGrid->CountRows() > 1)
2057 		AddLogicMenu(fAttrGrid->CountRows() - 2);
2058 
2059 	SetUpAddRemoveButtons(box);
2060 }
2061 
2062 
2063 void
2064 FindPanel::RemoveAttrRow()
2065 {
2066 	if (fAttrGrid->CountRows() < 2)
2067 		return;
2068 
2069 	BView* view;
2070 
2071 	int32 row = fAttrGrid->CountRows() - 1;
2072 	for (int32 col = fAttrGrid->CountColumns(); col > 0; col--) {
2073 		BLayoutItem* item = fAttrGrid->ItemAt(col - 1, row);
2074 		if (item == NULL)
2075 			continue;
2076 
2077 		view = item->View();
2078 		if (view == NULL)
2079 			continue;
2080 
2081 		view->RemoveSelf();
2082 		delete view;
2083 	}
2084 
2085 	BString string = "TextEntry";
2086 	string << (row - 1);
2087 	view = FindAttrView(string.String(), row - 1);
2088 	if (view != NULL)
2089 		view->MakeFocus();
2090 
2091 	if (fAttrGrid->CountRows() > 1) {
2092 		// remove the And/Or menu field of the previous row
2093 		BLayoutItem* item = fAttrGrid->ItemAt(3, row - 1);
2094 		if (item == NULL)
2095 			return;
2096 
2097 		view = item->View();
2098 		if (view == NULL)
2099 			return;
2100 
2101 		view->RemoveSelf();
2102 		delete view;
2103 		return;
2104 	}
2105 
2106 	// only one row remains
2107 
2108 	// disable the remove button
2109 	BButton* button = dynamic_cast<BButton*>(FindView("remove button"));
2110 	if (button != NULL)
2111 		button->SetEnabled(false);
2112 
2113 	// remove the And/Or menu field
2114 	BLayoutItem* item = fAttrGrid->RemoveItem(3);
2115 	if (item == NULL)
2116 		return;
2117 
2118 	view = item->View();
2119 	if (view == NULL)
2120 		return;
2121 
2122 	view->RemoveSelf();
2123 	delete view;
2124 }
2125 
2126 
2127 uint32
2128 FindPanel::InitialMode(const BNode* node)
2129 {
2130 	if (node == NULL || node->InitCheck() != B_OK)
2131 		return kByNameItem;
2132 
2133 	uint32 result;
2134 	if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0,
2135 		(int32*)&result, sizeof(int32)) <= 0)
2136 		return kByNameItem;
2137 
2138 	return result;
2139 }
2140 
2141 
2142 int32
2143 FindPanel::InitialAttrCount(const BNode* node)
2144 {
2145 	if (node == NULL || node->InitCheck() != B_OK)
2146 		return 1;
2147 
2148 	int32 result;
2149 	if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0,
2150 		&result, sizeof(int32)) <= 0)
2151 		return 1;
2152 
2153 	return result;
2154 }
2155 
2156 
2157 static int32
2158 SelectItemWithLabel(BMenu* menu, const char* label)
2159 {
2160 	for (int32 index = menu->CountItems(); index-- > 0;)  {
2161 		BMenuItem* item = menu->ItemAt(index);
2162 
2163 		if (strcmp(label, item->Label()) == 0) {
2164 			item->SetMarked(true);
2165 			return index;
2166 		}
2167 	}
2168 	return -1;
2169 }
2170 
2171 
2172 void
2173 FindPanel::SaveWindowState(BNode* node, bool editTemplate)
2174 {
2175 	ASSERT(node->InitCheck() == B_OK);
2176 
2177 	BMenuItem* item = CurrentMimeType();
2178 	if (item) {
2179 		BString label(item->Label());
2180 		node->WriteAttrString(kAttrQueryInitialMime, &label);
2181 	}
2182 
2183 	uint32 mode = Mode();
2184 	node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0,
2185 		(int32*)&mode, sizeof(int32));
2186 
2187 	MoreOptionsStruct saveMoreOptions;
2188 	saveMoreOptions.showMoreOptions = fLatch->Value() != 0;
2189 
2190 	saveMoreOptions.searchTrash = fSearchTrashCheck->Value() != 0;
2191 	saveMoreOptions.temporary = fTemporaryCheck->Value() != 0;
2192 
2193 	if (node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0,
2194 		&saveMoreOptions,
2195 		sizeof(saveMoreOptions)) == sizeof(saveMoreOptions)) {
2196 		node->RemoveAttr(kAttrQueryMoreOptionsForeign);
2197 	}
2198 
2199 	if (editTemplate) {
2200 		if (UserSpecifiedName()) {
2201 			BString name(UserSpecifiedName());
2202 			node->WriteAttrString(kAttrQueryTemplateName, &name);
2203 		}
2204 	}
2205 
2206 	switch (Mode()) {
2207 		case kByAttributeItem:
2208 		{
2209 			BMessage message;
2210 			int32 count = fAttrGrid->CountRows();
2211 			node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0,
2212 				&count, sizeof(int32));
2213 
2214 			for (int32 index = 0; index < count; index++)
2215 				SaveAttrState(&message, index);
2216 
2217 			ssize_t size = message.FlattenedSize();
2218 			char* buffer = new char[size];
2219 			status_t result = message.Flatten(buffer, size);
2220 			if (result == B_OK) {
2221 				node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0,
2222 					buffer, (size_t)size);
2223 			}
2224 
2225 			delete[] buffer;
2226 			break;
2227 		}
2228 
2229 		case kByNameItem:
2230 		case kByFormulaItem:
2231 		{
2232 			BTextControl* textControl = dynamic_cast<BTextControl*>
2233 				(FindView("TextControl"));
2234 			ASSERT(textControl);
2235 			BString formula(textControl->TextView()->Text());
2236 			node->WriteAttrString(kAttrQueryInitialString, &formula);
2237 			break;
2238 		}
2239 	}
2240 }
2241 
2242 
2243 void
2244 FindPanel::SwitchToTemplate(const BNode* node)
2245 {
2246 	SwitchMode(InitialMode(node));
2247 		// update the menu to correspond to the mode
2248 	MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true);
2249 
2250 	if (Mode() == (int32)kByAttributeItem) {
2251 		RemoveByAttributeItems();
2252 		AddByAttributeItems(node);
2253 	}
2254 
2255 	RestoreWindowState(node);
2256 }
2257 
2258 
2259 void
2260 FindPanel::RestoreMimeTypeMenuSelection(const BNode* node)
2261 {
2262 	if (Mode() == (int32)kByFormulaItem || node == NULL
2263 		|| node->InitCheck() != B_OK) {
2264 		return;
2265 	}
2266 
2267 	BString buffer;
2268 	if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK)
2269 		SetCurrentMimeType(buffer.String());
2270 }
2271 
2272 
2273 void
2274 FindPanel::RestoreWindowState(const BNode* node)
2275 {
2276 	fMode = InitialMode(node);
2277 	if (node == NULL || node->InitCheck() != B_OK)
2278 		return;
2279 
2280 	ShowOrHideMimeTypeMenu();
2281 	RestoreMimeTypeMenuSelection(node);
2282 	MoreOptionsStruct saveMoreOptions;
2283 
2284 	bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions,
2285 		kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions,
2286 		sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap)
2287 			!= kReadAttrFailed;
2288 
2289 	if (storesMoreOptions) {
2290 		// need to sanitize to true or false here, could have picked
2291 		// up garbage from attributes
2292 
2293 		saveMoreOptions.showMoreOptions =
2294 			(saveMoreOptions.showMoreOptions != 0);
2295 
2296 		fLatch->SetValue(saveMoreOptions.showMoreOptions);
2297 		if (saveMoreOptions.showMoreOptions == 1 && fMoreOptions->IsHidden())
2298 			fMoreOptions->Show();
2299 		else if (saveMoreOptions.showMoreOptions == 0 && !fMoreOptions->IsHidden())
2300 			fMoreOptions->Hide();
2301 
2302 		fSearchTrashCheck->SetValue(saveMoreOptions.searchTrash);
2303 		fTemporaryCheck->SetValue(saveMoreOptions.temporary);
2304 
2305 		fQueryName->SetModificationMessage(NULL);
2306 		FillCurrentQueryName(fQueryName, dynamic_cast<FindWindow*>(Window()));
2307 
2308 		// set modification message after checking the temporary check box,
2309 		// and filling out the text control so that we do not always trigger
2310 		// clearing of the temporary check box.
2311 		fQueryName->SetModificationMessage(
2312 			new BMessage(kNameModifiedMessage));
2313 	}
2314 
2315 	// get volumes to perform query on
2316 	bool searchAllVolumes = true;
2317 
2318 	attr_info info;
2319 	if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) {
2320 		char* buffer = new char[info.size];
2321 		if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer,
2322 				(size_t)info.size) == info.size) {
2323 			BMessage message;
2324 			if (message.Unflatten(buffer) == B_OK) {
2325 				for (int32 index = 0; ;index++) {
2326 					ASSERT(index < 100);
2327 					BVolume volume;
2328 						// match a volume with the info embedded in
2329 						// the message
2330 					status_t result
2331 						= MatchArchivedVolume(&volume, &message, index);
2332 					if (result == B_OK) {
2333 						char name[256];
2334 						volume.GetName(name);
2335 						SelectItemWithLabel(fVolMenu, name);
2336 						searchAllVolumes = false;
2337 					} else if (result != B_DEV_BAD_DRIVE_NUM)
2338 						// if B_DEV_BAD_DRIVE_NUM, the volume just isn't
2339 						// mounted this time around, keep looking for more
2340 						// if other error, bail
2341 						break;
2342 				}
2343 			}
2344 		}
2345 		delete[] buffer;
2346 	}
2347 	// mark or unmark "All disks"
2348 	fVolMenu->ItemAt(0)->SetMarked(searchAllVolumes);
2349 	ShowVolumeMenuLabel();
2350 
2351 	switch (Mode()) {
2352 		case kByAttributeItem:
2353 		{
2354 			int32 count = InitialAttrCount(node);
2355 
2356 			attr_info info;
2357 			if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK)
2358 				break;
2359 			char* buffer = new char[info.size];
2360 			if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0,
2361 					buffer, (size_t)info.size) == info.size) {
2362 				BMessage message;
2363 				if (message.Unflatten(buffer) == B_OK)
2364 					for (int32 index = 0; index < count; index++) {
2365 						RestoreAttrState(message, index);
2366 					}
2367 			}
2368 			delete[] buffer;
2369 			break;
2370 		}
2371 
2372 		case kByNameItem:
2373 		case kByFormulaItem:
2374 		{
2375 			BString buffer;
2376 			if (node->ReadAttrString(kAttrQueryInitialString, &buffer)
2377 					== B_OK) {
2378 				BTextControl* textControl = dynamic_cast<BTextControl*>
2379 					(FindView("TextControl"));
2380 				ASSERT(textControl);
2381 
2382 				textControl->TextView()->SetText(buffer.String());
2383 			}
2384 			break;
2385 		}
2386 	}
2387 
2388 	// try to restore focus and possibly text selection
2389 	BString focusedView;
2390 	if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) {
2391 		BView* view = FindView(focusedView.String());
2392 		if (view != NULL) {
2393 			view->MakeFocus();
2394 			BTextControl* textControl = dynamic_cast<BTextControl*>(view);
2395 			if (textControl != NULL && Mode() == kByFormulaItem) {
2396 				int32 selStart = 0;
2397 				int32 selEnd = INT32_MAX;
2398 				node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0,
2399 					&selStart, sizeof(selStart));
2400 				node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0,
2401 					&selEnd, sizeof(selEnd));
2402 				textControl->TextView()->Select(selStart, selEnd);
2403 			}
2404 		}
2405 	}
2406 }
2407 
2408 
2409 void
2410 FindPanel::AddByAttributeItems(const BNode* node)
2411 {
2412 	int32 numAttributes = InitialAttrCount(node);
2413 	if (numAttributes < 1)
2414 		numAttributes = 1;
2415 
2416 	for (int32 index = 0; index < numAttributes; index ++)
2417 		AddAttrRow();
2418 }
2419 
2420 
2421 void
2422 FindPanel::AddByNameOrFormulaItems()
2423 {
2424 	BBox* box = dynamic_cast<BBox*>(FindView("Box"));
2425 	// reset layout
2426 	BLayoutBuilder::Group<>(box, B_VERTICAL);
2427 
2428 	BTextControl* textControl = new BTextControl("TextControl",
2429 		"", "", NULL);
2430 	textControl->SetDivider(0.0f);
2431 	box->SetBorder(B_NO_BORDER);
2432 	box->AddChild(textControl);
2433 	textControl->MakeFocus();
2434 }
2435 
2436 
2437 void
2438 FindPanel::RemoveAttrViewItems(bool removeGrid)
2439 {
2440 	if (fAttrGrid == NULL)
2441 		return;
2442 
2443 	BView* view = fAttrGrid->View();
2444 	for (int32 index = view->CountChildren(); index > 0; index--) {
2445 		BView* child = view->ChildAt(index - 1);
2446 		child->RemoveSelf();
2447 		delete child;
2448 	}
2449 
2450 	if (removeGrid) {
2451 		view->RemoveSelf();
2452 		delete view;
2453 		fAttrGrid = NULL;
2454 	}
2455 }
2456 
2457 
2458 void
2459 FindPanel::RemoveByAttributeItems()
2460 {
2461 	RemoveAttrViewItems();
2462 	BView* view = FindView("add button");
2463 	if (view) {
2464 		view->RemoveSelf();
2465 		delete view;
2466 	}
2467 
2468 	view = FindView("remove button");
2469 	if (view) {
2470 		view->RemoveSelf();
2471 		delete view;
2472 	}
2473 
2474 	view = FindView("TextControl");
2475 	if (view) {
2476 		view->RemoveSelf();
2477 		delete view;
2478 	}
2479 }
2480 
2481 
2482 void
2483 FindPanel::ShowOrHideMimeTypeMenu()
2484 {
2485 	BView* menuFieldSpacer = FindView("MimeTypeMenuSpacer");
2486 	BMenuField* menuField
2487 		= dynamic_cast<BMenuField*>(FindView("MimeTypeMenu"));
2488 	if (Mode() == (int32)kByFormulaItem && !menuField->IsHidden(this)) {
2489 		BSize size = menuField->ExplicitMinSize();
2490 		menuField->Hide();
2491 		menuFieldSpacer->SetExplicitMinSize(size);
2492 		menuFieldSpacer->SetExplicitMaxSize(size);
2493 		if (menuFieldSpacer->IsHidden(this))
2494 			menuFieldSpacer->Show();
2495 	} else if (menuField->IsHidden(this)) {
2496 		menuFieldSpacer->Hide();
2497 		menuField->Show();
2498 	}
2499 }
2500 
2501 
2502 void
2503 FindPanel::AddAttributeControls(int32 gridRow)
2504 {
2505 	BPopUpMenu* menu = new BPopUpMenu("PopUp");
2506 
2507 	// add NAME attribute to popup
2508 	BMenu* submenu = new BMenu(B_TRANSLATE("Name"));
2509 	submenu->SetRadioMode(true);
2510 	submenu->SetFont(be_plain_font);
2511 	BMessage* message = new BMessage(kAttributeItemMain);
2512 	message->AddString("name", "name");
2513 	message->AddInt32("type", B_STRING_TYPE);
2514 	BMenuItem* item = new BMenuItem(submenu, message);
2515 	menu->AddItem(item);
2516 
2517 	for (int32 i = 0; i < 5; i++) {
2518 		message = new BMessage(kAttributeItem);
2519 		message->AddInt32("operator", operators[i]);
2520 		submenu->AddItem(new BMenuItem(
2521 			B_TRANSLATE_NOCOLLECT(operatorLabels[i]), message));
2522 	}
2523 
2524 	// mark first items initially
2525 	menu->ItemAt(0)->SetMarked(true);
2526 	submenu->ItemAt(0)->SetMarked(true);
2527 
2528 	// add SIZE attribute
2529 	submenu = new BMenu(B_TRANSLATE("Size"));
2530 	submenu->SetRadioMode(true);
2531 	submenu->SetFont(be_plain_font);
2532 	message = new BMessage(kAttributeItemMain);
2533 	message->AddString("name", "size");
2534 	message->AddInt32("type", B_OFF_T_TYPE);
2535 	item = new BMenuItem(submenu, message);
2536 	menu->AddItem(item);
2537 
2538 	message = new BMessage(kAttributeItem);
2539 	message->AddInt32("operator", B_GE);
2540 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[5]),
2541 		message));
2542 
2543 	message = new BMessage(kAttributeItem);
2544 	message->AddInt32("operator", B_LE);
2545 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[6]),
2546 		message));
2547 
2548 	message = new BMessage(kAttributeItem);
2549 	message->AddInt32("operator", B_EQ);
2550 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[1]),
2551 		message));
2552 
2553 	// add "modified" field
2554 	submenu = new BMenu(B_TRANSLATE("Modified"));
2555 	submenu->SetRadioMode(true);
2556 	submenu->SetFont(be_plain_font);
2557 	message = new BMessage(kAttributeItemMain);
2558 	message->AddString("name", "last_modified");
2559 	message->AddInt32("type", B_TIME_TYPE);
2560 	item = new BMenuItem(submenu, message);
2561 	menu->AddItem(item);
2562 
2563 	message = new BMessage(kAttributeItem);
2564 	message->AddInt32("operator", B_LE);
2565 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[7]),
2566 		message));
2567 
2568 	message = new BMessage(kAttributeItem);
2569 	message->AddInt32("operator", B_GE);
2570 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[8]),
2571 		message));
2572 
2573 	BMenuField* menuField = new BMenuField("MenuField", "", menu);
2574 	menuField->SetDivider(0.0f);
2575 	fAttrGrid->AddView(menuField, 0, gridRow);
2576 
2577 	BStringView* stringView = new BStringView("",
2578 		menu->FindMarked()->Submenu()->FindMarked()->Label());
2579 	BLayoutItem* layoutItem = fAttrGrid->AddView(stringView, 1, gridRow);
2580 	layoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
2581 		B_ALIGN_VERTICAL_UNSET));
2582 
2583 	BString title("TextEntry");
2584 	title << gridRow;
2585 	BTextControl* textControl = new BTextControl(title.String(), "", "", NULL);
2586 	textControl->SetDivider(0.0f);
2587 	fAttrGrid->AddView(textControl, 2, gridRow);
2588 	textControl->MakeFocus();
2589 
2590 	// target everything
2591 	menu->SetTargetForItems(this);
2592 	for (int32 index = menu->CountItems() - 1; index >= 0; index--)
2593 		menu->SubmenuAt(index)->SetTargetForItems(this);
2594 
2595 	// populate mime popup
2596 	AddMimeTypeAttrs(menu);
2597 }
2598 
2599 
2600 void
2601 FindPanel::RestoreAttrState(const BMessage &message, int32 index)
2602 {
2603 	BMenu* menu = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index))
2604 		->Menu();
2605 	// decode menu selections
2606 
2607 	AddMimeTypeAttrs(menu);
2608 
2609 	const char* label;
2610 	if (message.FindString("menuSelection", index, &label) == B_OK) {
2611 		int32 itemIndex = SelectItemWithLabel(menu, label);
2612 		if (itemIndex >=0) {
2613 			menu = menu->SubmenuAt(itemIndex);
2614 			if (menu && message.FindString("subMenuSelection", index, &label)
2615 					== B_OK)
2616 				SelectItemWithLabel(menu, label);
2617 		}
2618 	}
2619 
2620 	// decode attribute text
2621 	BString bstring = "TextEntry";
2622 	bstring << index;
2623 	BTextControl* textControl
2624 		= dynamic_cast<BTextControl*>(FindAttrView(bstring.String(), index));
2625 	ASSERT(textControl);
2626 	const char* string;
2627 	if (message.FindString("attrViewText", index, &string) == B_OK)
2628 		textControl->TextView()->SetText(string);
2629 
2630 	int32 logicMenuSelectedIndex;
2631 	BMenuField* field = dynamic_cast<BMenuField*>(FindAttrView("Logic", index));
2632 	if (message.FindInt32("logicalRelation", index,
2633 			&logicMenuSelectedIndex) == B_OK) {
2634 		if (field)
2635 			field->Menu()->ItemAt(logicMenuSelectedIndex)->SetMarked(true);
2636 		else
2637 			AddLogicMenu(index, logicMenuSelectedIndex == 0);
2638 	}
2639 }
2640 
2641 
2642 void
2643 FindPanel::SaveAttrState(BMessage* message, int32 index)
2644 {
2645 	BMenu* menu = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index))
2646 		->Menu();
2647 
2648 	// encode main attribute menu selection
2649 	BMenuItem* item = menu->FindMarked();
2650 	message->AddString("menuSelection", item ? item->Label() : "");
2651 
2652 	// encode submenu selection
2653 	const char* label = "";
2654 	if (item) {
2655 		BMenu* submenu = menu->SubmenuAt(menu->IndexOf(item));
2656 		if (submenu) {
2657 			item = submenu->FindMarked();
2658 			if (item)
2659 				label = item->Label();
2660 		}
2661 	}
2662 	message->AddString("subMenuSelection", label);
2663 
2664 	// encode attribute text
2665 	BString string = "TextEntry";
2666 	string << index;
2667 	BTextControl* textControl
2668 		= dynamic_cast<BTextControl*>(FindAttrView(string.String(), index));
2669 	ASSERT(textControl);
2670 	message->AddString("attrViewText", textControl->TextView()->Text());
2671 
2672 	BMenuField* field = dynamic_cast<BMenuField*>(FindAttrView("Logic", index));
2673 	if (field != NULL) {
2674 		BMenuItem* item = field->Menu()->FindMarked();
2675 		ASSERT(item);
2676 		message->AddInt32("logicalRelation",
2677 			item ? field->Menu()->IndexOf(item) : 0);
2678 	}
2679 }
2680 
2681 
2682 void
2683 FindPanel::AddLogicMenu(int32 index, bool selectAnd)
2684 {
2685 	// add "AND/OR" menu
2686 	BPopUpMenu* menu = new BPopUpMenu("");
2687 	BMessage* message = new BMessage();
2688 	message->AddInt32("combine", B_AND);
2689 	BMenuItem* item = new BMenuItem(B_TRANSLATE("And"), message);
2690 	menu->AddItem(item);
2691 	if (selectAnd)
2692 		item->SetMarked(true);
2693 
2694 	message = new BMessage();
2695 	message->AddInt32("combine", B_OR);
2696 	item = new BMenuItem(B_TRANSLATE("Or"), message);
2697 	menu->AddItem(item);
2698 	if (!selectAnd)
2699 		item->SetMarked(true);
2700 
2701 	menu->SetTargetForItems(this);
2702 
2703 	BMenuField* menufield = new BMenuField("Logic", "", menu, B_WILL_DRAW);
2704 	menufield->SetDivider(0.0f);
2705 
2706 	ResizeMenuField(menufield);
2707 
2708 	fAttrGrid->AddView(menufield, 3, index);
2709 }
2710 
2711 
2712 void
2713 FindPanel::RemoveLogicMenu(int32 index)
2714 {
2715 	BMenuField* menufield = dynamic_cast<BMenuField*>(FindAttrView("Logic", index));
2716 	if (menufield) {
2717 		menufield->RemoveSelf();
2718 		delete menufield;
2719 	}
2720 }
2721 
2722 
2723 void
2724 FindPanel::AddAttributes(BMenu* menu, const BMimeType &mimeType)
2725 {
2726 	// only add things to menu which have "user-visible" data
2727 	BMessage attributeMessage;
2728 	if (mimeType.GetAttrInfo(&attributeMessage) != B_OK)
2729 		return;
2730 
2731 	char desc[B_MIME_TYPE_LENGTH];
2732 	mimeType.GetShortDescription(desc);
2733 
2734 	// go through each field in meta mime and add it to a menu
2735 	for (int32 index = 0; ; index++) {
2736 		const char* publicName;
2737 		if (attributeMessage.FindString("attr:public_name", index,
2738 				&publicName) != B_OK) {
2739 			break;
2740 		}
2741 
2742 		if (!attributeMessage.FindBool("attr:viewable"))
2743 			continue;
2744 
2745 		const char* attributeName;
2746 		if (attributeMessage.FindString("attr:name", index, &attributeName)
2747 				!= B_OK) {
2748 			continue;
2749 		}
2750 
2751 		int32 type;
2752 		if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK)
2753 			continue;
2754 
2755 		BMenu* submenu = new BMenu(publicName);
2756 		submenu->SetRadioMode(true);
2757 		submenu->SetFont(be_plain_font);
2758 		BMessage* message = new BMessage(kAttributeItemMain);
2759 		message->AddString("name", attributeName);
2760 		message->AddInt32("type", type);
2761 		BMenuItem* item = new BMenuItem(submenu, message);
2762 		menu->AddItem(item);
2763 		menu->SetTargetForItems(this);
2764 
2765 		switch (type) {
2766 			case B_STRING_TYPE:
2767 				message = new BMessage(kAttributeItem);
2768 				message->AddInt32("operator", B_CONTAINS);
2769 				submenu->AddItem(new BMenuItem(operatorLabels[0], message));
2770 
2771 				message = new BMessage(kAttributeItem);
2772 				message->AddInt32("operator", B_EQ);
2773 				submenu->AddItem(new BMenuItem(operatorLabels[1], message));
2774 
2775 				message = new BMessage(kAttributeItem);
2776 				message->AddInt32("operator", B_NE);
2777 				submenu->AddItem(new BMenuItem(operatorLabels[2], message));
2778 				submenu->SetTargetForItems(this);
2779 
2780 				message = new BMessage(kAttributeItem);
2781 				message->AddInt32("operator", B_BEGINS_WITH);
2782 				submenu->AddItem(new BMenuItem(operatorLabels[3], message));
2783 				submenu->SetTargetForItems(this);
2784 
2785 				message = new BMessage(kAttributeItem);
2786 				message->AddInt32("operator", B_ENDS_WITH);
2787 				submenu->AddItem(new BMenuItem(operatorLabels[4], message));
2788 				break;
2789 
2790 			case B_BOOL_TYPE:
2791 			case B_INT16_TYPE:
2792 			case B_UINT8_TYPE:
2793 			case B_INT8_TYPE:
2794 			case B_UINT16_TYPE:
2795 			case B_INT32_TYPE:
2796 			case B_UINT32_TYPE:
2797 			case B_INT64_TYPE:
2798 			case B_UINT64_TYPE:
2799 			case B_OFF_T_TYPE:
2800 			case B_FLOAT_TYPE:
2801 			case B_DOUBLE_TYPE:
2802 				message = new BMessage(kAttributeItem);
2803 				message->AddInt32("operator", B_EQ);
2804 				submenu->AddItem(new BMenuItem(operatorLabels[1], message));
2805 
2806 				message = new BMessage(kAttributeItem);
2807 				message->AddInt32("operator", B_GE);
2808 				submenu->AddItem(new BMenuItem(operatorLabels[5], message));
2809 
2810 				message = new BMessage(kAttributeItem);
2811 				message->AddInt32("operator", B_LE);
2812 				submenu->AddItem(new BMenuItem(operatorLabels[6], message));
2813 				break;
2814 
2815 			case B_TIME_TYPE:
2816 				message = new BMessage(kAttributeItem);
2817 				message->AddInt32("operator", B_LE);
2818 				submenu->AddItem(new BMenuItem(operatorLabels[7], message));
2819 
2820 				message = new BMessage(kAttributeItem);
2821 				message->AddInt32("operator", B_GE);
2822 				submenu->AddItem(new BMenuItem(operatorLabels[8], message));
2823 				break;
2824 		}
2825 		submenu->SetTargetForItems(this);
2826 	}
2827 }
2828 
2829 
2830 void
2831 FindPanel::AddMimeTypeAttrs(BMenu* menu)
2832 {
2833 	const char* typeName;
2834 	if (CurrentMimeType(&typeName) == NULL)
2835 		return;
2836 
2837 	BMimeType mimeType(typeName);
2838 	if (!mimeType.IsInstalled())
2839 		return;
2840 
2841 	if (!mimeType.IsSupertypeOnly()) {
2842 		// add supertype attributes
2843 		BMimeType supertype;
2844 		mimeType.GetSupertype(&supertype);
2845 		AddAttributes(menu, supertype);
2846 	}
2847 
2848 	AddAttributes(menu, mimeType);
2849 }
2850 
2851 
2852 void
2853 FindPanel::GetDefaultAttrName(BString &result, int32 row) const
2854 {
2855 	BMenuItem* item = NULL;
2856 	BMenuField* menuField
2857 		= dynamic_cast<BMenuField*>(fAttrGrid->ItemAt(0, row)->View());
2858 	if (menuField->Menu() != NULL)
2859 		item = menuField->Menu()->FindMarked();
2860 	if (item != NULL)
2861 		result << item->Label();
2862 	else
2863 		result << B_TRANSLATE("Name");
2864 
2865 	if (item && item->Submenu() != NULL)
2866 		item = item->Submenu()->FindMarked();
2867 	else
2868 		item = NULL;
2869 
2870 	if (item != NULL)
2871 		result << " " << item->Label() << " ";
2872 	else
2873 		result << " = ";
2874 
2875 	BTextControl* textControl
2876 		= dynamic_cast<BTextControl*>(fAttrGrid->ItemAt(2, row)->View());
2877 	result << textControl->Text();
2878 }
2879 
2880 
2881 // #pragma mark -
2882 
2883 
2884 DeleteTransientQueriesTask::DeleteTransientQueriesTask()
2885 	:	state(kInitial),
2886 		fWalker(NULL)
2887 {
2888 }
2889 
2890 
2891 DeleteTransientQueriesTask::~DeleteTransientQueriesTask()
2892 {
2893 	delete fWalker;
2894 }
2895 
2896 
2897 bool
2898 DeleteTransientQueriesTask::DoSomeWork()
2899 {
2900 	switch (state) {
2901 		case kInitial:
2902 			Initialize();
2903 			break;
2904 
2905 		case kAllocatedWalker:
2906 		case kTraversing:
2907 			if (GetSome()) {
2908 				PRINT(("transient query killer done\n"));
2909 				return true;
2910 			}
2911 			break;
2912 
2913 		case kError:
2914 			return true;
2915 
2916 	}
2917 	return false;
2918 }
2919 
2920 
2921 void
2922 DeleteTransientQueriesTask::Initialize()
2923 {
2924 	PRINT(("starting up transient query killer\n"));
2925 	BPath path;
2926 	status_t result = find_directory(B_USER_DIRECTORY, &path, false);
2927 	if (result != B_OK) {
2928 		state = kError;
2929 		return;
2930 	}
2931 	fWalker = new BTrackerPrivate::TNodeWalker(path.Path());
2932 	state = kAllocatedWalker;
2933 }
2934 
2935 
2936 const int32 kBatchCount = 100;
2937 
2938 bool
2939 DeleteTransientQueriesTask::GetSome()
2940 {
2941 	state = kTraversing;
2942 	for (int32 count = kBatchCount; count > 0; count--) {
2943 		entry_ref ref;
2944 		if (fWalker->GetNextRef(&ref) != B_OK) {
2945 			state = kError;
2946 			return true;
2947 		}
2948 		Model model(&ref);
2949 		if (model.IsQuery())
2950 			ProcessOneRef(&model);
2951 #if xDEBUG
2952 		else
2953 			PRINT(("transient query killer: %s not a query\n", model.Name()));
2954 #endif
2955 	}
2956 	return false;
2957 }
2958 
2959 
2960 const int32 kDaysToExpire = 7;
2961 
2962 static bool
2963 QueryOldEnough(Model* model)
2964 {
2965 	// check if it is old and ready to be deleted
2966 	time_t now = time(0);
2967 
2968 	tm nowTimeData;
2969 	tm fileModData;
2970 
2971 	localtime_r(&now, &nowTimeData);
2972 	localtime_r(&model->StatBuf()->st_ctime, &fileModData);
2973 
2974 	if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire
2975 		&& (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) {
2976 		PRINT(("query %s, not old enough\n", model->Name()));
2977 		return false;
2978 	}
2979 	return true;
2980 }
2981 
2982 
2983 bool
2984 DeleteTransientQueriesTask::ProcessOneRef(Model* model)
2985 {
2986 	BModelOpener opener(model);
2987 
2988 	// is this a temporary query
2989 	if (!MoreOptionsStruct::QueryTemporary(model->Node())) {
2990 		PRINT(("query %s, not temporary\n", model->Name()));
2991 		return false;
2992 	}
2993 
2994 	if (!QueryOldEnough(model))
2995 		return false;
2996 
2997 	ASSERT(dynamic_cast<TTracker*>(be_app));
2998 
2999 	// check that it is not showing
3000 	if (dynamic_cast<TTracker*>(be_app)->EntryHasWindowOpen(
3001 			model->EntryRef())) {
3002 		PRINT(("query %s, showing, can't delete\n", model->Name()));
3003 		return false;
3004 	}
3005 
3006 	PRINT(("query %s, old, temporary, not shownig - deleting\n",
3007 		model->Name()));
3008 	BEntry entry(model->EntryRef());
3009 	entry.Remove();
3010 
3011 	return true;
3012 }
3013 
3014 
3015 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> {
3016 public:
3017 	DeleteTransientQueriesFunctor(DeleteTransientQueriesTask* task)
3018 		:	task(task)
3019 		{}
3020 
3021 	virtual ~DeleteTransientQueriesFunctor()
3022 		{
3023 			delete task;
3024 		}
3025 
3026 	virtual void operator()()
3027 		{ result = task->DoSomeWork(); }
3028 
3029 private:
3030 	DeleteTransientQueriesTask* task;
3031 };
3032 
3033 
3034 void
3035 DeleteTransientQueriesTask::StartUpTransientQueryCleaner()
3036 {
3037 	// set up a task that wakes up when the machine is idle and starts
3038 	// killing off old transient queries
3039 	DeleteTransientQueriesFunctor* worker
3040 		= new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask());
3041 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
3042 	ASSERT(tracker);
3043 	tracker->MainTaskLoop()->RunWhenIdle(worker,
3044 		30 * 60 * 1000000,	// half an hour initial delay
3045 		5 * 60 * 1000000,	// idle for five minutes
3046 		10 * 1000000);
3047 }
3048 
3049 
3050 //	#pragma mark -
3051 
3052 
3053 RecentFindItemsMenu::RecentFindItemsMenu(const char* title,
3054 	const BMessenger* target, uint32 what)
3055 	:
3056 	BMenu(title, B_ITEMS_IN_COLUMN),
3057 	fTarget(*target),
3058 	fWhat(what)
3059 {
3060 }
3061 
3062 
3063 void
3064 RecentFindItemsMenu::AttachedToWindow()
3065 {
3066 	// re-populate the menu with fresh items
3067 	for (int32 index = CountItems() - 1; index >= 0; index--)
3068 		delete RemoveItem(index);
3069 
3070 	FindPanel::AddRecentQueries(this, false, &fTarget, fWhat);
3071 	BMenu::AttachedToWindow();
3072 }
3073 
3074 
3075 #if !B_BEOS_VERSION_DANO
3076 _IMPEXP_TRACKER
3077 #endif
3078 BMenu*
3079 TrackerBuildRecentFindItemsMenu(const char* title)
3080 {
3081 	BMessenger tracker(kTrackerSignature);
3082 	return new RecentFindItemsMenu(title, &tracker, B_REFS_RECEIVED);
3083 }
3084 
3085 
3086 //	#pragma mark -
3087 
3088 
3089 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char* name,
3090 	const BMessage* message, BMessenger messenger, uint32 resizeFlags,
3091 		uint32 flags)
3092 	:
3093 	DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON,
3094 		message, messenger, resizeFlags, flags)
3095 {
3096 }
3097 
3098 
3099 bool
3100 DraggableQueryIcon::DragStarted(BMessage* dragMessage)
3101 {
3102 	// override to substitute the user-specified query name
3103 	dragMessage->RemoveData("be:clip_name");
3104 
3105 	FindWindow* window = dynamic_cast<FindWindow*>(Window());
3106 	ASSERT(window);
3107 	dragMessage->AddString("be:clip_name",
3108 		window->BackgroundView()->UserSpecifiedName() ?
3109 			window->BackgroundView()->UserSpecifiedName()
3110 			: B_TRANSLATE("New Query"));
3111 
3112 	return true;
3113 }
3114 
3115 
3116 //	#pragma mark -
3117 
3118 
3119 MostUsedNames::MostUsedNames(const char* fileName, const char* directory,
3120 	int32 maxCount)
3121 	:
3122 	fFileName(fileName),
3123 	fDirectory(directory),
3124 	fLoaded(false),
3125 	fCount(maxCount)
3126 {
3127 }
3128 
3129 
3130 MostUsedNames::~MostUsedNames()
3131 {
3132 	// only write back settings when we've been used
3133 	if (!fLoaded)
3134 		return;
3135 
3136 	// write most used list to file
3137 
3138 	BPath path;
3139 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
3140 		return;
3141 
3142 	path.Append(fDirectory);
3143 	path.Append(fFileName);
3144 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
3145 	if (file.InitCheck() == B_OK) {
3146 		for (int32 i = 0; i < fList.CountItems(); i++) {
3147 			list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3148 
3149 			char line[B_FILE_NAME_LENGTH + 5];
3150 
3151 			// limit upper bound to react more dynamically to changes
3152 			if (--entry->count > 20)
3153 				entry->count = 20;
3154 
3155 			// if the item hasn't been chosen in a while, remove it
3156 			// (but leave at least one item in the list)
3157 			if (entry->count < -10 && i > 0)
3158 				continue;
3159 
3160 			sprintf(line, "%" B_PRId32 " %s\n", entry->count, entry->name);
3161 			if (file.Write(line, strlen(line)) < B_OK)
3162 				break;
3163 		}
3164 	}
3165 	file.Unset();
3166 
3167 	// free data
3168 
3169 	for (int32 i = fList.CountItems(); i-- > 0;) {
3170 		list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3171 		free(entry->name);
3172 		delete entry;
3173 	}
3174 }
3175 
3176 
3177 bool
3178 MostUsedNames::ObtainList(BList* list)
3179 {
3180 	if (list == NULL)
3181 		return false;
3182 
3183 	if (!fLoaded)
3184 		UpdateList();
3185 
3186 	fLock.Lock();
3187 
3188 	list->MakeEmpty();
3189 	for (int32 i = 0; i < fCount; i++) {
3190 		list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3191 		if (entry == NULL)
3192 			return true;
3193 
3194 		list->AddItem(entry->name);
3195 	}
3196 	return true;
3197 }
3198 
3199 
3200 void
3201 MostUsedNames::ReleaseList()
3202 {
3203 	fLock.Unlock();
3204 }
3205 
3206 
3207 void
3208 MostUsedNames::AddName(const char* name)
3209 {
3210 	fLock.Lock();
3211 
3212 	if (!fLoaded)
3213 		LoadList();
3214 
3215 	// remove last entry if there are more than
3216 	// 2*fCount entries in the list
3217 
3218 	list_entry* entry = NULL;
3219 
3220 	if (fList.CountItems() > fCount * 2) {
3221 		entry = static_cast<list_entry*>(
3222 			fList.RemoveItem(fList.CountItems() - 1));
3223 
3224 		// is this the name we want to add here?
3225 		if (strcmp(name, entry->name)) {
3226 			free(entry->name);
3227 			delete entry;
3228 			entry = NULL;
3229 		} else
3230 			fList.AddItem(entry);
3231 	}
3232 
3233 	if (entry == NULL) {
3234 		for (int32 i = 0;
3235 				(entry = static_cast<list_entry*>(fList.ItemAt(i))) != NULL; i++) {
3236 			if (strcmp(entry->name, name) == 0)
3237 				break;
3238 		}
3239 	}
3240 
3241 	if (entry == NULL) {
3242 		entry = new list_entry;
3243 		entry->name = strdup(name);
3244 		entry->count = 1;
3245 
3246 		fList.AddItem(entry);
3247 	} else if (entry->count < 0)
3248 		entry->count = 1;
3249 	else
3250 		entry->count++;
3251 
3252 	fLock.Unlock();
3253 	UpdateList();
3254 }
3255 
3256 
3257 int
3258 MostUsedNames::CompareNames(const void* a,const void* b)
3259 {
3260 	list_entry* entryA = *(list_entry**)a;
3261 	list_entry* entryB = *(list_entry**)b;
3262 
3263 	if (entryA->count == entryB->count)
3264 		return strcasecmp(entryA->name,entryB->name);
3265 
3266 	return entryB->count - entryA->count;
3267 }
3268 
3269 
3270 void
3271 MostUsedNames::LoadList()
3272 {
3273 	if (fLoaded)
3274 		return;
3275 	fLoaded = true;
3276 
3277 	// load the most used names list
3278 
3279 	BPath path;
3280 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
3281 		return;
3282 
3283 	path.Append(fDirectory);
3284 	path.Append(fFileName);
3285 
3286 	FILE* file = fopen(path.Path(), "r");
3287 	if (file == NULL)
3288 		return;
3289 
3290 	char line[B_FILE_NAME_LENGTH + 5];
3291 	while (fgets(line, sizeof(line), file) != NULL) {
3292 		int32 length = (int32)strlen(line) - 1;
3293 		if (length >= 0 && line[length] == '\n')
3294 			line[length] = '\0';
3295 
3296 		int32 count = atoi(line);
3297 
3298 		char* name = strchr(line, ' ');
3299 		if (name == NULL || *(++name) == '\0')
3300 			continue;
3301 
3302 		list_entry* entry = new list_entry;
3303 		entry->name = strdup(name);
3304 		entry->count = count;
3305 
3306 		fList.AddItem(entry);
3307 	}
3308 	fclose(file);
3309 }
3310 
3311 
3312 void
3313 MostUsedNames::UpdateList()
3314 {
3315 	AutoLock<Benaphore> locker(fLock);
3316 
3317 	if (!fLoaded)
3318 		LoadList();
3319 
3320 	// sort list items
3321 
3322 	fList.SortItems(MostUsedNames::CompareNames);
3323 }
3324 
3325 }	// namespace BPrivate
3326