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