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