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