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