2 * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
8 #include "PreferredAppMenu.h"
11 #include <AppFileInfo.h>
24 #undef B_TRANSLATION_CONTEXT
25 #define B_TRANSLATION_CONTEXT "Preferred App Menu"
29 compare_menu_items(const void* _a
, const void* _b
)
31 BMenuItem
* a
= *(BMenuItem
**)_a
;
32 BMenuItem
* b
= *(BMenuItem
**)_b
;
34 return strcasecmp(a
->Label(), b
->Label());
39 is_application_in_message(BMessage
& applications
, const char* app
)
41 const char* signature
;
43 while (applications
.FindString("applications", i
++, &signature
) == B_OK
) {
44 if (!strcasecmp(signature
, app
))
53 add_signature(BMenuItem
* item
, const char* signature
)
55 const char* subType
= strchr(signature
, '/');
59 char label
[B_MIME_TYPE_LENGTH
];
60 snprintf(label
, sizeof(label
), "%s (%s)", item
->Label(), subType
+ 1);
62 item
->SetLabel(label
);
67 create_application_item(const char* signature
, uint32 what
)
69 char name
[B_FILE_NAME_LENGTH
];
71 BMessage
* message
= new BMessage(what
);
72 message
->AddString("signature", signature
);
74 BMimeType
applicationType(signature
);
75 if (applicationType
.GetShortDescription(name
) == B_OK
)
76 return new BMenuItem(name
, message
);
78 return new BMenuItem(signature
, message
);
82 // #pragma mark - Public functions
86 update_preferred_app_menu(BMenu
* menu
, BMimeType
* type
, uint32 what
,
87 const char* preferredFrom
)
89 // clear menu (but leave the first entry, ie. "None")
91 for (int32 i
= menu
->CountItems(); i
-- > 1;) {
92 delete menu
->RemoveItem(i
);
97 menu
->ItemAt(0)->SetMarked(true);
99 BMessage applications
;
100 if (type
== NULL
|| type
->GetSupportingApps(&applications
) != B_OK
)
103 char preferred
[B_MIME_TYPE_LENGTH
];
104 if (type
->GetPreferredApp(preferred
) != B_OK
)
107 int32 lastFullSupport
;
108 if (applications
.FindInt32("be:sub", &lastFullSupport
) != B_OK
)
109 lastFullSupport
= -1;
114 const char* signature
;
116 while (applications
.FindString("applications", i
, &signature
) == B_OK
) {
117 BMenuItem
* item
= create_application_item(signature
, what
);
119 if (i
< lastFullSupport
)
120 subList
.AddItem(item
);
122 superList
.AddItem(item
);
129 subList
.SortItems(compare_menu_items
);
130 superList
.SortItems(compare_menu_items
);
132 // add lists to the menu
134 if (subList
.CountItems() != 0 || superList
.CountItems() != 0)
135 menu
->AddSeparatorItem();
137 for (int32 i
= 0; i
< subList
.CountItems(); i
++) {
138 menu
->AddItem((BMenuItem
*)subList
.ItemAt(i
));
141 // Add type separator
142 if (superList
.CountItems() != 0 && subList
.CountItems() != 0)
143 menu
->AddSeparatorItem();
145 for (int32 i
= 0; i
< superList
.CountItems(); i
++) {
146 menu
->AddItem((BMenuItem
*)superList
.ItemAt(i
));
149 // make items unique and select current choice
151 bool lastItemSame
= false;
152 const char* lastSignature
= NULL
;
153 BMenuItem
* last
= NULL
;
154 BMenuItem
* select
= NULL
;
156 for (int32 index
= 0; index
< menu
->CountItems(); index
++) {
157 BMenuItem
* item
= menu
->ItemAt(index
);
161 if (item
->Message() == NULL
162 || item
->Message()->FindString("signature", &signature
) != B_OK
)
165 if ((preferredFrom
== NULL
&& !strcasecmp(signature
, preferred
))
166 || (preferredFrom
!= NULL
167 && !strcasecmp(signature
, preferredFrom
))) {
171 if (last
== NULL
|| strcmp(last
->Label(), item
->Label())) {
173 add_signature(last
, lastSignature
);
175 lastItemSame
= false;
177 lastSignature
= signature
;
182 add_signature(last
, lastSignature
);
185 lastSignature
= signature
;
189 add_signature(last
, lastSignature
);
191 if (select
!= NULL
) {
192 // We don't select the item earlier, so that the menu field can
193 // pick up the signature as well as label.
194 select
->SetMarked(true);
195 } else if ((preferredFrom
== NULL
&& preferred
[0])
196 || (preferredFrom
!= NULL
&& preferredFrom
[0])) {
197 // The preferred application is not an application that support
199 BMenuItem
* item
= create_application_item(preferredFrom
200 ? preferredFrom
: preferred
, what
);
202 menu
->AddSeparatorItem();
204 item
->SetMarked(item
);
210 retrieve_preferred_app(BMessage
* message
, bool sameAs
, const char* forType
,
211 BString
& preferredApp
)
214 if (message
== NULL
|| message
->FindRef("refs", &ref
) != B_OK
)
217 BFile
file(&ref
, B_READ_ONLY
);
218 status_t status
= file
.InitCheck();
220 char preferred
[B_MIME_TYPE_LENGTH
];
222 if (status
== B_OK
) {
224 // get preferred app from file
225 BNodeInfo
nodeInfo(&file
);
226 status
= nodeInfo
.InitCheck();
227 if (status
== B_OK
) {
228 if (nodeInfo
.GetPreferredApp(preferred
) != B_OK
)
232 // get MIME type from file
233 char type
[B_MIME_TYPE_LENGTH
];
234 if (nodeInfo
.GetType(type
) == B_OK
) {
235 BMimeType
mimeType(type
);
236 mimeType
.GetPreferredApp(preferred
);
241 // get application signature
242 BAppFileInfo
appInfo(&file
);
243 status
= appInfo
.InitCheck();
245 if (status
== B_OK
&& appInfo
.GetSignature(preferred
) != B_OK
)
250 if (status
!= B_OK
) {
251 error_alert(B_TRANSLATE("File could not be opened"),
252 status
, B_STOP_ALERT
);
258 ? B_TRANSLATE("Could not retrieve preferred application of this "
260 : B_TRANSLATE("Could not retrieve application signature"));
264 // Check if the application chosen supports this type
266 BMimeType
mimeType(forType
);
269 BMessage applications
;
270 if (mimeType
.GetSupportingApps(&applications
) == B_OK
271 && is_application_in_message(applications
, preferred
))
274 applications
.MakeEmpty();
276 if (!found
&& mimeType
.GetWildcardApps(&applications
) == B_OK
277 && is_application_in_message(applications
, preferred
))
282 BMimeType
appType(preferred
);
283 char description
[B_MIME_TYPE_LENGTH
];
284 if (appType
.GetShortDescription(description
) != B_OK
)
285 description
[0] = '\0';
288 snprintf(warning
, sizeof(warning
), B_TRANSLATE("The application "
289 "\"%s\" does not support this file type.\n"
290 "Are you sure you want to set it anyway?"),
291 description
[0] ? description
: preferred
);
293 BAlert
* alert
= new BAlert(B_TRANSLATE("FileTypes request"), warning
,
294 B_TRANSLATE("Set preferred application"), B_TRANSLATE("Cancel"),
295 NULL
, B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
296 alert
->SetShortcut(1, B_ESCAPE
);
297 if (alert
->Go() == 1)
301 preferredApp
= preferred
;