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