6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
35 #include "Attributes.h"
39 #include "IconMenuItem.h"
40 #include "OpenWithWindow.h"
41 #include "MimeTypes.h"
42 #include "StopWatch.h"
48 #include <GroupView.h>
55 #include <SpaceLayoutItem.h>
57 #include <VolumeRoster.h>
64 const char* kDefaultOpenWithTemplate
= "OpenWithSettings";
68 // allow column configuring
69 // make SaveState/RestoreState save the current window setting for
72 const float kMaxMenuWidth
= 150;
74 const int32 kDocumentKnobWidth
= 16;
75 const int32 kOpenAndMakeDefault
= 'OpDf';
76 const rgb_color kOpenWithDefaultColor
= { 0xFF, 0xFF, 0xCC, 255};
79 // #pragma mark - OpenWithContainerWindow
82 #undef B_TRANSLATION_CONTEXT
83 #define B_TRANSLATION_CONTEXT "OpenWithWindow"
86 OpenWithContainerWindow::OpenWithContainerWindow(BMessage
* entriesToOpen
,
87 LockingList
<BWindow
>* windowList
)
89 BContainerWindow(windowList
, 0),
90 fEntriesToOpen(entriesToOpen
)
92 AutoLock
<BWindow
> lock(this);
94 BRect
windowRect(85, 50, 718, 296);
95 MoveTo(windowRect
.LeftTop());
96 ResizeTo(windowRect
.Width(), windowRect
.Height());
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);
103 fLaunchButton
= new BButton("ok", B_TRANSLATE("Open"),
104 new BMessage(kDefaultButton
));
106 fLaunchButton
->MakeDefault(true);
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);
114 fCancelButton
= new BButton("cancel", B_TRANSLATE("Cancel"),
115 new BMessage(kCancelButton
));
118 fPoseView
= NewPoseView(NULL
, kListMode
);
119 fBorderedView
->GroupLayout()->AddView(fPoseView
);
121 fPoseView
->SetFlags(fPoseView
->Flags() | B_NAVIGABLE
);
122 fPoseView
->SetPoseEditing(false);
124 // set the window title
125 if (CountRefs(fEntriesToOpen
) == 1) {
126 // if opening just one file, use it in the title
128 fEntriesToOpen
->FindRef("refs", &ref
);
129 BString
buffer(B_TRANSLATE("Open %name with:"));
130 buffer
.ReplaceFirst("%name", ref
.name
);
132 SetTitle(buffer
.String());
135 SetTitle(B_TRANSLATE("Open selection with:"));
138 AddCommonFilter(new BMessageFilter(B_KEY_DOWN
,
139 &OpenWithContainerWindow::KeyDownFilter
));
143 OpenWithContainerWindow::~OpenWithContainerWindow()
145 delete fEntriesToOpen
;
150 OpenWithContainerWindow::NewPoseView(Model
*, uint32
)
152 return new OpenWithPoseView
;
157 OpenWithContainerWindow::PoseView() const
159 ASSERT(dynamic_cast<OpenWithPoseView
*>(fPoseView
) != NULL
);
161 return static_cast<OpenWithPoseView
*>(fPoseView
);
166 OpenWithContainerWindow::EntryList() const
168 return fEntriesToOpen
;
173 OpenWithContainerWindow::OpenWithSelection()
175 int32 count
= PoseView()->SelectionList()->CountItems();
180 PoseView()->OpenSelection(PoseView()->SelectionList()->FirstItem(), 0);
184 static const BString
*
185 FindOne(const BString
* element
, void* castToString
)
187 if (strcasecmp(element
->String(), (const char*)castToString
) == 0)
194 static const entry_ref
*
195 AddOneUniqueDocumentType(const entry_ref
* ref
, void* castToList
)
197 BObjectList
<BString
>* list
= (BObjectList
<BString
>*)castToList
;
199 BEntry
entry(ref
, true);
202 // get this documents type
203 char type
[B_MIME_TYPE_LENGTH
];
204 BFile
file(&entry
, O_RDONLY
);
205 if (file
.InitCheck() != B_OK
)
208 BNodeInfo
info(&file
);
209 if (info
.GetType(type
) != B_OK
)
212 if (list
->EachElement(FindOne
, &type
))
213 // type already in list, bail
217 list
->AddItem(new BString(type
));
223 static const BString
*
224 SetDefaultAppForOneType(const BString
* element
, void* castToEntryRef
)
226 const entry_ref
* appRef
= (const entry_ref
*)castToEntryRef
;
228 // set entry as default handler for one mime string
229 BMimeType
mime(element
->String());
230 if (!mime
.IsInstalled())
233 // first set it's app signature as the preferred type
234 BFile
appFile(appRef
, O_RDONLY
);
235 if (appFile
.InitCheck() != B_OK
)
238 char appSignature
[B_MIME_TYPE_LENGTH
];
239 if (GetAppSignatureFromAttr(&appFile
, appSignature
) != B_OK
)
242 if (mime
.SetPreferredApp(appSignature
) != B_OK
)
245 // set the app hint on the metamime for this signature
246 mime
.SetTo(appSignature
);
250 mime
.SetAppHint(appRef
);
253 BEntry
debugEntry(appRef
);
255 debugEntry
.GetPath(&debugPath
);
257 PRINT(("setting %s, sig %s as default app for %s, result %s\n",
258 debugPath
.Path(), appSignature
, element
->String(), strerror(result
)));
266 OpenWithContainerWindow::MakeDefaultAndOpen()
268 int32 count
= PoseView()->SelectionList()->CountItems();
273 BPose
* selectedAppPose
= PoseView()->SelectionList()->FirstItem();
274 ASSERT(selectedAppPose
!= NULL
);
275 if (selectedAppPose
== NULL
)
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);
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());
287 // done setting the default application, now launch the app with the
294 OpenWithContainerWindow::MessageReceived(BMessage
* message
)
296 switch (message
->what
) {
299 PostMessage(B_QUIT_REQUESTED
);
302 case kOpenAndMakeDefault
:
303 MakeDefaultAndOpen();
304 PostMessage(B_QUIT_REQUESTED
);
308 PostMessage(B_QUIT_REQUESTED
);
311 case B_OBSERVER_NOTICE_CHANGE
:
319 _inherited::MessageReceived(message
);
324 OpenWithContainerWindow::KeyDownFilter(BMessage
* message
, BHandler
**,
325 BMessageFilter
* filter
)
328 if (message
->FindInt8("byte", (int8
*)&key
) != B_OK
)
329 return B_DISPATCH_MESSAGE
;
332 message
->FindInt32("modifiers", &modifiers
);
333 if (modifiers
== 0 && key
== B_ESCAPE
) {
334 filter
->Looper()->PostMessage(kCancelButton
);
335 return B_SKIP_MESSAGE
;
338 return B_DISPATCH_MESSAGE
;
343 OpenWithContainerWindow::ShouldAddMenus() const
350 OpenWithContainerWindow::ShowContextMenu(BPoint
, const entry_ref
*, BView
*)
356 OpenWithContainerWindow::AddShortcuts()
358 AddShortcut('I', B_COMMAND_KEY
, new BMessage(kGetInfo
), PoseView());
359 AddShortcut('Y', B_COMMAND_KEY
, new BMessage(kResizeToFit
), PoseView());
364 OpenWithContainerWindow::NewAttributeMenu(BMenu
* menu
)
366 _inherited::NewAttributeMenu(menu
);
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);
378 BMenuItem
* item
= new BMenuItem(B_TRANSLATE("Relation"), message
);
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);
390 item
= new BMenuItem(B_TRANSLATE("Version"), message
);
396 OpenWithContainerWindow::SaveState(bool)
398 BNode defaultingNode
;
399 if (DefaultStateSourceNode(kDefaultOpenWithTemplate
, &defaultingNode
,
401 AttributeStreamFileNode
streamNodeDestination(&defaultingNode
);
402 SaveWindowState(&streamNodeDestination
);
403 fPoseView
->SaveState(&streamNodeDestination
);
409 OpenWithContainerWindow::SaveState(BMessage
&message
) const
411 _inherited::SaveState(message
);
416 OpenWithContainerWindow::Init(const BMessage
* message
)
418 _inherited::Init(message
);
423 OpenWithContainerWindow::InitLayout()
425 _inherited::InitLayout();
427 // Remove the menu container, since we don't have a menu bar
428 fMenuContainer
->RemoveSelf();
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);
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
);
445 OpenWithContainerWindow::RestoreState()
447 BNode defaultingNode
;
448 if (DefaultStateSourceNode(kDefaultOpenWithTemplate
, &defaultingNode
,
450 AttributeStreamFileNode
streamNodeSource(&defaultingNode
);
451 RestoreWindowState(&streamNodeSource
);
452 fPoseView
->Init(&streamNodeSource
);
454 RestoreWindowState(NULL
);
455 fPoseView
->Init(NULL
);
462 OpenWithContainerWindow::RestoreState(const BMessage
&message
)
464 _inherited::RestoreState(message
);
469 OpenWithContainerWindow::RestoreWindowState(AttributeStreamNode
* node
)
474 const char* rectAttributeName
= kAttrWindowFrame
;
475 BRect
frame(Frame());
476 if (node
->Read(rectAttributeName
, 0, B_RECT_TYPE
, sizeof(BRect
), &frame
)
478 MoveTo(frame
.LeftTop());
479 ResizeTo(frame
.Width(), frame
.Height());
485 OpenWithContainerWindow::RestoreWindowState(const BMessage
&message
)
487 _inherited::RestoreWindowState(message
);
492 OpenWithContainerWindow::NeedsDefaultStateSetup()
499 OpenWithContainerWindow::SetUpDefaultState()
505 OpenWithContainerWindow::IsShowing(const node_ref
*) const
512 OpenWithContainerWindow::IsShowing(const entry_ref
*) const
519 OpenWithContainerWindow::SetCanSetAppAsDefault(bool on
)
521 fLaunchAndMakeDefaultButton
->SetEnabled(on
);
526 OpenWithContainerWindow::SetCanOpen(bool on
)
528 fLaunchButton
->SetEnabled(on
);
532 // #pragma mark - OpenWithPoseView
535 OpenWithPoseView::OpenWithPoseView()
537 BPoseView(new Model(), kListMode
),
538 fHaveCommonPreferredApp(false),
542 fSavePoseLocations
= false;
543 fMultipleSelection
= false;
544 fDragEnabled
= false;
548 OpenWithPoseView::~OpenWithPoseView()
555 OpenWithContainerWindow
*
556 OpenWithPoseView::ContainerWindow() const
558 OpenWithContainerWindow
* window
559 = dynamic_cast<OpenWithContainerWindow
*>(Window());
560 ASSERT(window
!= NULL
);
567 OpenWithPoseView::AttachedToWindow()
569 _inherited::AttachedToWindow();
571 SetViewColor(kOpenWithDefaultColor
);
572 SetLowColor(kOpenWithDefaultColor
);
577 OpenWithPoseView::CanHandleDragSelection(const Model
*, const BMessage
*, bool)
584 AddSupportingAppForTypeToQuery(SearchForSignatureEntryList
* queryIterator
,
587 // get supporting apps for type
588 BMimeType
mime(type
);
589 if (!mime
.IsInstalled())
593 mime
.GetSupportingApps(&message
);
595 // push each of the supporting apps signature uniquely
597 const char* signature
;
598 for (int32 index
= 0; message
.FindString("applications", index
,
599 &signature
) == B_OK
; index
++) {
600 queryIterator
->PushUniqueSignature(signature
);
605 static const entry_ref
*
606 AddOneRefSignatures(const entry_ref
* ref
, void* castToIterator
)
608 // TODO: resolve cases where each entry has a different type and
609 // their supporting apps are disjoint sets
611 SearchForSignatureEntryList
* queryIterator
=
612 (SearchForSignatureEntryList
*)castToIterator
;
614 Model
model(ref
, true, true);
615 if (model
.InitCheck() != B_OK
)
618 BString
mimeType(model
.MimeType());
620 if (!mimeType
.Length() || mimeType
.ICompare(B_FILE_MIMETYPE
) == 0)
621 // if model is of unknown type, try mimeseting it first
624 entry_ref preferredRef
;
626 // add preferred app for file, if any
627 if (model
.PreferredAppSignature()[0]) {
628 // got one, mark it as preferred for this node
629 if (be_roster
->FindApp(model
.PreferredAppSignature(), &preferredRef
)
631 queryIterator
->PushUniqueSignature(model
.PreferredAppSignature());
632 queryIterator
->TrySettingPreferredAppForFile(&preferredRef
);
636 mimeType
= model
.MimeType();
639 if (mimeType
.Length() && mimeType
.ICompare(B_FILE_MIMETYPE
) != 0)
640 queryIterator
->NonGenericFileFound();
642 // get supporting apps for type
643 AddSupportingAppForTypeToQuery(queryIterator
, mimeType
.String());
645 // find the preferred app for this type
646 if (be_roster
->FindApp(mimeType
.String(), &preferredRef
) == B_OK
)
647 queryIterator
->TrySettingPreferredApp(&preferredRef
);
654 OpenWithPoseView::InitDirentIterator(const entry_ref
*)
656 OpenWithContainerWindow
* window
= ContainerWindow();
658 const BMessage
* entryList
= window
->EntryList();
660 fIterator
= new SearchForSignatureEntryList(true);
662 // push all the supporting apps from all the entries into the
663 // search for signature iterator
664 EachEntryRef(entryList
, AddOneRefSignatures
, fIterator
, 100);
666 // push superhandlers
667 AddSupportingAppForTypeToQuery(fIterator
, B_FILE_MIMETYPE
);
668 fHaveCommonPreferredApp
= fIterator
->GetPreferredApp(&fPreferredRef
);
670 if (fIterator
->Rewind() != B_OK
) {
677 fRefFilter
= new OpenWithRefFilter(fIterator
, entryList
,
678 fHaveCommonPreferredApp
? &fPreferredRef
: 0);
679 SetRefFilter(fRefFilter
);
686 OpenWithPoseView::ReturnDirentIterator(EntryListBase
* iterator
)
688 // Do nothing. We keep our fIterator around as it is used by fRefFilter.
693 OpenWithPoseView::OpenSelection(BPose
* pose
, int32
*)
695 OpenWithContainerWindow
* window
= ContainerWindow();
697 int32 count
= fSelectionList
->CountItems();
702 pose
= fSelectionList
->FirstItem();
704 ASSERT(pose
!= NULL
);
706 BEntry
entry(pose
->TargetModel()->EntryRef());
707 if (entry
.InitCheck() != B_OK
) {
709 B_TRANSLATE("Could not find application \"%appname\""));
710 errorString
.ReplaceFirst("%appname", pose
->TargetModel()->Name());
712 BAlert
* alert
= new BAlert("", errorString
.String(), B_TRANSLATE("OK"),
713 0, 0, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
714 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
719 if (OpenWithRelation(pose
->TargetModel()) == kNoRelation
) {
720 if (!fIterator
->GenericFilesOnly()) {
721 BString
warning(B_TRANSLATE(
722 "The application \"%appname\" does not support the type of "
723 "document you are about to open.\nAre you sure you want to "
724 "proceed?\n\nIf you know that the application supports the "
725 "document type, you should contact the publisher of the "
726 "application and ask them to update their application to list "
727 "the type of your document as supported."));
728 warning
.ReplaceFirst("%appname", pose
->TargetModel()->Name());
730 BAlert
* alert
= new BAlert("", warning
.String(),
731 B_TRANSLATE("Cancel"), B_TRANSLATE("Open"), 0, B_WIDTH_AS_USUAL
,
733 alert
->SetShortcut(0, B_ESCAPE
);
734 if (alert
->Go() == 0)
737 // else - once we have an extensible sniffer, tell users to ask
738 // publishers to fix up sniffers
741 BMessage
message(*window
->EntryList());
742 // make a clone to send
743 message
.RemoveName("launchUsingSelector");
744 // make sure the old selector is not in the message
745 message
.AddRef("handler", pose
->TargetModel()->EntryRef());
746 // add ref of the selected handler
748 ASSERT(fSelectionHandler
!= NULL
);
749 if (fSelectionHandler
!= NULL
)
750 fSelectionHandler
->PostMessage(&message
);
752 window
->PostMessage(B_QUIT_REQUESTED
);
757 OpenWithPoseView::Pulse()
759 // disable the Open and make default button if the default
760 // app matches the selected app
762 // disable the Open button if no apps selected
764 OpenWithContainerWindow
* window
= ContainerWindow();
766 if (!fSelectionList
->CountItems()) {
767 window
->SetCanSetAppAsDefault(false);
768 window
->SetCanOpen(false);
773 // if we selected a non-handling application, don't allow setting
775 Model
* firstSelected
= fSelectionList
->FirstItem()->TargetModel();
776 if (OpenWithRelation(firstSelected
) == kNoRelation
) {
777 window
->SetCanSetAppAsDefault(false);
778 window
->SetCanOpen(true);
783 // make the open button enabled, because we have na app selected
784 window
->SetCanOpen(true);
785 if (!fHaveCommonPreferredApp
) {
786 window
->SetCanSetAppAsDefault(true);
791 ASSERT(fSelectionList
->CountItems() == 1);
793 // enable the Open and make default if selected application different
794 // from preferred app ref
795 window
->SetCanSetAppAsDefault((*fSelectionList
->FirstItem()->
796 TargetModel()->EntryRef()) != fPreferredRef
);
803 OpenWithPoseView::SetUpDefaultColumnsIfNeeded()
805 // in case there were errors getting some columns
806 if (fColumnList
->CountItems() != 0)
809 BColumn
* nameColumn
= new BColumn(B_TRANSLATE("Name"), StartOffset(), 125,
810 B_ALIGN_LEFT
, kAttrStatName
, B_STRING_TYPE
, true, true);
811 fColumnList
->AddItem(nameColumn
);
812 BColumn
* relationColumn
= new BColumn(B_TRANSLATE("Relation"), 180, 100,
813 B_ALIGN_LEFT
, kAttrOpenWithRelation
, B_STRING_TYPE
, false, false);
814 fColumnList
->AddItem(relationColumn
);
815 fColumnList
->AddItem(new BColumn(B_TRANSLATE("Location"), 290, 225,
816 B_ALIGN_LEFT
, kAttrPath
, B_STRING_TYPE
, true, false));
817 fColumnList
->AddItem(new BColumn(B_TRANSLATE("Version"), 525, 70,
818 B_ALIGN_LEFT
, kAttrAppVersion
, B_STRING_TYPE
, false, false));
820 // sort by relation and by name
821 SetPrimarySort(relationColumn
->AttrHash());
822 SetSecondarySort(nameColumn
->AttrHash());
827 OpenWithPoseView::AddPosesThreadValid(const entry_ref
*) const
834 OpenWithPoseView::CreatePoses(Model
** models
, PoseInfo
* poseInfoArray
,
835 int32 count
, BPose
** resultingPoses
, bool insertionSort
,
836 int32
* lastPoseIndexPtr
, BRect
* boundsPtr
, bool forceDraw
)
838 // overridden to try to select the preferred handling app
839 _inherited::CreatePoses(models
, poseInfoArray
, count
, resultingPoses
,
840 insertionSort
, lastPoseIndexPtr
, boundsPtr
, forceDraw
);
842 if (resultingPoses
!= NULL
) {
843 for (int32 index
= 0; index
< count
; index
++) {
844 if (resultingPoses
[index
] && fHaveCommonPreferredApp
845 && *(models
[index
]->EntryRef()) == fPreferredRef
) {
846 // this is our preferred app, select it's pose
847 SelectPose(resultingPoses
[index
],
848 IndexOfPose(resultingPoses
[index
]));
856 OpenWithPoseView::KeyDown(const char* bytes
, int32 count
)
858 if (bytes
[0] == B_TAB
) {
859 // just shift the focus, don't tab to the next pose
860 BView::KeyDown(bytes
, count
);
862 _inherited::KeyDown(bytes
, count
);
867 OpenWithPoseView::SaveState(AttributeStreamNode
* node
)
869 _inherited::SaveState(node
);
874 OpenWithPoseView::RestoreState(AttributeStreamNode
* node
)
876 _inherited::RestoreState(node
);
877 fViewState
->SetViewMode(kListMode
);
882 OpenWithPoseView::SaveState(BMessage
&message
) const
884 _inherited::SaveState(message
);
889 OpenWithPoseView::RestoreState(const BMessage
&message
)
891 _inherited::RestoreState(message
);
892 fViewState
->SetViewMode(kListMode
);
897 OpenWithPoseView::SavePoseLocations(BRect
*)
903 OpenWithPoseView::MoveSelectionToTrash(bool)
909 OpenWithPoseView::MoveSelectionTo(BPoint
, BPoint
, BContainerWindow
*)
915 OpenWithPoseView::MoveSelectionInto(Model
*, BContainerWindow
*, bool, bool)
921 OpenWithPoseView::Represents(const node_ref
*) const
928 OpenWithPoseView::Represents(const entry_ref
*) const
935 OpenWithPoseView::HandleMessageDropped(BMessage
* DEBUG_ONLY(message
))
938 // in debug mode allow tweaking the colors
939 const rgb_color
* color
;
941 // handle roColour-style color drops
942 if (message
->FindData("RGBColor", 'RGBC', (const void**)&color
, &size
)
944 SetViewColor(*color
);
955 OpenWithPoseView::OpenWithRelation(const Model
* model
) const
957 OpenWithContainerWindow
* window
= ContainerWindow();
959 return SearchForSignatureEntryList::Relation(window
->EntryList(),
960 model
, fHaveCommonPreferredApp
? &fPreferredRef
: 0, 0);
965 OpenWithPoseView::OpenWithRelationDescription(const Model
* model
,
966 BString
* description
) const
968 OpenWithContainerWindow
* window
= ContainerWindow();
970 SearchForSignatureEntryList::RelationDescription(window
->EntryList(),
971 model
, description
, fHaveCommonPreferredApp
? &fPreferredRef
: 0, 0);
975 // #pragma mark - OpenWithRefFilter
978 OpenWithRefFilter::OpenWithRefFilter(SearchForSignatureEntryList
* iterator
,
979 const BMessage
*entryList
, entry_ref
* preferredRef
)
982 fEntryList(entryList
),
983 fPreferredRef(preferredRef
)
989 OpenWithRefFilter::Filter(const entry_ref
* ref
, BNode
* node
, stat_beos
* st
,
990 const char* filetype
)
992 Model
*model
= new Model(ref
, true, true);
993 bool canOpen
= fIterator
->CanOpenWithFilter(model
, fEntryList
,
1004 RelationCachingModelProxy::RelationCachingModelProxy(Model
* model
)
1007 fRelation(kUnknownRelation
)
1012 RelationCachingModelProxy::~RelationCachingModelProxy()
1019 RelationCachingModelProxy::Relation(SearchForSignatureEntryList
* iterator
,
1020 BMessage
* entries
) const
1022 if (fRelation
== kUnknownRelation
)
1023 fRelation
= iterator
->Relation(entries
, fModel
);
1029 // #pragma mark - OpenWithMenu
1032 OpenWithMenu::OpenWithMenu(const char* label
, const BMessage
* entriesToOpen
,
1033 BWindow
* parentWindow
, BHandler
* target
)
1036 fEntriesToOpen(*entriesToOpen
),
1039 fSupportingAppList(NULL
),
1040 fParentWindow(parentWindow
)
1042 InitIconPreloader();
1044 SetFont(be_plain_font
);
1046 // too long to have triggers
1047 SetTriggersEnabled(false);
1051 OpenWithMenu::OpenWithMenu(const char* label
, const BMessage
* entriesToOpen
,
1052 BWindow
* parentWindow
, const BMessenger
&messenger
)
1055 fEntriesToOpen(*entriesToOpen
),
1057 fMessenger(messenger
),
1059 fSupportingAppList(NULL
),
1060 fParentWindow(parentWindow
)
1062 InitIconPreloader();
1064 SetFont(be_plain_font
);
1066 // too long to have triggers
1067 SetTriggersEnabled(false);
1071 namespace BPrivate
{
1074 SortByRelationAndName(const RelationCachingModelProxy
* model1
,
1075 const RelationCachingModelProxy
* model2
, void* castToMenu
)
1077 OpenWithMenu
* menu
= (OpenWithMenu
*)castToMenu
;
1079 // find out the relations of app models to the opened entries
1080 int32 relation1
= model1
->Relation(menu
->fIterator
, &menu
->fEntriesToOpen
);
1081 int32 relation2
= model2
->Relation(menu
->fIterator
, &menu
->fEntriesToOpen
);
1083 if (relation1
< relation2
) {
1084 // relation with the lowest number goes first
1086 } else if (relation1
> relation2
)
1089 // if relations match, sort by app name
1090 return strcmp(model1
->fModel
->Name(), model2
->fModel
->Name());
1093 } // namespace BPrivate
1097 OpenWithMenu::StartBuildingItemList()
1099 fIterator
= new SearchForSignatureEntryList(false);
1100 // push all the supporting apps from all the entries into the
1101 // search for signature iterator
1102 EachEntryRef(&fEntriesToOpen
, AddOneRefSignatures
, fIterator
, 100);
1103 // add superhandlers
1104 AddSupportingAppForTypeToQuery(fIterator
, B_FILE_MIMETYPE
);
1106 fHaveCommonPreferredApp
= fIterator
->GetPreferredApp(&fPreferredRef
);
1107 status_t error
= fIterator
->Rewind();
1108 if (error
!= B_OK
) {
1109 PRINT(("failed to initialize iterator %s\n", strerror(error
)));
1113 fSupportingAppList
= new BObjectList
<RelationCachingModelProxy
>(20, true);
1115 //queryRetrieval = new BStopWatch("get next entry on BQuery");
1121 OpenWithMenu::AddNextItem()
1124 if (fIterator
->GetNextEntry(&entry
) != B_OK
)
1127 Model
* model
= new Model(&entry
, true);
1128 if (model
->InitCheck() != B_OK
1129 || !fIterator
->CanOpenWithFilter(model
, &fEntriesToOpen
,
1130 (fHaveCommonPreferredApp
? &fPreferredRef
: 0))) {
1131 // only allow executables, filter out multiple copies of the Tracker,
1132 // filter out version that don't list the correct types, etc.
1135 fSupportingAppList
->AddItem(new RelationCachingModelProxy(model
));
1142 OpenWithMenu::DoneBuildingItemList()
1145 fSupportingAppList
->SortItems(SortByRelationAndName
, this);
1147 // check if each app is unique
1148 bool isUnique
= true;
1149 int32 count
= fSupportingAppList
->CountItems();
1150 for (int32 index
= 0; index
< count
- 1; index
++) {
1151 // the list is sorted, just compare two adjacent models
1152 if (strcmp(fSupportingAppList
->ItemAt(index
)->fModel
->Name(),
1153 fSupportingAppList
->ItemAt(index
+ 1)->fModel
->Name()) == 0) {
1159 // add apps as menu items
1162 float scaling
= font
.Size() / 12.0f
;
1164 int32 lastRelation
= -1;
1165 for (int32 index
= 0; index
< count
; index
++) {
1166 RelationCachingModelProxy
* modelProxy
1167 = fSupportingAppList
->ItemAt(index
);
1168 Model
* model
= modelProxy
->fModel
;
1169 BMessage
* message
= new BMessage(fEntriesToOpen
);
1170 message
->AddRef("handler", model
->EntryRef());
1171 BContainerWindow
* window
1172 = dynamic_cast<BContainerWindow
*>(fParentWindow
);
1173 if (window
!= NULL
) {
1174 message
->AddData("nodeRefsToClose", B_RAW_TYPE
,
1175 window
->TargetModel()->NodeRef(), sizeof(node_ref
));
1180 // just use the app name
1181 result
= model
->Name();
1183 // get a truncated full path
1185 BEntry
entry(model
->EntryRef());
1186 if (entry
.GetPath(&path
) != B_OK
) {
1187 PRINT(("stale entry ref %s\n", model
->Name()));
1191 result
= path
.Path();
1192 font
.TruncateString(&result
, B_TRUNCATE_MIDDLE
,
1193 kMaxMenuWidth
* scaling
);
1196 BString relationDescription
;
1197 fIterator
->RelationDescription(&fEntriesToOpen
, model
, &relationDescription
);
1199 result
+= relationDescription
;
1203 // divide different relations of opening with a separator
1204 int32 relation
= modelProxy
->Relation(fIterator
, &fEntriesToOpen
);
1205 if (lastRelation
!= -1 && relation
!= lastRelation
)
1207 lastRelation
= relation
;
1209 ModelMenuItem
* item
= new ModelMenuItem(model
, result
.String(),
1212 // mark item if it represents the preferred app
1213 if (fHaveCommonPreferredApp
&& *(model
->EntryRef()) == fPreferredRef
) {
1214 //PRINT(("marking item for % as preferred", model->Name()));
1215 item
->SetMarked(true);
1221 SetTargetForItems(target
);
1223 SetTargetForItems(fMessenger
);
1225 if (CountItems() == 0) {
1226 BMenuItem
* item
= new BMenuItem(B_TRANSLATE("no supporting apps"), 0);
1227 item
->SetEnabled(false);
1234 OpenWithMenu::ClearMenuBuildingState()
1238 delete fSupportingAppList
;
1239 fSupportingAppList
= NULL
;
1243 // #pragma mark - SearchForSignatureEntryList
1246 SearchForSignatureEntryList::SearchForSignatureEntryList(bool canAddAllApps
)
1248 fIteratorList(NULL
),
1249 fSignatures(20, true),
1250 fPreferredAppCount(0),
1251 fPreferredAppForFileCount(0),
1252 fGenericFilesOnly(true),
1253 fCanAddAllApps(canAddAllApps
),
1254 fFoundOneNonSuperHandler(false)
1259 SearchForSignatureEntryList::~SearchForSignatureEntryList()
1261 delete fIteratorList
;
1266 SearchForSignatureEntryList::PushUniqueSignature(const char* str
)
1269 if (fSignatures
.EachElement(FindOne
, (void*)str
))
1272 fSignatures
.AddItem(new BString(str
));
1277 SearchForSignatureEntryList::GetNextEntry(BEntry
* entry
, bool)
1279 return fIteratorList
->GetNextEntry(entry
);
1284 SearchForSignatureEntryList::GetNextRef(entry_ref
* ref
)
1286 return fIteratorList
->GetNextRef(ref
);
1291 SearchForSignatureEntryList::GetNextDirents(struct dirent
* buffer
,
1292 size_t length
, int32 count
)
1294 return fIteratorList
->GetNextDirents(buffer
, length
, count
);
1298 struct AddOneTermParams
{
1304 static const BString
*
1305 AddOnePredicateTerm(const BString
* item
, void* castToParams
)
1307 AddOneTermParams
* params
= (AddOneTermParams
*)castToParams
;
1309 (*params
->result
) << " || ";
1310 (*params
->result
) << kAttrAppSignature
<< " = " << item
->String();
1312 params
->first
= false;
1319 SearchForSignatureEntryList::Rewind()
1322 return fIteratorList
->Rewind();
1324 if (!fSignatures
.CountItems())
1327 // build up the iterator
1328 fIteratorList
= new CachedEntryIteratorList(false);
1329 // We cannot sort the cached inodes, as CanOpenWithFilter() relies
1330 // on the fact that ConditionalAllAppsIterator results come last.
1332 // build the predicate string by oring queries for the individual
1334 BString predicateString
;
1336 AddOneTermParams params
;
1337 params
.result
= &predicateString
;
1338 params
.first
= true;
1340 fSignatures
.EachElement(AddOnePredicateTerm
, ¶ms
);
1342 ASSERT(predicateString
.Length());
1343 // PRINT(("query predicate %s\n", predicateString.String()));
1344 fIteratorList
->AddItem(new TWalkerWrapper(
1345 new BTrackerPrivate::TQueryWalker(predicateString
.String())));
1346 fIteratorList
->AddItem(new ConditionalAllAppsIterator(this));
1348 return fIteratorList
->Rewind();
1353 SearchForSignatureEntryList::CountEntries()
1360 SearchForSignatureEntryList::GetPreferredApp(entry_ref
* ref
) const
1362 if (fPreferredAppCount
== 1)
1363 *ref
= fPreferredRef
;
1365 return fPreferredAppCount
== 1;
1370 SearchForSignatureEntryList::TrySettingPreferredApp(const entry_ref
* ref
)
1372 if (!fPreferredAppCount
) {
1373 fPreferredRef
= *ref
;
1374 fPreferredAppCount
++;
1375 } else if (fPreferredRef
!= *ref
) {
1376 // if more than one, will not return any
1377 fPreferredAppCount
++;
1383 SearchForSignatureEntryList::TrySettingPreferredAppForFile(const entry_ref
* ref
)
1385 if (!fPreferredAppForFileCount
) {
1386 fPreferredRefForFile
= *ref
;
1387 fPreferredAppForFileCount
++;
1388 } else if (fPreferredRefForFile
!= *ref
) {
1389 // if more than one, will not return any
1390 fPreferredAppForFileCount
++;
1396 SearchForSignatureEntryList::NonGenericFileFound()
1398 fGenericFilesOnly
= false;
1403 SearchForSignatureEntryList::GenericFilesOnly() const
1405 return fGenericFilesOnly
;
1410 SearchForSignatureEntryList::ShowAllApplications() const
1412 return fCanAddAllApps
&& !fFoundOneNonSuperHandler
;
1417 SearchForSignatureEntryList::Relation(const Model
* nodeModel
,
1418 const Model
* applicationModel
)
1420 int32 supportsMimeType
= applicationModel
->SupportsMimeType(
1421 nodeModel
->MimeType(), 0, true);
1422 switch (supportsMimeType
) {
1423 case kDoesNotSupportType
:
1426 case kSuperhandlerModel
:
1427 return kSuperhandler
;
1429 case kModelSupportsSupertype
:
1430 return kSupportsSupertype
;
1432 case kModelSupportsType
:
1433 return kSupportsType
;
1442 SearchForSignatureEntryList::Relation(const BMessage
* entriesToOpen
,
1443 const Model
* model
) const
1445 return Relation(entriesToOpen
, model
,
1446 fPreferredAppCount
== 1 ? &fPreferredRef
: 0,
1447 fPreferredAppForFileCount
== 1 ? &fPreferredRefForFile
: 0);
1452 SearchForSignatureEntryList::RelationDescription(const BMessage
* entriesToOpen
,
1453 const Model
* model
, BString
* description
) const
1455 RelationDescription(entriesToOpen
, model
, description
,
1456 fPreferredAppCount
== 1 ? &fPreferredRef
: 0,
1457 fPreferredAppForFileCount
== 1 ? &fPreferredRefForFile
: 0);
1462 SearchForSignatureEntryList::Relation(const BMessage
* entriesToOpen
,
1463 const Model
* applicationModel
, const entry_ref
* preferredApp
,
1464 const entry_ref
* preferredAppForFile
)
1466 for (int32 index
= 0; ; index
++) {
1468 if (entriesToOpen
->FindRef("refs", index
, &ref
) != B_OK
)
1471 // need to init a model so that typeless folders etc. will still
1472 // appear to have a mime type
1474 Model
model(&ref
, true, true);
1475 if (model
.InitCheck())
1478 int32 result
= Relation(&model
, applicationModel
);
1479 if (result
!= kNoRelation
) {
1480 if (preferredAppForFile
1481 && *applicationModel
->EntryRef() == *preferredAppForFile
) {
1482 return kPreferredForFile
;
1485 if (result
== kSupportsType
&& preferredApp
1486 && *applicationModel
->EntryRef() == *preferredApp
) {
1487 // application matches cached preferred app, we are done
1488 return kPreferredForType
;
1500 SearchForSignatureEntryList::RelationDescription(const BMessage
* entriesToOpen
,
1501 const Model
* applicationModel
, BString
* description
,
1502 const entry_ref
* preferredApp
, const entry_ref
* preferredAppForFile
)
1504 for (int32 index
= 0; ;index
++) {
1506 if (entriesToOpen
->FindRef("refs", index
, &ref
) != B_OK
)
1509 if (preferredAppForFile
&& ref
== *preferredAppForFile
) {
1510 description
->SetTo(B_TRANSLATE("Preferred for file"));
1514 Model
model(&ref
, true, true);
1515 if (model
.InitCheck())
1519 int32 result
= Relation(&model
, applicationModel
);
1521 case kDoesNotSupportType
:
1525 description
->SetTo(B_TRANSLATE("Handles any file"));
1528 case kSupportsSupertype
:
1530 mimeType
.SetTo(model
.MimeType());
1531 // status_t result = mimeType.GetSupertype(&mimeType);
1533 char* type
= (char*)mimeType
.Type();
1534 char* tmp
= strchr(type
, '/');
1538 //PRINT(("getting supertype for %s, result %s, got %s\n",
1539 // model.MimeType(), strerror(result), mimeType.Type()));
1540 description
->SetTo(B_TRANSLATE("Handles any %type"));
1541 //*description += mimeType.Type();
1542 description
->ReplaceFirst("%type", type
);
1548 mimeType
.SetTo(model
.MimeType());
1550 if (preferredApp
!= NULL
1551 && *applicationModel
->EntryRef() == *preferredApp
) {
1552 // application matches cached preferred app, we are done
1553 description
->SetTo(B_TRANSLATE("Preferred for %type"));
1555 description
->SetTo(B_TRANSLATE("Handles %type"));
1557 char shortDescription
[256];
1558 if (mimeType
.GetShortDescription(shortDescription
) == B_OK
)
1559 description
->ReplaceFirst("%type", shortDescription
);
1561 description
->ReplaceFirst("%type", mimeType
.Type());
1568 description
->SetTo(B_TRANSLATE("Does not handle file"));
1573 SearchForSignatureEntryList::CanOpenWithFilter(const Model
* appModel
,
1574 const BMessage
* entriesToOpen
, const entry_ref
* preferredApp
)
1576 ThrowOnAssert(appModel
!= NULL
);
1578 if (!appModel
->IsExecutable() || !appModel
->Node()) {
1579 // weed out non-executable
1582 BEntry
entry(appModel
->EntryRef());
1583 entry
.GetPath(&path
);
1584 PRINT(("filtering out %s- not executable \n", path
.Path()));
1589 if (strcasecmp(appModel
->MimeType(), B_APP_MIME_TYPE
) != 0) {
1590 // filter out pe containers on PPC etc.
1594 BFile
* file
= dynamic_cast<BFile
*>(appModel
->Node());
1595 ASSERT(file
!= NULL
);
1597 char signature
[B_MIME_TYPE_LENGTH
];
1598 if (GetAppSignatureFromAttr(file
, signature
) == B_OK
1599 && strcasecmp(signature
, kTrackerSignature
) == 0) {
1600 // special case the Tracker - make sure only the running copy is
1602 app_info trackerInfo
;
1603 if (*appModel
->EntryRef() != trackerInfo
.ref
) {
1604 // this is an inactive copy of the Tracker, remove it
1609 BEntry
entry(appModel
->EntryRef());
1610 entry
.GetPath(&path1
);
1612 BEntry
entry2(&trackerInfo
.ref
);
1613 entry2
.GetPath(&path2
);
1615 PRINT(("filtering out %s, sig %s, active Tracker at %s, "
1616 "result %s, refName %s\n",
1617 path1
.Path(), signature
, path2
.Path(),
1618 strerror(be_roster
->GetActiveAppInfo(&trackerInfo
)),
1619 trackerInfo
.ref
.name
));
1625 if (FSInTrashDir(appModel
->EntryRef()))
1628 if (ShowAllApplications()) {
1629 // don't check for these if we didn't look for every single app
1630 // to not slow filtering down
1632 BAppFileInfo
appFileInfo(dynamic_cast<BFile
*>(appModel
->Node()));
1633 if (appFileInfo
.GetAppFlags(&flags
) != B_OK
)
1636 if ((flags
& B_BACKGROUND_APP
) || (flags
& B_ARGV_ONLY
))
1640 // weed out apps with empty signatures
1644 int32 relation
= Relation(entriesToOpen
, appModel
, preferredApp
, 0);
1645 if (relation
== kNoRelation
&& !ShowAllApplications()) {
1648 BEntry
entry(appModel
->EntryRef());
1649 entry
.GetPath(&path
);
1651 PRINT(("filtering out %s, does not handle any of opened files\n",
1657 if (relation
!= kNoRelation
&& relation
!= kSuperhandler
1658 && !fGenericFilesOnly
) {
1659 // we hit at least one app that is not a superhandler and
1660 // handles the document
1661 fFoundOneNonSuperHandler
= true;
1668 // #pragma mark - ConditionalAllAppsIterator
1671 ConditionalAllAppsIterator::ConditionalAllAppsIterator(
1672 SearchForSignatureEntryList
* parent
)
1681 ConditionalAllAppsIterator::Instantiate()
1683 if (fWalker
!= NULL
)
1686 BString lookForAppsPredicate
;
1687 lookForAppsPredicate
<< "(" << kAttrAppSignature
<< " = \"*\" ) && ( "
1688 << kAttrMIMEType
<< " = " << B_APP_MIME_TYPE
<< " ) ";
1690 = new BTrackerPrivate::TQueryWalker(lookForAppsPredicate
.String());
1694 ConditionalAllAppsIterator::~ConditionalAllAppsIterator()
1701 ConditionalAllAppsIterator::GetNextEntry(BEntry
* entry
, bool traverse
)
1704 return B_ENTRY_NOT_FOUND
;
1707 return fWalker
->GetNextEntry(entry
, traverse
);
1712 ConditionalAllAppsIterator::GetNextRef(entry_ref
* ref
)
1715 return B_ENTRY_NOT_FOUND
;
1718 return fWalker
->GetNextRef(ref
);
1723 ConditionalAllAppsIterator::GetNextDirents(struct dirent
* buffer
,
1724 size_t length
, int32 count
)
1730 return fWalker
->GetNextDirents(buffer
, length
, count
);
1735 ConditionalAllAppsIterator::Rewind()
1741 return fWalker
->Rewind();
1746 ConditionalAllAppsIterator::CountEntries()
1752 return fWalker
->CountEntries();
1757 ConditionalAllAppsIterator::Iterate() const
1759 return fParent
->ShowAllApplications();