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