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