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