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