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