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