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