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