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