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