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