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