xref: /haiku/src/kits/tracker/FindPanel.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
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 	BMenuItem* firstItem = fMimeTypeMenu->ItemAt(0);
939 	if (firstItem != NULL)
940 		firstItem->SetMarked(true);
941 
942 	if (fDraggableIcon != NULL)
943 		fDraggableIcon->SetTarget(BMessenger(this));
944 
945 	fRecentQueries->SetTargetForItems(findWindow);
946 }
947 
948 
949 void
950 FindPanel::ResizeMenuField(BMenuField* menuField)
951 {
952 	BSize size;
953 	menuField->GetPreferredSize(&size.width, &size.height);
954 
955 	BMenu* menu = menuField->Menu();
956 
957 	float padding = 0.0f;
958 	float width = 0.0f;
959 
960 	BMenuItem* markedItem = menu->FindMarked();
961 	if (markedItem != NULL) {
962 		if (markedItem->Submenu() != NULL) {
963 			BMenuItem* markedSubItem = markedItem->Submenu()->FindMarked();
964 			if (markedSubItem != NULL && markedSubItem->Label() != NULL) {
965 				float labelWidth
966 					= menuField->StringWidth(markedSubItem->Label());
967 				padding = size.width - labelWidth;
968 			}
969 		} else if (markedItem->Label() != NULL) {
970 			float labelWidth = menuField->StringWidth(markedItem->Label());
971 			padding = size.width - labelWidth;
972 		}
973 	}
974 
975 	for (int32 index = menu->CountItems(); index-- > 0; ) {
976 		BMenuItem* item = menu->ItemAt(index);
977 		if (item->Label() != NULL)
978 			width = std::max(width, menuField->StringWidth(item->Label()));
979 
980 		BMenu* submenu = item->Submenu();
981 		if (submenu != NULL) {
982 			for (int32 subIndex = submenu->CountItems(); subIndex-- > 0; ) {
983 				BMenuItem* subItem = submenu->ItemAt(subIndex);
984 				if (subItem->Label() == NULL)
985 					continue;
986 
987 				width = std::max(width,
988 					menuField->StringWidth(subItem->Label()));
989 			}
990 		}
991 	}
992 
993 	float maxWidth = be_control_look->DefaultItemSpacing() * 20;
994 	size.width = std::min(width + padding, maxWidth);
995 	menuField->SetExplicitSize(size);
996 }
997 
998 static void
999 PopUpMenuSetTitle(BMenu* menu, const char* title)
1000 {
1001 	// This should really be in BMenuField
1002 	BMenu* bar = menu->Supermenu();
1003 
1004 	ASSERT(bar);
1005 	ASSERT(bar->ItemAt(0));
1006 	if (bar == NULL || !bar->ItemAt(0))
1007 		return;
1008 
1009 	bar->ItemAt(0)->SetLabel(title);
1010 }
1011 
1012 
1013 void
1014 FindPanel::ShowVolumeMenuLabel()
1015 {
1016 	if (fVolMenu->ItemAt(0)->IsMarked()) {
1017 		// "all disks" selected
1018 		PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label());
1019 		return;
1020 	}
1021 
1022 	// find out if more than one items are marked
1023 	int32 count = fVolMenu->CountItems();
1024 	int32 countSelected = 0;
1025 	BMenuItem* tmpItem = NULL;
1026 	for (int32 index = 2; index < count; index++) {
1027 		BMenuItem* item = fVolMenu->ItemAt(index);
1028 		if (item->IsMarked()) {
1029 			countSelected++;
1030 			tmpItem = item;
1031 		}
1032 	}
1033 
1034 	if (countSelected == 0) {
1035 		// no disk selected, for now revert to search all disks
1036 		// ToDo:
1037 		// show no disks here and add a check that will not let the
1038 		// query go if the user doesn't pick at least one
1039 		fVolMenu->ItemAt(0)->SetMarked(true);
1040 		PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label());
1041 	} else if (countSelected > 1)
1042 		// if more than two disks selected, don't use the disk name
1043 		// as a label
1044 		PopUpMenuSetTitle(fVolMenu,	B_TRANSLATE("multiple disks"));
1045 	else {
1046 		ASSERT(tmpItem);
1047 		PopUpMenuSetTitle(fVolMenu, tmpItem->Label());
1048 	}
1049 }
1050 
1051 
1052 void
1053 FindPanel::Draw(BRect)
1054 {
1055 	if (fAttrGrid == NULL)
1056 		return;
1057 
1058 	for (int32 index = 0; index < fAttrGrid->CountRows(); index++) {
1059 		BMenuField* menuField
1060 			= dynamic_cast<BMenuField*>(FindAttrView("MenuField", index));
1061 		if (menuField == NULL)
1062 			continue;
1063 
1064 		BLayoutItem* stringViewLayoutItem = fAttrGrid->ItemAt(1, index);
1065 		if (stringViewLayoutItem == NULL)
1066 			continue;
1067 
1068 		BMenu* menuFieldMenu = menuField->Menu();
1069 		if (menuFieldMenu == NULL)
1070 			continue;
1071 
1072 		BMenuItem* item = menuFieldMenu->FindMarked();
1073 		if (item == NULL || item->Submenu() == NULL
1074 			|| item->Submenu()->FindMarked() == NULL) {
1075 			continue;
1076 		}
1077 
1078 		if (stringViewLayoutItem == NULL) {
1079 			stringViewLayoutItem = fAttrGrid->AddView(new BStringView("",
1080 				item->Submenu()->FindMarked()->Label()), 1, index);
1081 			stringViewLayoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
1082 				B_ALIGN_VERTICAL_UNSET));
1083 		}
1084 
1085 		if (stringViewLayoutItem != NULL) {
1086 			BStringView* stringView
1087 				= dynamic_cast<BStringView*>(stringViewLayoutItem->View());
1088 			if (stringView != NULL) {
1089 				BMenu* submenu = item->Submenu();
1090 				if (submenu != NULL) {
1091 					BMenuItem* selected = submenu->FindMarked();
1092 					if (selected != NULL)
1093 						stringView->SetText(selected->Label());
1094 				}
1095 			}
1096 		}
1097 	}
1098 }
1099 
1100 
1101 void
1102 FindPanel::MessageReceived(BMessage* message)
1103 {
1104 	entry_ref dir;
1105 	const char* name;
1106 	BMenuItem* item;
1107 
1108 	switch (message->what) {
1109 		case kVolumeItem:
1110 		{
1111 			// volume changed
1112 			BMenuItem* invokedItem;
1113 			dev_t dev;
1114 			if (message->FindPointer("source", (void**)&invokedItem) != B_OK)
1115 				return;
1116 
1117 			if (message->FindInt32("device", &dev) != B_OK)
1118 				break;
1119 
1120 			BMenu* menu = invokedItem->Menu();
1121 			ASSERT(menu);
1122 
1123 			if (dev == -1) {
1124 				// all disks selected, uncheck everything else
1125 				int32 count = menu->CountItems();
1126 				for (int32 index = 2; index < count; index++)
1127 					menu->ItemAt(index)->SetMarked(false);
1128 
1129 				// make all disks the title and check it
1130 				PopUpMenuSetTitle(menu, menu->ItemAt(0)->Label());
1131 				menu->ItemAt(0)->SetMarked(true);
1132 			} else {
1133 				// a specific volume selected, unmark "all disks"
1134 				menu->ItemAt(0)->SetMarked(false);
1135 
1136 				// toggle mark on invoked item
1137 				int32 count = menu->CountItems();
1138 				for (int32 index = 2; index < count; index++) {
1139 					BMenuItem* item = menu->ItemAt(index);
1140 
1141 					if (invokedItem == item) {
1142 						// we just selected this
1143 						bool wasMarked = item->IsMarked();
1144 						item->SetMarked(!wasMarked);
1145 					}
1146 				}
1147 			}
1148 			// make sure the right label is showing
1149 			ShowVolumeMenuLabel();
1150 
1151 			break;
1152 		}
1153 
1154 		case kByAttributeItem:
1155 		case kByNameItem:
1156 		case kByFormulaItem:
1157 			SwitchMode(message->what);
1158 			break;
1159 
1160 		case kAddItem:
1161 			AddAttrRow();
1162 			break;
1163 
1164 		case kRemoveItem:
1165 			RemoveAttrRow();
1166 			break;
1167 
1168 		case kMIMETypeItem:
1169 		{
1170 			BMenuItem* item;
1171 			if (message->FindPointer("source", (void**)&item) == B_OK) {
1172 				// don't add the "All files and folders" to the list
1173 				if (fMimeTypeMenu->IndexOf(item) != 0)
1174 					gMostUsedMimeTypes.AddName(item->Label());
1175 
1176 				SetCurrentMimeType(item);
1177 			}
1178 			if (fMode == kByAttributeItem) {
1179 				// the attributes for this type may be different
1180 				RemoveAttrViewItems(false);
1181 				AddAttrRow();
1182 			}
1183 
1184 			break;
1185 		}
1186 
1187 		case kNameModifiedMessage:
1188 			// the query name was edited, make the query permanent
1189 			fTemporaryCheck->SetValue(0);
1190 			break;
1191 
1192 		case kAttributeItem:
1193 			if (message->FindPointer("source", (void**)&item) != B_OK)
1194 				return;
1195 
1196 			item->Menu()->Superitem()->SetMarked(true);
1197 			Invalidate();
1198 			break;
1199 
1200 		case kAttributeItemMain:
1201 			// in case someone selected just an attribute without the
1202 			// comparator
1203 			if (message->FindPointer("source", (void**)&item) != B_OK)
1204 				return;
1205 
1206 			if (item->Submenu()->ItemAt(0) != NULL)
1207 				item->Submenu()->ItemAt(0)->SetMarked(true);
1208 
1209 			Invalidate();
1210 			break;
1211 
1212 		case kLatchChanged:
1213 		{
1214 			int32 value;
1215 			if (message->FindInt32("be:value", &value) != B_OK)
1216 				break;
1217 
1218 			if (value == 0 && !fMoreOptions->IsHidden(this))
1219 				fMoreOptions->Hide();
1220 			else if (value == 1 && fMoreOptions->IsHidden(this))
1221 				fMoreOptions->Show();
1222 
1223 			break;
1224 		}
1225 
1226 		case B_SAVE_REQUESTED:
1227 		{
1228 			// finish saving query template from a SaveAs panel
1229 			entry_ref ref;
1230 			status_t error = message->FindRef("refs", &ref);
1231 
1232 			if (error == B_OK) {
1233 				// direct entry selected, convert to parent dir and name
1234 				BEntry entry(&ref);
1235 				error = entry.GetParent(&entry);
1236 				if (error == B_OK) {
1237 					entry.GetRef(&dir);
1238 					name = ref.name;
1239 				}
1240 			} else {
1241 				// parent dir and name selected
1242 				error = message->FindRef("directory", &dir);
1243 				if (error == B_OK)
1244 					error = message->FindString("name", &name);
1245 			}
1246 
1247 			if (error == B_OK)
1248 				SaveAsQueryOrTemplate(&dir, name, true);
1249 
1250 			break;
1251 		}
1252 
1253 		case B_COPY_TARGET:
1254 		{
1255 			// finish drag&drop
1256 			const char* str;
1257 			const char* mimeType = NULL;
1258 			const char* actionSpecifier = NULL;
1259 
1260 			if (message->FindString("be:types", &str) == B_OK
1261 				&& strcasecmp(str, B_FILE_MIME_TYPE) == 0
1262 				&& (message->FindString("be:actionspecifier",
1263 						&actionSpecifier) == B_OK
1264 					|| message->FindString("be:filetypes", &mimeType) == B_OK)
1265 				&& message->FindString("name", &name) == B_OK
1266 				&& message->FindRef("directory", &dir) == B_OK) {
1267 
1268 				bool query = false;
1269 				bool queryTemplate = false;
1270 
1271 				if (actionSpecifier
1272 					&& strcasecmp(actionSpecifier,
1273 						B_TRANSLATE_NOCOLLECT(
1274 							kDragNDropActionSpecifiers[0])) == 0) {
1275 					query = true;
1276 				} else if (actionSpecifier
1277 					&& strcasecmp(actionSpecifier,
1278 						B_TRANSLATE_NOCOLLECT(
1279 							kDragNDropActionSpecifiers[1])) == 0) {
1280 					queryTemplate = true;
1281 				} else if (mimeType && strcasecmp(mimeType,
1282 						kDragNDropTypes[0]) == 0) {
1283 					query = true;
1284 				} else if (mimeType && strcasecmp(mimeType,
1285 					kDragNDropTypes[1]) == 0) {
1286 					queryTemplate = true;
1287 				}
1288 
1289 				if (query || queryTemplate)
1290 					SaveAsQueryOrTemplate(&dir, name, queryTemplate);
1291 			}
1292 
1293 			break;
1294 		}
1295 
1296 		default:
1297 			_inherited::MessageReceived(message);
1298 			break;
1299 	}
1300 }
1301 
1302 
1303 void
1304 FindPanel::SaveAsQueryOrTemplate(const entry_ref* dir, const char* name,
1305 	bool queryTemplate)
1306 {
1307 	BDirectory directory(dir);
1308 	BFile file(&directory, name, O_RDWR | O_CREAT | O_TRUNC);
1309 	BNodeInfo(&file).SetType(queryTemplate
1310 		? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE);
1311 
1312 	BMessage attach(kAttachFile);
1313 	attach.AddRef("directory", dir);
1314 	attach.AddString("name", name);
1315 	attach.AddBool("template", queryTemplate);
1316 	Window()->PostMessage(&attach, 0);
1317 }
1318 
1319 
1320 BView*
1321 FindPanel::FindAttrView(const char* name, int row) const
1322 {
1323 	for (int32 index = 0; index < fAttrGrid->CountColumns(); index++) {
1324 
1325 		BLayoutItem* item = fAttrGrid->ItemAt(index, row);
1326 		if (item == NULL)
1327 			continue;
1328 
1329 		BView* view = item->View();
1330 		if (view == NULL)
1331 			continue;
1332 
1333 		view = view->FindView(name);
1334 		if (view != NULL)
1335 			return view;
1336 
1337 	}
1338 
1339 	return NULL;
1340 }
1341 
1342 void
1343 FindPanel::BuildAttrQuery(BQuery* query, bool &dynamicDate) const
1344 {
1345 	dynamicDate = false;
1346 
1347 	// go through each attrview and add the attr and comparison info
1348 	for (int32 index = 0; index < fAttrGrid->CountRows(); index++) {
1349 
1350 		BString title;
1351 		title << "TextEntry" << index;
1352 
1353 		BTextControl* textControl = dynamic_cast<BTextControl*>(
1354 			FindAttrView(title, index));
1355 		if (textControl == NULL)
1356 			return;
1357 
1358 		BMenuField* menuField = dynamic_cast<BMenuField*>(
1359 			FindAttrView("MenuField", index));
1360 		if (menuField == NULL)
1361 			return;
1362 
1363 		BMenuItem* item = menuField->Menu()->FindMarked();
1364 		if (item == NULL)
1365 			continue;
1366 
1367 		BMessage* message = item->Message();
1368 		int32 type;
1369 		if (message->FindInt32("type", &type) == B_OK) {
1370 
1371 			const char* str;
1372 			if (message->FindString("name", &str) == B_OK)
1373 				query->PushAttr(str);
1374 			else
1375 				query->PushAttr(item->Label());
1376 
1377 			switch (type) {
1378 				case B_STRING_TYPE:
1379 					query->PushString(textControl->Text(), true);
1380 					break;
1381 
1382 				case B_TIME_TYPE:
1383 				{
1384 					int flags = 0;
1385 					DEBUG_ONLY(time_t result =)
1386 					parsedate_etc(textControl->Text(), -1,
1387 						&flags);
1388 					dynamicDate = (flags & PARSEDATE_RELATIVE_TIME) != 0;
1389 					PRINT(("parsedate_etc - date is %srelative, %"
1390 						B_PRIdTIME "\n",
1391 						dynamicDate ? "" : "not ", result));
1392 
1393 					query->PushDate(textControl->Text());
1394 					break;
1395 				}
1396 
1397 				case B_BOOL_TYPE:
1398 				{
1399 					uint32 value;
1400 					if (strcasecmp(textControl->Text(),
1401 							"true") == 0) {
1402 						value = 1;
1403 					} else if (strcasecmp(textControl->Text(),
1404 							"false") == 0) {
1405 						value = 0;
1406 					} else
1407 						value = (uint32)atoi(textControl->Text());
1408 
1409 					value %= 2;
1410 					query->PushUInt32(value);
1411 					break;
1412 				}
1413 
1414 				case B_UINT8_TYPE:
1415 				case B_UINT16_TYPE:
1416 				case B_UINT32_TYPE:
1417 					query->PushUInt32((uint32)StringToScalar(
1418 						textControl->Text()));
1419 					break;
1420 
1421 				case B_INT8_TYPE:
1422 				case B_INT16_TYPE:
1423 				case B_INT32_TYPE:
1424 					query->PushInt32((int32)StringToScalar(
1425 						textControl->Text()));
1426 					break;
1427 
1428 				case B_UINT64_TYPE:
1429 					query->PushUInt64((uint64)StringToScalar(
1430 						textControl->Text()));
1431 					break;
1432 
1433 				case B_OFF_T_TYPE:
1434 				case B_INT64_TYPE:
1435 					query->PushInt64(StringToScalar(
1436 						textControl->Text()));
1437 					break;
1438 
1439 				case B_FLOAT_TYPE:
1440 				{
1441 					float floatVal;
1442 					sscanf(textControl->Text(), "%f",
1443 						&floatVal);
1444 					query->PushFloat(floatVal);
1445 					break;
1446 				}
1447 
1448 				case B_DOUBLE_TYPE:
1449 				{
1450 					double doubleVal;
1451 					sscanf(textControl->Text(), "%lf",
1452 						&doubleVal);
1453 					query->PushDouble(doubleVal);
1454 					break;
1455 				}
1456 			}
1457 		}
1458 
1459 		query_op theOperator;
1460 		BMenuItem* operatorItem = item->Submenu()->FindMarked();
1461 		if (operatorItem && operatorItem->Message() != NULL) {
1462 			operatorItem->Message()->FindInt32("operator",
1463 				(int32*)&theOperator);
1464 			query->PushOp(theOperator);
1465 		} else
1466 			query->PushOp(B_EQ);
1467 
1468 		// add logic based on selection in Logic menufield
1469 		if (index > 0) {
1470 			menuField = dynamic_cast<BMenuField*>(
1471 				FindAttrView("Logic", index - 1));
1472 			if (menuField) {
1473 				item = menuField->Menu()->FindMarked();
1474 				if (item) {
1475 					message = item->Message();
1476 					message->FindInt32("combine", (int32*)&theOperator);
1477 					query->PushOp(theOperator);
1478 				}
1479 			} else
1480 				query->PushOp(B_AND);
1481 		}
1482 	}
1483 }
1484 
1485 
1486 void
1487 FindPanel::PushMimeType(BQuery* query) const
1488 {
1489 	const char* type;
1490 	if (CurrentMimeType(&type) == NULL)
1491 		return;
1492 
1493 	if (strcmp(kAllMimeTypes, type)) {
1494 		// add an asterisk if we are searching for a supertype
1495 		char buffer[B_FILE_NAME_LENGTH];
1496 		if (strchr(type, '/') == NULL) {
1497 			strlcpy(buffer, type, sizeof(buffer));
1498 			strlcat(buffer, "/*", sizeof(buffer));
1499 			type = buffer;
1500 		}
1501 
1502 		query->PushAttr(kAttrMIMEType);
1503 		query->PushString(type);
1504 		query->PushOp(B_EQ);
1505 		query->PushOp(B_AND);
1506 	}
1507 }
1508 
1509 
1510 void
1511 FindPanel::GetByAttrPredicate(BQuery* query, bool &dynamicDate) const
1512 {
1513 	ASSERT(Mode() == (int32)kByAttributeItem);
1514 	BuildAttrQuery(query, dynamicDate);
1515 	PushMimeType(query);
1516 }
1517 
1518 
1519 void
1520 FindPanel::GetDefaultName(BString& name) const
1521 {
1522 	BTextControl* textControl = dynamic_cast<BTextControl*>(
1523 		FindView("TextControl"));
1524 
1525 	switch (Mode()) {
1526 		case kByNameItem:
1527 			if (textControl != NULL) {
1528 				name.SetTo(B_TRANSLATE_COMMENT("Name = %name",
1529 					"FindResultTitle"));
1530 				name.ReplaceFirst("%name", textControl->Text());
1531 			}
1532 			break;
1533 
1534 		case kByFormulaItem:
1535 			if (textControl != NULL) {
1536 				name.SetTo(B_TRANSLATE_COMMENT("Formula %formula",
1537 					"FindResultTitle"));
1538 				name.ReplaceFirst("%formula", textControl->Text());
1539 			}
1540 			break;
1541 
1542 		case kByAttributeItem:
1543 		{
1544 			BMenuItem* item = fMimeTypeMenu->FindMarked();
1545 			if (item != NULL)
1546 				name << item->Label() << ": ";
1547 
1548 			for (int32 i = 0; i < fAttrGrid->CountRows(); i++) {
1549 				GetDefaultAttrName(name, i);
1550 				if (i + 1 < fAttrGrid->CountRows())
1551 					name << ", ";
1552 			}
1553 			break;
1554 		}
1555 	}
1556 }
1557 
1558 
1559 const char*
1560 FindPanel::UserSpecifiedName() const
1561 {
1562 	if (fQueryName->Text()[0] == '\0')
1563 		return NULL;
1564 
1565 	return fQueryName->Text();
1566 }
1567 
1568 
1569 void
1570 FindPanel::GetByNamePredicate(BQuery* query) const
1571 {
1572 	ASSERT(Mode() == (int32)kByNameItem);
1573 
1574 	BTextControl* textControl
1575 		= dynamic_cast<BTextControl*>(FindView("TextControl"));
1576 
1577 	ASSERT(textControl != NULL);
1578 
1579 	if (textControl == NULL)
1580 		return;
1581 
1582 	query->PushAttr("name");
1583 	query->PushString(textControl->Text(), true);
1584 
1585 	if (strstr(textControl->Text(), "*") != NULL) {
1586 		// assume pattern is a regular expression, try doing an exact match
1587 		query->PushOp(B_EQ);
1588 	} else
1589 		query->PushOp(B_CONTAINS);
1590 
1591 	PushMimeType(query);
1592 }
1593 
1594 
1595 void
1596 FindPanel::SwitchMode(uint32 mode)
1597 {
1598 	if (fMode == mode)
1599 		// no work, bail
1600 		return;
1601 
1602 	uint32 oldMode = fMode;
1603 	BString buffer;
1604 
1605 	switch (mode) {
1606 		case kByFormulaItem:
1607 		{
1608 			if (oldMode == kByAttributeItem || oldMode == kByNameItem) {
1609 				BQuery query;
1610 				if (oldMode == kByAttributeItem) {
1611 					bool dummy;
1612 					GetByAttrPredicate(&query, dummy);
1613 				} else
1614 					GetByNamePredicate(&query);
1615 
1616 				query.GetPredicate(&buffer);
1617 			}
1618 		}
1619 		// fall-through
1620 		case kByNameItem:
1621 		{
1622 			fMode = mode;
1623 			RemoveByAttributeItems();
1624 			ShowOrHideMimeTypeMenu();
1625 			AddByNameOrFormulaItems();
1626 
1627 			if (buffer.Length() > 0) {
1628 				ASSERT(mode == kByFormulaItem
1629 					|| oldMode == kByAttributeItem);
1630 				BTextControl* textControl
1631 					= dynamic_cast<BTextControl*>(FindView("TextControl"));
1632 				if (textControl != NULL)
1633 					textControl->SetText(buffer.String());
1634 			}
1635 			break;
1636 		}
1637 
1638 		case kByAttributeItem:
1639 		{
1640 			fMode = mode;
1641 			BTextControl* textControl
1642 				= dynamic_cast<BTextControl*>(FindView("TextControl"));
1643 			if (textControl != NULL) {
1644 				textControl->RemoveSelf();
1645 				delete textControl;
1646 			}
1647 
1648 			ShowOrHideMimeTypeMenu();
1649 			AddAttrRow();
1650 			break;
1651 		}
1652 	}
1653 }
1654 
1655 
1656 BMenuItem*
1657 FindPanel::CurrentMimeType(const char** type) const
1658 {
1659 	// search for marked item in the list
1660 	BMenuItem* item = MimeTypeMenu()->FindMarked();
1661 
1662 	if (item != NULL && MimeTypeMenu()->IndexOf(item) != 0
1663 		&& item->Submenu() == NULL) {
1664 		// if it's one of the most used items, ignore it
1665 		item = NULL;
1666 	}
1667 
1668 	if (item == NULL) {
1669 		for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) {
1670 			BMenu* submenu = MimeTypeMenu()->ItemAt(index)->Submenu();
1671 			if (submenu != NULL && (item = submenu->FindMarked()) != NULL)
1672 				break;
1673 		}
1674 	}
1675 
1676 	if (type != NULL && item != NULL) {
1677 		BMessage* message = item->Message();
1678 		if (message == NULL)
1679 			return NULL;
1680 
1681 		if (message->FindString("mimetype", type) != B_OK)
1682 			return NULL;
1683 	}
1684 	return item;
1685 }
1686 
1687 
1688 status_t
1689 FindPanel::SetCurrentMimeType(BMenuItem* item)
1690 {
1691 	// unmark old MIME type (in most used list, and the tree)
1692 
1693 	BMenuItem* marked = CurrentMimeType();
1694 	if (marked != NULL) {
1695 		marked->SetMarked(false);
1696 
1697 		if ((marked = MimeTypeMenu()->FindMarked()) != NULL)
1698 			marked->SetMarked(false);
1699 	}
1700 
1701 	// mark new MIME type (in most used list, and the tree)
1702 
1703 	if (item != NULL) {
1704 		item->SetMarked(true);
1705 		fMimeTypeField->MenuItem()->SetLabel(item->Label());
1706 
1707 		BMenuItem* search;
1708 		for (int32 i = 2; (search = MimeTypeMenu()->ItemAt(i)) != NULL; i++) {
1709 			if (item == search || search->Label() == NULL)
1710 				continue;
1711 
1712 			if (strcmp(item->Label(), search->Label()) == 0) {
1713 				search->SetMarked(true);
1714 				break;
1715 			}
1716 
1717 			BMenu* submenu = search->Submenu();
1718 			if (submenu == NULL)
1719 				continue;
1720 
1721 			for (int32 j = submenu->CountItems(); j-- > 0;) {
1722 				BMenuItem* sub = submenu->ItemAt(j);
1723 				if (strcmp(item->Label(), sub->Label()) == 0) {
1724 					sub->SetMarked(true);
1725 					break;
1726 				}
1727 			}
1728 		}
1729 	}
1730 
1731 	return B_OK;
1732 }
1733 
1734 
1735 status_t
1736 FindPanel::SetCurrentMimeType(const char* label)
1737 {
1738 	// unmark old MIME type (in most used list, and the tree)
1739 
1740 	BMenuItem* marked = CurrentMimeType();
1741 	if (marked != NULL) {
1742 		marked->SetMarked(false);
1743 
1744 		if ((marked = MimeTypeMenu()->FindMarked()) != NULL)
1745 			marked->SetMarked(false);
1746 	}
1747 
1748 	// mark new MIME type (in most used list, and the tree)
1749 
1750 	fMimeTypeField->MenuItem()->SetLabel(label);
1751 	bool found = false;
1752 
1753 	for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) {
1754 		BMenuItem* item = MimeTypeMenu()->ItemAt(index);
1755 		BMenu* submenu = item->Submenu();
1756 		if (submenu != NULL && !found) {
1757 			for (int32 subIndex = submenu->CountItems(); subIndex-- > 0;) {
1758 				BMenuItem* subItem = submenu->ItemAt(subIndex);
1759 				if (subItem->Label() != NULL
1760 					&& strcmp(label, subItem->Label()) == 0) {
1761 					subItem->SetMarked(true);
1762 					found = true;
1763 				}
1764 			}
1765 		}
1766 
1767 		if (item->Label() != NULL && strcmp(label, item->Label()) == 0) {
1768 			item->SetMarked(true);
1769 			return B_OK;
1770 		}
1771 	}
1772 
1773 	return found ? B_OK : B_ENTRY_NOT_FOUND;
1774 }
1775 
1776 
1777 static
1778 void AddSubtype(BString& text, const BMimeType& type)
1779 {
1780 	text.Append(" (");
1781 	text.Append(strchr(type.Type(), '/') + 1);
1782 		// omit the slash
1783 	text.Append(")");
1784 }
1785 
1786 
1787 bool
1788 FindPanel::AddOneMimeTypeToMenu(const ShortMimeInfo* info, void* castToMenu)
1789 {
1790 	BPopUpMenu* menu = static_cast<BPopUpMenu*>(castToMenu);
1791 
1792 	BMimeType type(info->InternalName());
1793 	BMimeType super;
1794 	type.GetSupertype(&super);
1795 	if (super.InitCheck() < B_OK)
1796 		return false;
1797 
1798 	BMenuItem* superItem = menu->FindItem(super.Type());
1799 	if (superItem != NULL) {
1800 		BMessage* message = new BMessage(kMIMETypeItem);
1801 		message->AddString("mimetype", info->InternalName());
1802 
1803 		// check to ensure previous item's name differs
1804 		BMenu* menu = superItem->Submenu();
1805 		BMenuItem* previous = menu->ItemAt(menu->CountItems() - 1);
1806 		BString text = info->ShortDescription();
1807 		if (previous != NULL
1808 			&& strcasecmp(previous->Label(), info->ShortDescription()) == 0) {
1809 			AddSubtype(text, type);
1810 
1811 			// update the previous item as well
1812 			BMimeType type(previous->Message()->GetString("mimetype", NULL));
1813 			BString label = ShortMimeInfo(type).ShortDescription();
1814 			AddSubtype(label, type);
1815 			previous->SetLabel(label.String());
1816 		}
1817 
1818 		menu->AddItem(new IconMenuItem(text.String(), message,
1819 			info->InternalName()));
1820 	}
1821 
1822 	return false;
1823 }
1824 
1825 
1826 void
1827 FindPanel::AddMimeTypesToMenu()
1828 {
1829 	BMessage* itemMessage = new BMessage(kMIMETypeItem);
1830 	itemMessage->AddString("mimetype", kAllMimeTypes);
1831 
1832 	IconMenuItem* firstItem = new IconMenuItem(
1833 		B_TRANSLATE("All files and folders"), itemMessage,
1834 		static_cast<BBitmap*>(NULL));
1835 	MimeTypeMenu()->AddItem(firstItem);
1836 	MimeTypeMenu()->AddSeparatorItem();
1837 
1838 	// add recent MIME types
1839 
1840 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
1841 	ASSERT(tracker != NULL);
1842 
1843 	BList list;
1844 	if (tracker != NULL && gMostUsedMimeTypes.ObtainList(&list)) {
1845 		int32 count = 0;
1846 		for (int32 index = 0; index < list.CountItems(); index++) {
1847 			const char* name = (const char*)list.ItemAt(index);
1848 
1849 			MimeTypeList* mimeTypes = tracker->MimeTypes();
1850 			if (mimeTypes != NULL) {
1851 				const ShortMimeInfo* info = mimeTypes->FindMimeType(name);
1852 				if (info == NULL)
1853 					continue;
1854 
1855 				BMessage* message = new BMessage(kMIMETypeItem);
1856 				message->AddString("mimetype", info->InternalName());
1857 
1858 				MimeTypeMenu()->AddItem(new BMenuItem(name, message));
1859 				count++;
1860 			}
1861 		}
1862 		if (count != 0)
1863 			MimeTypeMenu()->AddSeparatorItem();
1864 
1865 		gMostUsedMimeTypes.ReleaseList();
1866 	}
1867 
1868 	// add MIME type tree list
1869 
1870 	BMessage types;
1871 	if (BMimeType::GetInstalledSupertypes(&types) == B_OK) {
1872 		const char* superType;
1873 		int32 index = 0;
1874 
1875 		while (types.FindString("super_types", index++, &superType) == B_OK) {
1876 			BMenu* superMenu = new BMenu(superType);
1877 
1878 			BMessage* message = new BMessage(kMIMETypeItem);
1879 			message->AddString("mimetype", superType);
1880 
1881 			MimeTypeMenu()->AddItem(new IconMenuItem(superMenu, message,
1882 				superType));
1883 
1884 			// the MimeTypeMenu's font is not correct at this time
1885 			superMenu->SetFont(be_plain_font);
1886 		}
1887 	}
1888 
1889 	if (tracker != NULL) {
1890 		tracker->MimeTypes()->EachCommonType(
1891 			&FindPanel::AddOneMimeTypeToMenu, MimeTypeMenu());
1892 	}
1893 
1894 	// remove empty super type menus (and set target)
1895 
1896 	for (int32 index = MimeTypeMenu()->CountItems(); index-- > 2;) {
1897 		BMenuItem* item = MimeTypeMenu()->ItemAt(index);
1898 		BMenu* submenu = item->Submenu();
1899 		if (submenu == NULL)
1900 			continue;
1901 
1902 		if (submenu->CountItems() == 0) {
1903 			MimeTypeMenu()->RemoveItem(item);
1904 			delete item;
1905 		} else
1906 			submenu->SetTargetForItems(this);
1907 	}
1908 }
1909 
1910 
1911 void
1912 FindPanel::AddVolumes(BMenu* menu)
1913 {
1914 	// ToDo: add calls to this to rebuild the menu when a volume gets mounted
1915 
1916 	BMessage* message = new BMessage(kVolumeItem);
1917 	message->AddInt32("device", -1);
1918 	menu->AddItem(new BMenuItem(B_TRANSLATE("All disks"), message));
1919 	menu->AddSeparatorItem();
1920 	PopUpMenuSetTitle(menu, B_TRANSLATE("All disks"));
1921 
1922 	BVolumeRoster roster;
1923 	BVolume volume;
1924 	roster.Rewind();
1925 	while (roster.GetNextVolume(&volume) == B_OK) {
1926 		if (volume.IsPersistent() && volume.KnowsQuery()) {
1927 			BDirectory root;
1928 			if (volume.GetRootDirectory(&root) != B_OK)
1929 				continue;
1930 
1931 			BEntry entry;
1932 			root.GetEntry(&entry);
1933 
1934 			Model model(&entry, true);
1935 			if (model.InitCheck() != B_OK)
1936 				continue;
1937 
1938 			message = new BMessage(kVolumeItem);
1939 			message->AddInt32("device", volume.Device());
1940 			menu->AddItem(new ModelMenuItem(&model, model.Name(), message));
1941 		}
1942 	}
1943 
1944 	if (menu->ItemAt(0))
1945 		menu->ItemAt(0)->SetMarked(true);
1946 
1947 	menu->SetTargetForItems(this);
1948 }
1949 
1950 
1951 typedef std::pair<entry_ref, uint32> EntryWithDate;
1952 
1953 static int
1954 SortByDatePredicate(const EntryWithDate* entry1, const EntryWithDate* entry2)
1955 {
1956 	return entry1->second > entry2->second ?
1957 		-1 : (entry1->second == entry2->second ? 0 : 1);
1958 }
1959 
1960 struct AddOneRecentParams {
1961 	BMenu* menu;
1962 	const BMessenger* target;
1963 	uint32 what;
1964 };
1965 
1966 static const entry_ref*
1967 AddOneRecentItem(const entry_ref* ref, void* castToParams)
1968 {
1969 	AddOneRecentParams* params = (AddOneRecentParams*)castToParams;
1970 
1971 	BMessage* message = new BMessage(params->what);
1972 	message->AddRef("refs", ref);
1973 
1974 	char type[B_MIME_TYPE_LENGTH];
1975 	BNode node(ref);
1976 	BNodeInfo(&node).GetType(type);
1977 	BMenuItem* item = new IconMenuItem(ref->name, message, type);
1978 	item->SetTarget(*params->target);
1979 	params->menu->AddItem(item);
1980 
1981 	return NULL;
1982 }
1983 
1984 
1985 void
1986 FindPanel::AddRecentQueries(BMenu* menu, bool addSaveAsItem,
1987 	const BMessenger* target, uint32 what)
1988 {
1989 	BObjectList<entry_ref> templates(10, true);
1990 	BObjectList<EntryWithDate> recentQueries(10, true);
1991 
1992 	// find all the queries on all volumes
1993 	BVolumeRoster roster;
1994 	BVolume volume;
1995 	roster.Rewind();
1996 	while (roster.GetNextVolume(&volume) == B_OK) {
1997 		if (volume.IsPersistent() && volume.KnowsQuery()
1998 			&& volume.KnowsAttr()) {
1999 			BQuery query;
2000 			query.SetVolume(&volume);
2001 			query.SetPredicate("_trk/recentQuery == 1");
2002 			if (query.Fetch() != B_OK)
2003 				continue;
2004 
2005 			entry_ref ref;
2006 			while (query.GetNextRef(&ref) == B_OK) {
2007 				// ignore queries in the Trash
2008 				if (FSInTrashDir(&ref))
2009 					continue;
2010 
2011 				char type[B_MIME_TYPE_LENGTH];
2012 				BNode node(&ref);
2013 				BNodeInfo(&node).GetType(type);
2014 
2015 				if (strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0)
2016 					templates.AddItem(new entry_ref(ref));
2017 				else {
2018 					uint32 changeTime;
2019 					if (node.ReadAttr(kAttrQueryLastChange, B_INT32_TYPE, 0,
2020 						&changeTime, sizeof(uint32)) != sizeof(uint32))
2021 						continue;
2022 
2023 					recentQueries.AddItem(new EntryWithDate(ref, changeTime));
2024 				}
2025 			}
2026 		}
2027 	}
2028 
2029 	// we are only adding last ten queries
2030 	recentQueries.SortItems(SortByDatePredicate);
2031 
2032 	// but all templates
2033 	AddOneRecentParams params;
2034 	params.menu = menu;
2035 	params.target = target;
2036 	params.what = what;
2037 	templates.EachElement(AddOneRecentItem, &params);
2038 
2039 	int32 count = recentQueries.CountItems();
2040 	if (count > 10) {
2041 		// show only up to 10 recent queries
2042 		count = 10;
2043 	} else if (count < 0)
2044 		count = 0;
2045 
2046 	if (templates.CountItems() > 0 && count > 0)
2047 		menu->AddSeparatorItem();
2048 
2049 	for (int32 index = 0; index < count; index++)
2050 		AddOneRecentItem(&recentQueries.ItemAt(index)->first, &params);
2051 
2052 	if (addSaveAsItem) {
2053 		// add a Save as template item
2054 		if (count > 0 || templates.CountItems() > 0)
2055 			menu->AddSeparatorItem();
2056 
2057 		BMessage* message = new BMessage(kRunSaveAsTemplatePanel);
2058 		BMenuItem* item = new BMenuItem(
2059 			B_TRANSLATE("Save query as template" B_UTF8_ELLIPSIS), message);
2060 		menu->AddItem(item);
2061 	}
2062 }
2063 
2064 
2065 void
2066 FindPanel::SetUpAddRemoveButtons()
2067 {
2068 	BBox* box = dynamic_cast<BBox*>(FindView("Box"));
2069 
2070 	ASSERT(box != NULL);
2071 
2072 	if (box == NULL)
2073 		return;
2074 
2075 	BButton* removeButton = new BButton("remove button", B_TRANSLATE("Remove"),
2076 		new BMessage(kRemoveItem));
2077 	removeButton->SetEnabled(false);
2078 	removeButton->SetTarget(this);
2079 
2080 	BButton* addButton = new BButton("add button", B_TRANSLATE("Add"),
2081 		new BMessage(kAddItem));
2082 	addButton->SetTarget(this);
2083 
2084 	BGroupLayout* layout = dynamic_cast<BGroupLayout*>(box->GetLayout());
2085 
2086 	ASSERT(layout != NULL);
2087 
2088 	if (layout == NULL)
2089 		return;
2090 
2091 	BLayoutBuilder::Group<>(layout)
2092 		.AddGroup(B_HORIZONTAL)
2093 			.AddGlue()
2094 			.Add(removeButton)
2095 			.Add(addButton)
2096 			.End()
2097 		.End();
2098 }
2099 
2100 
2101 void
2102 FindPanel::FillCurrentQueryName(BTextControl* queryName, FindWindow* window)
2103 {
2104 	ASSERT(window);
2105 	queryName->SetText(window->QueryName());
2106 }
2107 
2108 
2109 void
2110 FindPanel::AddAttrRow()
2111 {
2112 	BBox* box = dynamic_cast<BBox*>(FindView("Box"));
2113 
2114 	ASSERT(box != NULL);
2115 
2116 	if (box == NULL)
2117 		return;
2118 
2119 	BGridView* grid = dynamic_cast<BGridView*>(box->FindView("AttrFields"));
2120 	if (grid == NULL) {
2121 		// reset layout
2122 		BLayoutBuilder::Group<>(box, B_VERTICAL);
2123 
2124 		grid = new BGridView("AttrFields");
2125 		box->AddChild(grid);
2126 	}
2127 
2128 	fAttrGrid = grid->GridLayout();
2129 
2130 	AddAttributeControls(fAttrGrid->CountRows());
2131 
2132 	// add logic to previous attrview
2133 	if (fAttrGrid->CountRows() > 1)
2134 		AddLogicMenu(fAttrGrid->CountRows() - 2);
2135 
2136 	BButton* removeButton = dynamic_cast<BButton*>(
2137 		box->FindView("remove button"));
2138 	if (removeButton != NULL)
2139 		removeButton->SetEnabled(fAttrGrid->CountRows() > 1);
2140 	else
2141 		SetUpAddRemoveButtons();
2142 }
2143 
2144 
2145 void
2146 FindPanel::RemoveAttrRow()
2147 {
2148 	if (fAttrGrid->CountRows() < 2)
2149 		return;
2150 
2151 	BView* view;
2152 
2153 	int32 row = fAttrGrid->CountRows() - 1;
2154 	for (int32 col = fAttrGrid->CountColumns(); col > 0; col--) {
2155 		BLayoutItem* item = fAttrGrid->ItemAt(col - 1, row);
2156 		if (item == NULL)
2157 			continue;
2158 
2159 		view = item->View();
2160 		if (view == NULL)
2161 			continue;
2162 
2163 		view->RemoveSelf();
2164 		delete view;
2165 	}
2166 
2167 	BString string = "TextEntry";
2168 	string << (row - 1);
2169 	view = FindAttrView(string.String(), row - 1);
2170 	if (view != NULL)
2171 		view->MakeFocus();
2172 
2173 	if (fAttrGrid->CountRows() > 1) {
2174 		// remove the And/Or menu field of the previous row
2175 		BLayoutItem* item = fAttrGrid->ItemAt(3, row - 1);
2176 		if (item == NULL)
2177 			return;
2178 
2179 		view = item->View();
2180 		if (view == NULL)
2181 			return;
2182 
2183 		view->RemoveSelf();
2184 		delete view;
2185 		return;
2186 	}
2187 
2188 	// only one row remains
2189 
2190 	// disable the remove button
2191 	BButton* button = dynamic_cast<BButton*>(FindView("remove button"));
2192 	if (button != NULL)
2193 		button->SetEnabled(false);
2194 
2195 	// remove the And/Or menu field
2196 	BLayoutItem* item = fAttrGrid->RemoveItem(3);
2197 	if (item == NULL)
2198 		return;
2199 
2200 	view = item->View();
2201 	if (view == NULL)
2202 		return;
2203 
2204 	view->RemoveSelf();
2205 	delete view;
2206 }
2207 
2208 
2209 uint32
2210 FindPanel::InitialMode(const BNode* node)
2211 {
2212 	if (node == NULL || node->InitCheck() != B_OK)
2213 		return kByNameItem;
2214 
2215 	uint32 result;
2216 	if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0,
2217 		(int32*)&result, sizeof(int32)) <= 0)
2218 		return kByNameItem;
2219 
2220 	return result;
2221 }
2222 
2223 
2224 int32
2225 FindPanel::InitialAttrCount(const BNode* node)
2226 {
2227 	if (node == NULL || node->InitCheck() != B_OK)
2228 		return 1;
2229 
2230 	int32 result;
2231 	if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0,
2232 		&result, sizeof(int32)) <= 0)
2233 		return 1;
2234 
2235 	return result;
2236 }
2237 
2238 
2239 static int32
2240 SelectItemWithLabel(BMenu* menu, const char* label)
2241 {
2242 	for (int32 index = menu->CountItems(); index-- > 0;)  {
2243 		BMenuItem* item = menu->ItemAt(index);
2244 
2245 		if (strcmp(label, item->Label()) == 0) {
2246 			item->SetMarked(true);
2247 			return index;
2248 		}
2249 	}
2250 	return -1;
2251 }
2252 
2253 
2254 void
2255 FindPanel::SaveWindowState(BNode* node, bool editTemplate)
2256 {
2257 	ASSERT(node->InitCheck() == B_OK);
2258 
2259 	BMenuItem* item = CurrentMimeType();
2260 	if (item) {
2261 		BString label(item->Label());
2262 		node->WriteAttrString(kAttrQueryInitialMime, &label);
2263 	}
2264 
2265 	uint32 mode = Mode();
2266 	node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0,
2267 		(int32*)&mode, sizeof(int32));
2268 
2269 	MoreOptionsStruct saveMoreOptions;
2270 	saveMoreOptions.searchTrash = fSearchTrashCheck->Value() != 0;
2271 	saveMoreOptions.temporary = fTemporaryCheck->Value() != 0;
2272 
2273 	if (node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0,
2274 		&saveMoreOptions,
2275 		sizeof(saveMoreOptions)) == sizeof(saveMoreOptions)) {
2276 		node->RemoveAttr(kAttrQueryMoreOptionsForeign);
2277 	}
2278 
2279 	if (editTemplate) {
2280 		if (UserSpecifiedName()) {
2281 			BString name(UserSpecifiedName());
2282 			node->WriteAttrString(kAttrQueryTemplateName, &name);
2283 		}
2284 	}
2285 
2286 	switch (Mode()) {
2287 		case kByAttributeItem:
2288 		{
2289 			BMessage message;
2290 			int32 count = fAttrGrid->CountRows();
2291 			node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0,
2292 				&count, sizeof(int32));
2293 
2294 			for (int32 index = 0; index < count; index++)
2295 				SaveAttrState(&message, index);
2296 
2297 			ssize_t size = message.FlattenedSize();
2298 			if (size > 0) {
2299 				char* buffer = new char[(size_t)size];
2300 				status_t result = message.Flatten(buffer, size);
2301 				if (result == B_OK) {
2302 					node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0,
2303 						buffer, (size_t)size);
2304 				}
2305 				delete[] buffer;
2306 			}
2307 			break;
2308 		}
2309 
2310 		case kByNameItem:
2311 		case kByFormulaItem:
2312 		{
2313 			BTextControl* textControl = dynamic_cast<BTextControl*>(
2314 				FindView("TextControl"));
2315 
2316 			ASSERT(textControl != NULL);
2317 
2318 			if (textControl != NULL) {
2319 				BString formula(textControl->Text());
2320 				node->WriteAttrString(kAttrQueryInitialString, &formula);
2321 			}
2322 			break;
2323 		}
2324 	}
2325 }
2326 
2327 
2328 void
2329 FindPanel::SwitchToTemplate(const BNode* node)
2330 {
2331 	SwitchMode(InitialMode(node));
2332 		// update the menu to correspond to the mode
2333 	MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true);
2334 
2335 	if (Mode() == (int32)kByAttributeItem) {
2336 		RemoveByAttributeItems();
2337 		AddByAttributeItems(node);
2338 	}
2339 
2340 	RestoreWindowState(node);
2341 }
2342 
2343 
2344 void
2345 FindPanel::RestoreMimeTypeMenuSelection(const BNode* node)
2346 {
2347 	if (Mode() == (int32)kByFormulaItem || node == NULL
2348 		|| node->InitCheck() != B_OK) {
2349 		return;
2350 	}
2351 
2352 	BString buffer;
2353 	if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK)
2354 		SetCurrentMimeType(buffer.String());
2355 }
2356 
2357 
2358 void
2359 FindPanel::RestoreWindowState(const BNode* node)
2360 {
2361 	fMode = InitialMode(node);
2362 	if (node == NULL || node->InitCheck() != B_OK)
2363 		return;
2364 
2365 	ShowOrHideMimeTypeMenu();
2366 	RestoreMimeTypeMenuSelection(node);
2367 	MoreOptionsStruct saveMoreOptions;
2368 
2369 	bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions,
2370 		kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions,
2371 		sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap)
2372 			!= kReadAttrFailed;
2373 
2374 	if (storesMoreOptions) {
2375 		// need to sanitize to true or false here, could have picked
2376 		// up garbage from attributes
2377 
2378 		saveMoreOptions.showMoreOptions = true; // Now unused
2379 
2380 		fSearchTrashCheck->SetValue(saveMoreOptions.searchTrash);
2381 		fTemporaryCheck->SetValue(saveMoreOptions.temporary);
2382 
2383 		fQueryName->SetModificationMessage(NULL);
2384 		FindWindow* findWindow = dynamic_cast<FindWindow*>(Window());
2385 		if (findWindow != NULL)
2386 			FillCurrentQueryName(fQueryName, findWindow);
2387 
2388 		// set modification message after checking the temporary check box,
2389 		// and filling out the text control so that we do not always trigger
2390 		// clearing of the temporary check box.
2391 		fQueryName->SetModificationMessage(
2392 			new BMessage(kNameModifiedMessage));
2393 	}
2394 
2395 	// get volumes to perform query on
2396 	bool searchAllVolumes = true;
2397 
2398 	attr_info info;
2399 	if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) {
2400 		char* buffer = new char[info.size];
2401 		if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer,
2402 				(size_t)info.size) == info.size) {
2403 			BMessage message;
2404 			if (message.Unflatten(buffer) == B_OK) {
2405 				for (int32 index = 0; ;index++) {
2406 					ASSERT(index < 100);
2407 					BVolume volume;
2408 						// match a volume with the info embedded in
2409 						// the message
2410 					status_t result
2411 						= MatchArchivedVolume(&volume, &message, index);
2412 					if (result == B_OK) {
2413 						char name[256];
2414 						volume.GetName(name);
2415 						SelectItemWithLabel(fVolMenu, name);
2416 						searchAllVolumes = false;
2417 					} else if (result != B_DEV_BAD_DRIVE_NUM)
2418 						// if B_DEV_BAD_DRIVE_NUM, the volume just isn't
2419 						// mounted this time around, keep looking for more
2420 						// if other error, bail
2421 						break;
2422 				}
2423 			}
2424 		}
2425 		delete[] buffer;
2426 	}
2427 	// mark or unmark "All disks"
2428 	fVolMenu->ItemAt(0)->SetMarked(searchAllVolumes);
2429 	ShowVolumeMenuLabel();
2430 
2431 	switch (Mode()) {
2432 		case kByAttributeItem:
2433 		{
2434 			int32 count = InitialAttrCount(node);
2435 
2436 			attr_info info;
2437 			if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK)
2438 				break;
2439 			char* buffer = new char[info.size];
2440 			if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0,
2441 					buffer, (size_t)info.size) == info.size) {
2442 				BMessage message;
2443 				if (message.Unflatten(buffer) == B_OK) {
2444 					for (int32 index = 0; index < count; index++)
2445 						RestoreAttrState(message, index);
2446 				}
2447 			}
2448 			delete[] buffer;
2449 			break;
2450 		}
2451 
2452 		case kByNameItem:
2453 		case kByFormulaItem:
2454 		{
2455 			BString buffer;
2456 			if (node->ReadAttrString(kAttrQueryInitialString, &buffer)
2457 					== B_OK) {
2458 				BTextControl* textControl = dynamic_cast<BTextControl*>(
2459 					FindView("TextControl"));
2460 
2461 				ASSERT(textControl != NULL);
2462 
2463 				if (textControl != NULL)
2464 					textControl->SetText(buffer.String());
2465 			}
2466 			break;
2467 		}
2468 	}
2469 
2470 	// try to restore focus and possibly text selection
2471 	BString focusedView;
2472 	if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) {
2473 		BView* view = FindView(focusedView.String());
2474 		if (view != NULL) {
2475 			view->MakeFocus();
2476 			BTextControl* textControl = dynamic_cast<BTextControl*>(view);
2477 			if (textControl != NULL && Mode() == kByFormulaItem) {
2478 				int32 selStart = 0;
2479 				int32 selEnd = INT32_MAX;
2480 				node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0,
2481 					&selStart, sizeof(selStart));
2482 				node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0,
2483 					&selEnd, sizeof(selEnd));
2484 				textControl->TextView()->Select(selStart, selEnd);
2485 			}
2486 		}
2487 	}
2488 }
2489 
2490 
2491 void
2492 FindPanel::AddByAttributeItems(const BNode* node)
2493 {
2494 	int32 numAttributes = InitialAttrCount(node);
2495 	if (numAttributes < 1)
2496 		numAttributes = 1;
2497 
2498 	for (int32 index = 0; index < numAttributes; index ++)
2499 		AddAttrRow();
2500 }
2501 
2502 
2503 void
2504 FindPanel::AddByNameOrFormulaItems()
2505 {
2506 	BBox* box = dynamic_cast<BBox*>(FindView("Box"));
2507 
2508 	ASSERT(box != NULL);
2509 
2510 	if (box == NULL)
2511 		return;
2512 
2513 	// reset layout
2514 	BLayoutBuilder::Group<>(box, B_VERTICAL);
2515 
2516 	BTextControl* textControl = new BTextControl("TextControl",
2517 		"", "", NULL);
2518 	textControl->SetDivider(0.0f);
2519 	box->SetBorder(B_NO_BORDER);
2520 	box->AddChild(textControl);
2521 	textControl->MakeFocus();
2522 }
2523 
2524 
2525 void
2526 FindPanel::RemoveAttrViewItems(bool removeGrid)
2527 {
2528 	if (fAttrGrid == NULL)
2529 		return;
2530 
2531 	BView* view = fAttrGrid->View();
2532 	for (int32 index = view->CountChildren(); index > 0; index--) {
2533 		BView* child = view->ChildAt(index - 1);
2534 		child->RemoveSelf();
2535 		delete child;
2536 	}
2537 
2538 	if (removeGrid) {
2539 		view->RemoveSelf();
2540 		delete view;
2541 		fAttrGrid = NULL;
2542 	}
2543 }
2544 
2545 
2546 void
2547 FindPanel::RemoveByAttributeItems()
2548 {
2549 	RemoveAttrViewItems();
2550 	BView* view = FindView("add button");
2551 	if (view) {
2552 		view->RemoveSelf();
2553 		delete view;
2554 	}
2555 
2556 	view = FindView("remove button");
2557 	if (view) {
2558 		view->RemoveSelf();
2559 		delete view;
2560 	}
2561 
2562 	view = FindView("TextControl");
2563 	if (view) {
2564 		view->RemoveSelf();
2565 		delete view;
2566 	}
2567 }
2568 
2569 
2570 void
2571 FindPanel::ShowOrHideMimeTypeMenu()
2572 {
2573 	BView* menuFieldSpacer = FindView("MimeTypeMenuSpacer");
2574 	BMenuField* menuField
2575 		= dynamic_cast<BMenuField*>(FindView("MimeTypeMenu"));
2576 	if (menuFieldSpacer == NULL || menuField == NULL)
2577 		return;
2578 
2579 	if (Mode() == (int32)kByFormulaItem && !menuField->IsHidden(this)) {
2580 		BSize size = menuField->ExplicitMinSize();
2581 		menuField->Hide();
2582 		menuFieldSpacer->SetExplicitMinSize(size);
2583 		menuFieldSpacer->SetExplicitMaxSize(size);
2584 		if (menuFieldSpacer->IsHidden(this))
2585 			menuFieldSpacer->Show();
2586 	} else if (menuField->IsHidden(this)) {
2587 		menuFieldSpacer->Hide();
2588 		menuField->Show();
2589 	}
2590 }
2591 
2592 
2593 void
2594 FindPanel::AddAttributeControls(int32 gridRow)
2595 {
2596 	BPopUpMenu* menu = new BPopUpMenu("PopUp");
2597 
2598 	// add NAME attribute to popup
2599 	BMenu* submenu = new BMenu(B_TRANSLATE("Name"));
2600 	submenu->SetRadioMode(true);
2601 	submenu->SetFont(be_plain_font);
2602 	BMessage* message = new BMessage(kAttributeItemMain);
2603 	message->AddString("name", "name");
2604 	message->AddInt32("type", B_STRING_TYPE);
2605 	BMenuItem* item = new BMenuItem(submenu, message);
2606 	menu->AddItem(item);
2607 
2608 	for (int32 i = 0; i < 5; i++) {
2609 		message = new BMessage(kAttributeItem);
2610 		message->AddInt32("operator", operators[i]);
2611 		submenu->AddItem(new BMenuItem(
2612 			B_TRANSLATE_NOCOLLECT(operatorLabels[i]), message));
2613 	}
2614 
2615 	// mark first items initially
2616 	menu->ItemAt(0)->SetMarked(true);
2617 	submenu->ItemAt(0)->SetMarked(true);
2618 
2619 	// add SIZE attribute
2620 	submenu = new BMenu(B_TRANSLATE("Size"));
2621 	submenu->SetRadioMode(true);
2622 	submenu->SetFont(be_plain_font);
2623 	message = new BMessage(kAttributeItemMain);
2624 	message->AddString("name", "size");
2625 	message->AddInt32("type", B_OFF_T_TYPE);
2626 	item = new BMenuItem(submenu, message);
2627 	menu->AddItem(item);
2628 
2629 	message = new BMessage(kAttributeItem);
2630 	message->AddInt32("operator", B_GT);
2631 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[5]),
2632 		message));
2633 
2634 	message = new BMessage(kAttributeItem);
2635 	message->AddInt32("operator", B_LT);
2636 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[6]),
2637 		message));
2638 
2639 	message = new BMessage(kAttributeItem);
2640 	message->AddInt32("operator", B_EQ);
2641 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[1]),
2642 		message));
2643 
2644 	// add "modified" field
2645 	submenu = new BMenu(B_TRANSLATE("Modified"));
2646 	submenu->SetRadioMode(true);
2647 	submenu->SetFont(be_plain_font);
2648 	message = new BMessage(kAttributeItemMain);
2649 	message->AddString("name", "last_modified");
2650 	message->AddInt32("type", B_TIME_TYPE);
2651 	item = new BMenuItem(submenu, message);
2652 	menu->AddItem(item);
2653 
2654 	message = new BMessage(kAttributeItem);
2655 	message->AddInt32("operator", B_LT);
2656 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[7]),
2657 		message));
2658 
2659 	message = new BMessage(kAttributeItem);
2660 	message->AddInt32("operator", B_GT);
2661 	submenu->AddItem(new BMenuItem(B_TRANSLATE_NOCOLLECT(operatorLabels[8]),
2662 		message));
2663 
2664 	BMenuField* menuField = new BMenuField("MenuField", "", menu);
2665 	menuField->SetDivider(0.0f);
2666 	fAttrGrid->AddView(menuField, 0, gridRow);
2667 
2668 	BStringView* stringView = new BStringView("",
2669 		menu->FindMarked()->Submenu()->FindMarked()->Label());
2670 	BLayoutItem* layoutItem = fAttrGrid->AddView(stringView, 1, gridRow);
2671 	layoutItem->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
2672 		B_ALIGN_VERTICAL_UNSET));
2673 
2674 	BString title("TextEntry");
2675 	title << gridRow;
2676 	BTextControl* textControl = new BTextControl(title.String(), "", "", NULL);
2677 	textControl->SetDivider(0.0f);
2678 	fAttrGrid->AddView(textControl, 2, gridRow);
2679 	textControl->MakeFocus();
2680 
2681 	// target everything
2682 	menu->SetTargetForItems(this);
2683 	for (int32 index = menu->CountItems() - 1; index >= 0; index--) {
2684 		BMenu* submenuAtIndex = menu->SubmenuAt(index);
2685 		if (submenuAtIndex != NULL)
2686 			submenuAtIndex->SetTargetForItems(this);
2687 	}
2688 
2689 	// populate mime popup
2690 	AddMimeTypeAttrs(menu);
2691 }
2692 
2693 
2694 void
2695 FindPanel::RestoreAttrState(const BMessage &message, int32 index)
2696 {
2697 	BMenuField* menuField
2698 		= dynamic_cast<BMenuField*>(FindAttrView("MenuField", index));
2699 	if (menuField != NULL) {
2700 		// decode menu selections
2701 		BMenu* menu = menuField->Menu();
2702 
2703 		ASSERT(menu != NULL);
2704 
2705 		AddMimeTypeAttrs(menu);
2706 		const char* label;
2707 		if (message.FindString("menuSelection", index, &label) == B_OK) {
2708 			int32 itemIndex = SelectItemWithLabel(menu, label);
2709 			if (itemIndex >= 0) {
2710 				menu = menu->SubmenuAt(itemIndex);
2711 				if (menu != NULL && message.FindString("subMenuSelection",
2712 						index, &label) == B_OK) {
2713 					SelectItemWithLabel(menu, label);
2714 				}
2715 			}
2716 		}
2717 	}
2718 
2719 	// decode attribute text
2720 	BString textEntryString = "TextEntry";
2721 	textEntryString << index;
2722 	BTextControl* textControl = dynamic_cast<BTextControl*>(
2723 		FindAttrView(textEntryString.String(), index));
2724 
2725 	ASSERT(textControl != NULL);
2726 
2727 	const char* string;
2728 	if (textControl != NULL
2729 		&& message.FindString("attrViewText", index, &string) == B_OK) {
2730 		textControl->SetText(string);
2731 	}
2732 
2733 	int32 logicMenuSelectedIndex;
2734 	if (message.FindInt32("logicalRelation", index,
2735 			&logicMenuSelectedIndex) == B_OK) {
2736 		BMenuField* field = dynamic_cast<BMenuField*>(
2737 			FindAttrView("Logic", index));
2738 		if (field != NULL) {
2739 			BMenu* fieldMenu = field->Menu();
2740 			if (fieldMenu != NULL) {
2741 				BMenuItem* logicItem
2742 					= fieldMenu->ItemAt(logicMenuSelectedIndex);
2743 				if (logicItem != NULL) {
2744 					logicItem->SetMarked(true);
2745 					return;
2746 				}
2747 			}
2748 		}
2749 
2750 		AddLogicMenu(index, logicMenuSelectedIndex == 0);
2751 	}
2752 }
2753 
2754 
2755 void
2756 FindPanel::SaveAttrState(BMessage* message, int32 index)
2757 {
2758 	BMenu* menu = dynamic_cast<BMenuField*>(FindAttrView("MenuField", index))
2759 		->Menu();
2760 
2761 	// encode main attribute menu selection
2762 	BMenuItem* item = menu->FindMarked();
2763 	message->AddString("menuSelection", item ? item->Label() : "");
2764 
2765 	// encode submenu selection
2766 	const char* label = "";
2767 	if (item) {
2768 		BMenu* submenu = menu->SubmenuAt(menu->IndexOf(item));
2769 		if (submenu) {
2770 			item = submenu->FindMarked();
2771 			if (item)
2772 				label = item->Label();
2773 		}
2774 	}
2775 	message->AddString("subMenuSelection", label);
2776 
2777 	// encode attribute text
2778 	BString textEntryString = "TextEntry";
2779 	textEntryString << index;
2780 	BTextControl* textControl = dynamic_cast<BTextControl*>(FindAttrView(
2781 		textEntryString.String(), index));
2782 
2783 	ASSERT(textControl != NULL);
2784 
2785 	if (textControl != NULL)
2786 		message->AddString("attrViewText", textControl->Text());
2787 
2788 	BMenuField* field = dynamic_cast<BMenuField*>(FindAttrView("Logic", index));
2789 	if (field != NULL) {
2790 		BMenu* fieldMenu = field->Menu();
2791 		if (fieldMenu != NULL) {
2792 			BMenuItem* item = fieldMenu->FindMarked();
2793 			ASSERT(item != NULL);
2794 			message->AddInt32("logicalRelation",
2795 				item != NULL ? field->Menu()->IndexOf(item) : 0);
2796 		}
2797 	}
2798 }
2799 
2800 
2801 void
2802 FindPanel::AddLogicMenu(int32 index, bool selectAnd)
2803 {
2804 	// add "AND/OR" menu
2805 	BPopUpMenu* menu = new BPopUpMenu("");
2806 	BMessage* message = new BMessage();
2807 	message->AddInt32("combine", B_AND);
2808 	BMenuItem* item = new BMenuItem(B_TRANSLATE("And"), message);
2809 	menu->AddItem(item);
2810 	if (selectAnd)
2811 		item->SetMarked(true);
2812 
2813 	message = new BMessage();
2814 	message->AddInt32("combine", B_OR);
2815 	item = new BMenuItem(B_TRANSLATE("Or"), message);
2816 	menu->AddItem(item);
2817 	if (!selectAnd)
2818 		item->SetMarked(true);
2819 
2820 	menu->SetTargetForItems(this);
2821 
2822 	BMenuField* menufield = new BMenuField("Logic", "", menu, B_WILL_DRAW);
2823 	menufield->SetDivider(0.0f);
2824 
2825 	ResizeMenuField(menufield);
2826 
2827 	fAttrGrid->AddView(menufield, 3, index);
2828 }
2829 
2830 
2831 void
2832 FindPanel::RemoveLogicMenu(int32 index)
2833 {
2834 	BMenuField* menufield = dynamic_cast<BMenuField*>(FindAttrView("Logic", index));
2835 	if (menufield) {
2836 		menufield->RemoveSelf();
2837 		delete menufield;
2838 	}
2839 }
2840 
2841 
2842 void
2843 FindPanel::AddAttributes(BMenu* menu, const BMimeType &mimeType)
2844 {
2845 	// only add things to menu which have "user-visible" data
2846 	BMessage attributeMessage;
2847 	if (mimeType.GetAttrInfo(&attributeMessage) != B_OK)
2848 		return;
2849 
2850 	char desc[B_MIME_TYPE_LENGTH];
2851 	mimeType.GetShortDescription(desc);
2852 
2853 	// go through each field in meta mime and add it to a menu
2854 	for (int32 index = 0; ; index++) {
2855 		const char* publicName;
2856 		if (attributeMessage.FindString("attr:public_name", index,
2857 				&publicName) != B_OK) {
2858 			break;
2859 		}
2860 
2861 		if (!attributeMessage.FindBool("attr:viewable"))
2862 			continue;
2863 
2864 		const char* attributeName;
2865 		if (attributeMessage.FindString("attr:name", index, &attributeName)
2866 				!= B_OK) {
2867 			continue;
2868 		}
2869 
2870 		int32 type;
2871 		if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK)
2872 			continue;
2873 
2874 		BMenu* submenu = new BMenu(publicName);
2875 		submenu->SetRadioMode(true);
2876 		submenu->SetFont(be_plain_font);
2877 		BMessage* message = new BMessage(kAttributeItemMain);
2878 		message->AddString("name", attributeName);
2879 		message->AddInt32("type", type);
2880 		BMenuItem* item = new BMenuItem(submenu, message);
2881 		menu->AddItem(item);
2882 		menu->SetTargetForItems(this);
2883 
2884 		switch (type) {
2885 			case B_STRING_TYPE:
2886 				message = new BMessage(kAttributeItem);
2887 				message->AddInt32("operator", B_CONTAINS);
2888 				submenu->AddItem(new BMenuItem(operatorLabels[0], message));
2889 
2890 				message = new BMessage(kAttributeItem);
2891 				message->AddInt32("operator", B_EQ);
2892 				submenu->AddItem(new BMenuItem(operatorLabels[1], message));
2893 
2894 				message = new BMessage(kAttributeItem);
2895 				message->AddInt32("operator", B_NE);
2896 				submenu->AddItem(new BMenuItem(operatorLabels[2], message));
2897 				submenu->SetTargetForItems(this);
2898 
2899 				message = new BMessage(kAttributeItem);
2900 				message->AddInt32("operator", B_BEGINS_WITH);
2901 				submenu->AddItem(new BMenuItem(operatorLabels[3], message));
2902 				submenu->SetTargetForItems(this);
2903 
2904 				message = new BMessage(kAttributeItem);
2905 				message->AddInt32("operator", B_ENDS_WITH);
2906 				submenu->AddItem(new BMenuItem(operatorLabels[4], message));
2907 				break;
2908 
2909 			case B_BOOL_TYPE:
2910 			case B_INT16_TYPE:
2911 			case B_UINT8_TYPE:
2912 			case B_INT8_TYPE:
2913 			case B_UINT16_TYPE:
2914 			case B_INT32_TYPE:
2915 			case B_UINT32_TYPE:
2916 			case B_INT64_TYPE:
2917 			case B_UINT64_TYPE:
2918 			case B_OFF_T_TYPE:
2919 			case B_FLOAT_TYPE:
2920 			case B_DOUBLE_TYPE:
2921 				message = new BMessage(kAttributeItem);
2922 				message->AddInt32("operator", B_EQ);
2923 				submenu->AddItem(new BMenuItem(operatorLabels[1], message));
2924 
2925 				message = new BMessage(kAttributeItem);
2926 				message->AddInt32("operator", B_GT);
2927 				submenu->AddItem(new BMenuItem(operatorLabels[5], message));
2928 
2929 				message = new BMessage(kAttributeItem);
2930 				message->AddInt32("operator", B_LT);
2931 				submenu->AddItem(new BMenuItem(operatorLabels[6], message));
2932 				break;
2933 
2934 			case B_TIME_TYPE:
2935 				message = new BMessage(kAttributeItem);
2936 				message->AddInt32("operator", B_LT);
2937 				submenu->AddItem(new BMenuItem(operatorLabels[7], message));
2938 
2939 				message = new BMessage(kAttributeItem);
2940 				message->AddInt32("operator", B_GT);
2941 				submenu->AddItem(new BMenuItem(operatorLabels[8], message));
2942 				break;
2943 		}
2944 		submenu->SetTargetForItems(this);
2945 	}
2946 }
2947 
2948 
2949 void
2950 FindPanel::AddMimeTypeAttrs(BMenu* menu)
2951 {
2952 	const char* typeName;
2953 	if (CurrentMimeType(&typeName) == NULL)
2954 		return;
2955 
2956 	BMimeType mimeType(typeName);
2957 	if (!mimeType.IsInstalled())
2958 		return;
2959 
2960 	if (!mimeType.IsSupertypeOnly()) {
2961 		// add supertype attributes
2962 		BMimeType supertype;
2963 		mimeType.GetSupertype(&supertype);
2964 		AddAttributes(menu, supertype);
2965 	}
2966 
2967 	AddAttributes(menu, mimeType);
2968 }
2969 
2970 
2971 void
2972 FindPanel::GetDefaultAttrName(BString& attrName, int32 row) const
2973 {
2974 	BMenuItem* item = NULL;
2975 	BMenuField* menuField
2976 		= dynamic_cast<BMenuField*>(fAttrGrid->ItemAt(0, row)->View());
2977 	if (menuField != NULL && menuField->Menu() != NULL)
2978 		item = menuField->Menu()->FindMarked();
2979 
2980 	if (item != NULL)
2981 		attrName << item->Label();
2982 	else
2983 		attrName << B_TRANSLATE("Name");
2984 
2985 	if (item != NULL && item->Submenu() != NULL)
2986 		item = item->Submenu()->FindMarked();
2987 	else
2988 		item = NULL;
2989 
2990 	if (item != NULL)
2991 		attrName << " " << item->Label() << " ";
2992 	else
2993 		attrName << " = ";
2994 
2995 	BTextControl* textControl
2996 		= dynamic_cast<BTextControl*>(fAttrGrid->ItemAt(2, row)->View());
2997 	if (textControl != NULL)
2998 		attrName << textControl->Text();
2999 }
3000 
3001 
3002 // #pragma mark -
3003 
3004 
3005 DeleteTransientQueriesTask::DeleteTransientQueriesTask()
3006 	:
3007 	state(kInitial),
3008 	fWalker(NULL)
3009 {
3010 }
3011 
3012 
3013 DeleteTransientQueriesTask::~DeleteTransientQueriesTask()
3014 {
3015 	delete fWalker;
3016 }
3017 
3018 
3019 bool
3020 DeleteTransientQueriesTask::DoSomeWork()
3021 {
3022 	switch (state) {
3023 		case kInitial:
3024 			Initialize();
3025 			break;
3026 
3027 		case kAllocatedWalker:
3028 		case kTraversing:
3029 			if (GetSome()) {
3030 				PRINT(("transient query killer done\n"));
3031 				return true;
3032 			}
3033 			break;
3034 
3035 		case kError:
3036 			return true;
3037 
3038 	}
3039 	return false;
3040 }
3041 
3042 
3043 void
3044 DeleteTransientQueriesTask::Initialize()
3045 {
3046 	PRINT(("starting up transient query killer\n"));
3047 	BPath path;
3048 	status_t result = find_directory(B_USER_DIRECTORY, &path, false);
3049 	if (result != B_OK) {
3050 		state = kError;
3051 		return;
3052 	}
3053 	fWalker = new BTrackerPrivate::TNodeWalker(path.Path());
3054 	state = kAllocatedWalker;
3055 }
3056 
3057 
3058 const int32 kBatchCount = 100;
3059 
3060 bool
3061 DeleteTransientQueriesTask::GetSome()
3062 {
3063 	state = kTraversing;
3064 	for (int32 count = kBatchCount; count > 0; count--) {
3065 		entry_ref ref;
3066 		if (fWalker->GetNextRef(&ref) != B_OK) {
3067 			state = kError;
3068 			return true;
3069 		}
3070 		Model model(&ref);
3071 		if (model.IsQuery())
3072 			ProcessOneRef(&model);
3073 #if xDEBUG
3074 		else
3075 			PRINT(("transient query killer: %s not a query\n", model.Name()));
3076 #endif
3077 	}
3078 	return false;
3079 }
3080 
3081 
3082 const int32 kDaysToExpire = 7;
3083 
3084 static bool
3085 QueryOldEnough(Model* model)
3086 {
3087 	// check if it is old and ready to be deleted
3088 	time_t now = time(0);
3089 
3090 	tm nowTimeData;
3091 	tm fileModData;
3092 
3093 	localtime_r(&now, &nowTimeData);
3094 	localtime_r(&model->StatBuf()->st_ctime, &fileModData);
3095 
3096 	if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire
3097 		&& (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) {
3098 		PRINT(("query %s, not old enough\n", model->Name()));
3099 		return false;
3100 	}
3101 	return true;
3102 }
3103 
3104 
3105 bool
3106 DeleteTransientQueriesTask::ProcessOneRef(Model* model)
3107 {
3108 	BModelOpener opener(model);
3109 
3110 	// is this a temporary query
3111 	if (!MoreOptionsStruct::QueryTemporary(model->Node())) {
3112 		PRINT(("query %s, not temporary\n", model->Name()));
3113 		return false;
3114 	}
3115 
3116 	if (!QueryOldEnough(model))
3117 		return false;
3118 
3119 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
3120 	ASSERT(tracker != NULL);
3121 
3122 	// check that it is not showing
3123 	if (tracker != NULL && tracker->EntryHasWindowOpen(model->EntryRef())) {
3124 		PRINT(("query %s, showing, can't delete\n", model->Name()));
3125 		return false;
3126 	}
3127 
3128 	PRINT(("query %s, old, temporary, not shownig - deleting\n",
3129 		model->Name()));
3130 
3131 	BEntry entry(model->EntryRef());
3132 	entry.Remove();
3133 
3134 	return true;
3135 }
3136 
3137 
3138 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> {
3139 public:
3140 	DeleteTransientQueriesFunctor(DeleteTransientQueriesTask* task)
3141 		:	task(task)
3142 		{}
3143 
3144 	virtual ~DeleteTransientQueriesFunctor()
3145 		{
3146 			delete task;
3147 		}
3148 
3149 	virtual void operator()()
3150 		{ result = task->DoSomeWork(); }
3151 
3152 private:
3153 	DeleteTransientQueriesTask* task;
3154 };
3155 
3156 
3157 void
3158 DeleteTransientQueriesTask::StartUpTransientQueryCleaner()
3159 {
3160 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
3161 	ASSERT(tracker != NULL);
3162 
3163 	if (tracker == NULL)
3164 		return;
3165 	// set up a task that wakes up when the machine is idle and starts
3166 	// killing off old transient queries
3167 	DeleteTransientQueriesFunctor* worker
3168 		= new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask());
3169 
3170 	tracker->MainTaskLoop()->RunWhenIdle(worker,
3171 		30 * 60 * 1000000,	// half an hour initial delay
3172 		5 * 60 * 1000000,	// idle for five minutes
3173 		10 * 1000000);
3174 }
3175 
3176 
3177 //	#pragma mark -
3178 
3179 
3180 RecentFindItemsMenu::RecentFindItemsMenu(const char* title,
3181 	const BMessenger* target, uint32 what)
3182 	:
3183 	BMenu(title, B_ITEMS_IN_COLUMN),
3184 	fTarget(*target),
3185 	fWhat(what)
3186 {
3187 }
3188 
3189 
3190 void
3191 RecentFindItemsMenu::AttachedToWindow()
3192 {
3193 	// re-populate the menu with fresh items
3194 	for (int32 index = CountItems() - 1; index >= 0; index--)
3195 		delete RemoveItem(index);
3196 
3197 	FindPanel::AddRecentQueries(this, false, &fTarget, fWhat);
3198 	BMenu::AttachedToWindow();
3199 }
3200 
3201 
3202 #if !B_BEOS_VERSION_DANO
3203 _IMPEXP_TRACKER
3204 #endif
3205 BMenu*
3206 TrackerBuildRecentFindItemsMenu(const char* title)
3207 {
3208 	BMessenger trackerMessenger(kTrackerSignature);
3209 	return new RecentFindItemsMenu(title, &trackerMessenger, B_REFS_RECEIVED);
3210 }
3211 
3212 
3213 //	#pragma mark -
3214 
3215 
3216 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char* name,
3217 	const BMessage* message, BMessenger messenger, uint32 resizeFlags,
3218 		uint32 flags)
3219 	:
3220 	DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON,
3221 		message, messenger, resizeFlags, flags)
3222 {
3223 }
3224 
3225 
3226 bool
3227 DraggableQueryIcon::DragStarted(BMessage* dragMessage)
3228 {
3229 	// override to substitute the user-specified query name
3230 	dragMessage->RemoveData("be:clip_name");
3231 
3232 	FindWindow* window = dynamic_cast<FindWindow*>(Window());
3233 
3234 	ASSERT(window != NULL);
3235 
3236 	return window != NULL && dragMessage->AddString("be:clip_name",
3237 		window->BackgroundView()->UserSpecifiedName() != NULL
3238 			? window->BackgroundView()->UserSpecifiedName()
3239 			: B_TRANSLATE("New Query")) == B_OK;
3240 }
3241 
3242 
3243 //	#pragma mark -
3244 
3245 
3246 MostUsedNames::MostUsedNames(const char* fileName, const char* directory,
3247 	int32 maxCount)
3248 	:
3249 	fFileName(fileName),
3250 	fDirectory(directory),
3251 	fLoaded(false),
3252 	fCount(maxCount)
3253 {
3254 }
3255 
3256 
3257 MostUsedNames::~MostUsedNames()
3258 {
3259 	// only write back settings when we've been used
3260 	if (!fLoaded)
3261 		return;
3262 
3263 	// write most used list to file
3264 
3265 	BPath path;
3266 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK
3267 		|| path.Append(fDirectory) != B_OK || path.Append(fFileName) != B_OK) {
3268 		return;
3269 	}
3270 
3271 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
3272 	if (file.InitCheck() == B_OK) {
3273 		for (int32 i = 0; i < fList.CountItems(); i++) {
3274 			list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3275 
3276 			char line[B_FILE_NAME_LENGTH + 5];
3277 
3278 			// limit upper bound to react more dynamically to changes
3279 			if (--entry->count > 20)
3280 				entry->count = 20;
3281 
3282 			// if the item hasn't been chosen in a while, remove it
3283 			// (but leave at least one item in the list)
3284 			if (entry->count < -10 && i > 0)
3285 				continue;
3286 
3287 			sprintf(line, "%" B_PRId32 " %s\n", entry->count, entry->name);
3288 			if (file.Write(line, strlen(line)) < B_OK)
3289 				break;
3290 		}
3291 	}
3292 	file.Unset();
3293 
3294 	// free data
3295 
3296 	for (int32 i = fList.CountItems(); i-- > 0;) {
3297 		list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3298 		free(entry->name);
3299 		delete entry;
3300 	}
3301 }
3302 
3303 
3304 bool
3305 MostUsedNames::ObtainList(BList* list)
3306 {
3307 	if (list == NULL)
3308 		return false;
3309 
3310 	if (!fLoaded)
3311 		UpdateList();
3312 
3313 	fLock.Lock();
3314 
3315 	list->MakeEmpty();
3316 	for (int32 i = 0; i < fCount; i++) {
3317 		list_entry* entry = static_cast<list_entry*>(fList.ItemAt(i));
3318 		if (entry == NULL)
3319 			return true;
3320 
3321 		list->AddItem(entry->name);
3322 	}
3323 	return true;
3324 }
3325 
3326 
3327 void
3328 MostUsedNames::ReleaseList()
3329 {
3330 	fLock.Unlock();
3331 }
3332 
3333 
3334 void
3335 MostUsedNames::AddName(const char* name)
3336 {
3337 	fLock.Lock();
3338 
3339 	if (!fLoaded)
3340 		LoadList();
3341 
3342 	// remove last entry if there are more than
3343 	// 2*fCount entries in the list
3344 
3345 	list_entry* entry = NULL;
3346 
3347 	if (fList.CountItems() > fCount * 2) {
3348 		entry = static_cast<list_entry*>(
3349 			fList.RemoveItem(fList.CountItems() - 1));
3350 
3351 		// is this the name we want to add here?
3352 		if (strcmp(name, entry->name)) {
3353 			free(entry->name);
3354 			delete entry;
3355 			entry = NULL;
3356 		} else
3357 			fList.AddItem(entry);
3358 	}
3359 
3360 	if (entry == NULL) {
3361 		for (int32 i = 0;
3362 				(entry = static_cast<list_entry*>(fList.ItemAt(i))) != NULL; i++) {
3363 			if (strcmp(entry->name, name) == 0)
3364 				break;
3365 		}
3366 	}
3367 
3368 	if (entry == NULL) {
3369 		entry = new list_entry;
3370 		entry->name = strdup(name);
3371 		entry->count = 1;
3372 
3373 		fList.AddItem(entry);
3374 	} else if (entry->count < 0)
3375 		entry->count = 1;
3376 	else
3377 		entry->count++;
3378 
3379 	fLock.Unlock();
3380 	UpdateList();
3381 }
3382 
3383 
3384 int
3385 MostUsedNames::CompareNames(const void* a,const void* b)
3386 {
3387 	list_entry* entryA = *(list_entry**)a;
3388 	list_entry* entryB = *(list_entry**)b;
3389 
3390 	if (entryA->count == entryB->count)
3391 		return strcasecmp(entryA->name,entryB->name);
3392 
3393 	return entryB->count - entryA->count;
3394 }
3395 
3396 
3397 void
3398 MostUsedNames::LoadList()
3399 {
3400 	if (fLoaded)
3401 		return;
3402 	fLoaded = true;
3403 
3404 	// load the most used names list
3405 
3406 	BPath path;
3407 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK
3408 		|| path.Append(fDirectory) != B_OK || path.Append(fFileName) != B_OK) {
3409 		return;
3410 	}
3411 
3412 	FILE* file = fopen(path.Path(), "r");
3413 	if (file == NULL)
3414 		return;
3415 
3416 	char line[B_FILE_NAME_LENGTH + 5];
3417 	while (fgets(line, sizeof(line), file) != NULL) {
3418 		int32 length = (int32)strlen(line) - 1;
3419 		if (length >= 0 && line[length] == '\n')
3420 			line[length] = '\0';
3421 
3422 		int32 count = atoi(line);
3423 
3424 		char* name = strchr(line, ' ');
3425 		if (name == NULL || *(++name) == '\0')
3426 			continue;
3427 
3428 		list_entry* entry = new list_entry;
3429 		entry->name = strdup(name);
3430 		entry->count = count;
3431 
3432 		fList.AddItem(entry);
3433 	}
3434 	fclose(file);
3435 }
3436 
3437 
3438 void
3439 MostUsedNames::UpdateList()
3440 {
3441 	AutoLock<Benaphore> locker(fLock);
3442 
3443 	if (!fLoaded)
3444 		LoadList();
3445 
3446 	// sort list items
3447 
3448 	fList.SortItems(MostUsedNames::CompareNames);
3449 }
3450 
3451 }	// namespace BPrivate
3452