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