xref: /haiku/src/kits/tracker/OpenWithWindow.cpp (revision 32832cbe47f991cc6d2b29824903181d8baaaa63)
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 //	#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));
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(0, 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 	ASSERT(dynamic_cast<OpenWithContainerWindow*>(Window()));
557 	return static_cast<OpenWithContainerWindow*>(Window());
558 }
559 
560 
561 void
562 OpenWithPoseView::AttachedToWindow()
563 {
564 	_inherited::AttachedToWindow();
565 	SetViewColor(kOpenWithDefaultColor);
566 	SetLowColor(kOpenWithDefaultColor);
567 }
568 
569 
570 bool
571 OpenWithPoseView::CanHandleDragSelection(const Model*, const BMessage*, bool)
572 {
573 	return false;
574 }
575 
576 
577 static void
578 AddSupportingAppForTypeToQuery(SearchForSignatureEntryList* queryIterator,
579 	const char* type)
580 {
581 	// get supporting apps for type
582 	BMimeType mime(type);
583 	if (!mime.IsInstalled())
584 		return;
585 
586 	BMessage message;
587 	mime.GetSupportingApps(&message);
588 
589 	// push each of the supporting apps signature uniquely
590 
591 	const char* signature;
592 	for (int32 index = 0; message.FindString("applications", index,
593 			&signature) == B_OK; index++) {
594 		queryIterator->PushUniqueSignature(signature);
595 	}
596 }
597 
598 
599 static const entry_ref*
600 AddOneRefSignatures(const entry_ref* ref, void* castToIterator)
601 {
602 	// TODO: resolve cases where each entry has a different type and
603 	// their supporting apps are disjoint sets
604 
605 	SearchForSignatureEntryList* queryIterator =
606 		(SearchForSignatureEntryList*)castToIterator;
607 
608 	Model model(ref, true, true);
609 	if (model.InitCheck() != B_OK)
610 		return NULL;
611 
612 	BString mimeType(model.MimeType());
613 
614 	if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0)
615 		// if model is of unknown type, try mimeseting it first
616 		model.Mimeset(true);
617 
618 	entry_ref preferredRef;
619 
620 	// add preferred app for file, if any
621 	if (model.PreferredAppSignature()[0]) {
622 		// got one, mark it as preferred for this node
623 		if (be_roster->FindApp(model.PreferredAppSignature(), &preferredRef)
624 				== B_OK) {
625 			queryIterator->PushUniqueSignature(model.PreferredAppSignature());
626 			queryIterator->TrySettingPreferredAppForFile(&preferredRef);
627 		}
628 	}
629 
630 	mimeType = model.MimeType();
631 	mimeType.ToLower();
632 
633 	if (mimeType.Length() && !mimeType.ICompare(B_FILE_MIMETYPE) == 0)
634 		queryIterator->NonGenericFileFound();
635 
636 	// get supporting apps for type
637 	AddSupportingAppForTypeToQuery(queryIterator, mimeType.String());
638 
639 	// find the preferred app for this type
640 	if (be_roster->FindApp(mimeType.String(), &preferredRef) == B_OK)
641 		queryIterator->TrySettingPreferredApp(&preferredRef);
642 
643 	return NULL;
644 }
645 
646 
647 EntryListBase*
648 OpenWithPoseView::InitDirentIterator(const entry_ref*)
649 {
650 	OpenWithContainerWindow* window = ContainerWindow();
651 
652 	const BMessage* entryList = window->EntryList();
653 
654 	fIterator = new SearchForSignatureEntryList(true);
655 
656 	// push all the supporting apps from all the entries into the
657 	// search for signature iterator
658 	EachEntryRef(entryList, AddOneRefSignatures, fIterator, 100);
659 
660 	// push superhandlers
661 	AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE);
662 	fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef);
663 
664 	if (fIterator->Rewind() != B_OK) {
665 		delete fIterator;
666 		fIterator = NULL;
667 		HideBarberPole();
668 		return NULL;
669 	}
670 	SetRefFilter(new OpenWithRefFilter(fIterator, entryList,
671 		(fHaveCommonPreferredApp ? &fPreferredRef : 0)));
672 
673 	return fIterator;
674 }
675 
676 
677 void
678 OpenWithPoseView::OpenSelection(BPose* pose, int32*)
679 {
680 	OpenWithContainerWindow* window = ContainerWindow();
681 
682 	int32 count = fSelectionList->CountItems();
683 	if (count == 0)
684 		return;
685 
686 	if (pose == NULL)
687 		pose = fSelectionList->FirstItem();
688 
689 	ASSERT(pose);
690 
691 	BEntry entry(pose->TargetModel()->EntryRef());
692 	if (entry.InitCheck() != B_OK) {
693 		BString errorString(
694 			B_TRANSLATE("Could not find application \"%appname\""));
695 		errorString.ReplaceFirst("%appname", pose->TargetModel()->Name());
696 
697 		BAlert* alert = new BAlert("", errorString.String(), B_TRANSLATE("OK"),
698 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
699 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
700 		alert->Go();
701 		return;
702 	}
703 
704 	if (OpenWithRelation(pose->TargetModel()) == kNoRelation) {
705 		if (!fIterator->GenericFilesOnly()) {
706 			BString warning(B_TRANSLATE(
707 				"The application \"%appname\" does not support the type of "
708 				"document you are about to open.\nAre you sure you want to "
709 				"proceed?\n\nIf you know that the application supports the "
710 				"document type, you should contact the publisher of the "
711 				"application and ask them to update their application to list "
712 				"the type of your document as supported."));
713 			warning.ReplaceFirst("%appname", pose->TargetModel()->Name());
714 
715 			BAlert* alert = new BAlert("", warning.String(),
716 				B_TRANSLATE("Cancel"), B_TRANSLATE("Open"),	0, B_WIDTH_AS_USUAL,
717 				B_WARNING_ALERT);
718 			alert->SetShortcut(0, B_ESCAPE);
719 			if (alert->Go() == 0)
720 				return;
721 		}
722 		// else - once we have an extensible sniffer, tell users to ask
723 		// publishers to fix up sniffers
724 	}
725 
726 	BMessage message(*window->EntryList());
727 		// make a clone to send
728 	message.RemoveName("launchUsingSelector");
729 		// make sure the old selector is not in the message
730 	message.AddRef("handler", pose->TargetModel()->EntryRef());
731 		// add ref of the selected handler
732 
733 	ASSERT(fSelectionHandler != NULL);
734 	if (fSelectionHandler != NULL)
735 		fSelectionHandler->PostMessage(&message);
736 
737 	window->PostMessage(B_QUIT_REQUESTED);
738 }
739 
740 
741 void
742 OpenWithPoseView::Pulse()
743 {
744 	// disable the Open and make default button if the default
745 	// app matches the selected app
746 	//
747 	// disable the Open button if no apps selected
748 
749 	OpenWithContainerWindow* window = ContainerWindow();
750 
751 	if (!fSelectionList->CountItems()) {
752 		window->SetCanSetAppAsDefault(false);
753 		window->SetCanOpen(false);
754 		_inherited::Pulse();
755 		return;
756 	}
757 
758 	// if we selected a non-handling application, don't allow setting
759 	// it as preferred
760 	Model* firstSelected = fSelectionList->FirstItem()->TargetModel();
761 	if (OpenWithRelation(firstSelected) == kNoRelation) {
762 		window->SetCanSetAppAsDefault(false);
763 		window->SetCanOpen(true);
764 		_inherited::Pulse();
765 		return;
766 	}
767 
768 	// make the open button enabled, because we have na app selected
769 	window->SetCanOpen(true);
770 	if (!fHaveCommonPreferredApp) {
771 		window->SetCanSetAppAsDefault(true);
772 		_inherited::Pulse();
773 		return;
774 	}
775 
776 	ASSERT(fSelectionList->CountItems() == 1);
777 
778 	// enable the Open and make default if selected application different
779 	// from preferred app ref
780 	window->SetCanSetAppAsDefault((*fSelectionList->FirstItem()->
781 		TargetModel()->EntryRef()) != fPreferredRef);
782 
783 	_inherited::Pulse();
784 }
785 
786 
787 void
788 OpenWithPoseView::SetUpDefaultColumnsIfNeeded()
789 {
790 	// in case there were errors getting some columns
791 	if (fColumnList->CountItems() != 0)
792 		return;
793 
794 	BColumn* nameColumn = new BColumn(B_TRANSLATE("Name"), kColumnStart, 125,
795 		B_ALIGN_LEFT, kAttrStatName, B_STRING_TYPE, true, true);
796 	fColumnList->AddItem(nameColumn);
797 	BColumn* relationColumn = new BColumn(B_TRANSLATE("Relation"), 180, 100,
798 		B_ALIGN_LEFT, kAttrOpenWithRelation, B_STRING_TYPE, false, false);
799 	fColumnList->AddItem(relationColumn);
800 	fColumnList->AddItem(new BColumn(B_TRANSLATE("Location"), 290, 225,
801 		B_ALIGN_LEFT, kAttrPath, B_STRING_TYPE, true, false));
802 	fColumnList->AddItem(new BColumn(B_TRANSLATE("Version"), 525, 70,
803 		B_ALIGN_LEFT, kAttrAppVersion, B_STRING_TYPE, false, false));
804 
805 	// sort by relation and by name
806 	SetPrimarySort(relationColumn->AttrHash());
807 	SetSecondarySort(nameColumn->AttrHash());
808 }
809 
810 
811 bool
812 OpenWithPoseView::AddPosesThreadValid(const entry_ref*) const
813 {
814 	return true;
815 }
816 
817 
818 void
819 OpenWithPoseView::CreatePoses(Model** models, PoseInfo* poseInfoArray,
820 	int32 count, BPose** resultingPoses, bool insertionSort,
821 	int32* lastPoseIndexPtr, BRect* boundsPtr, bool forceDraw)
822 {
823 	// overridden to try to select the preferred handling app
824 	_inherited::CreatePoses(models, poseInfoArray, count, resultingPoses,
825 		insertionSort, lastPoseIndexPtr, boundsPtr, forceDraw);
826 
827 	if (resultingPoses != NULL) {
828 		for (int32 index = 0; index < count; index++) {
829 			if (resultingPoses[index] && fHaveCommonPreferredApp
830 				&& *(models[index]->EntryRef()) == fPreferredRef) {
831 				// this is our preferred app, select it's pose
832 				SelectPose(resultingPoses[index],
833 					IndexOfPose(resultingPoses[index]));
834 			}
835 		}
836 	}
837 }
838 
839 
840 void
841 OpenWithPoseView::KeyDown(const char* bytes, int32 count)
842 {
843 	if (bytes[0] == B_TAB) {
844 		// just shift the focus, don't tab to the next pose
845 		BView::KeyDown(bytes, count);
846 	} else
847 		_inherited::KeyDown(bytes, count);
848 }
849 
850 
851 void
852 OpenWithPoseView::SaveState(AttributeStreamNode* node)
853 {
854 	_inherited::SaveState(node);
855 }
856 
857 
858 void
859 OpenWithPoseView::RestoreState(AttributeStreamNode* node)
860 {
861 	_inherited::RestoreState(node);
862 	fViewState->SetViewMode(kListMode);
863 }
864 
865 
866 void
867 OpenWithPoseView::SaveState(BMessage &message) const
868 {
869 	_inherited::SaveState(message);
870 }
871 
872 
873 void
874 OpenWithPoseView::RestoreState(const BMessage &message)
875 {
876 	_inherited::RestoreState(message);
877 	fViewState->SetViewMode(kListMode);
878 }
879 
880 
881 void
882 OpenWithPoseView::SavePoseLocations(BRect*)
883 {
884 }
885 
886 
887 void
888 OpenWithPoseView::MoveSelectionToTrash(bool)
889 {
890 }
891 
892 
893 void
894 OpenWithPoseView::MoveSelectionTo(BPoint, BPoint, BContainerWindow*)
895 {
896 }
897 
898 
899 void
900 OpenWithPoseView::MoveSelectionInto(Model*, BContainerWindow*, bool, bool)
901 {
902 }
903 
904 
905 bool
906 OpenWithPoseView::Represents(const node_ref*) const
907 {
908 	return false;
909 }
910 
911 
912 bool
913 OpenWithPoseView::Represents(const entry_ref*) const
914 {
915 	return false;
916 }
917 
918 
919 bool
920 OpenWithPoseView::HandleMessageDropped(BMessage* DEBUG_ONLY(message))
921 {
922 #if DEBUG
923 	// in debug mode allow tweaking the colors
924 	const rgb_color* color;
925 	ssize_t size;
926 	// handle roColour-style color drops
927 	if (message->FindData("RGBColor", 'RGBC', (const void**)&color, &size)
928 			== B_OK) {
929 		SetViewColor(*color);
930 		SetLowColor(*color);
931 		Invalidate();
932 		return true;
933 	}
934 #endif
935 	return false;
936 }
937 
938 
939 int32
940 OpenWithPoseView::OpenWithRelation(const Model* model) const
941 {
942 	OpenWithContainerWindow* window = ContainerWindow();
943 
944 	return SearchForSignatureEntryList::Relation(window->EntryList(),
945 		model, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0);
946 }
947 
948 
949 void
950 OpenWithPoseView::OpenWithRelationDescription(const Model* model,
951 	BString* description) const
952 {
953 	OpenWithContainerWindow* window = ContainerWindow();
954 
955 	SearchForSignatureEntryList::RelationDescription(window->EntryList(),
956 		model, description, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0);
957 }
958 
959 
960 //  #pragma mark - OpenWithRefFilter
961 
962 
963 OpenWithRefFilter::OpenWithRefFilter(SearchForSignatureEntryList* iterator,
964 	const BMessage *entryList, entry_ref* preferredRef)
965 	:
966 	fIterator(iterator),
967 	fEntryList(entryList),
968 	fPreferredRef(preferredRef)
969 {
970 }
971 
972 
973 bool
974 OpenWithRefFilter::Filter(const entry_ref* ref, BNode* node, stat_beos* st,
975 	const char* filetype)
976 {
977 	Model *model = new Model(ref, true, true);
978 	bool canOpen = fIterator->CanOpenWithFilter(model, fEntryList,
979 		fPreferredRef);
980 	delete model;
981 
982 	return canOpen;
983 }
984 
985 
986 //	#pragma mark -
987 
988 
989 RelationCachingModelProxy::RelationCachingModelProxy(Model* model)
990 	:
991 	fModel(model),
992 	fRelation(kUnknownRelation)
993 {
994 }
995 
996 
997 RelationCachingModelProxy::~RelationCachingModelProxy()
998 {
999 	delete fModel;
1000 }
1001 
1002 
1003 int32
1004 RelationCachingModelProxy::Relation(SearchForSignatureEntryList* iterator,
1005 	BMessage* entries) const
1006 {
1007 	if (fRelation == kUnknownRelation)
1008 		fRelation = iterator->Relation(entries, fModel);
1009 
1010 	return fRelation;
1011 }
1012 
1013 
1014 //	#pragma mark - OpenWithMenu
1015 
1016 
1017 OpenWithMenu::OpenWithMenu(const char* label, const BMessage* entriesToOpen,
1018 	BWindow* parentWindow, BHandler* target)
1019 	:
1020 	BSlowMenu(label),
1021 	fEntriesToOpen(*entriesToOpen),
1022 	target(target),
1023 	fIterator(NULL),
1024 	fSupportingAppList(NULL),
1025 	fParentWindow(parentWindow)
1026 {
1027 	InitIconPreloader();
1028 
1029 	SetFont(be_plain_font);
1030 
1031 	// too long to have triggers
1032 	SetTriggersEnabled(false);
1033 }
1034 
1035 
1036 OpenWithMenu::OpenWithMenu(const char* label, const BMessage* entriesToOpen,
1037 	BWindow* parentWindow, const BMessenger &messenger)
1038 	:
1039 	BSlowMenu(label),
1040 	fEntriesToOpen(*entriesToOpen),
1041 	target(NULL),
1042 	fMessenger(messenger),
1043 	fIterator(NULL),
1044 	fSupportingAppList(NULL),
1045 	fParentWindow(parentWindow)
1046 {
1047 	InitIconPreloader();
1048 
1049 	SetFont(be_plain_font);
1050 
1051 	// too long to have triggers
1052 	SetTriggersEnabled(false);
1053 }
1054 
1055 
1056 namespace BPrivate {
1057 
1058 int
1059 SortByRelationAndName(const RelationCachingModelProxy* model1,
1060 	const RelationCachingModelProxy* model2, void* castToMenu)
1061 {
1062 	OpenWithMenu* menu = (OpenWithMenu*)castToMenu;
1063 
1064 	// find out the relations of app models to the opened entries
1065 	int32 relation1 = model1->Relation(menu->fIterator, &menu->fEntriesToOpen);
1066 	int32 relation2 = model2->Relation(menu->fIterator, &menu->fEntriesToOpen);
1067 
1068 	if (relation1 < relation2) {
1069 		// relation with the lowest number goes first
1070 		return 1;
1071 	} else if (relation1 > relation2)
1072 		return -1;
1073 
1074 	// if relations match, sort by app name
1075 	return strcmp(model1->fModel->Name(), model2->fModel->Name());
1076 }
1077 
1078 } // namespace BPrivate
1079 
1080 
1081 bool
1082 OpenWithMenu::StartBuildingItemList()
1083 {
1084 	fIterator = new SearchForSignatureEntryList(false);
1085 	// push all the supporting apps from all the entries into the
1086 	// search for signature iterator
1087 	EachEntryRef(&fEntriesToOpen, AddOneRefSignatures, fIterator, 100);
1088 	// add superhandlers
1089 	AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE);
1090 
1091 	fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef);
1092 	status_t error = fIterator->Rewind();
1093 	if (error != B_OK) {
1094 		PRINT(("failed to initialize iterator %s\n", strerror(error)));
1095 		return false;
1096 	}
1097 
1098 	fSupportingAppList = new BObjectList<RelationCachingModelProxy>(20, true);
1099 
1100 	//queryRetrieval = new BStopWatch("get next entry on BQuery");
1101 	return true;
1102 }
1103 
1104 
1105 bool
1106 OpenWithMenu::AddNextItem()
1107 {
1108 	BEntry entry;
1109 	if (fIterator->GetNextEntry(&entry) != B_OK)
1110 		return false;
1111 
1112 	Model* model = new Model(&entry, true);
1113 	if (model->InitCheck() != B_OK
1114 		|| !fIterator->CanOpenWithFilter(model, &fEntriesToOpen,
1115 			(fHaveCommonPreferredApp ? &fPreferredRef : 0))) {
1116 		// only allow executables, filter out multiple copies of the Tracker,
1117 		// filter out version that don't list the correct types, etc.
1118 		delete model;
1119 	} else
1120 		fSupportingAppList->AddItem(new RelationCachingModelProxy(model));
1121 
1122 	return true;
1123 }
1124 
1125 
1126 void
1127 OpenWithMenu::DoneBuildingItemList()
1128 {
1129 	// sort by app name
1130 	fSupportingAppList->SortItems(SortByRelationAndName, this);
1131 
1132 	// check if each app is unique
1133 	bool isUnique = true;
1134 	int32 count = fSupportingAppList->CountItems();
1135 	for (int32 index = 0; index < count - 1; index++) {
1136 		// the list is sorted, just compare two adjacent models
1137 		if (strcmp(fSupportingAppList->ItemAt(index)->fModel->Name(),
1138 			fSupportingAppList->ItemAt(index + 1)->fModel->Name()) == 0) {
1139 			isUnique = false;
1140 			break;
1141 		}
1142 	}
1143 
1144 	// add apps as menu items
1145 	BFont font;
1146 	GetFont(&font);
1147 
1148 	int32 lastRelation = -1;
1149 	for (int32 index = 0; index < count ; index++) {
1150 		RelationCachingModelProxy* modelProxy
1151 			= fSupportingAppList->ItemAt(index);
1152 		Model* model = modelProxy->fModel;
1153 		BMessage* message = new BMessage(fEntriesToOpen);
1154 		message->AddRef("handler", model->EntryRef());
1155 		BContainerWindow* window
1156 			= dynamic_cast<BContainerWindow*>(fParentWindow);
1157 		if (window != NULL) {
1158 			message->AddData("nodeRefsToClose", B_RAW_TYPE,
1159 				window->TargetModel()->NodeRef(), sizeof(node_ref));
1160 		}
1161 
1162 		BString result;
1163 		if (isUnique) {
1164 			// just use the app name
1165 			result = model->Name();
1166 		} else {
1167 			// get a truncated full path
1168 			BPath path;
1169 			BEntry entry(model->EntryRef());
1170 			if (entry.GetPath(&path) != B_OK) {
1171 				PRINT(("stale entry ref %s\n", model->Name()));
1172 				delete message;
1173 				continue;
1174 			}
1175 			result = path.Path();
1176 			font.TruncateString(&result, B_TRUNCATE_MIDDLE, kMaxMenuWidth);
1177 		}
1178 #if DEBUG
1179 		BString relationDescription;
1180 		fIterator->RelationDescription(&fEntriesToOpen, model, &relationDescription);
1181 		result += " (";
1182 		result += relationDescription;
1183 		result += ")";
1184 #endif
1185 
1186 		// divide different relations of opening with a separator
1187 		int32 relation = modelProxy->Relation(fIterator, &fEntriesToOpen);
1188 		if (lastRelation != -1 && relation != lastRelation)
1189 			AddSeparatorItem();
1190 		lastRelation = relation;
1191 
1192 		ModelMenuItem* item = new ModelMenuItem(model, result.String(),
1193 			message);
1194 		AddItem(item);
1195 		// mark item if it represents the preferred app
1196 		if (fHaveCommonPreferredApp && *(model->EntryRef()) == fPreferredRef) {
1197 			//PRINT(("marking item for % as preferred", model->Name()));
1198 			item->SetMarked(true);
1199 		}
1200 	}
1201 
1202 	// target the menu
1203 	if (target != NULL)
1204 		SetTargetForItems(target);
1205 	else
1206 		SetTargetForItems(fMessenger);
1207 
1208 	if (CountItems() == 0) {
1209 		BMenuItem* item = new BMenuItem(B_TRANSLATE("no supporting apps"), 0);
1210 		item->SetEnabled(false);
1211 		AddItem(item);
1212 	}
1213 }
1214 
1215 
1216 void
1217 OpenWithMenu::ClearMenuBuildingState()
1218 {
1219 	delete fIterator;
1220 	fIterator = NULL;
1221 	delete fSupportingAppList;
1222 	fSupportingAppList = NULL;
1223 }
1224 
1225 
1226 //	#pragma mark - SearchForSignatureEntryList
1227 
1228 
1229 SearchForSignatureEntryList::SearchForSignatureEntryList(bool canAddAllApps)
1230 	:
1231 	fIteratorList(NULL),
1232 	fSignatures(20, true),
1233 	fPreferredAppCount(0),
1234 	fPreferredAppForFileCount(0),
1235 	fGenericFilesOnly(true),
1236 	fCanAddAllApps(canAddAllApps),
1237 	fFoundOneNonSuperHandler(false)
1238 {
1239 }
1240 
1241 
1242 SearchForSignatureEntryList::~SearchForSignatureEntryList()
1243 {
1244 	delete fIteratorList;
1245 }
1246 
1247 
1248 void
1249 SearchForSignatureEntryList::PushUniqueSignature(const char* str)
1250 {
1251 	// do a unique add
1252 	if (fSignatures.EachElement(FindOne, (void*)str))
1253 		return;
1254 
1255 	fSignatures.AddItem(new BString(str));
1256 }
1257 
1258 
1259 status_t
1260 SearchForSignatureEntryList::GetNextEntry(BEntry* entry, bool)
1261 {
1262 	return fIteratorList->GetNextEntry(entry);
1263 }
1264 
1265 
1266 status_t
1267 SearchForSignatureEntryList::GetNextRef(entry_ref* ref)
1268 {
1269 	return fIteratorList->GetNextRef(ref);
1270 }
1271 
1272 
1273 int32
1274 SearchForSignatureEntryList::GetNextDirents(struct dirent* buffer,
1275 	size_t length, int32 count)
1276 {
1277 	return fIteratorList->GetNextDirents(buffer, length, count);
1278 }
1279 
1280 
1281 struct AddOneTermParams {
1282 	BString* result;
1283 	bool first;
1284 };
1285 
1286 
1287 static const BString*
1288 AddOnePredicateTerm(const BString* item, void* castToParams)
1289 {
1290 	AddOneTermParams* params = (AddOneTermParams*)castToParams;
1291 	if (!params->first)
1292 		(*params->result) << " || ";
1293 	(*params->result) << kAttrAppSignature << " = " << item->String();
1294 
1295 	params->first = false;
1296 
1297 	return 0;
1298 }
1299 
1300 
1301 status_t
1302 SearchForSignatureEntryList::Rewind()
1303 {
1304 	if (fIteratorList)
1305 		return fIteratorList->Rewind();
1306 
1307 	if (!fSignatures.CountItems())
1308 		return ENOENT;
1309 
1310 	// build up the iterator
1311 	fIteratorList = new CachedEntryIteratorList(false);
1312 		// We cannot sort the cached inodes, as CanOpenWithFilter() relies
1313 		// on the fact that ConditionalAllAppsIterator results come last.
1314 
1315 	// build the predicate string by oring queries for the individual
1316 	// signatures
1317 	BString predicateString;
1318 
1319 	AddOneTermParams params;
1320 	params.result = &predicateString;
1321 	params.first = true;
1322 
1323 	fSignatures.EachElement(AddOnePredicateTerm, &params);
1324 
1325 	ASSERT(predicateString.Length());
1326 //	PRINT(("query predicate %s\n", predicateString.String()));
1327 	fIteratorList->AddItem(new TWalkerWrapper(
1328 		new BTrackerPrivate::TQueryWalker(predicateString.String())));
1329 	fIteratorList->AddItem(new ConditionalAllAppsIterator(this));
1330 
1331 	return fIteratorList->Rewind();
1332 }
1333 
1334 
1335 int32
1336 SearchForSignatureEntryList::CountEntries()
1337 {
1338 	return 0;
1339 }
1340 
1341 
1342 bool
1343 SearchForSignatureEntryList::GetPreferredApp(entry_ref* ref) const
1344 {
1345 	if (fPreferredAppCount == 1)
1346 		*ref = fPreferredRef;
1347 
1348 	return fPreferredAppCount == 1;
1349 }
1350 
1351 
1352 void
1353 SearchForSignatureEntryList::TrySettingPreferredApp(const entry_ref* ref)
1354 {
1355 	if (!fPreferredAppCount) {
1356 		fPreferredRef = *ref;
1357 		fPreferredAppCount++;
1358 	} else if (fPreferredRef != *ref) {
1359 		// if more than one, will not return any
1360 		fPreferredAppCount++;
1361 	}
1362 }
1363 
1364 
1365 void
1366 SearchForSignatureEntryList::TrySettingPreferredAppForFile(const entry_ref* ref)
1367 {
1368 	if (!fPreferredAppForFileCount) {
1369 		fPreferredRefForFile = *ref;
1370 		fPreferredAppForFileCount++;
1371 	} else if (fPreferredRefForFile != *ref) {
1372 		// if more than one, will not return any
1373 		fPreferredAppForFileCount++;
1374 	}
1375 }
1376 
1377 
1378 void
1379 SearchForSignatureEntryList::NonGenericFileFound()
1380 {
1381 	fGenericFilesOnly = false;
1382 }
1383 
1384 
1385 bool
1386 SearchForSignatureEntryList::GenericFilesOnly() const
1387 {
1388 	return fGenericFilesOnly;
1389 }
1390 
1391 
1392 bool
1393 SearchForSignatureEntryList::ShowAllApplications() const
1394 {
1395 	return fCanAddAllApps && !fFoundOneNonSuperHandler;
1396 }
1397 
1398 
1399 int32
1400 SearchForSignatureEntryList::Relation(const Model* nodeModel,
1401 	const Model* applicationModel)
1402 {
1403 	int32 supportsMimeType = applicationModel->SupportsMimeType(
1404 		nodeModel->MimeType(), 0, true);
1405  	switch (supportsMimeType) {
1406 		case kDoesNotSupportType:
1407 			return kNoRelation;
1408 
1409 		case kSuperhandlerModel:
1410 			return kSuperhandler;
1411 
1412 		case kModelSupportsSupertype:
1413 			return kSupportsSupertype;
1414 
1415 		case kModelSupportsType:
1416 			return kSupportsType;
1417 	}
1418 
1419 	TRESPASS();
1420 	return kNoRelation;
1421 }
1422 
1423 
1424 int32
1425 SearchForSignatureEntryList::Relation(const BMessage* entriesToOpen,
1426 	const Model* model) const
1427 {
1428 	return Relation(entriesToOpen, model,
1429 		fPreferredAppCount == 1 ? &fPreferredRef : 0,
1430 		fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0);
1431 }
1432 
1433 
1434 void
1435 SearchForSignatureEntryList::RelationDescription(const BMessage* entriesToOpen,
1436 	const Model* model, BString* description) const
1437 {
1438 	RelationDescription(entriesToOpen, model, description,
1439 		fPreferredAppCount == 1 ? &fPreferredRef : 0,
1440 		fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0);
1441 }
1442 
1443 
1444 int32
1445 SearchForSignatureEntryList::Relation(const BMessage* entriesToOpen,
1446 	const Model* applicationModel, const entry_ref* preferredApp,
1447 	const entry_ref* preferredAppForFile)
1448 {
1449 	for (int32 index = 0; ; index++) {
1450 		entry_ref ref;
1451 		if (entriesToOpen->FindRef("refs", index, &ref) != B_OK)
1452 			break;
1453 
1454 		// need to init a model so that typeless folders etc. will still
1455 		// appear to have a mime type
1456 
1457 		Model model(&ref, true, true);
1458 		if (model.InitCheck())
1459 			continue;
1460 
1461 		int32 result = Relation(&model, applicationModel);
1462 		if (result != kNoRelation) {
1463 			if (preferredAppForFile
1464 				&& *applicationModel->EntryRef() == *preferredAppForFile) {
1465 				return kPreferredForFile;
1466 			}
1467 
1468 			if (result == kSupportsType && preferredApp
1469 				&& *applicationModel->EntryRef() == *preferredApp) {
1470 				// application matches cached preferred app, we are done
1471 				return kPreferredForType;
1472 			}
1473 
1474 			return result;
1475 		}
1476 	}
1477 
1478 	return kNoRelation;
1479 }
1480 
1481 
1482 void
1483 SearchForSignatureEntryList::RelationDescription(const BMessage* entriesToOpen,
1484 	const Model* applicationModel, BString* description,
1485 	const entry_ref* preferredApp, const entry_ref* preferredAppForFile)
1486 {
1487 	for (int32 index = 0; ;index++) {
1488 		entry_ref ref;
1489 		if (entriesToOpen->FindRef("refs", index, &ref) != B_OK)
1490 			break;
1491 
1492 		if (preferredAppForFile && ref == *preferredAppForFile) {
1493 			description->SetTo(B_TRANSLATE("Preferred for file"));
1494 			return;
1495 		}
1496 
1497 		Model model(&ref, true, true);
1498 		if (model.InitCheck())
1499 			continue;
1500 
1501 		BMimeType mimeType;
1502 		int32 result = Relation(&model, applicationModel);
1503 		switch (result) {
1504 			case kDoesNotSupportType:
1505 				continue;
1506 
1507 			case kSuperhandler:
1508 				description->SetTo(B_TRANSLATE("Handles any file"));
1509 				return;
1510 
1511 			case kSupportsSupertype:
1512 			{
1513 				mimeType.SetTo(model.MimeType());
1514 				// status_t result = mimeType.GetSupertype(&mimeType);
1515 
1516 				char* type = (char*)mimeType.Type();
1517 				char* tmp = strchr(type, '/');
1518 				if (tmp != NULL)
1519 					*tmp = '\0';
1520 
1521 				//PRINT(("getting supertype for %s, result %s, got %s\n",
1522 				//	model.MimeType(), strerror(result), mimeType.Type()));
1523 				description->SetTo(B_TRANSLATE("Handles any %type"));
1524 				//*description += mimeType.Type();
1525 				description->ReplaceFirst("%type", type);
1526 				return;
1527 			}
1528 
1529 			case kSupportsType:
1530 			{
1531 				mimeType.SetTo(model.MimeType());
1532 
1533 				if (preferredApp != NULL
1534 					&& *applicationModel->EntryRef() == *preferredApp) {
1535 					// application matches cached preferred app, we are done
1536 					description->SetTo(B_TRANSLATE("Preferred for %type"));
1537 				} else
1538 					description->SetTo(B_TRANSLATE("Handles %type"));
1539 
1540 				char shortDescription[256];
1541 				if (mimeType.GetShortDescription(shortDescription) == B_OK)
1542 					description->ReplaceFirst("%type", shortDescription);
1543 				else
1544 					description->ReplaceFirst("%type", mimeType.Type());
1545 
1546 				return;
1547 			}
1548 		}
1549 	}
1550 
1551 	description->SetTo(B_TRANSLATE("Does not handle file"));
1552 }
1553 
1554 
1555 bool
1556 SearchForSignatureEntryList::CanOpenWithFilter(const Model* appModel,
1557 	const BMessage* entriesToOpen, const entry_ref* preferredApp)
1558 {
1559 	if (!appModel->IsExecutable() || !appModel->Node()) {
1560 		// weed out non-executable
1561 #if xDEBUG
1562 		BPath path;
1563 		BEntry entry(appModel->EntryRef());
1564 		entry.GetPath(&path);
1565 		PRINT(("filtering out %s- not executable \n", path.Path()));
1566 #endif
1567 		return false;
1568 	}
1569 
1570 	if (strcasecmp(appModel->MimeType(), B_APP_MIME_TYPE) != 0) {
1571 		// filter out pe containers on PPC etc.
1572 		return false;
1573 	}
1574 
1575 	ASSERT(dynamic_cast<BFile*>(appModel->Node()));
1576 	char signature[B_MIME_TYPE_LENGTH];
1577 	status_t result = GetAppSignatureFromAttr(
1578 		dynamic_cast<BFile*>(appModel->Node()), signature);
1579 
1580 	if (result == B_OK && strcasecmp(signature, kTrackerSignature) == 0) {
1581 		// special case the Tracker - make sure only the running copy is
1582 		// in the list
1583 		app_info trackerInfo;
1584 		result = be_roster->GetActiveAppInfo(&trackerInfo);
1585 		if (*appModel->EntryRef() != trackerInfo.ref) {
1586 			// this is an inactive copy of the Tracker, remove it
1587 
1588 #if xDEBUG
1589 			BPath path, path2;
1590 			BEntry entry(appModel->EntryRef());
1591 			entry.GetPath(&path);
1592 
1593 			BEntry entry2(&trackerInfo.ref);
1594 			entry2.GetPath(&path2);
1595 
1596 			PRINT(
1597 				("filtering out %s, sig %s, active Tracker at %s, "
1598 				 "result %s, refName %s\n",
1599 				path.Path(), signature, path2.Path(), strerror(result),
1600 				trackerInfo.ref.name));
1601 #endif
1602 			return false;
1603 		}
1604 	}
1605 
1606 	if (FSInTrashDir(appModel->EntryRef()))
1607 		return false;
1608 
1609 	if (ShowAllApplications()) {
1610 		// don't check for these if we didn't look for every single app
1611 		// to not slow filtering down
1612 		uint32 flags;
1613 		BAppFileInfo appFileInfo(dynamic_cast<BFile*>(appModel->Node()));
1614 		if (appFileInfo.GetAppFlags(&flags) != B_OK)
1615 			return false;
1616 
1617 		if ((flags & B_BACKGROUND_APP) || (flags & B_ARGV_ONLY))
1618 			return false;
1619 
1620 		if (!signature[0])
1621 			// weed out apps with empty signatures
1622 			return false;
1623 	}
1624 
1625 	int32 relation = Relation(entriesToOpen, appModel, preferredApp, 0);
1626 	if (relation == kNoRelation && !ShowAllApplications()) {
1627 #if xDEBUG
1628 		BPath path;
1629 		BEntry entry(appModel->EntryRef());
1630 		entry.GetPath(&path);
1631 
1632 		PRINT(("filtering out %s, does not handle any of opened files\n",
1633 			path.Path()));
1634 #endif
1635 		return false;
1636 	}
1637 
1638 	if (relation != kNoRelation && relation != kSuperhandler
1639 		&& !fGenericFilesOnly) {
1640 		// we hit at least one app that is not a superhandler and
1641 		// handles the document
1642 		fFoundOneNonSuperHandler = true;
1643 	}
1644 
1645 	return true;
1646 }
1647 
1648 
1649 //	#pragma mark - ConditionalAllAppsIterator
1650 
1651 
1652 ConditionalAllAppsIterator::ConditionalAllAppsIterator(
1653 	SearchForSignatureEntryList* parent)
1654 	:
1655 	fParent(parent),
1656 	fWalker(NULL)
1657 {
1658 }
1659 
1660 
1661 void
1662 ConditionalAllAppsIterator::Instantiate()
1663 {
1664 	if (fWalker != NULL)
1665 		return;
1666 
1667 	BString lookForAppsPredicate;
1668 	lookForAppsPredicate << "(" << kAttrAppSignature << " = \"*\" ) && ( "
1669 		<< kAttrMIMEType << " = " << B_APP_MIME_TYPE << " ) ";
1670 	fWalker
1671 		= new BTrackerPrivate::TQueryWalker(lookForAppsPredicate.String());
1672 }
1673 
1674 
1675 ConditionalAllAppsIterator::~ConditionalAllAppsIterator()
1676 {
1677 	delete fWalker;
1678 }
1679 
1680 
1681 status_t
1682 ConditionalAllAppsIterator::GetNextEntry(BEntry* entry, bool traverse)
1683 {
1684 	if (!Iterate())
1685 		return B_ENTRY_NOT_FOUND;
1686 
1687 	Instantiate();
1688 	return fWalker->GetNextEntry(entry, traverse);
1689 }
1690 
1691 
1692 status_t
1693 ConditionalAllAppsIterator::GetNextRef(entry_ref* ref)
1694 {
1695 	if (!Iterate())
1696 		return B_ENTRY_NOT_FOUND;
1697 
1698 	Instantiate();
1699 	return fWalker->GetNextRef(ref);
1700 }
1701 
1702 
1703 int32
1704 ConditionalAllAppsIterator::GetNextDirents(struct dirent* buffer,
1705 	size_t length, int32 count)
1706 {
1707 	if (!Iterate())
1708 		return 0;
1709 
1710 	Instantiate();
1711 	return fWalker->GetNextDirents(buffer, length, count);
1712 }
1713 
1714 
1715 status_t
1716 ConditionalAllAppsIterator::Rewind()
1717 {
1718 	if (!Iterate())
1719 		return B_OK;
1720 
1721 	Instantiate();
1722 	return fWalker->Rewind();
1723 }
1724 
1725 
1726 int32
1727 ConditionalAllAppsIterator::CountEntries()
1728 {
1729 	if (!Iterate())
1730 		return 0;
1731 
1732 	Instantiate();
1733 	return fWalker->CountEntries();
1734 }
1735 
1736 
1737 bool
1738 ConditionalAllAppsIterator::Iterate() const
1739 {
1740 	return fParent->ShowAllApplications();
1741 }
1742