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