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