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