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