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