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