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