1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All 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
61const 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
69const float kMaxMenuWidth = 150;
70
71const int32 kDocumentKnobWidth = 16;
72const int32 kOpenAndMakeDefault = 'OpDf';
73const rgb_color kOpenWithDefaultColor = { 0xFF, 0xFF, 0xCC, 255};
74
75
76#undef B_TRANSLATION_CONTEXT
77#define B_TRANSLATION_CONTEXT "OpenWithWindow"
78
79OpenWithContainerWindow::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
163OpenWithContainerWindow::~OpenWithContainerWindow()
164{
165	delete fEntriesToOpen;
166}
167
168
169BPoseView*
170OpenWithContainerWindow::NewPoseView(Model*, BRect rect, uint32)
171{
172	return new OpenWithPoseView(rect);
173}
174
175
176OpenWithPoseView*
177OpenWithContainerWindow::PoseView() const
178{
179	ASSERT(dynamic_cast<OpenWithPoseView*>(fPoseView));
180	return static_cast<OpenWithPoseView*>(fPoseView);
181}
182
183
184const BMessage*
185OpenWithContainerWindow::EntryList() const
186{
187	return fEntriesToOpen;
188}
189
190
191void
192OpenWithContainerWindow::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
203static const BString*
204FindOne(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
213static const entry_ref*
214AddOneUniqueDocumentType(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
241static const BString*
242SetDefaultAppForOneType(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
283void
284OpenWithContainerWindow::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
311void
312OpenWithContainerWindow::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
340filter_result
341OpenWithContainerWindow::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
359bool
360OpenWithContainerWindow::ShouldAddMenus() const
361{
362	return false;
363}
364
365
366void
367OpenWithContainerWindow::ShowContextMenu(BPoint, const entry_ref*, BView*)
368{
369}
370
371
372void
373OpenWithContainerWindow::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
380void
381OpenWithContainerWindow::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
407void
408OpenWithContainerWindow::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
420void
421OpenWithContainerWindow::SaveState(BMessage &message) const
422{
423	_inherited::SaveState(message);
424}
425
426
427void
428OpenWithContainerWindow::Init(const BMessage* message)
429{
430	_inherited::Init(message);
431}
432
433
434void
435OpenWithContainerWindow::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
449void
450OpenWithContainerWindow::RestoreState(const BMessage &message)
451{
452	_inherited::RestoreState(message);
453}
454
455
456void
457OpenWithContainerWindow::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
473void
474OpenWithContainerWindow::RestoreWindowState(const BMessage &message)
475{
476	_inherited::RestoreWindowState(message);
477}
478
479
480bool
481OpenWithContainerWindow::NeedsDefaultStateSetup()
482{
483	return true;
484}
485
486
487void
488OpenWithContainerWindow::SetUpDefaultState()
489{
490}
491
492
493bool
494OpenWithContainerWindow::IsShowing(const node_ref*) const
495{
496	return false;
497}
498
499
500bool
501OpenWithContainerWindow::IsShowing(const entry_ref*) const
502{
503	return false;
504}
505
506
507void
508OpenWithContainerWindow::SetCanSetAppAsDefault(bool on)
509{
510	fLaunchAndMakeDefaultButton->SetEnabled(on);
511}
512
513
514void
515OpenWithContainerWindow::SetCanOpen(bool on)
516{
517	fLaunchButton->SetEnabled(on);
518}
519
520
521//	#pragma mark -
522
523
524OpenWithPoseView::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
535OpenWithContainerWindow*
536OpenWithPoseView::ContainerWindow() const
537{
538	ASSERT(dynamic_cast<OpenWithContainerWindow*>(Window()));
539	return static_cast<OpenWithContainerWindow*>(Window());
540}
541
542
543void
544OpenWithPoseView::AttachedToWindow()
545{
546	_inherited::AttachedToWindow();
547	SetViewColor(kOpenWithDefaultColor);
548	SetLowColor(kOpenWithDefaultColor);
549}
550
551
552bool
553OpenWithPoseView::CanHandleDragSelection(const Model*, const BMessage*, bool)
554{
555	return false;
556}
557
558
559static void
560AddSupportingAppForTypeToQuery(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
581static const entry_ref*
582AddOneRefSignatures(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
628EntryListBase*
629OpenWithPoseView::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	SetRefFilter(new OpenWithRefFilter(fIterator, entryList,
652		(fHaveCommonPreferredApp ? &fPreferredRef : 0)));
653	return fIterator;
654}
655
656
657void
658OpenWithPoseView::OpenSelection(BPose* pose, int32*)
659{
660	OpenWithContainerWindow* window = ContainerWindow();
661
662	int32 count = fSelectionList->CountItems();
663	if (!count)
664		return;
665
666	if (!pose)
667		pose = fSelectionList->FirstItem();
668
669	ASSERT(pose);
670
671	BEntry entry(pose->TargetModel()->EntryRef());
672	if (entry.InitCheck() != B_OK) {
673		BString errorString(
674			B_TRANSLATE("Could not find application \"%appname\""));
675		errorString.ReplaceFirst("%appname", pose->TargetModel()->Name());
676
677		BAlert* alert = new BAlert("", errorString.String(), B_TRANSLATE("OK"),
678			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
679		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
680		alert->Go();
681		return;
682	}
683
684	if (OpenWithRelation(pose->TargetModel()) == kNoRelation) {
685		if (!fIterator->GenericFilesOnly()) {
686			BString warning(B_TRANSLATE(
687				"The application \"%appname\" does not support the type of "
688				"document you are about to open.\nAre you sure you want to "
689				"proceed?\n\nIf you know that the application supports the "
690				"document type, you should contact the publisher of the "
691				"application and ask them to update their application to list "
692				"the type of your document as supported."));
693			warning.ReplaceFirst("%appname", pose->TargetModel()->Name());
694
695			BAlert* alert = new BAlert("", warning.String(),
696				B_TRANSLATE("Cancel"), B_TRANSLATE("Open"),	0, B_WIDTH_AS_USUAL,
697				B_WARNING_ALERT);
698			alert->SetShortcut(0, B_ESCAPE);
699			if (alert->Go() == 0)
700				return;
701		}
702		// else - once we have an extensible sniffer, tell users to ask
703		// publishers to fix up sniffers
704	}
705
706	BMessage message(*window->EntryList());
707		// make a clone to send
708	message.RemoveName("launchUsingSelector");
709		// make sure the old selector is not in the message
710	message.AddRef("handler", pose->TargetModel()->EntryRef());
711		// add ref of the selected handler
712
713	ASSERT(fSelectionHandler);
714
715	if (fSelectionHandler)
716		fSelectionHandler->PostMessage(&message);
717
718	window->PostMessage(B_QUIT_REQUESTED);
719}
720
721
722void
723OpenWithPoseView::Pulse()
724{
725	// disable the Open and make default button if the default
726	// app matches the selected app
727	//
728	// disable the Open button if no apps selected
729
730	OpenWithContainerWindow* window = ContainerWindow();
731
732	if (!fSelectionList->CountItems()) {
733		window->SetCanSetAppAsDefault(false);
734		window->SetCanOpen(false);
735		_inherited::Pulse();
736		return;
737	}
738
739	// if we selected a non-handling application, don't allow setting
740	// it as preferred
741	Model* firstSelected = fSelectionList->FirstItem()->TargetModel();
742	if (OpenWithRelation(firstSelected) == kNoRelation) {
743		window->SetCanSetAppAsDefault(false);
744		window->SetCanOpen(true);
745		_inherited::Pulse();
746		return;
747	}
748
749	// make the open button enabled, because we have na app selected
750	window->SetCanOpen(true);
751	if (!fHaveCommonPreferredApp) {
752		window->SetCanSetAppAsDefault(true);
753		_inherited::Pulse();
754		return;
755	}
756
757	ASSERT(fSelectionList->CountItems() == 1);
758
759	// enable the Open and make default if selected application different
760	// from preferred app ref
761	window->SetCanSetAppAsDefault((*fSelectionList->FirstItem()->
762		TargetModel()->EntryRef()) != fPreferredRef);
763
764	_inherited::Pulse();
765}
766
767
768void
769OpenWithPoseView::SetUpDefaultColumnsIfNeeded()
770{
771	// in case there were errors getting some columns
772	if (fColumnList->CountItems() != 0)
773		return;
774
775	BColumn* nameColumn = new BColumn(B_TRANSLATE("Name"), kColumnStart, 125,
776		B_ALIGN_LEFT, kAttrStatName, B_STRING_TYPE, true, true);
777	fColumnList->AddItem(nameColumn);
778	BColumn* relationColumn = new BColumn(B_TRANSLATE("Relation"), 180, 100,
779		B_ALIGN_LEFT, kAttrOpenWithRelation, B_STRING_TYPE, false, false);
780	fColumnList->AddItem(relationColumn);
781	fColumnList->AddItem(new BColumn(B_TRANSLATE("Location"), 290, 225,
782		B_ALIGN_LEFT, kAttrPath, B_STRING_TYPE, true, false));
783	fColumnList->AddItem(new BColumn(B_TRANSLATE("Version"), 525, 70,
784		B_ALIGN_LEFT, kAttrAppVersion, B_STRING_TYPE, false, false));
785
786	// sort by relation and by name
787	SetPrimarySort(relationColumn->AttrHash());
788	SetSecondarySort(nameColumn->AttrHash());
789}
790
791
792bool
793OpenWithPoseView::AddPosesThreadValid(const entry_ref*) const
794{
795	return true;
796}
797
798
799void
800OpenWithPoseView::CreatePoses(Model** models, PoseInfo* poseInfoArray, int32 count,
801	BPose** resultingPoses, bool insertionSort, int32* lastPoseIndexPtr,
802	BRect* boundsPtr, bool forceDraw)
803{
804	// overridden to try to select the preferred handling app
805	_inherited::CreatePoses(models, poseInfoArray, count, resultingPoses, insertionSort,
806		lastPoseIndexPtr, boundsPtr, forceDraw);
807
808	if (resultingPoses) {
809		for (int32 index = 0; index < count; index++) {
810			if (resultingPoses[index] && fHaveCommonPreferredApp
811				&& *(models[index]->EntryRef()) == fPreferredRef) {
812				// this is our preferred app, select it's pose
813				SelectPose(resultingPoses[index], IndexOfPose(resultingPoses[index]));
814			}
815		}
816	}
817}
818
819
820void
821OpenWithPoseView::KeyDown(const char* bytes, int32 count)
822{
823	if (bytes[0] == B_TAB) {
824		// just shift the focus, don't tab to the next pose
825		BView::KeyDown(bytes, count);
826	} else
827		_inherited::KeyDown(bytes, count);
828}
829
830
831void
832OpenWithPoseView::SaveState(AttributeStreamNode* node)
833{
834	_inherited::SaveState(node);
835}
836
837
838void
839OpenWithPoseView::RestoreState(AttributeStreamNode* node)
840{
841	_inherited::RestoreState(node);
842	fViewState->SetViewMode(kListMode);
843}
844
845
846void
847OpenWithPoseView::SaveState(BMessage &message) const
848{
849	_inherited::SaveState(message);
850}
851
852
853void
854OpenWithPoseView::RestoreState(const BMessage &message)
855{
856	_inherited::RestoreState(message);
857	fViewState->SetViewMode(kListMode);
858}
859
860
861void
862OpenWithPoseView::SavePoseLocations(BRect*)
863{
864	// do nothing
865}
866
867
868void
869OpenWithPoseView::MoveSelectionToTrash(bool)
870{
871}
872
873
874void
875OpenWithPoseView::MoveSelectionTo(BPoint, BPoint, BContainerWindow*)
876{
877}
878
879
880void
881OpenWithPoseView::MoveSelectionInto(Model*, BContainerWindow*, bool, bool)
882{
883}
884
885
886bool
887OpenWithPoseView::Represents(const node_ref*) const
888{
889	return false;
890}
891
892
893bool
894OpenWithPoseView::Represents(const entry_ref*) const
895{
896	return false;
897}
898
899
900bool
901OpenWithPoseView::HandleMessageDropped(BMessage* DEBUG_ONLY(message))
902{
903#if DEBUG
904	// in debug mode allow tweaking the colors
905	const rgb_color* color;
906	ssize_t size;
907	// handle roColour-style color drops
908	if (message->FindData("RGBColor", 'RGBC', (const void**)&color, &size) == B_OK) {
909		SetViewColor(*color);
910		SetLowColor(*color);
911		Invalidate();
912		return true;
913	}
914#endif
915	return false;
916}
917
918
919int32
920OpenWithPoseView::OpenWithRelation(const Model* model) const
921{
922	OpenWithContainerWindow* window = ContainerWindow();
923
924	return SearchForSignatureEntryList::Relation(window->EntryList(),
925		model, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0);
926}
927
928
929void
930OpenWithPoseView::OpenWithRelationDescription(const Model* model,
931	BString* description) const
932{
933	OpenWithContainerWindow* window = ContainerWindow();
934
935	SearchForSignatureEntryList::RelationDescription(window->EntryList(),
936		model, description, fHaveCommonPreferredApp ? &fPreferredRef : 0, 0);
937}
938
939
940//  #pragma mark -
941
942
943OpenWithRefFilter::OpenWithRefFilter(SearchForSignatureEntryList* iterator,
944	const BMessage *entryList, entry_ref* preferredRef)
945	:
946	fIterator(iterator),
947	fEntryList(entryList),
948	fPreferredRef(preferredRef)
949{
950}
951
952
953bool
954OpenWithRefFilter::Filter(const entry_ref* ref, BNode* node, stat_beos* st,
955	const char* filetype)
956{
957	Model *model = new Model(ref, true, true);
958	bool canOpen = fIterator->CanOpenWithFilter(model, fEntryList,
959		fPreferredRef);
960	delete model;
961	return canOpen;
962}
963
964
965//	#pragma mark -
966
967
968RelationCachingModelProxy::RelationCachingModelProxy(Model* model)
969	:
970	fModel(model),
971	fRelation(kUnknownRelation)
972{
973}
974
975
976RelationCachingModelProxy::~RelationCachingModelProxy()
977{
978	delete fModel;
979}
980
981
982int32
983RelationCachingModelProxy::Relation(SearchForSignatureEntryList* iterator,
984	BMessage* entries) const
985{
986	if (fRelation == kUnknownRelation)
987		fRelation = iterator->Relation(entries, fModel);
988
989	return fRelation;
990}
991
992
993//	#pragma mark -
994
995
996OpenWithMenu::OpenWithMenu(const char* label, const BMessage* entriesToOpen,
997		BWindow* parentWindow, BHandler* target)
998	: BSlowMenu(label),
999	fEntriesToOpen(*entriesToOpen),
1000	target(target),
1001	fIterator(NULL),
1002	fSupportingAppList(NULL),
1003	fParentWindow(parentWindow)
1004{
1005	InitIconPreloader();
1006
1007	SetFont(be_plain_font);
1008
1009	// too long to have triggers
1010	SetTriggersEnabled(false);
1011}
1012
1013
1014OpenWithMenu::OpenWithMenu(const char* label, const BMessage* entriesToOpen,
1015		BWindow* parentWindow, const BMessenger &messenger)
1016	: BSlowMenu(label),
1017	fEntriesToOpen(*entriesToOpen),
1018	target(NULL),
1019	fMessenger(messenger),
1020	fIterator(NULL),
1021	fSupportingAppList(NULL),
1022	fParentWindow(parentWindow)
1023{
1024	InitIconPreloader();
1025
1026	SetFont(be_plain_font);
1027
1028	// too long to have triggers
1029	SetTriggersEnabled(false);
1030}
1031
1032
1033namespace BPrivate {
1034
1035int
1036SortByRelationAndName(const RelationCachingModelProxy* model1,
1037	const RelationCachingModelProxy* model2, void* castToMenu)
1038{
1039	OpenWithMenu* menu = (OpenWithMenu*)castToMenu;
1040
1041	// find out the relations of app models to the opened entries
1042	int32 relation1 = model1->Relation(menu->fIterator, &menu->fEntriesToOpen);
1043	int32 relation2 = model2->Relation(menu->fIterator, &menu->fEntriesToOpen);
1044
1045	if (relation1 < relation2) {
1046		// relation with the lowest number goes first
1047		return 1;
1048	} else if (relation1 > relation2)
1049		return -1;
1050
1051	// if relations match, sort by app name
1052	return strcmp(model1->fModel->Name(), model2->fModel->Name());
1053}
1054
1055} // namespace BPrivate
1056
1057
1058bool
1059OpenWithMenu::StartBuildingItemList()
1060{
1061	fIterator = new SearchForSignatureEntryList(false);
1062	// push all the supporting apps from all the entries into the
1063	// search for signature iterator
1064	EachEntryRef(&fEntriesToOpen, AddOneRefSignatures, fIterator, 100);
1065	// add superhandlers
1066	AddSupportingAppForTypeToQuery(fIterator, B_FILE_MIMETYPE);
1067
1068	fHaveCommonPreferredApp = fIterator->GetPreferredApp(&fPreferredRef);
1069	status_t error = fIterator->Rewind();
1070	if (error != B_OK) {
1071		PRINT(("failed to initialize iterator %s\n", strerror(error)));
1072		return false;
1073	}
1074
1075	fSupportingAppList = new BObjectList<RelationCachingModelProxy>(20, true);
1076
1077	//queryRetrieval = new BStopWatch("get next entry on BQuery");
1078	return true;
1079}
1080
1081
1082bool
1083OpenWithMenu::AddNextItem()
1084{
1085	BEntry entry;
1086	if (fIterator->GetNextEntry(&entry) != B_OK)
1087		return false;
1088
1089	Model* model = new Model(&entry, true);
1090	if (model->InitCheck() != B_OK
1091		|| !fIterator->CanOpenWithFilter(model, &fEntriesToOpen,
1092				fHaveCommonPreferredApp ? &fPreferredRef : 0)) {
1093		// only allow executables, filter out multiple copies of the
1094		// Tracker, filter out version that don't list the correct types,
1095		// etc.
1096		delete model;
1097	} else
1098		fSupportingAppList->AddItem(new RelationCachingModelProxy(model));
1099
1100	return true;
1101}
1102
1103
1104void
1105OpenWithMenu::DoneBuildingItemList()
1106{
1107	// sort by app name
1108	fSupportingAppList->SortItems(SortByRelationAndName, this);
1109
1110	// check if each app is unique
1111	bool unique = true;
1112	int32 count = fSupportingAppList->CountItems();
1113	for (int32 index = 0; index < count - 1; index++) {
1114		// the list is sorted, just compare two adjacent models
1115		if (strcmp(fSupportingAppList->ItemAt(index)->fModel->Name(),
1116			fSupportingAppList->ItemAt(index + 1)->fModel->Name()) == 0) {
1117			unique = false;
1118			break;
1119		}
1120	}
1121
1122	// add apps as menu items
1123	BFont font;
1124	GetFont(&font);
1125
1126	int32 lastRelation = -1;
1127	for (int32 index = 0; index < count ; index++) {
1128		RelationCachingModelProxy* modelProxy = fSupportingAppList->ItemAt(index);
1129		Model* model = modelProxy->fModel;
1130		BMessage* message = new BMessage(fEntriesToOpen);
1131		message->AddRef("handler", model->EntryRef());
1132		BContainerWindow* window = dynamic_cast<BContainerWindow*>(fParentWindow);
1133		if (window)
1134			message->AddData("nodeRefsToClose", B_RAW_TYPE, window->TargetModel()->NodeRef(),
1135				sizeof (node_ref));
1136
1137		BString result;
1138		if (unique) {
1139			// just use the app name
1140			result = model->Name();
1141		} else {
1142			// get a truncated full path
1143			BPath path;
1144			BEntry entry(model->EntryRef());
1145			if (entry.GetPath(&path) != B_OK) {
1146				PRINT(("stale entry ref %s\n", model->Name()));
1147				delete message;
1148				continue;
1149			}
1150			result = path.Path();
1151			font.TruncateString(&result, B_TRUNCATE_MIDDLE, kMaxMenuWidth);
1152		}
1153#if DEBUG
1154		BString relationDescription;
1155		fIterator->RelationDescription(&fEntriesToOpen, model, &relationDescription);
1156		result += " (";
1157		result += relationDescription;
1158		result += ")";
1159#endif
1160
1161		// divide different relations of opening with a separator
1162		int32 relation = modelProxy->Relation(fIterator, &fEntriesToOpen);
1163		if (lastRelation != -1 && relation != lastRelation)
1164			AddSeparatorItem();
1165		lastRelation = relation;
1166
1167		ModelMenuItem* item = new ModelMenuItem(model, result.String(), message);
1168		AddItem(item);
1169		// mark item if it represents the preferred app
1170		if (fHaveCommonPreferredApp && *(model->EntryRef()) == fPreferredRef) {
1171			//PRINT(("marking item for % as preferred", model->Name()));
1172			item->SetMarked(true);
1173		}
1174	}
1175
1176	// target the menu
1177	if (target)
1178		SetTargetForItems(target);
1179	else
1180		SetTargetForItems(fMessenger);
1181
1182	if (!CountItems()) {
1183		BMenuItem* item = new BMenuItem(B_TRANSLATE("no supporting apps"), 0);
1184		item->SetEnabled(false);
1185		AddItem(item);
1186	}
1187}
1188
1189
1190void
1191OpenWithMenu::ClearMenuBuildingState()
1192{
1193	delete fIterator;
1194	fIterator = NULL;
1195	delete fSupportingAppList;
1196	fSupportingAppList = NULL;
1197}
1198
1199
1200//	#pragma mark -
1201
1202
1203SearchForSignatureEntryList::SearchForSignatureEntryList(bool canAddAllApps)
1204	:
1205	fIteratorList(NULL),
1206	fSignatures(20, true),
1207	fPreferredAppCount(0),
1208	fPreferredAppForFileCount(0),
1209	fGenericFilesOnly(true),
1210	fCanAddAllApps(canAddAllApps),
1211	fFoundOneNonSuperHandler(false)
1212{
1213}
1214
1215
1216SearchForSignatureEntryList::~SearchForSignatureEntryList()
1217{
1218	delete fIteratorList;
1219}
1220
1221
1222void
1223SearchForSignatureEntryList::PushUniqueSignature(const char* str)
1224{
1225	// do a unique add
1226	if (fSignatures.EachElement(FindOne, (void*)str))
1227		return;
1228
1229	fSignatures.AddItem(new BString(str));
1230}
1231
1232
1233status_t
1234SearchForSignatureEntryList::GetNextEntry(BEntry* entry, bool)
1235{
1236	return fIteratorList->GetNextEntry(entry);
1237}
1238
1239
1240status_t
1241SearchForSignatureEntryList::GetNextRef(entry_ref* ref)
1242{
1243	return fIteratorList->GetNextRef(ref);
1244}
1245
1246
1247int32
1248SearchForSignatureEntryList::GetNextDirents(struct dirent* buffer,
1249	size_t length, int32 count)
1250{
1251	return fIteratorList->GetNextDirents(buffer, length, count);
1252}
1253
1254
1255struct AddOneTermParams {
1256	BString* result;
1257	bool first;
1258};
1259
1260static const BString*
1261AddOnePredicateTerm(const BString* item, void* castToParams)
1262{
1263	AddOneTermParams* params = (AddOneTermParams*)castToParams;
1264	if (!params->first)
1265		(*params->result) << " || ";
1266	(*params->result) << kAttrAppSignature << " = " << item->String();
1267
1268	params->first = false;
1269
1270	return 0;
1271}
1272
1273
1274status_t
1275SearchForSignatureEntryList::Rewind()
1276{
1277	if (fIteratorList)
1278		return fIteratorList->Rewind();
1279
1280	if (!fSignatures.CountItems())
1281		return ENOENT;
1282
1283	// build up the iterator
1284	fIteratorList = new CachedEntryIteratorList(false);
1285		// We cannot sort the cached inodes, as CanOpenWithFilter() relies
1286		// on the fact that ConditionalAllAppsIterator results come last.
1287
1288	// build the predicate string by oring queries for the individual
1289	// signatures
1290	BString predicateString;
1291
1292	AddOneTermParams params;
1293	params.result = &predicateString;
1294	params.first = true;
1295
1296	fSignatures.EachElement(AddOnePredicateTerm, &params);
1297
1298	ASSERT(predicateString.Length());
1299// 	PRINT(("query predicate %s\n", predicateString.String()));
1300	fIteratorList->AddItem(new TWalkerWrapper(
1301		new BTrackerPrivate::TQueryWalker(predicateString.String())));
1302	fIteratorList->AddItem(new ConditionalAllAppsIterator(this));
1303
1304	return fIteratorList->Rewind();
1305}
1306
1307
1308int32
1309SearchForSignatureEntryList::CountEntries()
1310{
1311	return 0;
1312}
1313
1314
1315bool
1316SearchForSignatureEntryList::GetPreferredApp(entry_ref* ref) const
1317{
1318	if (fPreferredAppCount == 1)
1319		*ref = fPreferredRef;
1320
1321	return fPreferredAppCount == 1;
1322}
1323
1324
1325void
1326SearchForSignatureEntryList::TrySettingPreferredApp(const entry_ref* ref)
1327{
1328	if (!fPreferredAppCount) {
1329		fPreferredRef = *ref;
1330		fPreferredAppCount++;
1331	} else if (fPreferredRef != *ref)
1332		// if more than one, will not return any
1333		fPreferredAppCount++;
1334}
1335
1336
1337void
1338SearchForSignatureEntryList::TrySettingPreferredAppForFile(const entry_ref* ref)
1339{
1340	if (!fPreferredAppForFileCount) {
1341		fPreferredRefForFile = *ref;
1342		fPreferredAppForFileCount++;
1343	} else if (fPreferredRefForFile != *ref) {
1344		// if more than one, will not return any
1345		fPreferredAppForFileCount++;
1346	}
1347}
1348
1349
1350void
1351SearchForSignatureEntryList::NonGenericFileFound()
1352{
1353	fGenericFilesOnly = false;
1354}
1355
1356
1357bool
1358SearchForSignatureEntryList::GenericFilesOnly() const
1359{
1360	return fGenericFilesOnly;
1361}
1362
1363
1364bool
1365SearchForSignatureEntryList::ShowAllApplications() const
1366{
1367	return fCanAddAllApps && !fFoundOneNonSuperHandler;
1368}
1369
1370
1371int32
1372SearchForSignatureEntryList::Relation(const Model* nodeModel,
1373	const Model* applicationModel)
1374{
1375 	switch (applicationModel->SupportsMimeType(nodeModel->MimeType(), 0, true)) {
1376		case kDoesNotSupportType:
1377			return kNoRelation;
1378
1379		case kSuperhandlerModel:
1380			return kSuperhandler;
1381
1382		case kModelSupportsSupertype:
1383			return kSupportsSupertype;
1384
1385		case kModelSupportsType:
1386			return kSupportsType;
1387	}
1388
1389	TRESPASS();
1390	return kNoRelation;
1391}
1392
1393
1394int32
1395SearchForSignatureEntryList::Relation(const BMessage* entriesToOpen,
1396	const Model* model) const
1397{
1398	return Relation(entriesToOpen, model,
1399		fPreferredAppCount == 1 ? &fPreferredRef : 0,
1400		fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0);
1401}
1402
1403
1404void
1405SearchForSignatureEntryList::RelationDescription(const BMessage* entriesToOpen,
1406	const Model* model, BString* description) const
1407{
1408	RelationDescription(entriesToOpen, model, description,
1409		fPreferredAppCount == 1 ? &fPreferredRef : 0,
1410		fPreferredAppForFileCount == 1 ? &fPreferredRefForFile : 0);
1411}
1412
1413
1414int32
1415SearchForSignatureEntryList::Relation(const BMessage* entriesToOpen,
1416	const Model* applicationModel, const entry_ref* preferredApp,
1417	const entry_ref* preferredAppForFile)
1418{
1419	for (int32 index = 0; ; index++) {
1420		entry_ref ref;
1421		if (entriesToOpen->FindRef("refs", index, &ref) != B_OK)
1422			break;
1423
1424		// need to init a model so that typeless folders etc. will still appear to
1425		// have a mime type
1426
1427		Model model(&ref, true, true);
1428		if (model.InitCheck())
1429			continue;
1430
1431		int32 result = Relation(&model, applicationModel);
1432		if (result != kNoRelation) {
1433			if (preferredAppForFile
1434				&& *applicationModel->EntryRef() == *preferredAppForFile)
1435				return kPreferredForFile;
1436
1437			if (result == kSupportsType && preferredApp
1438				&& *applicationModel->EntryRef() == *preferredApp)
1439				// application matches cached preferred app, we are done
1440				return kPreferredForType;
1441
1442			return result;
1443		}
1444	}
1445
1446	return kNoRelation;
1447}
1448
1449
1450void
1451SearchForSignatureEntryList::RelationDescription(const BMessage* entriesToOpen,
1452	const Model* applicationModel, BString* description,
1453	const entry_ref* preferredApp, const entry_ref* preferredAppForFile)
1454{
1455	for (int32 index = 0; ;index++) {
1456		entry_ref ref;
1457		if (entriesToOpen->FindRef("refs", index, &ref) != B_OK)
1458			break;
1459
1460		if (preferredAppForFile && ref == *preferredAppForFile) {
1461			description->SetTo(B_TRANSLATE("Preferred for file"));
1462			return;
1463		}
1464
1465		Model model(&ref, true, true);
1466		if (model.InitCheck())
1467			continue;
1468
1469		BMimeType mimeType;
1470		int32 result = Relation(&model, applicationModel);
1471		switch (result) {
1472			case kDoesNotSupportType:
1473				continue;
1474
1475			case kSuperhandler:
1476				description->SetTo(B_TRANSLATE("Handles any file"));
1477				return;
1478
1479			case kSupportsSupertype:
1480			{
1481				mimeType.SetTo(model.MimeType());
1482				// status_t result = mimeType.GetSupertype(&mimeType);
1483
1484				char* type = (char*)mimeType.Type();
1485				char* tmp = strchr(type, '/');
1486				if (tmp)
1487					*tmp = '\0';
1488
1489				//PRINT(("getting supertype for %s, result %s, got %s\n",
1490				//	model.MimeType(), strerror(result), mimeType.Type()));
1491				description->SetTo(B_TRANSLATE("Handles any %type"));
1492				// *description += mimeType.Type();
1493				description->ReplaceFirst("%type", type);
1494				return;
1495			}
1496
1497			case kSupportsType:
1498			{
1499				mimeType.SetTo(model.MimeType());
1500
1501				if (preferredApp
1502					&& *applicationModel->EntryRef() == *preferredApp) {
1503					// application matches cached preferred app, we are done
1504					description->SetTo(B_TRANSLATE("Preferred for %type"));
1505				} else
1506					description->SetTo(B_TRANSLATE("Handles %type"));
1507
1508				char shortDescription[256];
1509				if (mimeType.GetShortDescription(shortDescription) == B_OK)
1510					description->ReplaceFirst("%type", shortDescription);
1511				else
1512					description->ReplaceFirst("%type", mimeType.Type());
1513				return;
1514			}
1515		}
1516	}
1517
1518	description->SetTo(B_TRANSLATE("Does not handle file"));
1519}
1520
1521
1522bool
1523SearchForSignatureEntryList::CanOpenWithFilter(const Model* appModel,
1524	const BMessage* entriesToOpen, const entry_ref* preferredApp)
1525{
1526	if (!appModel->IsExecutable() || !appModel->Node()) {
1527		// weed out non-executable
1528#if xDEBUG
1529		BPath path;
1530		BEntry entry(appModel->EntryRef());
1531		entry.GetPath(&path);
1532		PRINT(("filtering out %s- not executable \n", path.Path()));
1533#endif
1534		return false;
1535	}
1536
1537	if (strcasecmp(appModel->MimeType(), B_APP_MIME_TYPE) != 0) {
1538		// filter out pe containers on PPC etc.
1539		return false;
1540	}
1541
1542	ASSERT(dynamic_cast<BFile*>(appModel->Node()));
1543	char signature[B_MIME_TYPE_LENGTH];
1544	status_t result = GetAppSignatureFromAttr(
1545		dynamic_cast<BFile*>(appModel->Node()), signature);
1546
1547	if (result == B_OK && strcasecmp(signature, kTrackerSignature) == 0) {
1548		// special case the Tracker - make sure only the running copy is
1549		// in the list
1550		app_info trackerInfo;
1551		result = be_roster->GetActiveAppInfo(&trackerInfo);
1552		if (*appModel->EntryRef() != trackerInfo.ref) {
1553			// this is an inactive copy of the Tracker, remove it
1554
1555#if xDEBUG
1556			BPath path, path2;
1557			BEntry entry(appModel->EntryRef());
1558			entry.GetPath(&path);
1559
1560			BEntry entry2(&trackerInfo.ref);
1561			entry2.GetPath(&path2);
1562
1563			PRINT(
1564				("filtering out %s, sig %s, active Tracker at %s, "
1565				 "result %s, refName %s\n",
1566				path.Path(), signature, path2.Path(), strerror(result),
1567				trackerInfo.ref.name));
1568#endif
1569			return false;
1570		}
1571	}
1572
1573	if (FSInTrashDir(appModel->EntryRef()))
1574		return false;
1575
1576	if (ShowAllApplications()) {
1577		// don't check for these if we didn't look for every single app
1578		// to not slow filtering down
1579		uint32 flags;
1580		BAppFileInfo appFileInfo(dynamic_cast<BFile*>(appModel->Node()));
1581		if (appFileInfo.GetAppFlags(&flags) != B_OK)
1582			return false;
1583
1584		if ((flags & B_BACKGROUND_APP) || (flags & B_ARGV_ONLY))
1585			return false;
1586
1587		if (!signature[0])
1588			// weed out apps with empty signatures
1589			return false;
1590	}
1591
1592	int32 relation = Relation(entriesToOpen, appModel, preferredApp, 0);
1593	if (relation == kNoRelation && !ShowAllApplications()) {
1594#if xDEBUG
1595		BPath path;
1596		BEntry entry(appModel->EntryRef());
1597		entry.GetPath(&path);
1598
1599		PRINT(("filtering out %s, does not handle any of opened files\n",
1600			path.Path()));
1601#endif
1602		return false;
1603	}
1604
1605	if (relation != kNoRelation && relation != kSuperhandler
1606		&& !fGenericFilesOnly) {
1607		// we hit at least one app that is not a superhandler and
1608		// handles the document
1609		fFoundOneNonSuperHandler = true;
1610	}
1611
1612	return true;
1613}
1614
1615
1616//	#pragma mark -
1617
1618
1619ConditionalAllAppsIterator::ConditionalAllAppsIterator(
1620		SearchForSignatureEntryList* parent)
1621	:
1622	fParent(parent),
1623	fWalker(NULL)
1624{
1625}
1626
1627
1628void
1629ConditionalAllAppsIterator::Instantiate()
1630{
1631	if (fWalker)
1632		return;
1633
1634	BString lookForAppsPredicate;
1635	lookForAppsPredicate << "(" << kAttrAppSignature << " = \"*\" ) && ( "
1636		<< kAttrMIMEType << " = " << B_APP_MIME_TYPE << " ) ";
1637	fWalker
1638		= new BTrackerPrivate::TQueryWalker(lookForAppsPredicate.String());
1639}
1640
1641
1642ConditionalAllAppsIterator::~ConditionalAllAppsIterator()
1643{
1644	delete fWalker;
1645}
1646
1647
1648status_t
1649ConditionalAllAppsIterator::GetNextEntry(BEntry* entry, bool traverse)
1650{
1651	if (!Iterate())
1652		return B_ENTRY_NOT_FOUND;
1653
1654	Instantiate();
1655	return fWalker->GetNextEntry(entry, traverse);
1656}
1657
1658
1659status_t
1660ConditionalAllAppsIterator::GetNextRef(entry_ref* ref)
1661{
1662	if (!Iterate())
1663		return B_ENTRY_NOT_FOUND;
1664
1665	Instantiate();
1666	return fWalker->GetNextRef(ref);
1667}
1668
1669
1670int32
1671ConditionalAllAppsIterator::GetNextDirents(struct dirent* buffer,
1672	size_t length, int32 count)
1673{
1674	if (!Iterate())
1675		return 0;
1676
1677	Instantiate();
1678	return fWalker->GetNextDirents(buffer, length, count);
1679}
1680
1681
1682status_t
1683ConditionalAllAppsIterator::Rewind()
1684{
1685	if (!Iterate())
1686		return B_OK;
1687
1688	Instantiate();
1689	return fWalker->Rewind();
1690}
1691
1692
1693int32
1694ConditionalAllAppsIterator::CountEntries()
1695{
1696	if (!Iterate())
1697		return 0;
1698
1699	Instantiate();
1700	return fWalker->CountEntries();
1701}
1702
1703
1704bool
1705ConditionalAllAppsIterator::Iterate() const
1706{
1707	return fParent->ShowAllApplications();
1708}
1709