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