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