xref: /haiku/src/kits/tracker/OpenWithWindow.cpp (revision fc7456e9b1ec38c941134ed6d01c438cf289381e)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include "Attributes.h"
36 #include "AutoLock.h"
37 #include "Commands.h"
38 #include "FSUtils.h"
39 #include "IconMenuItem.h"
40 #include "OpenWithWindow.h"
41 #include "MimeTypes.h"
42 #include "StopWatch.h"
43 #include "Tracker.h"
44 
45 #include <map>
46 
47 #include <Alert.h>
48 #include <Button.h>
49 #include <Catalog.h>
50 #include <ControlLook.h>
51 #include <Collator.h>
52 #include <GroupView.h>
53 #include <GridView.h>
54 #include <Locale.h>
55 #include <Mime.h>
56 #include <NodeInfo.h>
57 #include <Path.h>
58 #include <Roster.h>
59 #include <SpaceLayoutItem.h>
60 #include <Volume.h>
61 #include <VolumeRoster.h>
62 
63 #include <stdlib.h>
64 #include <stdio.h>
65 #include <strings.h>
66 
67 
68 const char* kDefaultOpenWithTemplate = "OpenWithSettings";
69 
70 // ToDo:
71 // filter out trash
72 // allow column configuring
73 // make SaveState/RestoreState save the current window setting for
74 // other windows
75 
76 const float kMaxMenuWidthFactor = 33.0f;
77 
78 const int32 kDocumentKnobWidth = 16;
79 const int32 kOpenAndMakeDefault = 'OpDf';
80 
81 
82 //	#pragma mark - OpenWithContainerWindow
83 
84 
85 #undef B_TRANSLATION_CONTEXT
86 #define B_TRANSLATION_CONTEXT "OpenWithWindow"
87 
88 
89 OpenWithContainerWindow::OpenWithContainerWindow(BMessage* entriesToOpen,
90 	LockingList<BWindow>* windowList)
91 	:
92 	BContainerWindow(windowList, 0),
93 	fEntriesToOpen(entriesToOpen)
94 {
95 	AutoLock<BWindow> lock(this);
96 
97 	BRect windowRect(85, 50, 718, 296);
98 	MoveTo(windowRect.LeftTop());
99 	ResizeTo(windowRect.Width(), windowRect.Height());
100 
101 	// Create controls
102 	fButtonContainer = new BGroupView(B_HORIZONTAL, B_USE_ITEM_SPACING);
103 	fButtonContainer->GroupLayout()->SetInsets(0, B_USE_ITEM_INSETS,
104 		B_USE_ITEM_INSETS, 0);
105 
106 	fLaunchButton = new BButton("ok", B_TRANSLATE("Open"),
107 		new BMessage(kDefaultButton));
108 
109 	fLaunchButton->MakeDefault(true);
110 
111 	fLaunchAndMakeDefaultButton = new BButton("make default",
112 		B_TRANSLATE("Open and make preferred"),
113 		new BMessage(kOpenAndMakeDefault));
114 	// wide button, have to resize to fit text
115 	fLaunchAndMakeDefaultButton->SetEnabled(false);
116 
117 	fCancelButton = new BButton("cancel", B_TRANSLATE("Cancel"),
118 		new BMessage(kCancelButton));
119 
120 	// Add pose view
121 	fPoseView = NewPoseView(NULL, kListMode);
122 	fBorderedView->GroupLayout()->AddView(fPoseView);
123 
124 	fPoseView->SetFlags(fPoseView->Flags() | B_NAVIGABLE);
125 	fPoseView->SetPoseEditing(false);
126 
127 	// set the window title
128 	if (CountRefs(fEntriesToOpen) == 1) {
129 		// if opening just one file, use it in the title
130 		entry_ref ref;
131 		fEntriesToOpen->FindRef("refs", &ref);
132 		BString buffer(B_TRANSLATE("Open %name with:"));
133 		buffer.ReplaceFirst("%name", ref.name);
134 
135 		SetTitle(buffer.String());
136 	} else {
137 		// use generic title
138 		SetTitle(B_TRANSLATE("Open selection with:"));
139 	}
140 
141 	AddCommonFilter(new BMessageFilter(B_KEY_DOWN,
142 		&OpenWithContainerWindow::KeyDownFilter));
143 }
144 
145 
146 OpenWithContainerWindow::~OpenWithContainerWindow()
147 {
148 	delete fEntriesToOpen;
149 }
150 
151 
152 BPoseView*
153 OpenWithContainerWindow::NewPoseView(Model*, uint32)
154 {
155 	return new OpenWithPoseView;
156 }
157 
158 
159 OpenWithPoseView*
160 OpenWithContainerWindow::PoseView() const
161 {
162 	ASSERT(dynamic_cast<OpenWithPoseView*>(fPoseView) != NULL);
163 
164 	return static_cast<OpenWithPoseView*>(fPoseView);
165 }
166 
167 
168 const BMessage*
169 OpenWithContainerWindow::EntryList() const
170 {
171 	return fEntriesToOpen;
172 }
173 
174 
175 void
176 OpenWithContainerWindow::OpenWithSelection()
177 {
178 	int32 count = PoseView()->SelectionList()->CountItems();
179 	ASSERT(count == 1);
180 	if (count == 0)
181 		return;
182 
183 	PoseView()->OpenSelection(PoseView()->SelectionList()->FirstItem(), 0);
184 }
185 
186 
187 static const BString*
188 FindOne(const BString* element, void* castToString)
189 {
190 	if (strcasecmp(element->String(), (const char*)castToString) == 0)
191 		return element;
192 
193 	return 0;
194 }
195 
196 
197 static const entry_ref*
198 AddOneUniqueDocumentType(const entry_ref* ref, void* castToList)
199 {
200 	BObjectList<BString>* list = (BObjectList<BString>*)castToList;
201 
202 	BEntry entry(ref, true);
203 		// traverse symlinks
204 
205 	// get this documents type
206 	char type[B_MIME_TYPE_LENGTH];
207 	BFile file(&entry, O_RDONLY);
208 	if (file.InitCheck() != B_OK)
209 		return 0;
210 
211 	BNodeInfo info(&file);
212 	if (info.GetType(type) != B_OK)
213 		return 0;
214 
215 	if (list->EachElement(FindOne, &type))
216 		// type already in list, bail
217 		return 0;
218 
219 	// add type to list
220 	list->AddItem(new BString(type));
221 
222 	return 0;
223 }
224 
225 
226 static const BString*
227 SetDefaultAppForOneType(const BString* element, void* castToEntryRef)
228 {
229 	const entry_ref* appRef = (const entry_ref*)castToEntryRef;
230 
231 	// set entry as default handler for one mime string
232 	BMimeType mime(element->String());
233 	if (!mime.IsInstalled())
234 		return 0;
235 
236 	// first set it's app signature as the preferred type
237 	BFile appFile(appRef, O_RDONLY);
238 	if (appFile.InitCheck() != B_OK)
239 		return 0;
240 
241 	char appSignature[B_MIME_TYPE_LENGTH];
242 	if (GetAppSignatureFromAttr(&appFile, appSignature) != B_OK)
243 		return 0;
244 
245 	if (mime.SetPreferredApp(appSignature) != B_OK)
246 		return 0;
247 
248 	// set the app hint on the metamime for this signature
249 	mime.SetTo(appSignature);
250 #if xDEBUG
251 	status_t result =
252 #endif
253 	mime.SetAppHint(appRef);
254 
255 #if xDEBUG
256 	BEntry debugEntry(appRef);
257 	BPath debugPath;
258 	debugEntry.GetPath(&debugPath);
259 
260 	PRINT(("setting %s, sig %s as default app for %s, result %s\n",
261 		debugPath.Path(), appSignature, element->String(), strerror(result)));
262 #endif
263 
264 	return 0;
265 }
266 
267 
268 void
269 OpenWithContainerWindow::MakeDefaultAndOpen()
270 {
271 	int32 count = PoseView()->SelectionList()->CountItems();
272 	ASSERT(count == 1);
273 	if (count == 0)
274 		return;
275 
276 	BPose* selectedAppPose = PoseView()->SelectionList()->FirstItem();
277 	ASSERT(selectedAppPose != NULL);
278 	if (selectedAppPose == NULL)
279 		return;
280 
281 	// collect all the types of all the opened documents into a list
282 	BObjectList<BString> openedFileTypes(10, true);
283 	EachEntryRef(EntryList(), AddOneUniqueDocumentType, &openedFileTypes, 100);
284 
285 	// set the default application to be the selected pose for all the
286 	// mime types in the list
287 	openedFileTypes.EachElement(SetDefaultAppForOneType,
288 		(void*)selectedAppPose->TargetModel()->EntryRef());
289 
290 	// done setting the default application, now launch the app with the
291 	// documents
292 	OpenWithSelection();
293 }
294 
295 
296 void
297 OpenWithContainerWindow::MessageReceived(BMessage* message)
298 {
299 	switch (message->what) {
300 		case kDefaultButton:
301 			OpenWithSelection();
302 			PostMessage(B_QUIT_REQUESTED);
303 			return;
304 
305 		case kOpenAndMakeDefault:
306 			MakeDefaultAndOpen();
307 			PostMessage(B_QUIT_REQUESTED);
308 			return;
309 
310 		case kCancelButton:
311 			PostMessage(B_QUIT_REQUESTED);
312 			return;
313 
314 		case B_OBSERVER_NOTICE_CHANGE:
315 			return;
316 
317 		case kResizeToFit:
318 			ResizeToFit();
319 			break;
320 	}
321 
322 	_inherited::MessageReceived(message);
323 }
324 
325 
326 filter_result
327 OpenWithContainerWindow::KeyDownFilter(BMessage* message, BHandler**,
328 	BMessageFilter* filter)
329 {
330 	uchar key;
331 	if (message->FindInt8("byte", (int8*)&key) != B_OK)
332 		return B_DISPATCH_MESSAGE;
333 
334 	int32 modifiers = 0;
335 	message->FindInt32("modifiers", &modifiers);
336 	if (modifiers == 0 && key == B_ESCAPE) {
337 		filter->Looper()->PostMessage(kCancelButton);
338 		return B_SKIP_MESSAGE;
339 	}
340 
341 	return B_DISPATCH_MESSAGE;
342 }
343 
344 
345 bool
346 OpenWithContainerWindow::ShouldAddMenus() const
347 {
348 	return false;
349 }
350 
351 
352 void
353 OpenWithContainerWindow::ShowContextMenu(BPoint, const entry_ref*)
354 {
355 	// do nothing here so open with context menu doesn't get shown
356 }
357 
358 
359 void
360 OpenWithContainerWindow::AddShortcuts()
361 {
362 	AddShortcut('I', B_COMMAND_KEY, new BMessage(kGetInfo), PoseView());
363 	AddShortcut('Y', B_COMMAND_KEY, new BMessage(kResizeToFit), PoseView());
364 }
365 
366 
367 void
368 OpenWithContainerWindow::NewAttributesMenu(BMenu* menu)
369 {
370 	_inherited::NewAttributesMenu(menu);
371 
372 	BMessage* message = new BMessage(kAttributeItem);
373 	message->AddString("attr_name", kAttrOpenWithRelation);
374 	message->AddInt32("attr_type", B_STRING_TYPE);
375 	message->AddInt32("attr_hash",
376 		(int32)AttrHashString(kAttrOpenWithRelation, B_STRING_TYPE));
377 	message->AddFloat("attr_width", 180);
378 	message->AddInt32("attr_align", B_ALIGN_LEFT);
379 	message->AddBool("attr_editable", false);
380 	message->AddBool("attr_statfield", false);
381 
382 	BMenuItem* item = new BMenuItem(B_TRANSLATE("Relation"), message);
383 	menu->AddItem(item);
384 	message = new BMessage(kAttributeItem);
385 	message->AddString("attr_name", kAttrAppVersion);
386 	message->AddInt32("attr_type", B_STRING_TYPE);
387 	message->AddInt32("attr_hash",
388 		(int32)AttrHashString(kAttrAppVersion, B_STRING_TYPE));
389 	message->AddFloat("attr_width", 70);
390 	message->AddInt32("attr_align", B_ALIGN_LEFT);
391 	message->AddBool("attr_editable", false);
392 	message->AddBool("attr_statfield", false);
393 
394 	item = new BMenuItem(B_TRANSLATE("Version"), message);
395 	menu->AddItem(item);
396 }
397 
398 
399 void
400 OpenWithContainerWindow::SaveState(bool)
401 {
402 	BNode defaultingNode;
403 	if (DefaultStateSourceNode(kDefaultOpenWithTemplate, &defaultingNode,
404 			true, false)) {
405 		AttributeStreamFileNode streamNodeDestination(&defaultingNode);
406 		SaveWindowState(&streamNodeDestination);
407 		fPoseView->SaveState(&streamNodeDestination);
408 	}
409 }
410 
411 
412 void
413 OpenWithContainerWindow::SaveState(BMessage &message) const
414 {
415 	_inherited::SaveState(message);
416 }
417 
418 
419 void
420 OpenWithContainerWindow::Init(const BMessage* message)
421 {
422 	_inherited::Init(message);
423 }
424 
425 
426 void
427 OpenWithContainerWindow::InitLayout()
428 {
429 	_inherited::InitLayout();
430 
431 	// Remove the menu container, since we don't have a menu bar
432 	fMenuContainer->RemoveSelf();
433 
434 	// Reset insets
435 	fRootLayout->SetInsets(B_USE_ITEM_INSETS);
436 	fPoseContainer->GridLayout()->SetInsets(0);
437 	fVScrollBarContainer->GroupLayout()->SetInsets(-1, 0, 0, 0);
438 
439 	fRootLayout->AddView(fButtonContainer);
440 	fButtonContainer->GroupLayout()->AddItem(BSpaceLayoutItem::CreateGlue());
441 	fButtonContainer->GroupLayout()->AddView(fCancelButton);
442 	fButtonContainer->GroupLayout()->AddView(fLaunchAndMakeDefaultButton);
443 	fButtonContainer->GroupLayout()->AddView(fLaunchButton);
444 }
445 
446 
447 void
448 OpenWithContainerWindow::RestoreState()
449 {
450 	BNode defaultingNode;
451 	if (DefaultStateSourceNode(kDefaultOpenWithTemplate, &defaultingNode,
452 			false)) {
453 		AttributeStreamFileNode streamNodeSource(&defaultingNode);
454 		RestoreWindowState(&streamNodeSource);
455 		fPoseView->Init(&streamNodeSource);
456 	} else {
457 		RestoreWindowState(NULL);
458 		fPoseView->Init(NULL);
459 	}
460 	InitLayout();
461 }
462 
463 
464 void
465 OpenWithContainerWindow::RestoreState(const BMessage &message)
466 {
467 	_inherited::RestoreState(message);
468 }
469 
470 
471 void
472 OpenWithContainerWindow::RestoreWindowState(AttributeStreamNode* node)
473 {
474 	if (node == NULL)
475 		return;
476 
477 	const char* rectAttributeName = kAttrWindowFrame;
478 	BRect frame(Frame());
479 	if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame)
480 			== sizeof(BRect)) {
481 		MoveTo(frame.LeftTop());
482 		ResizeTo(frame.Width(), frame.Height());
483 	}
484 }
485 
486 
487 void
488 OpenWithContainerWindow::RestoreWindowState(const BMessage &message)
489 {
490 	_inherited::RestoreWindowState(message);
491 }
492 
493 
494 bool
495 OpenWithContainerWindow::NeedsDefaultStateSetup()
496 {
497 	return true;
498 }
499 
500 
501 void
502 OpenWithContainerWindow::SetupDefaultState()
503 {
504 }
505 
506 
507 bool
508 OpenWithContainerWindow::IsShowing(const node_ref*) const
509 {
510 	return false;
511 }
512 
513 
514 bool
515 OpenWithContainerWindow::IsShowing(const entry_ref*) const
516 {
517 	return false;
518 }
519 
520 
521 void
522 OpenWithContainerWindow::SetCanSetAppAsDefault(bool on)
523 {
524 	fLaunchAndMakeDefaultButton->SetEnabled(on);
525 }
526 
527 
528 void
529 OpenWithContainerWindow::SetCanOpen(bool on)
530 {
531 	fLaunchButton->SetEnabled(on);
532 }
533 
534 
535 //	#pragma mark - OpenWithPoseView
536 
537 
538 OpenWithPoseView::OpenWithPoseView()
539 	:
540 	BPoseView(new Model(), kListMode),
541 	fHaveCommonPreferredApp(false),
542 	fIterator(NULL),
543 	fRefFilter(NULL)
544 {
545 	fSavePoseLocations = false;
546 	fMultipleSelection = false;
547 	fDragEnabled = false;
548 }
549 
550 
551 OpenWithPoseView::~OpenWithPoseView()
552 {
553 	delete fRefFilter;
554 	delete fIterator;
555 }
556 
557 
558 OpenWithContainerWindow*
559 OpenWithPoseView::ContainerWindow() const
560 {
561 	OpenWithContainerWindow* window
562 		= dynamic_cast<OpenWithContainerWindow*>(Window());
563 	ASSERT(window != NULL);
564 
565 	return window;
566 }
567 
568 
569 void
570 OpenWithPoseView::AttachedToWindow()
571 {
572 	_inherited::AttachedToWindow();
573 
574 	SetViewUIColor(B_TOOL_TIP_BACKGROUND_COLOR);
575 	SetLowUIColor(B_TOOL_TIP_BACKGROUND_COLOR);
576 }
577 
578 
579 rgb_color
580 OpenWithPoseView::TextColor(bool selected) const
581 {
582 	if (selected)
583 		return ui_color(B_TOOL_TIP_BACKGROUND_COLOR);
584 	else
585 		return ui_color(B_TOOL_TIP_TEXT_COLOR);
586 }
587 
588 
589 rgb_color
590 OpenWithPoseView::BackColor(bool selected) const
591 {
592 	if (selected)
593 		return InvertedBackColor(ui_color(B_TOOL_TIP_BACKGROUND_COLOR));
594 	else
595 		return ui_color(B_TOOL_TIP_BACKGROUND_COLOR);
596 }
597 
598 
599 bool
600 OpenWithPoseView::CanHandleDragSelection(const Model*, const BMessage*, bool)
601 {
602 	return false;
603 }
604 
605 
606 static void
607 AddSupportingAppForTypeToQuery(SearchForSignatureEntryList* queryIterator,
608 	const char* type)
609 {
610 	// get supporting apps for type
611 	BMimeType mime(type);
612 	if (!mime.IsInstalled())
613 		return;
614 
615 	BMessage message;
616 	mime.GetSupportingApps(&message);
617 
618 	// push each of the supporting apps signature uniquely
619 
620 	const char* signature;
621 	for (int32 index = 0; message.FindString("applications", index,
622 			&signature) == B_OK; index++) {
623 		queryIterator->PushUniqueSignature(signature);
624 	}
625 }
626 
627 
628 static const entry_ref*
629 AddOneRefSignatures(const entry_ref* ref, void* castToIterator)
630 {
631 	// TODO: resolve cases where each entry has a different type and
632 	// their supporting apps are disjoint sets
633 
634 	SearchForSignatureEntryList* queryIterator =
635 		(SearchForSignatureEntryList*)castToIterator;
636 
637 	Model model(ref, true, true);
638 	if (model.InitCheck() != B_OK)
639 		return NULL;
640 
641 	BString mimeType(model.MimeType());
642 
643 	if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0)
644 		// if model is of unknown type, try mimeseting it first
645 		model.Mimeset(true);
646 
647 	entry_ref preferredRef;
648 
649 	// add preferred app for file, if any
650 	if (model.PreferredAppSignature()[0]) {
651 		// got one, mark it as preferred for this node
652 		if (be_roster->FindApp(model.PreferredAppSignature(), &preferredRef)
653 				== B_OK) {
654 			queryIterator->PushUniqueSignature(model.PreferredAppSignature());
655 			queryIterator->TrySettingPreferredAppForFile(&preferredRef);
656 		}
657 	}
658 
659 	mimeType = model.MimeType();
660 	mimeType.ToLower();
661 
662 	if (mimeType.Length() && mimeType.ICompare(B_FILE_MIMETYPE) != 0)
663 		queryIterator->NonGenericFileFound();
664 
665 	// get supporting apps for type
666 	AddSupportingAppForTypeToQuery(queryIterator, mimeType.String());
667 
668 	// find the preferred app for this type
669 	if (be_roster->FindApp(mimeType.String(), &preferredRef) == B_OK)
670 		queryIterator->TrySettingPreferredApp(&preferredRef);
671 
672 	return NULL;
673 }
674 
675 
676 EntryListBase*
677 OpenWithPoseView::InitDirentIterator(const entry_ref*)
678 {
679 	OpenWithContainerWindow* window = ContainerWindow();
680 
681 	const BMessage* entryList = window->EntryList();
682 
683 	fIterator = new SearchForSignatureEntryList(true);
684 
685 	// push all the supporting apps from all the entries into the
686 	// search for signature iterator
687 	EachEntryRef(entryList, AddOneRefSignatures, fIterator, 100);
688 
689 	// push superhandlers
690 	AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE);
691 	fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef);
692 
693 	if (fIterator->Rewind() != B_OK) {
694 		delete fIterator;
695 		fIterator = NULL;
696 		HideBarberPole();
697 		return NULL;
698 	}
699 
700 	fRefFilter = new OpenWithRefFilter(fIterator, entryList,
701 		fHaveCommonPreferredApp ? &fPreferredRef : 0);
702 	SetRefFilter(fRefFilter);
703 
704 	return fIterator;
705 }
706 
707 
708 void
709 OpenWithPoseView::ReturnDirentIterator(EntryListBase* iterator)
710 {
711 	// Do nothing. We keep our fIterator around as it is used by fRefFilter.
712 }
713 
714 
715 void
716 OpenWithPoseView::OpenSelection(BPose* pose, int32*)
717 {
718 	OpenWithContainerWindow* window = ContainerWindow();
719 
720 	int32 count = SelectionList()->CountItems();
721 	if (count == 0)
722 		return;
723 
724 	if (pose == NULL)
725 		pose = SelectionList()->FirstItem();
726 
727 	ASSERT(pose != NULL);
728 
729 	BEntry entry(pose->TargetModel()->EntryRef());
730 	if (entry.InitCheck() != B_OK) {
731 		BString errorString(
732 			B_TRANSLATE("Could not find application \"%appname\""));
733 		errorString.ReplaceFirst("%appname", pose->TargetModel()->Name());
734 
735 		BAlert* alert = new BAlert("", errorString.String(), B_TRANSLATE("OK"),
736 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
737 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
738 		alert->Go();
739 		return;
740 	}
741 
742 	if (OpenWithRelation(pose->TargetModel()) == kNoRelation) {
743 		if (!fIterator->GenericFilesOnly()) {
744 			BString warning(B_TRANSLATE(
745 				"The application \"%appname\" does not support the type of "
746 				"document you are about to open.\nAre you sure you want to "
747 				"proceed?\n\nIf you know that the application supports the "
748 				"document type, you should contact the publisher of the "
749 				"application and ask them to update their application to list "
750 				"the type of your document as supported."));
751 			warning.ReplaceFirst("%appname", pose->TargetModel()->Name());
752 
753 			BAlert* alert = new BAlert("", warning.String(),
754 				B_TRANSLATE("Cancel"), B_TRANSLATE("Open"),	0, B_WIDTH_AS_USUAL,
755 				B_WARNING_ALERT);
756 			alert->SetShortcut(0, B_ESCAPE);
757 			if (alert->Go() == 0)
758 				return;
759 		}
760 		// else - once we have an extensible sniffer, tell users to ask
761 		// publishers to fix up sniffers
762 	}
763 
764 	BMessage message(*window->EntryList());
765 		// make a clone to send
766 	message.RemoveName("launchUsingSelector");
767 		// make sure the old selector is not in the message
768 	message.AddRef("handler", pose->TargetModel()->EntryRef());
769 		// add ref of the selected handler
770 
771 	ASSERT(fSelectionHandler != NULL);
772 	if (fSelectionHandler != NULL)
773 		fSelectionHandler->PostMessage(&message);
774 
775 	window->PostMessage(B_QUIT_REQUESTED);
776 }
777 
778 
779 void
780 OpenWithPoseView::Pulse()
781 {
782 	// disable the Open and make default button if the default
783 	// app matches the selected app
784 	//
785 	// disable the Open button if no apps selected
786 
787 	OpenWithContainerWindow* window = ContainerWindow();
788 
789 	if (!SelectionList()->CountItems()) {
790 		window->SetCanSetAppAsDefault(false);
791 		window->SetCanOpen(false);
792 		_inherited::Pulse();
793 		return;
794 	}
795 
796 	// if we selected a non-handling application, don't allow setting
797 	// it as preferred
798 	Model* firstSelected = SelectionList()->FirstItem()->TargetModel();
799 	if (OpenWithRelation(firstSelected) == kNoRelation) {
800 		window->SetCanSetAppAsDefault(false);
801 		window->SetCanOpen(true);
802 		_inherited::Pulse();
803 		return;
804 	}
805 
806 	// make the open button enabled, because we have na app selected
807 	window->SetCanOpen(true);
808 	if (!fHaveCommonPreferredApp) {
809 		window->SetCanSetAppAsDefault(true);
810 		_inherited::Pulse();
811 		return;
812 	}
813 
814 	ASSERT(SelectionList()->CountItems() == 1);
815 
816 	// enable the Open and make default if selected application different
817 	// from preferred app ref
818 	window->SetCanSetAppAsDefault((*SelectionList()->FirstItem()->
819 		TargetModel()->EntryRef()) != fPreferredRef);
820 
821 	_inherited::Pulse();
822 }
823 
824 
825 void
826 OpenWithPoseView::SetupDefaultColumnsIfNeeded()
827 {
828 	// in case there were errors getting some columns
829 	if (CountColumns() != 0)
830 		return;
831 
832 	BColumn* nameColumn = new BColumn(B_TRANSLATE("Name"), 125,
833 		B_ALIGN_LEFT, kAttrStatName, B_STRING_TYPE, true, true);
834 	AddColumn(nameColumn);
835 
836 	BColumn* relationColumn = new BColumn(B_TRANSLATE("Relation"), 100,
837 		B_ALIGN_LEFT, kAttrOpenWithRelation, B_STRING_TYPE, false, false);
838 	AddColumn(relationColumn);
839 
840 	AddColumn(new BColumn(B_TRANSLATE("Location"), 225,
841 		B_ALIGN_LEFT, kAttrPath, B_STRING_TYPE, true, false));
842 	AddColumn(new BColumn(B_TRANSLATE("Version"), 70,
843 		B_ALIGN_LEFT, kAttrAppVersion, B_STRING_TYPE, false, false));
844 
845 	// sort by relation and by name
846 	SetPrimarySort(relationColumn->AttrHash());
847 	SetSecondarySort(nameColumn->AttrHash());
848 }
849 
850 
851 bool
852 OpenWithPoseView::AddPosesThreadValid(const entry_ref*) const
853 {
854 	return true;
855 }
856 
857 
858 void
859 OpenWithPoseView::CreatePoses(Model** models, PoseInfo* poseInfoArray,
860 	int32 count, BPose** resultingPoses, bool insertionSort,
861 	int32* lastPoseIndexPtr, BRect* boundsPtr, bool forceDraw)
862 {
863 	// overridden to try to select the preferred handling app
864 	_inherited::CreatePoses(models, poseInfoArray, count, resultingPoses,
865 		insertionSort, lastPoseIndexPtr, boundsPtr, forceDraw);
866 
867 	if (resultingPoses != NULL) {
868 		for (int32 index = 0; index < count; index++) {
869 			if (resultingPoses[index] && fHaveCommonPreferredApp
870 				&& *(models[index]->EntryRef()) == fPreferredRef) {
871 				// this is our preferred app, select it's pose
872 				SelectPose(resultingPoses[index],
873 					IndexOfPose(resultingPoses[index]));
874 			}
875 		}
876 	}
877 }
878 
879 
880 void
881 OpenWithPoseView::KeyDown(const char* bytes, int32 count)
882 {
883 	if (bytes[0] == B_TAB) {
884 		// just shift the focus, don't tab to the next pose
885 		BView::KeyDown(bytes, count);
886 	} else
887 		_inherited::KeyDown(bytes, count);
888 }
889 
890 
891 void
892 OpenWithPoseView::SaveState(AttributeStreamNode* node)
893 {
894 	_inherited::SaveState(node);
895 }
896 
897 
898 void
899 OpenWithPoseView::RestoreState(AttributeStreamNode* node)
900 {
901 	_inherited::RestoreState(node);
902 	fViewState->SetViewMode(kListMode);
903 }
904 
905 
906 void
907 OpenWithPoseView::SaveState(BMessage &message) const
908 {
909 	_inherited::SaveState(message);
910 }
911 
912 
913 void
914 OpenWithPoseView::RestoreState(const BMessage &message)
915 {
916 	_inherited::RestoreState(message);
917 	fViewState->SetViewMode(kListMode);
918 }
919 
920 
921 void
922 OpenWithPoseView::SavePoseLocations(BRect*)
923 {
924 }
925 
926 
927 void
928 OpenWithPoseView::MoveSelectionToTrash(bool)
929 {
930 }
931 
932 
933 void
934 OpenWithPoseView::MoveSelectionTo(BPoint, BPoint, BContainerWindow*)
935 {
936 }
937 
938 
939 void
940 OpenWithPoseView::MoveSelectionInto(Model*, BContainerWindow*, bool, bool)
941 {
942 }
943 
944 
945 bool
946 OpenWithPoseView::Represents(const node_ref*) const
947 {
948 	return false;
949 }
950 
951 
952 bool
953 OpenWithPoseView::Represents(const entry_ref*) const
954 {
955 	return false;
956 }
957 
958 
959 bool
960 OpenWithPoseView::HandleMessageDropped(BMessage* DEBUG_ONLY(message))
961 {
962 #if DEBUG
963 	// in debug mode allow tweaking the colors
964 	const rgb_color* color;
965 	ssize_t size;
966 	// handle roColour-style color drops
967 	if (message->FindData("RGBColor", 'RGBC', (const void**)&color, &size)
968 			== B_OK) {
969 		SetViewColor(*color);
970 		SetLowColor(*color);
971 		Invalidate();
972 		return true;
973 	}
974 #endif
975 	return false;
976 }
977 
978 
979 int32
980 OpenWithPoseView::OpenWithRelation(const Model* model) const
981 {
982 	OpenWithContainerWindow* window = ContainerWindow();
983 
984 	return SearchForSignatureEntryList::Relation(window->EntryList(),
985 		model, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0);
986 }
987 
988 
989 void
990 OpenWithPoseView::OpenWithRelationDescription(const Model* model,
991 	BString* description) const
992 {
993 	OpenWithContainerWindow* window = ContainerWindow();
994 
995 	SearchForSignatureEntryList::RelationDescription(window->EntryList(),
996 		model, description, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0);
997 }
998 
999 
1000 //  #pragma mark - OpenWithRefFilter
1001 
1002 
1003 OpenWithRefFilter::OpenWithRefFilter(SearchForSignatureEntryList* iterator,
1004 	const BMessage *entryList, entry_ref* preferredRef)
1005 	:
1006 	fIterator(iterator),
1007 	fEntryList(entryList),
1008 	fPreferredRef(preferredRef)
1009 {
1010 }
1011 
1012 
1013 bool
1014 OpenWithRefFilter::Filter(const entry_ref* ref, BNode* node, stat_beos* st,
1015 	const char* filetype)
1016 {
1017 	Model *model = new Model(ref, true, true);
1018 	bool canOpen = fIterator->CanOpenWithFilter(model, fEntryList,
1019 		fPreferredRef);
1020 	delete model;
1021 
1022 	return canOpen;
1023 }
1024 
1025 
1026 //	#pragma mark -
1027 
1028 
1029 RelationCachingModelProxy::RelationCachingModelProxy(Model* model)
1030 	:
1031 	fModel(model),
1032 	fRelation(kUnknownRelation)
1033 {
1034 }
1035 
1036 
1037 RelationCachingModelProxy::~RelationCachingModelProxy()
1038 {
1039 	delete fModel;
1040 }
1041 
1042 
1043 int32
1044 RelationCachingModelProxy::Relation(SearchForSignatureEntryList* iterator,
1045 	BMessage* entries) const
1046 {
1047 	if (fRelation == kUnknownRelation)
1048 		fRelation = iterator->Relation(entries, fModel);
1049 
1050 	return fRelation;
1051 }
1052 
1053 
1054 //	#pragma mark - OpenWithMenu
1055 
1056 
1057 OpenWithMenu::OpenWithMenu(const char* label, const BMessage* entriesToOpen,
1058 	BWindow* parentWindow, BHandler* target)
1059 	:
1060 	BSlowMenu(label),
1061 	fEntriesToOpen(*entriesToOpen),
1062 	target(target),
1063 	fIterator(NULL),
1064 	fSupportingAppList(NULL),
1065 	fParentWindow(parentWindow)
1066 {
1067 	InitIconPreloader();
1068 
1069 	// too long to have triggers
1070 	SetTriggersEnabled(false);
1071 }
1072 
1073 
1074 OpenWithMenu::OpenWithMenu(const char* label, const BMessage* entriesToOpen,
1075 	BWindow* parentWindow, const BMessenger &messenger)
1076 	:
1077 	BSlowMenu(label),
1078 	fEntriesToOpen(*entriesToOpen),
1079 	target(NULL),
1080 	fMessenger(messenger),
1081 	fIterator(NULL),
1082 	fSupportingAppList(NULL),
1083 	fParentWindow(parentWindow)
1084 {
1085 	InitIconPreloader();
1086 
1087 	// too long to have triggers
1088 	SetTriggersEnabled(false);
1089 }
1090 
1091 
1092 namespace BPrivate {
1093 
1094 int
1095 SortByRelation(const RelationCachingModelProxy* proxy1,
1096 	const RelationCachingModelProxy* proxy2, void* castToMenu)
1097 {
1098 	OpenWithMenu* menu = (OpenWithMenu*)castToMenu;
1099 
1100 	// find out the relations of app models to the opened entries
1101 	int32 relation1 = proxy1->Relation(menu->fIterator, &menu->fEntriesToOpen);
1102 	int32 relation2 = proxy2->Relation(menu->fIterator, &menu->fEntriesToOpen);
1103 
1104 	// relation with the lowest number goes first
1105 	if (relation1 < relation2)
1106 		return 1;
1107 	else if (relation1 > relation2)
1108 		return -1;
1109 
1110 	// relations match
1111 	return 0;
1112 }
1113 
1114 
1115 int
1116 SortByName(const RelationCachingModelProxy* proxy1,
1117 	const RelationCachingModelProxy* proxy2, void* castToMenu)
1118 {
1119 	BCollator collator;
1120 	BLocale::Default()->GetCollator(&collator);
1121 
1122 	// sort by app name
1123 	int nameDiff = collator.Compare(proxy1->fModel->Name(),
1124 		proxy2->fModel->Name());
1125 	if (nameDiff < 0)
1126 		return -1;
1127 	else if (nameDiff > 0)
1128 		return 1;
1129 
1130 	// if app names match, sort by volume name
1131 	BVolume volume1(proxy1->fModel->NodeRef()->device);
1132 	BVolume volume2(proxy2->fModel->NodeRef()->device);
1133 	char volumeName1[B_FILE_NAME_LENGTH];
1134 	char volumeName2[B_FILE_NAME_LENGTH];
1135 	if (volume1.InitCheck() == B_OK && volume2.InitCheck() == B_OK
1136 		&& volume1.GetName(volumeName1) == B_OK
1137 		&& volume2.GetName(volumeName2) == B_OK) {
1138 		int volumeNameDiff = collator.Compare(volumeName1, volumeName2);
1139 		if (volumeNameDiff < 0)
1140 			return -1;
1141 		else if (volumeNameDiff > 0)
1142 			return 1;
1143 	}
1144 
1145 	// app names and volume names match
1146 	return 0;
1147 }
1148 
1149 
1150 int
1151 SortByRelationAndName(const RelationCachingModelProxy* proxy1,
1152 	const RelationCachingModelProxy* proxy2, void* castToMenu)
1153 {
1154 	int relationDiff = SortByRelation(proxy1, proxy2, castToMenu);
1155 	if (relationDiff != 0)
1156 		return relationDiff;
1157 
1158 	int nameDiff = SortByName(proxy1, proxy2, castToMenu);
1159 	if (nameDiff != 0)
1160 		return nameDiff;
1161 
1162 	// relations, app names and volume names all match
1163 	return 0;
1164 }
1165 
1166 } // namespace BPrivate
1167 
1168 
1169 bool
1170 OpenWithMenu::StartBuildingItemList()
1171 {
1172 	fIterator = new SearchForSignatureEntryList(false);
1173 	// push all the supporting apps from all the entries into the
1174 	// search for signature iterator
1175 	EachEntryRef(&fEntriesToOpen, AddOneRefSignatures, fIterator, 100);
1176 	// add superhandlers
1177 	AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE);
1178 
1179 	fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef);
1180 	status_t error = fIterator->Rewind();
1181 	if (error != B_OK) {
1182 		PRINT(("failed to initialize iterator %s\n", strerror(error)));
1183 		return false;
1184 	}
1185 
1186 	fSupportingAppList = new BObjectList<RelationCachingModelProxy>(20, true);
1187 
1188 	//queryRetrieval = new BStopWatch("get next entry on BQuery");
1189 	return true;
1190 }
1191 
1192 
1193 bool
1194 OpenWithMenu::AddNextItem()
1195 {
1196 	BEntry entry;
1197 	if (fIterator->GetNextEntry(&entry) != B_OK)
1198 		return false;
1199 
1200 	Model* model = new Model(&entry, true);
1201 	if (model->InitCheck() != B_OK
1202 		|| !fIterator->CanOpenWithFilter(model, &fEntriesToOpen,
1203 			(fHaveCommonPreferredApp ? &fPreferredRef : 0))) {
1204 		// only allow executables, filter out multiple copies of the Tracker,
1205 		// filter out version that don't list the correct types, etc.
1206 		delete model;
1207 	} else
1208 		fSupportingAppList->AddItem(new RelationCachingModelProxy(model));
1209 
1210 	return true;
1211 }
1212 
1213 
1214 void
1215 OpenWithMenu::DoneBuildingItemList()
1216 {
1217 	// sort list by name and volume name first to fill out labels
1218 	fSupportingAppList->SortItems(SortByName, this);
1219 
1220 	int32 count = fSupportingAppList->CountItems();
1221 
1222 	bool nameRepeats[count];
1223 	bool volumeRepeats[count];
1224 	// initialize to false
1225 	memset(nameRepeats, 0, sizeof(bool) * count);
1226 	memset(volumeRepeats, 0, sizeof(bool) * count);
1227 
1228 	std::map<RelationCachingModelProxy*, BString> labels;
1229 
1230 	BCollator collator;
1231 	BLocale::Default()->GetCollator(&collator);
1232 
1233 	// check if each app is unique
1234 	for (int32 index = 0; index < count - 1; index++) {
1235 		// the list is sorted, compare adjacent models
1236 		Model* model = fSupportingAppList->ItemAt(index)->fModel;
1237 		Model* next = fSupportingAppList->ItemAt(index + 1)->fModel;
1238 
1239 		// check if name repeats
1240 		if (collator.Compare(model->Name(), next->Name()) == 0) {
1241 			nameRepeats[index] = nameRepeats[index + 1] = true;
1242 
1243 			// check if volume name repeats
1244 			BVolume volume(model->NodeRef()->device);
1245 			BVolume nextVol(next->NodeRef()->device);
1246 			char volumeName[B_FILE_NAME_LENGTH];
1247 			char nextVolName[B_FILE_NAME_LENGTH];
1248 			if (volume.InitCheck() == B_OK && nextVol.InitCheck() == B_OK
1249 				&& volume.GetName(volumeName) == B_OK
1250 				&& nextVol.GetName(nextVolName) == B_OK
1251 				&& collator.Compare(volumeName, nextVolName) == 0) {
1252 				volumeRepeats[index] = volumeRepeats[index + 1] = true;
1253 			}
1254 		}
1255 	}
1256 
1257 	BFont font;
1258 	GetFont(&font);
1259 
1260 	// fill out the item labels
1261 	for (int32 index = 0; index < count; index++) {
1262 		RelationCachingModelProxy* proxy
1263 			= fSupportingAppList->ItemAt(index);
1264 		Model* model = proxy->fModel;
1265 		BString label;
1266 
1267 		if (!nameRepeats[index]) {
1268 			// one of a kind, print the app name
1269 			label = model->Name();
1270 		} else {
1271 			// name repeats, check if same volume
1272 			if (!volumeRepeats[index]) {
1273 				// different volume, print
1274 				// [volume name] app name
1275 				BVolume volume(model->NodeRef()->device);
1276 				if (volume.InitCheck() == B_OK) {
1277 					char volumeName[B_FILE_NAME_LENGTH];
1278 					if (volume.GetName(volumeName) == B_OK)
1279 						label << "[" << volumeName << "] ";
1280 				}
1281 				label << model->Name();
1282 			} else {
1283 				// same volume, print full path
1284 				BPath path;
1285 				BEntry entry(model->EntryRef());
1286 				if (entry.GetPath(&path) != B_OK) {
1287 					PRINT(("stale entry ref %s\n", model->Name()));
1288 					continue;
1289 				}
1290 				label = path.Path();
1291 			}
1292 			font.TruncateString(&label, B_TRUNCATE_MIDDLE,
1293 				kMaxMenuWidthFactor * be_control_look->DefaultLabelSpacing());
1294 		}
1295 
1296 #if DEBUG
1297 		BString relationDescription;
1298 		fIterator->RelationDescription(&fEntriesToOpen, model,
1299 			&relationDescription);
1300 		label << " (" << relationDescription << ")";
1301 #endif
1302 
1303 		labels[proxy] = label;
1304 	}
1305 
1306 	// sort again by relation and name after labels are filled out
1307 	fSupportingAppList->SortItems(SortByRelationAndName, this);
1308 
1309 	// add apps as menu items
1310 	int32 lastRelation = -1;
1311 	for (int32 index = 0; index < count; index++) {
1312 		RelationCachingModelProxy* proxy = fSupportingAppList->ItemAt(index);
1313 		Model* model = proxy->fModel;
1314 
1315 		// divide different relations of opening with a separator
1316 		int32 relation = proxy->Relation(fIterator, &fEntriesToOpen);
1317 		if (lastRelation != -1 && relation != lastRelation)
1318 			AddSeparatorItem();
1319 
1320 		lastRelation = relation;
1321 
1322 		// build message
1323 		BMessage* message = new BMessage(fEntriesToOpen);
1324 		message->AddRef("handler", model->EntryRef());
1325 		BContainerWindow* window
1326 			= dynamic_cast<BContainerWindow*>(fParentWindow);
1327 		if (window != NULL) {
1328 			message->AddData("nodeRefsToClose", B_RAW_TYPE,
1329 				window->TargetModel()->NodeRef(), sizeof(node_ref));
1330 		}
1331 
1332 		// add item
1333 		ModelMenuItem* item = new ModelMenuItem(model,
1334 			labels.find(proxy)->second.String(), message);
1335 		AddItem(item);
1336 
1337 		// mark preferred app item
1338 		if (fHaveCommonPreferredApp && *(model->EntryRef()) == fPreferredRef) {
1339 			//PRINT(("marking item for % as preferred", model->Name()));
1340 			item->SetMarked(true);
1341 		}
1342 	}
1343 
1344 	// target the menu
1345 	if (target != NULL)
1346 		SetTargetForItems(target);
1347 	else
1348 		SetTargetForItems(fMessenger);
1349 
1350 	if (CountItems() == 0) {
1351 		BMenuItem* item = new BMenuItem(B_TRANSLATE("no supporting apps"), 0);
1352 		item->SetEnabled(false);
1353 		AddItem(item);
1354 	}
1355 }
1356 
1357 
1358 void
1359 OpenWithMenu::ClearMenuBuildingState()
1360 {
1361 	delete fIterator;
1362 	fIterator = NULL;
1363 	delete fSupportingAppList;
1364 	fSupportingAppList = NULL;
1365 }
1366 
1367 
1368 //	#pragma mark - SearchForSignatureEntryList
1369 
1370 
1371 SearchForSignatureEntryList::SearchForSignatureEntryList(bool canAddAllApps)
1372 	:
1373 	fIteratorList(NULL),
1374 	fSignatures(20, true),
1375 	fPreferredAppCount(0),
1376 	fPreferredAppForFileCount(0),
1377 	fGenericFilesOnly(true),
1378 	fCanAddAllApps(canAddAllApps),
1379 	fFoundOneNonSuperHandler(false)
1380 {
1381 }
1382 
1383 
1384 SearchForSignatureEntryList::~SearchForSignatureEntryList()
1385 {
1386 	delete fIteratorList;
1387 }
1388 
1389 
1390 void
1391 SearchForSignatureEntryList::PushUniqueSignature(const char* str)
1392 {
1393 	// do a unique add
1394 	if (fSignatures.EachElement(FindOne, (void*)str))
1395 		return;
1396 
1397 	fSignatures.AddItem(new BString(str));
1398 }
1399 
1400 
1401 status_t
1402 SearchForSignatureEntryList::GetNextEntry(BEntry* entry, bool)
1403 {
1404 	return fIteratorList->GetNextEntry(entry);
1405 }
1406 
1407 
1408 status_t
1409 SearchForSignatureEntryList::GetNextRef(entry_ref* ref)
1410 {
1411 	return fIteratorList->GetNextRef(ref);
1412 }
1413 
1414 
1415 int32
1416 SearchForSignatureEntryList::GetNextDirents(struct dirent* buffer,
1417 	size_t length, int32 count)
1418 {
1419 	return fIteratorList->GetNextDirents(buffer, length, count);
1420 }
1421 
1422 
1423 struct AddOneTermParams {
1424 	BString* result;
1425 	bool first;
1426 };
1427 
1428 
1429 static const BString*
1430 AddOnePredicateTerm(const BString* item, void* castToParams)
1431 {
1432 	AddOneTermParams* params = (AddOneTermParams*)castToParams;
1433 	if (!params->first)
1434 		(*params->result) << " || ";
1435 	(*params->result) << kAttrAppSignature << " = " << item->String();
1436 
1437 	params->first = false;
1438 
1439 	return 0;
1440 }
1441 
1442 
1443 status_t
1444 SearchForSignatureEntryList::Rewind()
1445 {
1446 	if (fIteratorList)
1447 		return fIteratorList->Rewind();
1448 
1449 	if (!fSignatures.CountItems())
1450 		return ENOENT;
1451 
1452 	// build up the iterator
1453 	fIteratorList = new CachedEntryIteratorList(false);
1454 		// We cannot sort the cached inodes, as CanOpenWithFilter() relies
1455 		// on the fact that ConditionalAllAppsIterator results come last.
1456 
1457 	// build the predicate string by oring queries for the individual
1458 	// signatures
1459 	BString predicateString;
1460 
1461 	AddOneTermParams params;
1462 	params.result = &predicateString;
1463 	params.first = true;
1464 
1465 	fSignatures.EachElement(AddOnePredicateTerm, &params);
1466 
1467 	ASSERT(predicateString.Length());
1468 //	PRINT(("query predicate %s\n", predicateString.String()));
1469 	fIteratorList->AddItem(new TWalkerWrapper(
1470 		new BTrackerPrivate::TQueryWalker(predicateString.String())));
1471 	fIteratorList->AddItem(new ConditionalAllAppsIterator(this));
1472 
1473 	return fIteratorList->Rewind();
1474 }
1475 
1476 
1477 int32
1478 SearchForSignatureEntryList::CountEntries()
1479 {
1480 	return 0;
1481 }
1482 
1483 
1484 bool
1485 SearchForSignatureEntryList::GetPreferredApp(entry_ref* ref) const
1486 {
1487 	if (fPreferredAppCount == 1)
1488 		*ref = fPreferredRef;
1489 
1490 	return fPreferredAppCount == 1;
1491 }
1492 
1493 
1494 void
1495 SearchForSignatureEntryList::TrySettingPreferredApp(const entry_ref* ref)
1496 {
1497 	if (!fPreferredAppCount) {
1498 		fPreferredRef = *ref;
1499 		fPreferredAppCount++;
1500 	} else if (fPreferredRef != *ref) {
1501 		// if more than one, will not return any
1502 		fPreferredAppCount++;
1503 	}
1504 }
1505 
1506 
1507 void
1508 SearchForSignatureEntryList::TrySettingPreferredAppForFile(const entry_ref* ref)
1509 {
1510 	if (!fPreferredAppForFileCount) {
1511 		fPreferredRefForFile = *ref;
1512 		fPreferredAppForFileCount++;
1513 	} else if (fPreferredRefForFile != *ref) {
1514 		// if more than one, will not return any
1515 		fPreferredAppForFileCount++;
1516 	}
1517 }
1518 
1519 
1520 void
1521 SearchForSignatureEntryList::NonGenericFileFound()
1522 {
1523 	fGenericFilesOnly = false;
1524 }
1525 
1526 
1527 bool
1528 SearchForSignatureEntryList::GenericFilesOnly() const
1529 {
1530 	return fGenericFilesOnly;
1531 }
1532 
1533 
1534 bool
1535 SearchForSignatureEntryList::ShowAllApplications() const
1536 {
1537 	return fCanAddAllApps && !fFoundOneNonSuperHandler;
1538 }
1539 
1540 
1541 int32
1542 SearchForSignatureEntryList::Relation(const Model* nodeModel,
1543 	const Model* applicationModel)
1544 {
1545 	int32 supportsMimeType = applicationModel->SupportsMimeType(
1546 		nodeModel->MimeType(), 0, true);
1547 	switch (supportsMimeType) {
1548 		case kDoesNotSupportType:
1549 			return kNoRelation;
1550 
1551 		case kSuperhandlerModel:
1552 			return kSuperhandler;
1553 
1554 		case kModelSupportsSupertype:
1555 			return kSupportsSupertype;
1556 
1557 		case kModelSupportsType:
1558 			return kSupportsType;
1559 	}
1560 
1561 	TRESPASS();
1562 	return kNoRelation;
1563 }
1564 
1565 
1566 int32
1567 SearchForSignatureEntryList::Relation(const BMessage* entriesToOpen,
1568 	const Model* model) const
1569 {
1570 	return Relation(entriesToOpen, model,
1571 		fPreferredAppCount == 1 ? &fPreferredRef : 0,
1572 		fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0);
1573 }
1574 
1575 
1576 void
1577 SearchForSignatureEntryList::RelationDescription(const BMessage* entriesToOpen,
1578 	const Model* model, BString* description) const
1579 {
1580 	RelationDescription(entriesToOpen, model, description,
1581 		fPreferredAppCount == 1 ? &fPreferredRef : 0,
1582 		fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0);
1583 }
1584 
1585 
1586 int32
1587 SearchForSignatureEntryList::Relation(const BMessage* entriesToOpen,
1588 	const Model* applicationModel, const entry_ref* preferredApp,
1589 	const entry_ref* preferredAppForFile)
1590 {
1591 	for (int32 index = 0; ; index++) {
1592 		entry_ref ref;
1593 		if (entriesToOpen->FindRef("refs", index, &ref) != B_OK)
1594 			break;
1595 
1596 		// need to init a model so that typeless folders etc. will still
1597 		// appear to have a mime type
1598 
1599 		Model model(&ref, true, true);
1600 		if (model.InitCheck())
1601 			continue;
1602 
1603 		int32 result = Relation(&model, applicationModel);
1604 		if (result != kNoRelation) {
1605 			if (preferredAppForFile
1606 				&& *applicationModel->EntryRef() == *preferredAppForFile) {
1607 				return kPreferredForFile;
1608 			}
1609 
1610 			if (result == kSupportsType && preferredApp
1611 				&& *applicationModel->EntryRef() == *preferredApp) {
1612 				// application matches cached preferred app, we are done
1613 				return kPreferredForType;
1614 			}
1615 
1616 			return result;
1617 		}
1618 	}
1619 
1620 	return kNoRelation;
1621 }
1622 
1623 
1624 void
1625 SearchForSignatureEntryList::RelationDescription(const BMessage* entriesToOpen,
1626 	const Model* applicationModel, BString* description,
1627 	const entry_ref* preferredApp, const entry_ref* preferredAppForFile)
1628 {
1629 	for (int32 index = 0; ;index++) {
1630 		entry_ref ref;
1631 		if (entriesToOpen->FindRef("refs", index, &ref) != B_OK)
1632 			break;
1633 
1634 		if (preferredAppForFile && ref == *preferredAppForFile) {
1635 			description->SetTo(B_TRANSLATE("Preferred for file"));
1636 			return;
1637 		}
1638 
1639 		Model model(&ref, true, true);
1640 		if (model.InitCheck())
1641 			continue;
1642 
1643 		BMimeType mimeType;
1644 		int32 result = Relation(&model, applicationModel);
1645 		switch (result) {
1646 			case kDoesNotSupportType:
1647 				continue;
1648 
1649 			case kSuperhandler:
1650 				description->SetTo(B_TRANSLATE("Handles any file"));
1651 				return;
1652 
1653 			case kSupportsSupertype:
1654 			{
1655 				mimeType.SetTo(model.MimeType());
1656 				// status_t result = mimeType.GetSupertype(&mimeType);
1657 
1658 				char* type = (char*)mimeType.Type();
1659 				char* tmp = strchr(type, '/');
1660 				if (tmp != NULL)
1661 					*tmp = '\0';
1662 
1663 				//PRINT(("getting supertype for %s, result %s, got %s\n",
1664 				//	model.MimeType(), strerror(result), mimeType.Type()));
1665 				description->SetTo(B_TRANSLATE("Handles any %type"));
1666 				//*description += mimeType.Type();
1667 				description->ReplaceFirst("%type", type);
1668 				return;
1669 			}
1670 
1671 			case kSupportsType:
1672 			{
1673 				mimeType.SetTo(model.MimeType());
1674 
1675 				if (preferredApp != NULL
1676 					&& *applicationModel->EntryRef() == *preferredApp) {
1677 					// application matches cached preferred app, we are done
1678 					description->SetTo(B_TRANSLATE("Preferred for %type"));
1679 				} else
1680 					description->SetTo(B_TRANSLATE("Handles %type"));
1681 
1682 				char shortDescription[256];
1683 				if (mimeType.GetShortDescription(shortDescription) == B_OK)
1684 					description->ReplaceFirst("%type", shortDescription);
1685 				else
1686 					description->ReplaceFirst("%type", mimeType.Type());
1687 
1688 				return;
1689 			}
1690 		}
1691 	}
1692 
1693 	description->SetTo(B_TRANSLATE("Does not handle file"));
1694 }
1695 
1696 
1697 bool
1698 SearchForSignatureEntryList::CanOpenWithFilter(const Model* appModel,
1699 	const BMessage* entriesToOpen, const entry_ref* preferredApp)
1700 {
1701 	ThrowOnAssert(appModel != NULL);
1702 
1703 	if (!appModel->IsExecutable() || !appModel->Node()) {
1704 		// weed out non-executable
1705 #if xDEBUG
1706 		BPath path;
1707 		BEntry entry(appModel->EntryRef());
1708 		entry.GetPath(&path);
1709 		PRINT(("filtering out %s- not executable \n", path.Path()));
1710 #endif
1711 		return false;
1712 	}
1713 
1714 	if (strcasecmp(appModel->MimeType(), B_APP_MIME_TYPE) != 0) {
1715 		// filter out pe containers on PPC etc.
1716 		return false;
1717 	}
1718 
1719 	BFile* file = dynamic_cast<BFile*>(appModel->Node());
1720 	ASSERT(file != NULL);
1721 
1722 	char signature[B_MIME_TYPE_LENGTH];
1723 	if (GetAppSignatureFromAttr(file, signature) == B_OK
1724 		&& strcasecmp(signature, kTrackerSignature) == 0) {
1725 		// special case the Tracker - make sure only the running copy is
1726 		// in the list
1727 		app_info trackerInfo;
1728 		if (*appModel->EntryRef() != trackerInfo.ref) {
1729 			// this is an inactive copy of the Tracker, remove it
1730 
1731 #if xDEBUG
1732 			BPath path1;
1733 			BPath path2;
1734 			BEntry entry(appModel->EntryRef());
1735 			entry.GetPath(&path1);
1736 
1737 			BEntry entry2(&trackerInfo.ref);
1738 			entry2.GetPath(&path2);
1739 
1740 			PRINT(("filtering out %s, sig %s, active Tracker at %s, "
1741 				   "result %s, refName %s\n",
1742 				path1.Path(), signature, path2.Path(),
1743 				strerror(be_roster->GetActiveAppInfo(&trackerInfo)),
1744 				trackerInfo.ref.name));
1745 #endif
1746 			return false;
1747 		}
1748 	}
1749 
1750 	if (FSInTrashDir(appModel->EntryRef()))
1751 		return false;
1752 
1753 	if (ShowAllApplications()) {
1754 		// don't check for these if we didn't look for every single app
1755 		// to not slow filtering down
1756 		uint32 flags;
1757 		BAppFileInfo appFileInfo(dynamic_cast<BFile*>(appModel->Node()));
1758 		if (appFileInfo.GetAppFlags(&flags) != B_OK)
1759 			return false;
1760 
1761 		if ((flags & B_BACKGROUND_APP) || (flags & B_ARGV_ONLY))
1762 			return false;
1763 
1764 		if (!signature[0])
1765 			// weed out apps with empty signatures
1766 			return false;
1767 	}
1768 
1769 	int32 relation = Relation(entriesToOpen, appModel, preferredApp, 0);
1770 	if (relation == kNoRelation && !ShowAllApplications()) {
1771 #if xDEBUG
1772 		BPath path;
1773 		BEntry entry(appModel->EntryRef());
1774 		entry.GetPath(&path);
1775 
1776 		PRINT(("filtering out %s, does not handle any of opened files\n",
1777 			path.Path()));
1778 #endif
1779 		return false;
1780 	}
1781 
1782 	if (relation != kNoRelation && relation != kSuperhandler
1783 		&& !fGenericFilesOnly) {
1784 		// we hit at least one app that is not a superhandler and
1785 		// handles the document
1786 		fFoundOneNonSuperHandler = true;
1787 	}
1788 
1789 	return true;
1790 }
1791 
1792 
1793 //	#pragma mark - ConditionalAllAppsIterator
1794 
1795 
1796 ConditionalAllAppsIterator::ConditionalAllAppsIterator(
1797 	SearchForSignatureEntryList* parent)
1798 	:
1799 	fParent(parent),
1800 	fWalker(NULL)
1801 {
1802 }
1803 
1804 
1805 void
1806 ConditionalAllAppsIterator::Instantiate()
1807 {
1808 	if (fWalker != NULL)
1809 		return;
1810 
1811 	BString lookForAppsPredicate;
1812 	lookForAppsPredicate << "(" << kAttrAppSignature << " = \"*\" ) && ( "
1813 		<< kAttrMIMEType << " = " << B_APP_MIME_TYPE << " ) ";
1814 	fWalker
1815 		= new BTrackerPrivate::TQueryWalker(lookForAppsPredicate.String());
1816 }
1817 
1818 
1819 ConditionalAllAppsIterator::~ConditionalAllAppsIterator()
1820 {
1821 	delete fWalker;
1822 }
1823 
1824 
1825 status_t
1826 ConditionalAllAppsIterator::GetNextEntry(BEntry* entry, bool traverse)
1827 {
1828 	if (!Iterate())
1829 		return B_ENTRY_NOT_FOUND;
1830 
1831 	Instantiate();
1832 	return fWalker->GetNextEntry(entry, traverse);
1833 }
1834 
1835 
1836 status_t
1837 ConditionalAllAppsIterator::GetNextRef(entry_ref* ref)
1838 {
1839 	if (!Iterate())
1840 		return B_ENTRY_NOT_FOUND;
1841 
1842 	Instantiate();
1843 	return fWalker->GetNextRef(ref);
1844 }
1845 
1846 
1847 int32
1848 ConditionalAllAppsIterator::GetNextDirents(struct dirent* buffer,
1849 	size_t length, int32 count)
1850 {
1851 	if (!Iterate())
1852 		return 0;
1853 
1854 	Instantiate();
1855 	return fWalker->GetNextDirents(buffer, length, count);
1856 }
1857 
1858 
1859 status_t
1860 ConditionalAllAppsIterator::Rewind()
1861 {
1862 	if (!Iterate())
1863 		return B_OK;
1864 
1865 	Instantiate();
1866 	return fWalker->Rewind();
1867 }
1868 
1869 
1870 int32
1871 ConditionalAllAppsIterator::CountEntries()
1872 {
1873 	if (!Iterate())
1874 		return 0;
1875 
1876 	Instantiate();
1877 	return fWalker->CountEntries();
1878 }
1879 
1880 
1881 bool
1882 ConditionalAllAppsIterator::Iterate() const
1883 {
1884 	return fParent->ShowAllApplications();
1885 }
1886