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