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