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