xref: /haiku/src/kits/tracker/FindPanel.cpp (revision 0562493379cd52eb7103531f895f10bb8e77c085)
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 #include <Application.h>
36 #include <Box.h>
37 #include <Button.h>
38 #include <CheckBox.h>
39 #include <Debug.h>
40 #include <Directory.h>
41 #include <FindDirectory.h>
42 #include <File.h>
43 #include <FilePanel.h>
44 #include <MenuField.h>
45 #include <MenuItem.h>
46 #include <Mime.h>
47 #include <NodeInfo.h>
48 #include <PopUpMenu.h>
49 #include <Path.h>
50 #include <Query.h>
51 #include <TextControl.h>
52 #include <TextView.h>
53 #include <View.h>
54 #include <Volume.h>
55 #include <VolumeRoster.h>
56 
57 #include <fs_attr.h>
58 #include <parsedate.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <utility>
62 
63 #include "Attributes.h"
64 #include "AutoLock.h"
65 #include "Commands.h"
66 #include "ContainerWindow.h"
67 #include "FindPanel.h"
68 #include "FSUtils.h"
69 #include "FunctionObject.h"
70 #include "IconMenuItem.h"
71 #include "MimeTypes.h"
72 #include "MiniMenuField.h"
73 #include "Tracker.h"
74 #include "Utilities.h"
75 
76 const char *kAllMimeTypes = "mime/ALLTYPES";
77 
78 const BRect kInitialRect(100, 100, 530, 210);
79 const int32 kInitialAttrModeWindowHeight = 140;
80 const int32 kIncrementPerAttribute = 30;
81 const float kMoreOptionsDelta = 20;
82 
83 const uint32 kMoreOptionsMessage = 'mrop';
84 const uint32 kNameModifiedMessage = 'nmmd';
85 const uint32 kSwitchToQueryTemplate = 'swqt';
86 const uint32 kRunSaveAsTemplatePanel = 'svtm';
87 
88 const char *kDragNDropTypes [] = { B_QUERY_MIMETYPE, B_QUERY_TEMPLATE_MIMETYPE };
89 const char *kDragNDropActionSpecifiers [] = { "Create a Query", "Create a Query template" };
90 
91 const uint32 kAttachFile = 'attf';
92 
93 
94 namespace BPrivate {
95 
96 class MostUsedNames {
97 	public:
98 		MostUsedNames(const char *fileName, const char *directory, int32 maxCount = 5);
99 		~MostUsedNames();
100 
101 		bool ObtainList(BList *list);
102 		void ReleaseList();
103 
104 		void AddName(const char *);
105 
106 	protected:
107 		struct list_entry {
108 			char *name;
109 			int32 count;
110 		};
111 
112 		static int CompareNames(const void *a, const void *b);
113 		void LoadList();
114 		void UpdateList();
115 
116 		const char 	*fFileName;
117 		const char	*fDirectory;
118 		bool		fLoaded;
119 		mutable Benaphore fLock;
120 		BList		fList;
121 		int32		fCount;
122 };
123 
124 MostUsedNames gMostUsedMimeTypes("MostUsedMimeTypes", "Tracker");
125 
126 
127 void
128 MoreOptionsStruct::EndianSwap(void *)
129 {
130 	// noop for now
131 }
132 
133 
134 void
135 MoreOptionsStruct::SetQueryTemporary(BNode *node, bool on)
136 {
137 	MoreOptionsStruct saveMoreOptions;
138 
139 	ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign,
140 		B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct),
141 		&MoreOptionsStruct::EndianSwap);
142 	saveMoreOptions.temporary = on;
143 	node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions,
144 		sizeof(saveMoreOptions));
145 }
146 
147 
148 bool
149 MoreOptionsStruct::QueryTemporary(const BNode *node)
150 {
151 	MoreOptionsStruct saveMoreOptions;
152 
153 	if (ReadAttr(node, kAttrQueryMoreOptions, kAttrQueryMoreOptionsForeign,
154 		B_RAW_TYPE, 0, &saveMoreOptions, sizeof(MoreOptionsStruct),
155 		&MoreOptionsStruct::EndianSwap) == kReadAttrFailed)
156 		return false;
157 
158 	return saveMoreOptions.temporary;
159 }
160 
161 
162 //	#pragma mark -
163 
164 
165 FindWindow::FindWindow(const entry_ref *newRef, bool editIfTemplateOnly)
166 	:	BWindow(kInitialRect, "Find", B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE),
167 		fFile(TryOpening(newRef)),
168 		fFromTemplate(false),
169 		fEditTemplateOnly(false),
170 		fSaveAsTemplatePanel(NULL)
171 {
172 	if (fFile) {
173 		fRef = *newRef;
174 		if (editIfTemplateOnly) {
175 			char type[B_MIME_TYPE_LENGTH];
176 			if (BNodeInfo(fFile).GetType(type) == B_OK
177 				&& strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0) {
178 				fEditTemplateOnly = true;
179 				SetTitle("Edit Query Template");
180 			}
181 		}
182 	} else {
183 		// no initial query, fall back on the default query template
184 		BEntry entry;
185 		GetDefaultQuery(entry);
186 		entry.GetRef(&fRef);
187 
188 		if (entry.Exists())
189 			fFile = TryOpening(&fRef);
190 		else {
191 			// no default query template yet
192 			fFile = new BFile(&entry, O_RDWR | O_CREAT);
193 			if (fFile->InitCheck() < B_OK) {
194 				delete fFile;
195 				fFile = NULL;
196 			} else
197 				SaveQueryAttributes(fFile, true);
198 		}
199 	}
200 
201 	if (fFile) {
202 	 	BRect initialRect(FindPanel::InitialViewSize(fFile));
203 		ResizeTo(initialRect.Width(), initialRect.Height());
204 	}
205 
206 	fFromTemplate = IsQueryTemplate(fFile);
207 
208 	fBackground = new FindPanel(Bounds(), fFile, this, fFromTemplate, fEditTemplateOnly);
209 	AddChild(fBackground);
210 }
211 
212 
213 FindWindow::~FindWindow()
214 {
215 	delete fFile;
216 	delete fSaveAsTemplatePanel;
217 }
218 
219 
220 BFile *
221 FindWindow::TryOpening(const entry_ref *ref)
222 {
223 	if (!ref)
224 		return NULL;
225 
226 	BFile *result = new BFile(ref, O_RDWR);
227 	if (result->InitCheck() != B_OK) {
228 		delete result;
229 		result = NULL;
230 	}
231 	return result;
232 }
233 
234 
235 void
236 FindWindow::GetDefaultQuery(BEntry &entry)
237 {
238 	BPath path;
239 	find_directory(B_USER_DIRECTORY, &path, true);
240 	path.Append("queries");
241 	mkdir(path.Path(), 0777);
242 	BDirectory directory(path.Path());
243 
244 	entry.SetTo(&directory, "default");
245 }
246 
247 
248 bool
249 FindWindow::IsQueryTemplate(BNode *file)
250 {
251 	char type[B_MIME_TYPE_LENGTH];
252 	if (BNodeInfo(file).GetType(type) != B_OK)
253 		return false;
254 
255 	return strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0;
256 }
257 
258 
259 void
260 FindWindow::SwitchToTemplate(const entry_ref *ref)
261 {
262 	try {
263 		BEntry entry(ref, true);
264 		BFile templateFile(&entry, O_RDONLY);
265 
266 		ThrowOnInitCheckError(&templateFile);
267 		DisableUpdates();
268 			// turn off updates to reduce flicker while re-populating the
269 			// window
270 		fBackground->SwitchToTemplate(&templateFile);
271 		EnableUpdates();
272 
273 	} catch (...) {
274 	}
275 }
276 
277 
278 const char *
279 FindWindow::QueryName() const
280 {
281 	if (fFromTemplate) {
282 		if (!fQueryNameFromTemplate.Length())
283 			fFile->ReadAttrString(kAttrQueryTemplateName, &fQueryNameFromTemplate);
284 
285 		return fQueryNameFromTemplate.String();
286 	}
287 	if (!fFile)
288 		return "";
289 
290 	return fRef.name;
291 }
292 
293 
294 static const char *
295 MakeValidFilename(BString &string)
296 {
297 	// make a file name that is legal under bfs and hfs - possibly could
298 	// add code here to accomodate FAT32 etc. too
299 	if (string.Length() > B_FILE_NAME_LENGTH - 1) {
300 		string.Truncate(B_FILE_NAME_LENGTH - 4);
301 		string += B_UTF8_ELLIPSIS;
302 	}
303 
304 	// replace slashes
305 	int32 length = string.Length();
306 	char *buf = string.LockBuffer(length);
307 	for (int32 index = length; index-- > 0;)
308 		if (buf[index] == '/' /*|| buf[index] == ':'*/)
309 			buf[index] = '_';
310 	string.UnlockBuffer(length);
311 
312 	return string.String();
313 }
314 
315 
316 void
317 FindWindow::GetPredicateString(BString &predicate, bool &dynamicDate)
318 {
319 	BQuery query;
320 	BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("TextControl"));
321 	switch (fBackground->Mode()) {
322 		case kByNameItem:
323 			fBackground->GetByNamePredicate(&query);
324 			query.GetPredicate(&predicate);
325 			break;
326 
327 		case kByFormulaItem:
328 			predicate.SetTo(textControl->TextView()->Text(), 1023);
329 			break;
330 
331 		case kByAttributeItem:
332 			fBackground->GetByAttrPredicate(&query, dynamicDate);
333 			query.GetPredicate(&predicate);
334 			break;
335 	}
336 }
337 
338 
339 void
340 FindWindow::GetDefaultName(BString &result)
341 {
342 	fBackground->GetDefaultName(result);
343 
344 	time_t timeValue = time(0);
345 	char namebuf[B_FILE_NAME_LENGTH];
346 
347 	tm timeData;
348 	localtime_r(&timeValue, &timeData);
349 
350 	strftime(namebuf, 32, " - %b %d, %I:%M:%S %p", &timeData);
351 	result << namebuf;
352 
353 	MakeValidFilename(result);
354 }
355 
356 
357 void
358 FindWindow::SaveQueryAttributes(BNode *file, bool queryTemplate)
359 {
360 	ThrowOnError( BNodeInfo(file).SetType(
361 		queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE) );
362 
363 	// save date/time info for recent query support and transient query killer
364 	int32 currentTime = (int32)time(0);
365 	file->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, &currentTime, sizeof(int32));
366 	int32 tmp = 1;
367 	file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32));
368 }
369 
370 
371 status_t
372 FindWindow::SaveQueryAsAttributes(BNode *file, BEntry *entry, bool queryTemplate,
373 	const BMessage *oldAttributes, const BPoint *oldLocation)
374 {
375 	if (oldAttributes)
376 		// revive old window settings
377 		BContainerWindow::SetLayoutState(file, oldAttributes);
378 
379 	if (oldLocation)
380 		// and the file's location
381 		FSSetPoseLocation(entry, *oldLocation);
382 
383 	BNodeInfo(file).SetType(queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE);
384 
385 	BString predicate;
386 	bool dynamicDate;
387 	GetPredicateString(predicate, dynamicDate);
388 	file->WriteAttrString(kAttrQueryString, &predicate);
389 
390 	if (dynamicDate)
391 		file->WriteAttr(kAttrDynamicDateQuery, B_BOOL_TYPE, 0, &dynamicDate,
392 			sizeof(dynamicDate));
393 
394 	int32 tmp = 1;
395 	file->WriteAttr("_trk/recentQuery", B_INT32_TYPE, 0, &tmp, sizeof(int32));
396 
397 	// write some useful info to help locate the volume to query
398 	BMenuItem *item = fBackground->VolMenu()->FindMarked();
399 	if (item) {
400 		dev_t dev;
401 		BMessage message;
402 		int32 count = 0;
403 
404 		int32 itemCount = fBackground->VolMenu()->CountItems();
405 		for (int32 index = 2; index < itemCount; index++) {
406 			BMenuItem *item = fBackground->VolMenu()->ItemAt(index);
407 
408 			if (!item->IsMarked())
409 				continue;
410 
411 			if (item->Message()->FindInt32("device", &dev) != B_OK)
412 				continue;
413 
414 			count++;
415 			BVolume volume(dev);
416 			EmbedUniqueVolumeInfo(&message, &volume);
417 		}
418 
419 		if (count) {
420 			// do we need to embed any volumes
421 			ssize_t size = message.FlattenedSize();
422 			BString buffer;
423 			status_t result = message.Flatten(buffer.LockBuffer(size), size);
424 			ASSERT(result == B_OK);
425 			result = file->WriteAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0,
426 				buffer.String(), (size_t)size);
427 			ASSERT(result == size);
428 			buffer.UnlockBuffer();
429 		}
430 		// default to query for everything
431 	}
432 
433 	fBackground->SaveWindowState(file, fEditTemplateOnly);
434 		// write out all the dialog items as attributes so that the query can
435 		// be reopened and edited later
436 
437 	BView *focusedItem = CurrentFocus();
438 	if (focusedItem) {
439 		// text controls never get the focus, their internal text views do
440 		BView *parent = focusedItem->Parent();
441 		if (dynamic_cast<BTextControl *>(parent))
442 			focusedItem = parent;
443 
444 		// write out the current focus and, if text control, selection
445 		BString name(focusedItem->Name());
446 		file->WriteAttrString("_trk/focusedView", &name);
447 		BTextControl *textControl = dynamic_cast<BTextControl *>(focusedItem);
448 		if (textControl) {
449 			int32 selStart, selEnd;
450 			textControl->TextView()->GetSelection(&selStart, &selEnd);
451 			file->WriteAttr("_trk/focusedSelStart", B_INT32_TYPE, 0,
452 				&selStart, sizeof(selStart));
453 			file->WriteAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0,
454 				&selEnd, sizeof(selEnd));
455 		}
456 	}
457 	return B_OK;
458 }
459 
460 
461 void
462 FindWindow::Save()
463 {
464 	FindSaveCommon(false);
465 
466 	// close the find panel
467 	PostMessage(B_QUIT_REQUESTED);
468 }
469 
470 
471 void
472 FindWindow::Find()
473 {
474 	if (!FindSaveCommon(true)) {
475 		// have to wait for the node monitor to force old query to close
476 		// to avoid a race condition
477 		TTracker *tracker = dynamic_cast<TTracker *>(be_app);
478 		ASSERT(tracker);
479 		for (int32 timeOut = 0; ; timeOut++) {
480 			if (!tracker->EntryHasWindowOpen(&fRef))
481 				// window quit, we can post refs received to open a
482 				// new copy
483 				break;
484 
485 			// PRINT(("waiting for query window to quit, %d\n", timeOut));
486 			if (timeOut == 5000) {
487 				// the old query window would not quit for some reason
488 				TRESPASS();
489 				PostMessage(B_QUIT_REQUESTED);
490 				return;
491 			}
492 			snooze(1000);
493 		}
494 	}
495 
496 	int32 currentTime = (int32)time(0);
497 	fFile->WriteAttr(kAttrQueryLastChange, B_INT32_TYPE, 0, &currentTime, sizeof(int32));
498 
499 	// tell the tracker about it
500 	BMessage message(B_REFS_RECEIVED);
501 	message.AddRef("refs", &fRef);
502 	be_app->PostMessage(&message);
503 
504 	// close the find panel
505 	PostMessage(B_QUIT_REQUESTED);
506 }
507 
508 
509 bool
510 FindWindow::FindSaveCommon(bool find)
511 {
512 	// figure out what we need to do
513 	bool readFromOldFile = fFile != NULL;
514 	bool replaceOriginal = fFile && (!fFromTemplate || fEditTemplateOnly);
515 	bool keepPoseLocation = replaceOriginal;
516 	bool newFile = !fFile || (fFromTemplate && !fEditTemplateOnly);
517 
518 	BEntry entry;
519 	BMessage oldAttributes;
520 	BPoint location;
521 	bool hadLocation = false;
522 	const char *userSpecifiedName = fBackground->UserSpecifiedName();
523 
524 	if (readFromOldFile) {
525 		entry.SetTo(&fRef);
526 		BContainerWindow::GetLayoutState(fFile, &oldAttributes);
527 		hadLocation = FSGetPoseLocation(fFile, &location);
528 	}
529 
530 	if (replaceOriginal) {
531 		fFile->Unset();
532 		entry.Remove();
533 			// remove the current entry - need to do this to quit the
534 			// running query and to close the corresponding window
535 
536 		if (userSpecifiedName && !fEditTemplateOnly) {
537 			// change the name of the old query per users request
538 			fRef.set_name(userSpecifiedName);
539 			entry.SetTo(&fRef);
540 		}
541 	}
542 
543 	if (newFile) {
544 		// create query file in the user's directory
545 		BPath path;
546 		find_directory(B_USER_DIRECTORY, &path, true);
547 		path.Append("queries");
548 		// there might be no queries folder yet, create one
549 		mkdir(path.Path(), 0777);
550 
551 		// either use the user specified name, or go with the name
552 		// generated from the predicate, etc.
553 		if (!userSpecifiedName) {
554 			BString text;
555 			GetDefaultName(text);
556 			path.Append(text.String());
557 		} else
558 			path.Append(userSpecifiedName);
559 
560 		entry.SetTo(path.Path());
561 		entry.Remove();
562 		entry.GetRef(&fRef);
563 	}
564 
565 	fFile = new BFile(&entry, O_RDWR | O_CREAT);
566 	ASSERT(fFile->InitCheck() == B_OK);
567 
568 	SaveQueryAsAttributes(fFile, &entry, !find, newFile ? 0 : &oldAttributes,
569 		(hadLocation && keepPoseLocation) ? &location : 0);
570 
571 	return newFile;
572 }
573 
574 
575 void
576 FindWindow::MessageReceived(BMessage *message)
577 {
578 	switch (message->what) {
579 		case kFindButton:
580 			Find();
581 			break;
582 
583 		case kSaveButton:
584 			Save();
585 			break;
586 
587 		case kAttachFile:
588 			{
589 				entry_ref dir;
590 				const char *name;
591 				bool queryTemplate;
592 				if (message->FindString("name", &name) == B_OK
593 					&& message->FindRef("directory", &dir) == B_OK
594 					&& message->FindBool("template", &queryTemplate) == B_OK) {
595 					delete fFile;
596 					fFile = NULL;
597 					BDirectory directory(&dir);
598 					BEntry entry(&directory, name);
599 					entry_ref tmpRef;
600 					entry.GetRef(&tmpRef);
601 					fFile = TryOpening(&tmpRef);
602 					if (fFile) {
603 						fRef = tmpRef;
604 						SaveQueryAsAttributes(fFile, &entry, queryTemplate, 0, 0);
605 							// try to save whatever state we aleady have
606 							// to the new query so that if the user
607 							// opens it before runing it from the find panel,
608 							// something reasonable happens
609 					}
610 				}
611 			}
612 			break;
613 
614 		case kSwitchToQueryTemplate:
615 			{
616 				entry_ref ref;
617 				if (message->FindRef("refs", &ref) == B_OK)
618 					SwitchToTemplate(&ref);
619 			}
620 			break;
621 
622 		case kRunSaveAsTemplatePanel:
623 			if (fSaveAsTemplatePanel)
624 				fSaveAsTemplatePanel->Show();
625 			else {
626 				BMessenger panel(BackgroundView());
627 				fSaveAsTemplatePanel = new BFilePanel(B_SAVE_PANEL, &panel);
628 				fSaveAsTemplatePanel->SetSaveText("Query template");
629 				fSaveAsTemplatePanel->Window()->SetTitle("Save As Query Template:");
630 				fSaveAsTemplatePanel->Show();
631 			}
632 			break;
633 
634 		default:
635 			_inherited::MessageReceived(message);
636 			break;
637 	}
638 }
639 
640 
641 //	#pragma mark -
642 
643 
644 FindPanel::FindPanel(BRect frame, BFile *node, FindWindow *parent,
645 	bool , bool editTemplateOnly)
646 	:	BView(frame, "MainView", B_FOLLOW_ALL, B_WILL_DRAW),
647 		fMode(kByNameItem),
648 		fDraggableIcon(NULL)
649 {
650 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
651 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
652 
653 	uint32 initialMode = InitialMode(node);
654 
655 	BRect bounds(Bounds());
656 	BRect boxBounds(bounds);
657 
658 	boxBounds.InsetBy(15, 30);
659 	boxBounds.bottom -= 10;
660 	AddChild(new BBox(boxBounds, "Box"));
661 
662 	BRect rect(boxBounds);
663 	rect.top -= 25;
664 	rect.right = rect.left + 20;
665 	rect.bottom = rect.top + 20;
666 
667 	BMessenger self(this);
668 	fRecentQueries = new BPopUpMenu("RecentQueries");
669 	FindPanel::AddRecentQueries(fRecentQueries, true, &self, kSwitchToQueryTemplate);
670 
671 	AddChild(new MiniMenuField(rect, "RecentQueries", fRecentQueries));
672 
673 	rect.left = rect.right + 15;
674 
675 	// add popup for mime types
676 	fMimeTypeMenu = new BPopUpMenu("MimeTypeMenu");
677 	fMimeTypeMenu->SetRadioMode(false);
678 	AddMimeTypesToMenu();
679 
680 	rect.right = rect.left + 150;
681 	fMimeTypeField = new BMenuField(rect, "MimeTypeMenu", "", fMimeTypeMenu);
682 	fMimeTypeField->SetDivider(0.0f);
683 	fMimeTypeField->MenuItem()->SetLabel("All files and folders");
684 	AddChild(fMimeTypeField);
685 
686 	// add popup for search criteria
687 	fSearchModeMenu = new BPopUpMenu("searchMode");
688 	fSearchModeMenu->AddItem(new BMenuItem("by Name", new BMessage(kByNameItem)));
689 	fSearchModeMenu->AddItem(new BMenuItem("by Attribute", new BMessage(kByAttributeItem)));
690 	fSearchModeMenu->AddItem(new BMenuItem("by Formula", new BMessage(kByFormulaItem)));
691 
692 	fSearchModeMenu->ItemAt(initialMode == kByNameItem ? 0 :
693 		(initialMode == kByAttributeItem ? 1 : 2))->SetMarked(true);
694 		// mark the appropriate mode
695 	rect.left = rect.right + 10;
696 	rect.right = rect.left + 100;
697 	rect.bottom = rect.top + 15;
698 	BMenuField *menuField = new BMenuField(rect, "", "", fSearchModeMenu);
699 	menuField->SetDivider(0.0f);
700 	AddChild(menuField);
701 
702 	// add popup for volume list
703 	rect.right = bounds.right - 15;
704 	rect.left = rect.right - 100;
705 	fVolMenu = new BPopUpMenu("", false, false);	// don't radioMode
706 	menuField = new BMenuField(rect, "", "On", fVolMenu);
707 	menuField->SetDivider(menuField->StringWidth(menuField->Label()) + 8);
708 	AddChild(menuField);
709 	AddVolumes(fVolMenu);
710 
711 	if (!editTemplateOnly) {
712 		BPoint draggableIconOrigin(15, bounds.bottom - 35);
713 		BMessage dragNDropMessage(B_SIMPLE_DATA);
714 		dragNDropMessage.AddInt32("be:actions", B_COPY_TARGET);
715 		dragNDropMessage.AddString("be:types", B_FILE_MIME_TYPE);
716 		dragNDropMessage.AddString("be:filetypes", kDragNDropTypes[0]);
717 		dragNDropMessage.AddString("be:filetypes", kDragNDropTypes[1]);
718 		dragNDropMessage.AddString("be:actionspecifier", kDragNDropActionSpecifiers[0]);
719 		dragNDropMessage.AddString("be:actionspecifier", kDragNDropActionSpecifiers[1]);
720 
721 		BMessenger self(this);
722 		fDraggableIcon = new DraggableQueryIcon(DraggableIcon::PreferredRect(draggableIconOrigin,
723 			B_LARGE_ICON), "saveHere", &dragNDropMessage,
724 			self, B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
725 		AddChild(fDraggableIcon);
726 	}
727 
728 	// add the more options collapsible pane
729 	BRect paneInitialRect(bounds);
730 	paneInitialRect.InsetBy(80, 5);
731 	paneInitialRect.right = paneInitialRect.left + 255;
732 	paneInitialRect.top = paneInitialRect.bottom - 30;
733 	BRect paneExpandedRect(paneInitialRect);
734 	paneExpandedRect.bottom += kMoreOptionsDelta;
735 	fMoreOptionsPane = new DialogPane(paneInitialRect, paneExpandedRect, 0,
736 		"moreOptions", B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
737 	AddChild(fMoreOptionsPane);
738 
739 	fMoreOptionsPane->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
740 	fMoreOptionsPane->SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
741 
742 	// set up the contents of the more options pane
743 	BRect expandedBounds(paneExpandedRect);
744 	expandedBounds.OffsetTo(0, 0);
745 	expandedBounds.InsetBy(5, 5);
746 
747 	rect = expandedBounds;
748 	rect.right = rect.left + 200;
749 	rect.bottom = rect.top + 20;;
750 	fQueryName = new BTextControl(rect, "queryName", "Query name:", "", 0);
751 	fQueryName->SetDivider(fQueryName->StringWidth(fQueryName->Label()) + 5);
752 	fMoreOptionsPane->AddItem(fQueryName, 1);
753 	FillCurrentQueryName(fQueryName, parent);
754 
755 	rect.top = rect.bottom + 6;
756 	rect.bottom = rect.top + 16;
757 	rect.right = rect.left + 100;
758 	fSearchTrashCheck = new BCheckBox(rect, "searchTrash", "Include trash", 0);
759 	fMoreOptionsPane->AddItem(fSearchTrashCheck, 1);
760 
761 	rect.OffsetBy(120, 0);
762 	fTemporaryCheck = new BCheckBox(rect, "temporary", "Temporary", 0);
763 	fMoreOptionsPane->AddItem(fTemporaryCheck, 1);
764 	fTemporaryCheck->SetValue(1);
765 
766 	BRect latchRect(paneInitialRect);
767 	latchRect.left -= 20;
768 	latchRect.right = latchRect.left + 10;
769 	latchRect.top = paneInitialRect.top + paneInitialRect.Height() / 2 - 5;
770 
771 	latchRect.bottom = latchRect.top + 12;
772 
773 	fLatch = new PaneSwitch(latchRect, "moreOptionsLatch", true,
774 		B_FOLLOW_BOTTOM | B_FOLLOW_LEFT);
775 	AddChild(fLatch);
776 	fMoreOptionsPane->SetSwitch(fLatch);
777 
778 	if (initialMode != kByAttributeItem)
779 		AddByNameOrFormulaItems();
780 	else
781 		AddByAttributeItems(node);
782 
783 	// add Search button
784 	rect = bounds;
785 	rect.left = rect.right - 80;
786 	rect.top = rect.bottom - 30;
787 	rect.right = rect.left + 60;
788 	rect.bottom = rect.top + 20;
789 	BButton *button;
790 	if (editTemplateOnly)
791 		button = new BButton(rect, "save", "Save",
792 			new BMessage(kSaveButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
793 	else
794 		button = new BButton(rect, "find", "Search",
795 			new BMessage(kFindButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
796 
797 	AddChild(button);
798 	button->MakeDefault(true);
799 }
800 
801 
802 FindPanel::~FindPanel()
803 {
804 }
805 
806 
807 void
808 FindPanel::AttachedToWindow()
809 {
810 	BNode *node = dynamic_cast<FindWindow *>(Window())->QueryNode();
811 	fSearchModeMenu->SetTargetForItems(this);
812 	fQueryName->SetTarget(this);
813 	fLatch->SetTarget(fMoreOptionsPane);
814 	RestoreMimeTypeMenuSelection(node);
815 		// preselect the mime we used the last time
816 		// have to do it here because AddByAttributeItems will build different
817 		// menus based on which mime type is preselected
818 	RestoreWindowState(node);
819 
820 	if (!Window()->CurrentFocus()) {
821 		// try to pick a good focus if we restore to one already
822 		BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("TextControl"));
823 		if (!textControl) {
824 			// pick the last text control in the attribute view
825 			BString title("TextEntry");
826 			title << (fAttrViewList.CountItems() - 1);
827 			textControl = dynamic_cast<BTextControl *>(FindView(title.String()));
828 		}
829 		if (textControl)
830 			textControl->MakeFocus();
831 	}
832 
833 	BButton *button = dynamic_cast<BButton *>(FindView("remove"));
834 	if (button)
835 		button->SetTarget(this);
836 
837 	button = dynamic_cast<BButton *>(FindView("add"));
838 	if (button)
839 		button->SetTarget(this);
840 
841 	fVolMenu->SetTargetForItems(this);
842 
843 	// set target for MIME type items
844 	for (int32 index = MimeTypeMenu()->CountItems();index-- > 2;) {
845 		BMenu *submenu = MimeTypeMenu()->ItemAt(index)->Submenu();
846 		if (submenu != NULL)
847 				submenu->SetTargetForItems(this);
848 	}
849 	fMimeTypeMenu->SetTargetForItems(this);
850 
851 
852 	if (fDraggableIcon)
853 		fDraggableIcon->SetTarget(BMessenger(this));
854 
855 	fRecentQueries->SetTargetForItems(Window());
856 }
857 
858 const float kAttrViewDelta = 30;
859 
860 BRect
861 FindPanel::InitialViewSize(const BNode *node)
862 {
863 	if (!node || InitialMode(node) != (int32)kByAttributeItem)
864 		return kInitialRect;
865 
866 	int32 numAttributes = InitialAttrCount(node);
867 	if (numAttributes < 1)
868 		numAttributes = 1;
869 
870 	BRect result = kInitialRect;
871 	result.bottom = result.top + kInitialAttrModeWindowHeight
872 		+ (numAttributes - 1) * kIncrementPerAttribute;
873 
874 	return result;
875 }
876 
877 
878 float
879 FindPanel::ViewHeightForMode(uint32 mode, bool moreOptions)
880 {
881 	float result = moreOptions ? kMoreOptionsDelta : 0;
882 	switch (mode) {
883 		case kByFormulaItem:
884 		case kByNameItem:
885 			return 110 + result;
886 
887 		case kByAttributeItem:
888 			return 110 + result + kAttrViewDelta;
889 
890 	}
891 	TRESPASS();
892 	return 0;
893 }
894 
895 
896 float
897 FindPanel::BoxHeightForMode(uint32 mode, bool /*moreOptions*/)
898 {
899 	switch (mode) {
900 		case kByFormulaItem:
901 		case kByNameItem:
902 			return 40;
903 
904 		case kByAttributeItem:
905 			return 40 + kAttrViewDelta;
906 
907 	}
908 	TRESPASS();
909 	return 0;
910 }
911 
912 
913 static void
914 PopUpMenuSetTitle(BMenu *menu, const char *title)
915 {
916 	// This should really be in BMenuField
917 	BMenu *bar = menu->Supermenu();
918 
919 	ASSERT(bar);
920 	ASSERT(bar->ItemAt(0));
921 	if (!bar || !bar->ItemAt(0))
922 		return;
923 
924 	bar->ItemAt(0)->SetLabel(title);
925 }
926 
927 
928 void
929 FindPanel::ShowVolumeMenuLabel()
930 {
931 	if (fVolMenu->ItemAt(0)->IsMarked()) {
932 		// "all disks" selected
933 		PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label());
934 		return;
935 	}
936 
937 	// find out if more than one items are marked
938 	int32 count = fVolMenu->CountItems();
939 	int32 countSelected = 0;
940 	BMenuItem *tmpItem = NULL;
941 	for (int32 index = 2; index < count; index++) {
942 		BMenuItem *item = fVolMenu->ItemAt(index);
943 		if (item->IsMarked()) {
944 			countSelected++;
945 			tmpItem = item;
946 		}
947 	}
948 	if (countSelected == 0) {
949 		// no disk selected, for now revert to search all disks
950 		// ToDo:
951 		// show no disks here and add a check that will not let the
952 		// query go if the user doesn't pick at least one
953 		fVolMenu->ItemAt(0)->SetMarked(true);
954 		PopUpMenuSetTitle(fVolMenu, fVolMenu->ItemAt(0)->Label());
955 	} else if (countSelected > 1)
956 		// if more than two disks selected, don't use the disk name
957 		// as a label
958 		PopUpMenuSetTitle(fVolMenu, "multiple disks");
959 	else {
960 		ASSERT(tmpItem);
961 		PopUpMenuSetTitle(fVolMenu, tmpItem->Label());
962 	}
963 }
964 
965 
966 void
967 FindPanel::MessageReceived(BMessage *message)
968 {
969 	entry_ref dir;
970 	const char *name;
971 
972 	switch (message->what) {
973 		case kVolumeItem:
974 			{
975 				// volume changed
976 				BMenuItem *invokedItem;
977 				dev_t dev;
978 				if (message->FindPointer("source", (void **)&invokedItem) != B_OK)
979 					return;
980 
981 				if (message->FindInt32("device", &dev) != B_OK)
982 					break;
983 
984 				BMenu *menu = invokedItem->Menu();
985 				ASSERT(menu);
986 
987 				if (dev == -1) {
988 
989 					// all disks selected, uncheck everything else
990 					int32 count = menu->CountItems();
991 					for (int32 index = 2; index < count; index++)
992 						menu->ItemAt(index)->SetMarked(false);
993 
994 					// make all disks the title and check it
995 					PopUpMenuSetTitle(menu, menu->ItemAt(0)->Label());
996 					menu->ItemAt(0)->SetMarked(true);
997 
998 				} else {
999 					// a specific volume selected, unmark "all disks"
1000 					menu->ItemAt(0)->SetMarked(false);
1001 
1002 					// toggle mark on invoked item
1003 					int32 count = menu->CountItems();
1004 					for (int32 index = 2; index < count; index++) {
1005 						BMenuItem *item = menu->ItemAt(index);
1006 
1007 						if (invokedItem == item) {
1008 							// we just selected this
1009 							bool wasMarked = item->IsMarked();
1010 							item->SetMarked(!wasMarked);
1011 						}
1012 					}
1013 				}
1014 				// make sure the right label is showing
1015 				ShowVolumeMenuLabel();
1016 
1017 				break;
1018 			}
1019 
1020 		case kByAttributeItem:
1021 		case kByNameItem:
1022 		case kByFormulaItem:
1023 			SwitchMode(message->what);
1024 			break;
1025 
1026 		case kAddItem:
1027 			AddAttrView();
1028 			break;
1029 
1030 		case kRemoveItem:
1031 			RemoveAttrView();
1032 			break;
1033 
1034 		case kMIMETypeItem:
1035 			{
1036 				BMenuItem *item;
1037 				if (message->FindPointer("source", (void **)&item) == B_OK) {
1038 					// don't add the "All files and folders" to the list
1039 					if (fMimeTypeMenu->IndexOf(item) != 0)
1040 						gMostUsedMimeTypes.AddName(item->Label());
1041 
1042 					SetCurrentMimeType(item);
1043 				}
1044 
1045 				// mime type switched
1046 				if (fMode != kByAttributeItem)
1047 					break;
1048 
1049 				// the attributes for this type may be different, rip out the existing ones
1050 				RemoveAttrViewItems();
1051 
1052 				Window()->ResizeTo(Window()->Frame().Width(),
1053 					ViewHeightForMode(kByAttributeItem, fLatch->Value() != 0));
1054 
1055 				BBox *box = dynamic_cast<BBox *>(FindView("Box"));
1056 				ASSERT(box);
1057 				box->ResizeTo(box->Bounds().Width(),
1058 					BoxHeightForMode(kByAttributeItem, fLatch->Value() != 0));
1059 
1060 				AddAttrView();
1061 				break;
1062 			}
1063 
1064 		case kNameModifiedMessage:
1065 			// the query name was edited, make the query permanent
1066 			fTemporaryCheck->SetValue(0);
1067 			break;
1068 
1069 		case B_SAVE_REQUESTED:
1070 			{
1071 				// finish saving query template from a SaveAs panel
1072 				entry_ref ref;
1073 				status_t error = message->FindRef("refs", &ref);
1074 
1075 				if (error == B_OK) {
1076 					// direct entry selected, convert to parent dir and name
1077 					BEntry entry(&ref);
1078 					error = entry.GetParent(&entry);
1079 					if (error == B_OK) {
1080 						entry.GetRef(&dir);
1081 						name = ref.name;
1082 					}
1083 				} else {
1084 					// parent dir and name selected
1085 					error = message->FindRef("directory", &dir);
1086 					if (error == B_OK)
1087 						error = message->FindString("name", &name);
1088 				}
1089 				if (error == B_OK)
1090 					SaveAsQueryOrTemplate(&dir, name, true);
1091 			}
1092 			break;
1093 
1094 		case B_COPY_TARGET:
1095 			{
1096 				// finish drag&drop
1097 				const char *str;
1098 				const char *mimeType = NULL;
1099 				const char *actionSpecifier = NULL;
1100 				if (message->FindString("be:types", &str) == B_OK
1101 					&& strcasecmp(str, B_FILE_MIME_TYPE) == 0
1102 					&& (message->FindString("be:actionspecifier", &actionSpecifier) == B_OK
1103 						|| message->FindString("be:filetypes", &mimeType) == B_OK)
1104 					&& message->FindString("name", &name) == B_OK
1105 					&& message->FindRef("directory", &dir) == B_OK) {
1106 
1107 					bool query = false;
1108 					bool queryTemplate = false;
1109 
1110 					if (actionSpecifier
1111 						&& strcasecmp(actionSpecifier, kDragNDropActionSpecifiers[0]) == 0)
1112 						query = true;
1113 					else if (actionSpecifier
1114 						&& strcasecmp(actionSpecifier, kDragNDropActionSpecifiers[1]) == 0)
1115 						queryTemplate = true;
1116 					else if (mimeType && strcasecmp(mimeType, kDragNDropTypes[0]) == 0)
1117 						query = true;
1118 					else if (mimeType && strcasecmp(mimeType, kDragNDropTypes[1]) == 0)
1119 						queryTemplate = true;
1120 
1121 					if (query || queryTemplate)
1122 						SaveAsQueryOrTemplate(&dir, name, queryTemplate);
1123 				}
1124 			}
1125 			break;
1126 
1127 		default:
1128 			_inherited::MessageReceived(message);
1129 			break;
1130 	}
1131 }
1132 
1133 
1134 void
1135 FindPanel::SaveAsQueryOrTemplate(const entry_ref *dir, const char *name, bool queryTemplate)
1136 {
1137 	BDirectory directory(dir);
1138 	BFile file(&directory, name, O_RDWR | O_CREAT | O_TRUNC);
1139 	BNodeInfo(&file).SetType(queryTemplate ? B_QUERY_TEMPLATE_MIMETYPE : B_QUERY_MIMETYPE);
1140 
1141 	BMessage attach(kAttachFile);
1142 	attach.AddRef("directory", dir);
1143 	attach.AddString("name", name);
1144 	attach.AddBool("template", queryTemplate);
1145 	Window()->PostMessage(&attach, 0);
1146 }
1147 
1148 
1149 void
1150 FindPanel::BuildAttrQuery(BQuery *query, bool &dynamicDate) const
1151 {
1152 	dynamicDate = false;
1153 
1154 	// go through each attrview and add the attr and comparison info
1155 	for (int32 index = 0; index < fAttrViewList.CountItems(); index++) {
1156 
1157 		TAttrView *view = fAttrViewList.ItemAt(index);
1158 		BString title;
1159 		title << "TextEntry" << index;
1160 
1161 		BTextControl *textControl = dynamic_cast<BTextControl *>
1162 			(view->FindView(title.String()));
1163 		if (!textControl)
1164 			return;
1165 
1166 		BMenuField *menuField = dynamic_cast<BMenuField *>(view->FindView("MenuField"));
1167 		if (!menuField)
1168 			return;
1169 
1170 		BMenuItem *item = menuField->Menu()->FindMarked();
1171 		if (!item)
1172 			continue;
1173 
1174 		BMessage *message = item->Message();
1175 		int32 type;
1176 		if (message->FindInt32("type", &type) == B_OK) {
1177 
1178 			const char *str;
1179 			if (message->FindString("name", &str) == B_OK)
1180 				query->PushAttr(str);
1181 			else
1182 				query->PushAttr(item->Label());
1183 
1184 			switch (type) {
1185 				case B_STRING_TYPE:
1186 					query->PushString(textControl->TextView()->Text(), true);
1187 					break;
1188 
1189 				case B_TIME_TYPE:
1190 					{
1191 						int flags = 0;
1192 						DEBUG_ONLY(time_t result =)
1193 						parsedate_etc(textControl->TextView()->Text(), -1, &flags);
1194 						dynamicDate = (flags & PARSEDATE_RELATIVE_TIME) != 0;
1195 						PRINT(("parsedate_etc - date is %srelative, %l\n",
1196 							dynamicDate ? "" : "not ", result));
1197 
1198 						query->PushDate(textControl->TextView()->Text());
1199 					}
1200 					break;
1201 
1202 				case B_BOOL_TYPE:
1203 					{
1204 						uint32 value;
1205 						if (strcasecmp(textControl->TextView()->Text(), "true") == 0)
1206 							value = 1;
1207 						else if (strcasecmp(textControl->TextView()->Text(), "true") == 0)
1208 							value = 1;
1209 						else
1210 							value = (uint32)atoi(textControl->TextView()->Text());
1211 
1212 						value %= 2;
1213 						query->PushUInt32(value);
1214 					}
1215 					break;
1216 
1217 				case B_UINT8_TYPE:
1218 				case B_UINT16_TYPE:
1219 				case B_UINT32_TYPE:
1220 					query->PushUInt32((uint32)StringToScalar(textControl->TextView()->Text()));
1221 					break;
1222 
1223 				case B_INT8_TYPE:
1224 				case B_INT16_TYPE:
1225 				case B_INT32_TYPE:
1226 					query->PushInt32((int32)StringToScalar(textControl->TextView()->Text()));
1227 					break;
1228 
1229 				case B_UINT64_TYPE:
1230 					query->PushUInt64((uint64)StringToScalar(textControl->TextView()->Text()));
1231 					break;
1232 
1233 				case B_OFF_T_TYPE:
1234 				case B_INT64_TYPE:
1235 					query->PushInt64(StringToScalar(textControl->TextView()->Text()));
1236 					break;
1237 
1238 				case B_FLOAT_TYPE:
1239 					{
1240 						float floatVal;
1241 						sscanf(textControl->TextView()->Text(), "%f", &floatVal);
1242 						query->PushFloat(floatVal);
1243 					}
1244 					break;
1245 
1246 				case B_DOUBLE_TYPE:
1247 					{
1248 						double doubleVal;
1249 						sscanf(textControl->TextView()->Text(), "%lf", &doubleVal);
1250 						query->PushDouble(doubleVal);
1251 					}
1252 					break;
1253 
1254 			}
1255 		}
1256 
1257 		query_op theOperator;
1258 		BMenuItem *operatorItem = item->Submenu()->FindMarked();
1259 		if (operatorItem && operatorItem->Message() != NULL) {
1260 			operatorItem->Message()->FindInt32("operator", (int32 *)&theOperator);
1261 			query->PushOp(theOperator);
1262 		} else
1263 			query->PushOp(B_EQ);
1264 
1265 		// add logic based on selection in Logic menufield
1266 		if (index > 0) {
1267 			TAttrView *prevView = fAttrViewList.ItemAt(index - 1);
1268 			menuField = dynamic_cast<BMenuField *>(prevView->FindView("Logic"));
1269 			if (menuField) {
1270 				item = menuField->Menu()->FindMarked();
1271 				if (item) {
1272 					message = item->Message();
1273 					message->FindInt32("combine", (int32 *)&theOperator);
1274 					query->PushOp(theOperator);
1275 				}
1276 			} else
1277 				query->PushOp(B_AND);
1278 		}
1279 	}
1280 }
1281 
1282 
1283 void
1284 FindPanel::PushMimeType(BQuery *query) const
1285 {
1286 	const char *type;
1287 	if (CurrentMimeType(&type) == NULL)
1288 		return;
1289 
1290 	if (strcmp(kAllMimeTypes, type)) {
1291 		// add an asterisk if we are searching for a supertype
1292 		char buffer[B_FILE_NAME_LENGTH];
1293 		if (strchr(type,'/') == NULL) {
1294 			strcpy(buffer,type);
1295 			strcat(buffer,"/*");
1296 			type = buffer;
1297 		}
1298 
1299 		query->PushAttr(kAttrMIMEType);
1300 		query->PushString(type);
1301 		query->PushOp(B_EQ);
1302 		query->PushOp(B_AND);
1303 	}
1304 }
1305 
1306 
1307 void
1308 FindPanel::GetByAttrPredicate(BQuery *query, bool &dynamicDate) const
1309 {
1310 	ASSERT(Mode() == (int32)kByAttributeItem);
1311 	BuildAttrQuery(query, dynamicDate);
1312 	PushMimeType(query);
1313 }
1314 
1315 
1316 void
1317 FindPanel::GetDefaultName(BString &result) const
1318 {
1319 	BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("TextControl"));
1320 	switch (Mode()) {
1321 		case kByNameItem:
1322 			result << "Name = " << textControl->TextView()->Text();
1323 			break;
1324 
1325 		case kByFormulaItem:
1326 			result << "Formula " << textControl->TextView()->Text();
1327 			break;
1328 
1329 		case kByAttributeItem:
1330 		{
1331 			BMenuItem *item = fMimeTypeMenu->FindMarked();
1332 			if (item != NULL)
1333 				result << item->Label() << ": ";
1334 
1335 			for (int32 i = 0; i < fAttrViewList.CountItems(); i++) {
1336 				fAttrViewList.ItemAt(i)->GetDefaultName(result);
1337 				if (i + 1 < fAttrViewList.CountItems())
1338 					result << ", ";
1339 			}
1340 			break;
1341 		}
1342 	}
1343 }
1344 
1345 
1346 const char *
1347 FindPanel::UserSpecifiedName() const
1348 {
1349 	if (fQueryName->Text()[0] == '\0')
1350 		return NULL;
1351 
1352 	return fQueryName->Text();
1353 }
1354 
1355 
1356 void
1357 FindPanel::GetByNamePredicate(BQuery *query) const
1358 {
1359 	ASSERT(Mode() == (int32)kByNameItem);
1360 	BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("TextControl"));
1361 	ASSERT(textControl);
1362 
1363 	query->PushAttr("name");
1364 	query->PushString(textControl->TextView()->Text(), true);
1365 
1366 	if (strstr(textControl->TextView()->Text(), "*"))
1367 		// assume pattern is a regular expression and try doing an exact match
1368 		query->PushOp(B_EQ);
1369 	else
1370 		query->PushOp(B_CONTAINS);
1371 
1372 	PushMimeType(query);
1373 }
1374 
1375 
1376 void
1377 FindPanel::SwitchMode(uint32 mode)
1378 {
1379 	if (fMode == mode)
1380 		// no work, bail
1381 		return;
1382 
1383 	BBox *box = dynamic_cast<BBox *>(FindView("Box"));
1384 	ASSERT(box);
1385 
1386 	uint32 oldMode = fMode;
1387 	BString buffer;
1388 
1389 	switch (mode) {
1390 		case kByFormulaItem:
1391 			if (oldMode == kByAttributeItem || oldMode == kByNameItem) {
1392 				BQuery query;
1393 				if (oldMode == kByAttributeItem) {
1394 					bool dummy;
1395 					GetByAttrPredicate(&query, dummy);
1396 				} else
1397 					GetByNamePredicate(&query);
1398 
1399 				query.GetPredicate(&buffer);
1400 			}
1401 			// fall thru
1402 
1403 		case kByNameItem:
1404 			{
1405 				fMode = mode;
1406 				Window()->ResizeTo(Window()->Frame().Width(),
1407 					ViewHeightForMode(mode, fLatch->Value() != 0));
1408 				BRect bounds(Bounds());
1409 				bounds.InsetBy(15, 30);
1410 				bounds.bottom -= 10;
1411 				if (fLatch->Value())
1412 					bounds.bottom -= kMoreOptionsDelta;
1413 				box->ResizeTo(bounds.Width(), BoxHeightForMode(mode, fLatch->Value() != 0));
1414 
1415 				RemoveByAttributeItems();
1416 				ShowOrHideMimeTypeMenu();
1417 				AddByNameOrFormulaItems();
1418 
1419 				if (buffer.Length()) {
1420 					ASSERT(mode == kByFormulaItem || oldMode == kByAttributeItem);
1421 					BTextControl *textControl = dynamic_cast<BTextControl *>
1422 						(FindView("TextControl"));
1423 					textControl->SetText(buffer.String());
1424 				}
1425 				break;
1426 			}
1427 
1428 		case kByAttributeItem:
1429 			{
1430 				fMode = mode;
1431 				box->ResizeTo(box->Bounds().Width(),
1432 					BoxHeightForMode(mode, fLatch->Value() != 0));
1433 
1434 				Window()->ResizeTo(Window()->Frame().Width(),
1435 					ViewHeightForMode(mode, fLatch->Value() != 0));
1436 
1437 				BTextControl *textControl = dynamic_cast<BTextControl *>
1438 					(FindView("TextControl"));
1439 
1440 				if (textControl) {
1441 					textControl->RemoveSelf();
1442 					delete textControl;
1443 				}
1444 
1445 				ShowOrHideMimeTypeMenu();
1446 				AddAttrView();
1447 				break;
1448 			}
1449 	}
1450 }
1451 
1452 
1453 BMenuItem *
1454 FindPanel::CurrentMimeType(const char **type) const
1455 {
1456 	// search for marked item in the list
1457 	BMenuItem *item = MimeTypeMenu()->FindMarked();
1458 
1459 	// if it's one of the most used items, ignore it
1460 	if (item != NULL && MimeTypeMenu()->IndexOf(item) != 0 && item->Submenu() == NULL)
1461 		item = NULL;
1462 
1463 	if (item == NULL) {
1464 		for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) {
1465 			BMenu *submenu = MimeTypeMenu()->ItemAt(index)->Submenu();
1466 			if (submenu != NULL && (item = submenu->FindMarked()) != NULL)
1467 				break;
1468 		}
1469 	}
1470 
1471 	if (type && item != NULL) {
1472 		BMessage *message = item->Message();
1473 		if (!message)
1474 			return NULL;
1475 
1476 		if (message->FindString("mimetype", type) != B_OK)
1477 			return NULL;
1478 	}
1479 	return item;
1480 }
1481 
1482 
1483 status_t
1484 FindPanel::SetCurrentMimeType(BMenuItem *item)
1485 {
1486 	// unmark old MIME type (in most used list, and the tree)
1487 
1488 	BMenuItem *marked = CurrentMimeType();
1489 	if (marked != NULL) {
1490 		marked->SetMarked(false);
1491 
1492 		if ((marked = MimeTypeMenu()->FindMarked()) != NULL)
1493 			marked->SetMarked(false);
1494 	}
1495 
1496 	// mark new MIME type (in most used list, and the tree)
1497 
1498 	if (item != NULL) {
1499 		item->SetMarked(true);
1500 		fMimeTypeField->MenuItem()->SetLabel(item->Label());
1501 
1502 		BMenuItem *search;
1503 		for (int32 i = 2;(search = MimeTypeMenu()->ItemAt(i)) != NULL;i++) {
1504 			if (item == search || !search->Label())
1505 				continue;
1506 			if (!strcmp(item->Label(),search->Label())) {
1507 				search->SetMarked(true);
1508 				break;
1509 			}
1510 			BMenu *submenu = search->Submenu();
1511 			if (submenu) {
1512 				for (int32 j = submenu->CountItems();j-- > 0;) {
1513 					BMenuItem *sub = submenu->ItemAt(j);
1514 					if (!strcmp(item->Label(),sub->Label())) {
1515 						sub->SetMarked(true);
1516 						break;
1517 					}
1518 				}
1519 			}
1520 		}
1521 	}
1522 	return B_OK;
1523 }
1524 
1525 
1526 status_t
1527 FindPanel::SetCurrentMimeType(const char *label)
1528 {
1529 	// unmark old MIME type (in most used list, and the tree)
1530 
1531 	BMenuItem *marked = CurrentMimeType();
1532 	if (marked != NULL) {
1533 		marked->SetMarked(false);
1534 
1535 		if ((marked = MimeTypeMenu()->FindMarked()) != NULL)
1536 			marked->SetMarked(false);
1537 	}
1538 
1539 	// mark new MIME type (in most used list, and the tree)
1540 
1541 	fMimeTypeField->MenuItem()->SetLabel(label);
1542 	bool found = false;
1543 
1544 	for (int32 index = MimeTypeMenu()->CountItems(); index-- > 0;) {
1545 		BMenuItem *item = MimeTypeMenu()->ItemAt(index);
1546 		BMenu *submenu = item->Submenu();
1547 		if (submenu != NULL && !found) {
1548 			for (int32 subIndex = submenu->CountItems(); subIndex-- > 0;) {
1549 				BMenuItem *subItem = submenu->ItemAt(subIndex);
1550 				if (subItem->Label() != NULL && !strcmp(label, subItem->Label())) {
1551 					subItem->SetMarked(true);
1552 					found = true;
1553 				}
1554 			}
1555 		}
1556 		if (item->Label() != NULL && !strcmp(label, item->Label())) {
1557 			item->SetMarked(true);
1558 			return B_OK;
1559 		}
1560 	}
1561 
1562 	return found ? B_OK : B_ENTRY_NOT_FOUND;
1563 }
1564 
1565 
1566 bool
1567 FindPanel::AddOneMimeTypeToMenu(const ShortMimeInfo *info, void *castToMenu)
1568 {
1569 	BPopUpMenu *menu = static_cast<BPopUpMenu *>(castToMenu);
1570 
1571 	BMimeType type(info->InternalName());
1572 	BMimeType super;
1573 	type.GetSupertype(&super);
1574 	if (super.InitCheck() < B_OK)
1575 		return false;
1576 
1577 	BMenuItem *superItem = menu->FindItem(super.Type());
1578 	if (superItem != NULL) {
1579 		BMessage *msg = new BMessage(kMIMETypeItem);
1580 		msg->AddString("mimetype", info->InternalName());
1581 
1582 		superItem->Submenu()->AddItem(new IconMenuItem(info->ShortDescription(),
1583 			msg, info->InternalName(), B_MINI_ICON));
1584 	}
1585 
1586 	return false;
1587 }
1588 
1589 
1590 void
1591 FindPanel::AddMimeTypesToMenu()
1592 {
1593 	BMessage *itemMessage = new BMessage(kMIMETypeItem);
1594 	itemMessage->AddString("mimetype", kAllMimeTypes);
1595 	MimeTypeMenu()->AddItem(new BMenuItem("All files and folders", itemMessage));
1596 	MimeTypeMenu()->AddSeparatorItem();
1597 	MimeTypeMenu()->ItemAt(0)->SetMarked(true);
1598 
1599 	// add recent MIME types
1600 
1601 	TTracker *tracker = dynamic_cast<TTracker *>(be_app);
1602 
1603 	BList list;
1604 	if (gMostUsedMimeTypes.ObtainList(&list) && tracker) {
1605 		int32 count = 0;
1606 		for (int32 index = 0; index < list.CountItems(); index++) {
1607 			const char *name = (const char *)list.ItemAt(index);
1608 
1609 			const ShortMimeInfo *info;
1610 			if ((info = tracker->MimeTypes()->FindMimeType(name)) == NULL)
1611 				continue;
1612 
1613 			BMessage *message = new BMessage(kMIMETypeItem);
1614 			message->AddString("mimetype", info->InternalName());
1615 
1616 			MimeTypeMenu()->AddItem(new BMenuItem(name, message));
1617 			count++;
1618 		}
1619 		if (count != 0)
1620 			MimeTypeMenu()->AddSeparatorItem();
1621 
1622 		gMostUsedMimeTypes.ReleaseList();
1623 	}
1624 
1625 	// add MIME type tree list
1626 
1627 	BMessage types;
1628 	if (BMimeType::GetInstalledSupertypes(&types) == B_OK) {
1629 		const char *superType;
1630 		int32 index = 0;
1631 
1632 		while (types.FindString("super_types",index++,&superType) == B_OK) {
1633 			BMenu *superMenu = new BMenu(superType);
1634 
1635 			BMessage *message = new BMessage(kMIMETypeItem);
1636 			message->AddString("mimetype", superType);
1637 
1638 			MimeTypeMenu()->AddItem(new IconMenuItem(superMenu, message,
1639 				superType, B_MINI_ICON));
1640 
1641 			// the MimeTypeMenu's font is not correct at this time
1642 			superMenu->SetFont(be_plain_font);
1643 		}
1644 	}
1645 
1646 	if (tracker)
1647 		tracker->MimeTypes()->EachCommonType(&FindPanel::AddOneMimeTypeToMenu,
1648 			MimeTypeMenu());
1649 
1650 	// remove empty super type menus (and set target)
1651 
1652 	for (int32 index = MimeTypeMenu()->CountItems();index-- > 2;) {
1653 		BMenuItem *item = MimeTypeMenu()->ItemAt(index);
1654 		BMenu *submenu = item->Submenu();
1655 		if (submenu != NULL) {
1656 			if (submenu->CountItems() == 0) {
1657 				MimeTypeMenu()->RemoveItem(item);
1658 				delete item;
1659 			} else
1660 				submenu->SetTargetForItems(this);
1661 		}
1662 	}
1663 
1664 	MimeTypeMenu()->SetTargetForItems(this);
1665 }
1666 
1667 
1668 void
1669 FindPanel::AddVolumes(BMenu *menu)
1670 {
1671 // ToDo: add calls to this to rebuild the menu when a volume gets mounted
1672 
1673 	BMessage *message = new BMessage(kVolumeItem);
1674 	message->AddInt32("device", -1);
1675 	menu->AddItem(new BMenuItem("All disks", message));
1676 	menu->AddSeparatorItem();
1677 	PopUpMenuSetTitle(menu, "All disks");
1678 
1679 	BVolumeRoster roster;
1680 	BVolume volume;
1681 	roster.Rewind();
1682 	while (roster.GetNextVolume(&volume) == B_OK) {
1683 		if (volume.IsPersistent() && volume.KnowsQuery()) {
1684 			BDirectory root;
1685 			if (volume.GetRootDirectory(&root) != B_OK)
1686 				continue;
1687 
1688 			BEntry entry;
1689 			root.GetEntry(&entry);
1690 
1691 			Model model(&entry, true);
1692 			if (model.InitCheck() != B_OK)
1693 				continue;
1694 
1695 			message = new BMessage(kVolumeItem);
1696 			message->AddInt32("device", volume.Device());
1697 			menu->AddItem(new ModelMenuItem(&model, model.Name(), message));
1698 		}
1699 	}
1700 
1701 	if (menu->ItemAt(0))
1702 		menu->ItemAt(0)->SetMarked(true);
1703 
1704 	menu->SetTargetForItems(this);
1705 }
1706 
1707 
1708 typedef std::pair<entry_ref, uint32> EntryWithDate;
1709 
1710 static int
1711 SortByDatePredicate(const EntryWithDate *entry1, const EntryWithDate *entry2)
1712 {
1713 	return entry1->second > entry2->second ?
1714 		-1 : (entry1->second == entry2->second ? 0 : 1);
1715 }
1716 
1717 struct AddOneRecentParams {
1718 	BMenu *menu;
1719 	const BMessenger *target;
1720 	uint32 what;
1721 };
1722 
1723 static const entry_ref *
1724 AddOneRecentItem(const entry_ref *ref, void *castToParams)
1725 {
1726 	AddOneRecentParams *params = (AddOneRecentParams *)castToParams;
1727 
1728 	BMessage *message = new BMessage(params->what);
1729 	message->AddRef("refs", ref);
1730 
1731 	char type[B_MIME_TYPE_LENGTH];
1732 	BNode node(ref);
1733 	BNodeInfo(&node).GetType(type);
1734 	BMenuItem *item = new IconMenuItem(ref->name, message, type, B_MINI_ICON);
1735 	item->SetTarget(*params->target);
1736 	params->menu->AddItem(item);
1737 
1738 	return NULL;
1739 }
1740 
1741 
1742 void
1743 FindPanel::AddRecentQueries(BMenu *menu, bool addSaveAsItem, const BMessenger *target,
1744 	uint32 what)
1745 {
1746 	BObjectList<entry_ref> templates(10, true);
1747 	BObjectList<EntryWithDate> recentQueries(10, true);
1748 
1749 	// find all the queries on all volumes
1750 	BVolumeRoster roster;
1751 	BVolume volume;
1752 	roster.Rewind();
1753 	while (roster.GetNextVolume(&volume) == B_OK) {
1754 		if (volume.IsPersistent() && volume.KnowsQuery() && volume.KnowsAttr()) {
1755 
1756 			BQuery query;
1757 			query.SetVolume(&volume);
1758 			query.SetPredicate("_trk/recentQuery == 1");
1759 			if (query.Fetch() != B_OK)
1760 				continue;
1761 
1762 			entry_ref ref;
1763 			while (query.GetNextRef(&ref) == B_OK) {
1764 				// ignore queries in the Trash
1765 				if (FSInTrashDir(&ref))
1766 					continue;
1767 
1768 				char type[B_MIME_TYPE_LENGTH];
1769 				BNode node(&ref);
1770 				BNodeInfo(&node).GetType(type);
1771 
1772 				if (strcasecmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0)
1773 					templates.AddItem(new entry_ref(ref));
1774 				else {
1775 					uint32 changeTime;
1776 					if (node.ReadAttr(kAttrQueryLastChange, B_INT32_TYPE, 0,
1777 						&changeTime, sizeof(uint32)) != sizeof(uint32))
1778 						continue;
1779 
1780 					recentQueries.AddItem(new EntryWithDate(ref, changeTime));
1781 				}
1782 
1783 			}
1784 		}
1785 	}
1786 
1787 	// we are only adding last ten queries
1788 	recentQueries.SortItems(SortByDatePredicate);
1789 
1790 	// but all templates
1791 	AddOneRecentParams params;
1792 	params.menu = menu;
1793 	params.target = target;
1794 	params.what = what;
1795 	templates.EachElement(AddOneRecentItem, &params);
1796 
1797 	int32 count = recentQueries.CountItems();
1798 	// show only up to 10 recent queries
1799 	if (count > 10)
1800 		count = 10;
1801 
1802 	if (templates.CountItems() && count)
1803 		menu->AddSeparatorItem();
1804 
1805 	for (int32 index = 0; index < count; index++)
1806 		AddOneRecentItem(&recentQueries.ItemAt(index)->first, &params);
1807 
1808 
1809 	if (addSaveAsItem) {
1810 		// add a Save as template item
1811 		if (count || templates.CountItems())
1812 			menu->AddSeparatorItem();
1813 
1814 		BMessage *message = new BMessage(kRunSaveAsTemplatePanel);
1815 		BMenuItem *item = new BMenuItem("Save Query as template"B_UTF8_ELLIPSIS, message);
1816 		menu->AddItem(item);
1817 	}
1818 }
1819 
1820 
1821 void
1822 FindPanel::AddOneAttributeItem(BBox *box, BRect rect)
1823 {
1824 	TAttrView *attrView = new TAttrView(rect, fAttrViewList.CountItems());
1825 	fAttrViewList.AddItem(attrView);
1826 
1827 	box->AddChild(attrView);
1828 	attrView->MakeTextViewFocus();
1829 }
1830 
1831 
1832 void
1833 FindPanel::SetUpAddRemoveButtons(BBox *box)
1834 {
1835 	BButton *button = Window() != NULL
1836 		? dynamic_cast<BButton *>(Window()->FindView("remove"))
1837 		: NULL;
1838 	if (button == NULL) {
1839 		BRect rect = box->Bounds();
1840 		rect.InsetBy(5, 10);
1841 		rect.top = rect.bottom - 20;
1842 		rect.right = rect.left + 40;
1843 
1844 		button = new BButton(rect, "add", "Add", new BMessage(kAddItem),
1845 			B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
1846 		button->SetTarget(this);
1847 		box->AddChild(button);
1848 
1849 		rect.OffsetBy(50, 0);
1850 		rect.right = rect.left + 55;
1851 		button = new BButton(rect, "remove", "Remove", new BMessage(kRemoveItem),
1852 			B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM);
1853 
1854 		button->SetEnabled(false);
1855 		button->SetTarget(this);
1856 		box->AddChild(button);
1857 	}
1858 	// enable remove button as needed
1859 	button->SetEnabled(fAttrViewList.CountItems() > 1);
1860 }
1861 
1862 
1863 void
1864 FindPanel::FillCurrentQueryName(BTextControl *queryName, FindWindow *window)
1865 {
1866 	ASSERT(window);
1867 	queryName->SetText(window->QueryName());
1868 }
1869 
1870 
1871 void
1872 FindPanel::AddAttrView()
1873 {
1874 	BBox *box = dynamic_cast<BBox *>(FindView("Box"));
1875 	BRect bounds(Bounds());
1876 
1877 	TAttrView *previous = fAttrViewList.LastItem();
1878 
1879 	if (previous)
1880 		Window()->ResizeBy(0, 30);
1881 
1882 	bounds = Bounds();
1883 	bounds.InsetBy(15, 30);
1884 	bounds.bottom -= 10 + (fLatch->Value() ? kAttrViewDelta : 0);
1885 
1886 	if (previous) {
1887 		box->ResizeTo(bounds.Width(), bounds.Height());
1888 		bounds = previous->Frame();
1889 		bounds.OffsetBy(0, 30);
1890 	} else {
1891 		bounds = box->Bounds();
1892 		bounds.InsetBy(5, 5);
1893 		bounds.bottom = bounds.top + 25;
1894 	}
1895 	AddOneAttributeItem(box, bounds);
1896 
1897 		// add logic to previous attrview
1898 	if (previous)
1899 		previous->AddLogicMenu();
1900 
1901 	SetUpAddRemoveButtons(box);
1902 
1903 	// populate mime popup
1904 	TAttrView *last = fAttrViewList.LastItem();
1905 	last->AddMimeTypeAttrs();
1906 }
1907 
1908 
1909 void
1910 FindPanel::RemoveAttrView()
1911 {
1912 	if (fAttrViewList.CountItems() < 2)
1913 		return;
1914 
1915 	BBox *box = dynamic_cast<BBox *>(FindView("Box"));
1916 	TAttrView *attrView = fAttrViewList.LastItem();
1917 	if (!box || !attrView)
1918 		return;
1919 
1920 	Window()->ResizeBy(0, -30);
1921 	BRect bounds(Bounds());
1922 	bounds.InsetBy(15, 30);
1923 	bounds.bottom -= 10 + (fLatch->Value() ? kAttrViewDelta : 0);
1924 	box->ResizeTo(bounds.Width(), bounds.Height());
1925 
1926 	fAttrViewList.RemoveItem(attrView);
1927 	attrView->RemoveSelf();
1928 	delete attrView;
1929 
1930 	attrView = fAttrViewList.LastItem();
1931 	attrView->RemoveLogicMenu();
1932 	attrView->MakeTextViewFocus();
1933 
1934 	if (fAttrViewList.CountItems() != 1)
1935 		return;
1936 
1937 	BButton *button = dynamic_cast<BButton *>(Window()->FindView("remove"));
1938 	if (button)
1939 		button->SetEnabled(false);
1940 }
1941 
1942 
1943 uint32
1944 FindPanel::InitialMode(const BNode *node)
1945 {
1946 	if (!node || node->InitCheck() != B_OK)
1947 		return kByNameItem;
1948 
1949 	uint32 result;
1950 	if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0,
1951 		(int32 *)&result, sizeof(int32)) <= 0)
1952 		return kByNameItem;
1953 
1954 	return result;
1955 }
1956 
1957 
1958 int32
1959 FindPanel::InitialAttrCount(const BNode *node)
1960 {
1961 	if (!node || node->InitCheck() != B_OK)
1962 		return 1;
1963 
1964 	int32 result;
1965 	if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0,
1966 		&result, sizeof(int32)) <= 0)
1967 		return 1;
1968 
1969 	return result;
1970 }
1971 
1972 
1973 static int32
1974 SelectItemWithLabel(BMenu *menu, const char *label)
1975 {
1976 	for (int32 index = menu->CountItems(); index-- > 0;)  {
1977 		BMenuItem *item = menu->ItemAt(index);
1978 
1979 		if (strcmp(label, item->Label()) == 0) {
1980 			item->SetMarked(true);
1981 			return index;
1982 		}
1983 	}
1984 	return -1;
1985 }
1986 
1987 
1988 void
1989 FindPanel::SaveWindowState(BNode *node, bool editTemplate)
1990 {
1991 	ASSERT(node->InitCheck() == B_OK);
1992 
1993 	BMenuItem *item = CurrentMimeType();
1994 	if (item) {
1995 		BString label(item->Label());
1996 		node->WriteAttrString(kAttrQueryInitialMime, &label);
1997 	}
1998 
1999 	uint32 mode = Mode();
2000 	node->WriteAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0,
2001 		(int32 *)&mode, sizeof(int32));
2002 
2003 	MoreOptionsStruct saveMoreOptions;
2004 	saveMoreOptions.showMoreOptions = fLatch->Value() != 0;
2005 
2006 	saveMoreOptions.searchTrash = fSearchTrashCheck->Value() != 0;
2007 	saveMoreOptions.temporary = fTemporaryCheck->Value() != 0;
2008 
2009 	if (node->WriteAttr(kAttrQueryMoreOptions, B_RAW_TYPE, 0, &saveMoreOptions,
2010 		sizeof(saveMoreOptions)) == sizeof(saveMoreOptions))
2011 		node->RemoveAttr(kAttrQueryMoreOptionsForeign);
2012 
2013 	if (editTemplate) {
2014 		if (UserSpecifiedName()) {
2015 			BString name(UserSpecifiedName());
2016 			node->WriteAttrString(kAttrQueryTemplateName, &name);
2017 		}
2018 	}
2019 
2020 	switch (Mode()) {
2021 		case kByAttributeItem:
2022 			{
2023 				BMessage message;
2024 				int32 count = fAttrViewList.CountItems();
2025 				node->WriteAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0,
2026 					&count, sizeof(int32));
2027 
2028 				for (int32 index = 0; index < count; index++)
2029 					fAttrViewList.ItemAt(index)->SaveState(&message, index);
2030 
2031 				ssize_t size = message.FlattenedSize();
2032 				char *buffer = new char[size];
2033 				status_t result = message.Flatten(buffer, size);
2034 				ASSERT(result == B_OK);
2035 				result = node->WriteAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0,
2036 					buffer, (size_t)size);
2037 				ASSERT(result == size);
2038 				delete [] buffer;
2039 			}
2040 			break;
2041 
2042 		case kByNameItem:
2043 		case kByFormulaItem:
2044 			{
2045 				BTextControl *textControl = dynamic_cast<BTextControl *>
2046 					(FindView("TextControl"));
2047 				ASSERT(textControl);
2048 				BString formula(textControl->TextView()->Text());
2049 				node->WriteAttrString(kAttrQueryInitialString, &formula);
2050 				break;
2051 			}
2052 	}
2053 }
2054 
2055 
2056 void
2057 FindPanel::SwitchToTemplate(const BNode *node)
2058 {
2059 	if (fLatch->Value()) {
2060 		// this is kind of a hack - the following code up to
2061 		// RestoreWindowState assumes the latch is closed
2062 		// Would be nicer if all the size of the window were set once
2063 		// and correctly - this is not easy thought because the latch
2064 		// controls the window size in relative increments
2065 		fLatch->SetValue(0);
2066 		fMoreOptionsPane->SetMode(0);
2067 	}
2068 
2069 	SwitchMode(InitialMode(node));
2070 		// update the menu to correspond to the mode
2071 	MarkNamedMenuItem(fSearchModeMenu, InitialMode(node), true);
2072 
2073  	BRect initialRect(InitialViewSize(node));
2074 	Window()->ResizeTo(initialRect.Width(), initialRect.Height());
2075 	if (Mode() == (int32)kByAttributeItem) {
2076 		RemoveByAttributeItems();
2077 		ResizeAttributeBox(node);
2078 		AddByAttributeItems(node);
2079 	}
2080 
2081 	RestoreWindowState(node);
2082 }
2083 
2084 
2085 void
2086 FindPanel::RestoreMimeTypeMenuSelection(const BNode *node)
2087 {
2088 	if (Mode() == (int32)kByFormulaItem || node == NULL || node->InitCheck() != B_OK)
2089 		return;
2090 
2091 	BString buffer;
2092 	if (node->ReadAttrString(kAttrQueryInitialMime, &buffer) == B_OK)
2093 		SetCurrentMimeType(buffer.String());
2094 }
2095 
2096 
2097 void
2098 FindPanel::RestoreWindowState(const BNode *node)
2099 {
2100 	fMode = InitialMode(node);
2101 	if (!node || node->InitCheck() != B_OK)
2102 		return;
2103 
2104 	ShowOrHideMimeTypeMenu();
2105 	RestoreMimeTypeMenuSelection(node);
2106 	MoreOptionsStruct saveMoreOptions;
2107 
2108 	bool storesMoreOptions = ReadAttr(node, kAttrQueryMoreOptions,
2109 		kAttrQueryMoreOptionsForeign, B_RAW_TYPE, 0, &saveMoreOptions,
2110 		sizeof(saveMoreOptions), &MoreOptionsStruct::EndianSwap)
2111 		!= kReadAttrFailed;
2112 
2113 	if (storesMoreOptions) {
2114 		// need to sanitize to true or false here, could have picked
2115 		// up garbage from attributes
2116 
2117 		saveMoreOptions.showMoreOptions =
2118 			(saveMoreOptions.showMoreOptions != 0);
2119 
2120 		fLatch->SetValue(saveMoreOptions.showMoreOptions);
2121 		fMoreOptionsPane->SetMode(saveMoreOptions.showMoreOptions);
2122 
2123 		fSearchTrashCheck->SetValue(saveMoreOptions.searchTrash);
2124 		fTemporaryCheck->SetValue(saveMoreOptions.temporary);
2125 
2126 		fQueryName->SetModificationMessage(NULL);
2127 		FillCurrentQueryName(fQueryName, dynamic_cast<FindWindow *>(Window()));
2128 
2129 		// set modification message after checking the temporary check box,
2130 		// and filling out the text control so that we do not
2131 		// always trigger clearing of the temporary check box.
2132 		fQueryName->SetModificationMessage(new BMessage(kNameModifiedMessage));
2133 	}
2134 
2135 	// get volumes to perform query on
2136 	bool searchAllVolumes = true;
2137 
2138 	attr_info info;
2139 	if (node->GetAttrInfo(kAttrQueryVolume, &info) == B_OK) {
2140 		char *buffer = new char[info.size];
2141 		if (node->ReadAttr(kAttrQueryVolume, B_MESSAGE_TYPE, 0, buffer, (size_t)info.size)
2142 			== info.size) {
2143 			BMessage message;
2144 			if (message.Unflatten(buffer) == B_OK) {
2145 				for (int32 index = 0; ;index++) {
2146 					ASSERT(index < 100);
2147 					BVolume volume;
2148 						// match a volume with the info embedded in the message
2149 					status_t result = MatchArchivedVolume(&volume, &message, index);
2150 					if (result == B_OK) {
2151 						char name[256];
2152 						volume.GetName(name);
2153 						SelectItemWithLabel(fVolMenu, name);
2154 						searchAllVolumes = false;
2155 					} else if (result != B_DEV_BAD_DRIVE_NUM)
2156 						// if B_DEV_BAD_DRIVE_NUM, the volume just isn't mounted this
2157 						// time around, keep looking for more
2158 						// if other error, bail
2159 						break;
2160 				}
2161 			}
2162 		}
2163 		delete [] buffer;
2164 	}
2165 	// mark or unmark "All disks"
2166 	fVolMenu->ItemAt(0)->SetMarked(searchAllVolumes);
2167 	ShowVolumeMenuLabel();
2168 
2169 	switch (Mode()) {
2170 		case kByAttributeItem:
2171 			{
2172 				int32 count = InitialAttrCount(node);
2173 
2174 				attr_info info;
2175 				if (node->GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK)
2176 					break;
2177 				char *buffer = new char[info.size];
2178 				if (node->ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, buffer, (size_t)info.size)
2179 					== info.size) {
2180 					BMessage message;
2181 					if (message.Unflatten(buffer) == B_OK)
2182 						for (int32 index = 0; index < count; index++)
2183 							fAttrViewList.ItemAt(index)->RestoreState(message, index);
2184 				}
2185 				delete [] buffer;
2186 				break;
2187 			}
2188 
2189 		case kByNameItem:
2190 		case kByFormulaItem:
2191 			{
2192 				BString buffer;
2193 				if (node->ReadAttrString(kAttrQueryInitialString, &buffer) == B_OK) {
2194 					BTextControl *textControl = dynamic_cast<BTextControl *>
2195 						(FindView("TextControl"));
2196 					ASSERT(textControl);
2197 
2198 					textControl->TextView()->SetText(buffer.String());
2199 				}
2200 			}
2201 			break;
2202 	}
2203 
2204 	// try to restore focus and possibly text selection
2205 	BString focusedView;
2206 	if (node->ReadAttrString("_trk/focusedView", &focusedView) == B_OK) {
2207 		BView *view = FindView(focusedView.String());
2208 		if (view) {
2209 			view->MakeFocus();
2210 			BTextControl *textControl = dynamic_cast<BTextControl *>(view);
2211 			if (textControl) {
2212 				int32 selStart = 0, selEnd = LONG_MAX;
2213 				node->ReadAttr("_trk/focusedSelStart", B_INT32_TYPE, 0,
2214 					&selStart, sizeof(selStart));
2215 				node->ReadAttr("_trk/focusedSelEnd", B_INT32_TYPE, 0,
2216 					&selEnd, sizeof(selEnd));
2217 				textControl->TextView()->Select(selStart, selEnd);
2218 			}
2219 		}
2220 	}
2221 }
2222 
2223 
2224 void
2225 FindPanel::ResizeAttributeBox(const BNode *node)
2226 {
2227 	BBox *box = dynamic_cast<BBox *>(FindView("Box"));
2228 	BRect bounds(box->Bounds());
2229 	int32 count = InitialAttrCount(node);
2230 
2231 	bounds.bottom = count * 30 + 40;
2232 	box->ResizeTo(bounds.Width(), bounds.Height());
2233 }
2234 
2235 
2236 void
2237 FindPanel::AddByAttributeItems(const BNode *node)
2238 {
2239 	BBox *box = dynamic_cast<BBox *>(FindView("Box"));
2240 	ASSERT(box);
2241 	BRect bounds(box->Bounds());
2242 
2243 	int32 numAttributes = InitialAttrCount(node);
2244 	if (numAttributes < 1)
2245 		numAttributes = 1;
2246 
2247 	BRect rect(bounds);
2248 	rect.InsetBy(5, 5);
2249 	rect.bottom = rect.top + 25;
2250 
2251 	for (int32 index = 0; index < numAttributes; index ++) {
2252 		AddOneAttributeItem(box, rect);
2253 		rect.OffsetBy(0, 30);
2254 	}
2255 	SetUpAddRemoveButtons(box);
2256 }
2257 
2258 
2259 void
2260 FindPanel::AddByNameOrFormulaItems()
2261 {
2262 	BBox *box = dynamic_cast<BBox *>(FindView("Box"));
2263 
2264 	BRect bounds(box->Bounds());
2265 	bounds.InsetBy(10, 10);
2266 	BTextControl *textControl = new BTextControl(bounds, "TextControl", "", "", NULL);
2267 	textControl->SetDivider(0.0f);
2268 	box->AddChild(textControl);
2269 	textControl->MakeFocus();
2270 }
2271 
2272 
2273 void
2274 FindPanel::RemoveAttrViewItems()
2275 {
2276 	for (;;) {
2277 		BView *view = FindView("AttrView");
2278 		if (view == NULL)
2279 			break;
2280 		view->RemoveSelf();
2281 		delete view;
2282 	}
2283 
2284 	fAttrViewList.MakeEmpty();
2285 }
2286 
2287 
2288 void
2289 FindPanel::RemoveByAttributeItems()
2290 {
2291 	RemoveAttrViewItems();
2292 	BView *view = FindView("add");
2293 	if (view) {
2294 		view->RemoveSelf();
2295 		delete view;
2296 	}
2297 
2298 	view = FindView("remove");
2299 	if (view) {
2300 		view->RemoveSelf();
2301 		delete view;
2302 	}
2303 
2304 	view = dynamic_cast<BTextControl *>(FindView("TextControl"));
2305 	if (view) {
2306 		view->RemoveSelf();
2307 		delete view;
2308 	}
2309 }
2310 
2311 
2312 void
2313 FindPanel::ShowOrHideMimeTypeMenu()
2314 {
2315 	BMenuField *menuField = dynamic_cast<BMenuField *>(FindView("MimeTypeMenu"));
2316 	if (Mode() == (int32)kByFormulaItem && !menuField->IsHidden())
2317 		menuField->Hide();
2318 	else if (menuField->IsHidden())
2319 		menuField->Show();
2320 }
2321 
2322 
2323 // #pragma mark -
2324 
2325 
2326 TAttrView::TAttrView(BRect frame, int32 index)
2327 	:	BView(frame, "AttrView", B_FOLLOW_NONE, B_WILL_DRAW)
2328 {
2329 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
2330 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
2331 
2332 	BPopUpMenu *menu = new BPopUpMenu("PopUp");
2333 
2334 	// add NAME attribute to popup
2335 	BMenu *submenu = new BMenu("Name");
2336 	submenu->SetRadioMode(true);
2337 	submenu->SetFont(be_plain_font);
2338 	BMessage *message = new BMessage(kAttributeItemMain);
2339 	message->AddString("name", "name");
2340 	message->AddInt32("type", B_STRING_TYPE);
2341 	BMenuItem *item = new BMenuItem(submenu, message);
2342 	menu->AddItem(item);
2343 
2344 	const int32 operators[] = {B_CONTAINS, B_EQ, B_NE, B_BEGINS_WITH, B_ENDS_WITH};
2345 	const char *operatorLabels[] = {"contains", "is", "is not", "starts with", "ends with"};
2346 
2347 	for (int32 i = 0;i < 5;i++) {
2348 		message = new BMessage(kAttributeItem);
2349 		message->AddInt32("operator", operators[i]);
2350 		submenu->AddItem(new BMenuItem(operatorLabels[i], message));
2351 	}
2352 
2353 	// mark first item
2354 	menu->ItemAt(0)->SetMarked(true);
2355 	submenu->ItemAt(0)->SetMarked(true);
2356 
2357 	// add SIZE attribute
2358 	submenu = new BMenu("Size");
2359 	submenu->SetRadioMode(true);
2360 	submenu->SetFont(be_plain_font);
2361 	message = new BMessage(kAttributeItemMain);
2362 	message->AddString("name", "size");
2363 	message->AddInt32("type", B_OFF_T_TYPE);
2364 	item = new BMenuItem(submenu, message);
2365 	menu->AddItem(item);
2366 
2367 	message = new BMessage(kAttributeItem);
2368 	message->AddInt32("operator", B_GE);
2369 	submenu->AddItem(new BMenuItem("greater than", message));
2370 
2371 	message = new BMessage(kAttributeItem);
2372 	message->AddInt32("operator", B_LE);
2373 	submenu->AddItem(new BMenuItem("less than", message));
2374 
2375 	message = new BMessage(kAttributeItem);
2376 	message->AddInt32("operator", B_EQ);
2377 	submenu->AddItem(new BMenuItem("is", message));
2378 
2379 	// add "modified" field
2380 	submenu = new BMenu("Modified");
2381 	submenu->SetRadioMode(true);
2382 	submenu->SetFont(be_plain_font);
2383 	message = new BMessage(kAttributeItemMain);
2384 	message->AddString("name", "last_modified");
2385 	message->AddInt32("type", B_TIME_TYPE);
2386 	item = new BMenuItem(submenu, message);
2387 	menu->AddItem(item);
2388 
2389 	message = new BMessage(kAttributeItem);
2390 	message->AddInt32("operator", B_LE);
2391 	submenu->AddItem(new BMenuItem("before", message));
2392 
2393 	message = new BMessage(kAttributeItem);
2394 	message->AddInt32("operator", B_GE);
2395 	submenu->AddItem(new BMenuItem("after", message));
2396 
2397 	BRect bounds(Bounds());
2398 	bounds.right = bounds.left + 100;
2399 	bounds.bottom = bounds.top + 15;
2400 	fMenuField = new BMenuField(bounds, "MenuField", "", menu);
2401 	fMenuField->SetDivider(0.0f);
2402 
2403 	// add text entry box
2404 	bounds = Bounds();
2405 	bounds.left += bounds.right - 180;
2406 	bounds.top += 2;
2407 	bounds.right -= 42;
2408 	BString title("TextEntry");
2409 	title << index;
2410 	fTextControl = new BTextControl(bounds, title.String(), "", "", NULL);
2411 	fTextControl->SetDivider(0.0f);
2412 	AddChild(fTextControl);
2413 
2414 	AddChild(fMenuField);
2415 	// add attributes from currently selected mimetype
2416 }
2417 
2418 
2419 TAttrView::~TAttrView()
2420 {
2421 }
2422 
2423 
2424 void
2425 TAttrView::AttachedToWindow()
2426 {
2427 	BMenu *menu = fMenuField->Menu();
2428 	// target everything
2429 	menu->SetTargetForItems(this);
2430 
2431 	for (int32 index = menu->CountItems() - 1; index >= 0; index--)
2432 		menu->SubmenuAt(index)->SetTargetForItems(this);
2433 }
2434 
2435 
2436 void
2437 TAttrView::MakeTextViewFocus()
2438 {
2439 	fTextControl->MakeFocus();
2440 }
2441 
2442 
2443 void
2444 TAttrView::RestoreState(const BMessage &message, int32 index)
2445 {
2446 	BMenu *menu = fMenuField->Menu();
2447 	// decode menu selections
2448 
2449 	AddMimeTypeAttrs(menu);
2450 
2451 	const char *label;
2452 	if (message.FindString("menuSelection", index, &label) == B_OK) {
2453 		int32 itemIndex = SelectItemWithLabel(menu, label);
2454 		if (itemIndex >=0) {
2455 			menu = menu->SubmenuAt(itemIndex);
2456 			if (menu && message.FindString("subMenuSelection", index, &label)
2457 					== B_OK)
2458 				SelectItemWithLabel(menu, label);
2459 		}
2460 	}
2461 
2462 	// decode attribute text
2463 	ASSERT(fTextControl);
2464 	const char *string;
2465 	if (message.FindString("attrViewText", index, &string) == B_OK)
2466 		fTextControl->TextView()->SetText(string);
2467 
2468 	int32 logicMenuSelectedIndex;
2469 	BMenuField *field = dynamic_cast<BMenuField *>(FindView("Logic"));
2470 	if (message.FindInt32("logicalRelation", index, &logicMenuSelectedIndex) == B_OK)
2471 		if (field)
2472 			field->Menu()->ItemAt(logicMenuSelectedIndex)->SetMarked(true);
2473 		else
2474 			AddLogicMenu(logicMenuSelectedIndex == 0);
2475 }
2476 
2477 
2478 void
2479 TAttrView::SaveState(BMessage *message, int32)
2480 {
2481 	BMenu *menu = fMenuField->Menu();
2482 
2483 	// encode main attribute menu selection
2484 	BMenuItem *item = menu->FindMarked();
2485 	message->AddString("menuSelection", item ? item->Label() : "");
2486 
2487 	// encode submenu selection
2488 	const char *label = "";
2489 	if (item) {
2490 		BMenu *submenu = menu->SubmenuAt(menu->IndexOf(item));
2491 		if (submenu) {
2492 			item = submenu->FindMarked();
2493 			if (item)
2494 				label = item->Label();
2495 		}
2496 	}
2497 	message->AddString("subMenuSelection", label);
2498 
2499 	// encode attribute text
2500 	ASSERT(fTextControl);
2501 	message->AddString("attrViewText", fTextControl->TextView()->Text());
2502 
2503 	BMenuField *field = dynamic_cast<BMenuField *>(FindView("Logic"));
2504 	if (field) {
2505 		BMenuItem *item = field->Menu()->FindMarked();
2506 		ASSERT(item);
2507 		message->AddInt32("logicalRelation", item ? field->Menu()->IndexOf(item) : 0);
2508 	}
2509 }
2510 
2511 void
2512 TAttrView::AddLogicMenu(bool selectAnd)
2513 {
2514 	// add "AND/OR" menu
2515 	BPopUpMenu *menu = new BPopUpMenu("");
2516 	BMessage *message = new BMessage();
2517 	message->AddInt32("combine", B_AND);
2518 	BMenuItem *item = new BMenuItem("And", message);
2519 	menu->AddItem(item);
2520 	if (selectAnd)
2521 		item->SetMarked(true);
2522 
2523 	message = new BMessage();
2524 	message->AddInt32("combine", B_OR);
2525 	item = new BMenuItem("Or", message);
2526 	menu->AddItem(item);
2527 	if (!selectAnd)
2528 		item->SetMarked(true);
2529 
2530 	menu->SetTargetForItems(this);
2531 
2532 	BRect bounds(Bounds());
2533 	bounds.left = bounds.right - 40;
2534 	bounds.bottom = bounds.top + 15;
2535 	BMenuField *menufield = new BMenuField(bounds, "Logic", "", menu);
2536 	menufield->SetDivider(0.0f);
2537 	menufield->HidePopUpMarker();
2538 	AddChild(menufield);
2539 }
2540 
2541 
2542 void
2543 TAttrView::RemoveLogicMenu()
2544 {
2545 	BMenuField *menufield = dynamic_cast<BMenuField *>(FindView("Logic"));
2546 	if (menufield) {
2547 		menufield->RemoveSelf();
2548 		delete menufield;
2549 	}
2550 }
2551 
2552 
2553 void
2554 TAttrView::Draw(BRect)
2555 {
2556 	BMenuItem *item = fMenuField->Menu()->FindMarked();
2557 	if (!item)
2558 		return;
2559 
2560 	if (item->Submenu()->FindMarked()) {
2561 		float width = StringWidth(item->Submenu()->FindMarked()->Label());
2562 		BRect bounds(fTextControl->Frame());
2563 
2564 		// draws the is/contains, etc. string
2565 		bounds.left -= (width + 10);
2566 		bounds.bottom -= 6;
2567 		MovePenTo(bounds.LeftBottom());
2568 		DrawString(item->Submenu()->FindMarked()->Label());
2569 	}
2570 }
2571 
2572 
2573 void
2574 TAttrView::MessageReceived(BMessage *message)
2575 {
2576 	BMenuItem *item;
2577 
2578 	switch (message->what) {
2579 		case kAttributeItem:
2580 			if (message->FindPointer("source", (void **)&item) != B_OK)
2581 				return;
2582 
2583 			item->Menu()->Superitem()->SetMarked(true);
2584 			Invalidate();
2585 			break;
2586 
2587 		case kAttributeItemMain:
2588 			// in case someone selected just and attribute without the
2589 			// comparator
2590 			if (message->FindPointer("source", (void **)&item) != B_OK)
2591 				return;
2592 
2593 			if (item->Submenu()->ItemAt(0))
2594 				item->Submenu()->ItemAt(0)->SetMarked(true);
2595 			Invalidate();
2596 			break;
2597 
2598 		default:
2599 			_inherited::MessageReceived(message);
2600 			break;
2601 	}
2602 }
2603 
2604 
2605 void
2606 TAttrView::AddMimeTypeAttrs()
2607 {
2608 	BMenu *menu = fMenuField->Menu();
2609 	AddMimeTypeAttrs(menu);
2610 }
2611 
2612 
2613 void
2614 TAttrView::AddAttributes(BMenu *menu, const BMimeType &mimeType)
2615 {
2616 	// only add things to menu which have "user-visible" data
2617 	BMessage attributeMessage;
2618 	if (mimeType.GetAttrInfo(&attributeMessage) != B_OK)
2619 		return;
2620 
2621 	char desc[B_MIME_TYPE_LENGTH];
2622 	mimeType.GetShortDescription(desc);
2623 
2624 	// go through each field in meta mime and add it to a menu
2625 	for (int32 index = 0; ; index++) {
2626 		const char *publicName;
2627 		if (attributeMessage.FindString("attr:public_name", index, &publicName) != B_OK)
2628 			break;
2629 
2630 		if (!attributeMessage.FindBool("attr:viewable"))
2631 			continue;
2632 
2633 		const char *attributeName;
2634 		if (attributeMessage.FindString("attr:name", index, &attributeName) != B_OK)
2635 			continue;
2636 
2637 		int32 type;
2638 		if (attributeMessage.FindInt32("attr:type", index, &type) != B_OK)
2639 			continue;
2640 
2641 		BMenu *submenu = new BMenu(publicName);
2642 		submenu->SetRadioMode(true);
2643 		submenu->SetFont(be_plain_font);
2644 		BMessage *message = new BMessage(kAttributeItemMain);
2645 		message->AddString("name", attributeName);
2646 		message->AddInt32("type", type);
2647 		BMenuItem *item = new BMenuItem(submenu, message);
2648 		menu->AddItem(item);
2649 		menu->SetTargetForItems(this);
2650 
2651 		switch (type) {
2652 			case B_STRING_TYPE:
2653 				message = new BMessage(kAttributeItem);
2654 				message->AddInt32("operator", B_CONTAINS);
2655 				submenu->AddItem(new BMenuItem("contains", message));
2656 
2657 				message = new BMessage(kAttributeItem);
2658 				message->AddInt32("operator", B_EQ);
2659 				submenu->AddItem(new BMenuItem("is", message));
2660 
2661 				message = new BMessage(kAttributeItem);
2662 				message->AddInt32("operator", B_NE);
2663 				submenu->AddItem(new BMenuItem("is not", message));
2664 				submenu->SetTargetForItems(this);
2665 
2666 				message = new BMessage(kAttributeItem);
2667 				message->AddInt32("operator", B_BEGINS_WITH);
2668 				submenu->AddItem(new BMenuItem("starts with", message));
2669 				submenu->SetTargetForItems(this);
2670 
2671 				message = new BMessage(kAttributeItem);
2672 				message->AddInt32("operator", B_ENDS_WITH);
2673 				submenu->AddItem(new BMenuItem("ends with", message));
2674 				break;
2675 
2676 			case B_BOOL_TYPE:
2677 			case B_INT16_TYPE:
2678 			case B_UINT8_TYPE:
2679 			case B_INT8_TYPE:
2680 			case B_UINT16_TYPE:
2681 			case B_INT32_TYPE:
2682 			case B_UINT32_TYPE:
2683 			case B_INT64_TYPE:
2684 			case B_UINT64_TYPE:
2685 			case B_OFF_T_TYPE:
2686 			case B_FLOAT_TYPE:
2687 			case B_DOUBLE_TYPE:
2688 				message = new BMessage(kAttributeItem);
2689 				message->AddInt32("operator", B_EQ);
2690 				submenu->AddItem(new BMenuItem("is", message));
2691 
2692 				message = new BMessage(kAttributeItem);
2693 				message->AddInt32("operator", B_GE);
2694 				submenu->AddItem(new BMenuItem("greater than", message));
2695 
2696 				message = new BMessage(kAttributeItem);
2697 				message->AddInt32("operator", B_LE);
2698 				submenu->AddItem(new BMenuItem("less than", message));
2699 				break;
2700 
2701 			case B_TIME_TYPE:
2702 				message = new BMessage(kAttributeItem);
2703 				message->AddInt32("operator", B_LE);
2704 				submenu->AddItem(new BMenuItem("before", message));
2705 
2706 				message = new BMessage(kAttributeItem);
2707 				message->AddInt32("operator", B_GE);
2708 				submenu->AddItem(new BMenuItem("after", message));
2709 				break;
2710 		}
2711 		submenu->SetTargetForItems(this);
2712 	}
2713 }
2714 
2715 
2716 void
2717 TAttrView::AddMimeTypeAttrs(BMenu *menu)
2718 {
2719 	FindPanel *mainView = dynamic_cast<FindPanel *>(Parent()->
2720 		Parent()->FindView("MainView"));
2721 	if (!mainView)
2722 		return;
2723 
2724 	const char *typeName;
2725 	if (mainView->CurrentMimeType(&typeName) == NULL)
2726 		return;
2727 
2728 	BMimeType mimeType(typeName);
2729 	if (!mimeType.IsInstalled())
2730 		return;
2731 
2732 	if (!mimeType.IsSupertypeOnly()) {
2733 		// add supertype attributes
2734 		BMimeType supertype;
2735 		mimeType.GetSupertype(&supertype);
2736 		AddAttributes(menu, supertype);
2737 	}
2738 
2739 	AddAttributes(menu, mimeType);
2740 }
2741 
2742 
2743 void
2744 TAttrView::GetDefaultName(BString &result) const
2745 {
2746 	BMenuItem *item = NULL;
2747 	if (fMenuField->Menu() != NULL)
2748 		item = fMenuField->Menu()->FindMarked();
2749 	if (item != NULL)
2750 		result << item->Label();
2751 	else
2752 		result << "Name";
2753 
2754 	if (item->Submenu() != NULL)
2755 		item = item->Submenu()->FindMarked();
2756 	else
2757 		item = NULL;
2758 
2759 	if (item != NULL)
2760 		result << " " << item->Label() << " ";
2761 	else
2762 		result << " = ";
2763 
2764 	result << fTextControl->Text();
2765 }
2766 
2767 
2768 // #pragma mark -
2769 
2770 
2771 DeleteTransientQueriesTask::DeleteTransientQueriesTask()
2772 	:	state(kInitial),
2773 		fWalker(NULL)
2774 {
2775 }
2776 
2777 
2778 DeleteTransientQueriesTask::~DeleteTransientQueriesTask()
2779 {
2780 	delete fWalker;
2781 }
2782 
2783 
2784 bool
2785 DeleteTransientQueriesTask::DoSomeWork()
2786 {
2787 	switch (state) {
2788 		case kInitial:
2789 			Initialize();
2790 			break;
2791 
2792 		case kAllocatedWalker:
2793 		case kTraversing:
2794 			if (GetSome()) {
2795 				PRINT(("transient query killer done\n"));
2796 				return true;
2797 			}
2798 			break;
2799 
2800 		case kError:
2801 			return true;
2802 
2803 	}
2804 	return false;
2805 }
2806 
2807 
2808 void
2809 DeleteTransientQueriesTask::Initialize()
2810 {
2811 	PRINT(("starting up transient query killer\n"));
2812 	BPath path;
2813 	status_t result = find_directory(B_USER_DIRECTORY, &path, false);
2814 	if (result != B_OK) {
2815 		state = kError;
2816 		return;
2817 	}
2818 	fWalker = new WALKER_NS::TNodeWalker(path.Path());
2819 	state = kAllocatedWalker;
2820 }
2821 
2822 
2823 const int32 kBatchCount = 100;
2824 
2825 bool
2826 DeleteTransientQueriesTask::GetSome()
2827 {
2828 	state = kTraversing;
2829 	for (int32 count = kBatchCount; count > 0; count--) {
2830 		entry_ref ref;
2831 		if (fWalker->GetNextRef(&ref) != B_OK) {
2832 			state = kError;
2833 			return true;
2834 		}
2835 		Model model(&ref);
2836 		if (model.IsQuery())
2837 			ProcessOneRef(&model);
2838 #if xDEBUG
2839 		else
2840 			PRINT(("transient query killer: %s not a query\n", model.Name()));
2841 #endif
2842 	}
2843 	return false;
2844 }
2845 
2846 
2847 const int32 kDaysToExpire = 7;
2848 
2849 static bool
2850 QueryOldEnough(Model *model)
2851 {
2852 	// check if it is old and ready to be deleted
2853 	time_t now = time(0);
2854 
2855 	tm nowTimeData;
2856 	tm fileModData;
2857 
2858 	localtime_r(&now, &nowTimeData);
2859 	localtime_r(&model->StatBuf()->st_ctime, &fileModData);
2860 
2861 	if ((nowTimeData.tm_mday - fileModData.tm_mday) < kDaysToExpire
2862 		&& (nowTimeData.tm_mday - fileModData.tm_mday) > -kDaysToExpire) {
2863 		PRINT(("query %s, not old enough\n", model->Name()));
2864 		return false;
2865 	}
2866 	return true;
2867 }
2868 
2869 
2870 bool
2871 DeleteTransientQueriesTask::ProcessOneRef(Model *model)
2872 {
2873 	BModelOpener opener(model);
2874 
2875 	// is this a temporary query
2876 	if (!MoreOptionsStruct::QueryTemporary(model->Node())) {
2877 		PRINT(("query %s, not temporary\n", model->Name()));
2878 		return false;
2879 	}
2880 
2881 	if (!QueryOldEnough(model))
2882 		return false;
2883 
2884 	ASSERT(dynamic_cast<TTracker *>(be_app));
2885 
2886 	// check that it is not showing
2887 	if (dynamic_cast<TTracker *>(be_app)->EntryHasWindowOpen(model->EntryRef())) {
2888 		PRINT(("query %s, showing, can't delete\n", model->Name()));
2889 		return false;
2890 	}
2891 
2892 	PRINT(("query %s, old, temporary, not shownig - deleting\n", model->Name()));
2893 	BEntry entry(model->EntryRef());
2894 	entry.Remove();
2895 
2896 	return true;
2897 }
2898 
2899 
2900 class DeleteTransientQueriesFunctor : public FunctionObjectWithResult<bool> {
2901 public:
2902 	DeleteTransientQueriesFunctor(DeleteTransientQueriesTask *task)
2903 		:	task(task)
2904 		{}
2905 
2906 	virtual ~DeleteTransientQueriesFunctor()
2907 		{
2908 			delete task;
2909 		}
2910 
2911 	virtual void operator()()
2912 		{ result = task->DoSomeWork(); }
2913 
2914 private:
2915 	DeleteTransientQueriesTask *task;
2916 };
2917 
2918 
2919 void
2920 DeleteTransientQueriesTask::StartUpTransientQueryCleaner()
2921 {
2922 	// set up a task that wakes up when the machine is idle and starts
2923 	// killing off old transient queries
2924 	DeleteTransientQueriesFunctor *worker
2925 		= new DeleteTransientQueriesFunctor(new DeleteTransientQueriesTask());
2926 	TTracker *tracker = dynamic_cast<TTracker *>(be_app);
2927 	ASSERT(tracker);
2928 	tracker->MainTaskLoop()->RunWhenIdle(worker,
2929 		30 * 60 * 1000000,	// half an hour initial delay
2930 		5 * 60 * 1000000,	// idle for five minutes
2931 		10 * 1000000);
2932 }
2933 
2934 
2935 //	#pragma mark -
2936 
2937 
2938 RecentFindItemsMenu::RecentFindItemsMenu(const char *title, const BMessenger *target,
2939 	uint32 what)
2940 	:	BMenu(title, B_ITEMS_IN_COLUMN),
2941 		fTarget(*target),
2942 		fWhat(what)
2943 {
2944 }
2945 
2946 
2947 void
2948 RecentFindItemsMenu::AttachedToWindow()
2949 {
2950 	// re-populate the menu with fresh items
2951 	for (int32 index = CountItems() - 1; index >= 0; index--)
2952 		delete RemoveItem(index);
2953 
2954 	FindPanel::AddRecentQueries(this, false, &fTarget, fWhat);
2955 	BMenu::AttachedToWindow();
2956 }
2957 
2958 
2959 #if !B_BEOS_VERSION_DANO
2960 _IMPEXP_TRACKER
2961 #endif
2962 BMenu *
2963 TrackerBuildRecentFindItemsMenu(const char *title)
2964 {
2965 	BMessenger tracker(kTrackerSignature);
2966 	return new RecentFindItemsMenu(title, &tracker, B_REFS_RECEIVED);
2967 }
2968 
2969 
2970 //	#pragma mark -
2971 
2972 
2973 DraggableQueryIcon::DraggableQueryIcon(BRect frame, const char *name,
2974 	const BMessage *message, BMessenger messenger, uint32 resizeFlags, uint32 flags)
2975 	:	DraggableIcon(frame, name, B_QUERY_MIMETYPE, B_LARGE_ICON,
2976 			message, messenger, resizeFlags, flags)
2977 {
2978 }
2979 
2980 
2981 bool
2982 DraggableQueryIcon::DragStarted(BMessage *dragMessage)
2983 {
2984 	// override to substitute the user-specified query name
2985 	dragMessage->RemoveData("be:clip_name");
2986 
2987 	FindWindow *window = dynamic_cast<FindWindow *>(Window());
2988 	ASSERT(window);
2989 	dragMessage->AddString("be:clip_name",
2990 		window->BackgroundView()->UserSpecifiedName() ?
2991 			window->BackgroundView()->UserSpecifiedName() : "New Query");
2992 
2993 	return true;
2994 }
2995 
2996 
2997 //	#pragma mark -
2998 
2999 
3000 MostUsedNames::MostUsedNames(const char *fileName, const char *directory, int32 maxCount)
3001 	:
3002 	fFileName(fileName),
3003 	fDirectory(directory),
3004 	fLoaded(false),
3005 	fCount(maxCount)
3006 {
3007 }
3008 
3009 
3010 MostUsedNames::~MostUsedNames()
3011 {
3012 	// only write back settings when we've been used
3013 	if (!fLoaded)
3014 		return;
3015 
3016 	// write most used list to file
3017 
3018 	BPath path;
3019 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
3020 		return;
3021 
3022 	path.Append(fDirectory);
3023 	path.Append(fFileName);
3024 	BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
3025 	if (file.InitCheck() == B_OK) {
3026 		for (int32 i = 0; i < fList.CountItems(); i++) {
3027 			list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i));
3028 
3029 			char line[B_FILE_NAME_LENGTH + 5];
3030 
3031 			// limit upper bound to react more dynamically to changes
3032 			if (--entry->count > 20)
3033 				entry->count = 20;
3034 
3035 			// if the item hasn't been chosen in a while, remove it
3036 			// (but leave at least one item in the list)
3037 			if (entry->count < -10 && i > 0)
3038 				continue;
3039 
3040 			sprintf(line, "%ld %s\n", entry->count, entry->name);
3041 			if (file.Write(line, strlen(line)) < B_OK)
3042 				break;
3043 		}
3044 	}
3045 	file.Unset();
3046 
3047 	// free data
3048 
3049 	for (int32 i = fList.CountItems(); i-- > 0;) {
3050 		list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i));
3051 		free(entry->name);
3052 		delete entry;
3053 	}
3054 }
3055 
3056 
3057 bool
3058 MostUsedNames::ObtainList(BList *list)
3059 {
3060 	if (!list)
3061 		return false;
3062 
3063 	if (!fLoaded)
3064 		UpdateList();
3065 
3066 	fLock.Lock();
3067 
3068 	list->MakeEmpty();
3069 	for (int32 i = 0; i < fCount; i++) {
3070 		list_entry *entry = static_cast<list_entry *>(fList.ItemAt(i));
3071 		if (entry == NULL)
3072 			return true;
3073 
3074 		list->AddItem(entry->name);
3075 	}
3076 	return true;
3077 }
3078 
3079 
3080 void
3081 MostUsedNames::ReleaseList()
3082 {
3083 	fLock.Unlock();
3084 }
3085 
3086 
3087 void
3088 MostUsedNames::AddName(const char *name)
3089 {
3090 	fLock.Lock();
3091 
3092 	if (!fLoaded)
3093 		LoadList();
3094 
3095 	// remove last entry if there are more than
3096 	// 2*fCount entries in the list
3097 
3098 	list_entry *entry = NULL;
3099 
3100 	if (fList.CountItems() > fCount * 2) {
3101 		entry = static_cast<list_entry *>(fList.RemoveItem(fList.CountItems() - 1));
3102 
3103 		// is this the name we want to add here?
3104 		if (strcmp(name, entry->name)) {
3105 			free(entry->name);
3106 			delete entry;
3107 			entry = NULL;
3108 		} else
3109 			fList.AddItem(entry);
3110 	}
3111 
3112 	if (entry == NULL) {
3113 		for (int32 i = 0; (entry = static_cast<list_entry *>(fList.ItemAt(i))) != NULL; i++)
3114 			if (!strcmp(entry->name, name))
3115 				break;
3116 	}
3117 
3118 	if (entry == NULL) {
3119 		entry = new list_entry;
3120 		entry->name = strdup(name);
3121 		entry->count = 1;
3122 
3123 		fList.AddItem(entry);
3124 	} else if (entry->count < 0)
3125 		entry->count = 1;
3126 	else
3127 		entry->count++;
3128 
3129 	fLock.Unlock();
3130 	UpdateList();
3131 }
3132 
3133 
3134 int
3135 MostUsedNames::CompareNames(const void *a,const void *b)
3136 {
3137 	list_entry *entryA = *(list_entry **)a;
3138 	list_entry *entryB = *(list_entry **)b;
3139 
3140 	if (entryA->count == entryB->count)
3141 		return strcasecmp(entryA->name,entryB->name);
3142 
3143 	return entryB->count - entryA->count;
3144 }
3145 
3146 
3147 void
3148 MostUsedNames::LoadList()
3149 {
3150 	if (fLoaded)
3151 		return;
3152 	fLoaded = true;
3153 
3154 	// load the most used names list
3155 
3156 	BPath path;
3157 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK)
3158 		return;
3159 
3160 	path.Append(fDirectory);
3161 	path.Append(fFileName);
3162 
3163 	FILE *file = fopen(path.Path(), "r");
3164 	if (file == NULL)
3165 		return;
3166 
3167 	char line[B_FILE_NAME_LENGTH + 5];
3168 	while (fgets(line, sizeof(line), file) != NULL) {
3169 		int32 length = (int32)strlen(line) - 1;
3170 		if (length >= 0 && line[length] == '\n')
3171 			line[length] = '\0';
3172 
3173 		int32 count = atoi(line);
3174 
3175 		char *name = strchr(line, ' ');
3176 		if (name == NULL || *(++name) == '\0')
3177 			continue;
3178 
3179 		list_entry *entry = new list_entry;
3180 		entry->name = strdup(name);
3181 		entry->count = count;
3182 
3183 		fList.AddItem(entry);
3184 	}
3185 	fclose(file);
3186 }
3187 
3188 
3189 void
3190 MostUsedNames::UpdateList()
3191 {
3192 	AutoLock<Benaphore> locker(fLock);
3193 
3194 	if (!fLoaded)
3195 		LoadList();
3196 
3197 	// sort list items
3198 
3199 	fList.SortItems(MostUsedNames::CompareNames);
3200 }
3201 
3202 }	// namespace BPrivate
3203 
3204