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