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