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