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