1 /* 2 * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "FileTypes.h" 8 #include "PreferredAppMenu.h" 9 10 #include <Alert.h> 11 #include <AppFileInfo.h> 12 #include <Catalog.h> 13 #include <Locale.h> 14 #include <Menu.h> 15 #include <MenuItem.h> 16 #include <Mime.h> 17 #include <NodeInfo.h> 18 #include <String.h> 19 20 #include <stdio.h> 21 #include <strings.h> 22 23 24 #undef B_TRANSLATION_CONTEXT 25 #define B_TRANSLATION_CONTEXT "Preferred App Menu" 26 27 28 static int 29 compare_menu_items(const void* _a, const void* _b) 30 { 31 BMenuItem* a = *(BMenuItem**)_a; 32 BMenuItem* b = *(BMenuItem**)_b; 33 34 return strcasecmp(a->Label(), b->Label()); 35 } 36 37 38 static bool 39 is_application_in_message(BMessage& applications, const char* app) 40 { 41 const char* signature; 42 int32 i = 0; 43 while (applications.FindString("applications", i++, &signature) == B_OK) { 44 if (!strcasecmp(signature, app)) 45 return true; 46 } 47 48 return false; 49 } 50 51 52 static void 53 add_signature(BMenuItem* item, const char* signature) 54 { 55 const char* subType = strchr(signature, '/'); 56 if (subType == NULL) 57 return; 58 59 char label[B_MIME_TYPE_LENGTH]; 60 snprintf(label, sizeof(label), "%s (%s)", item->Label(), subType + 1); 61 62 item->SetLabel(label); 63 } 64 65 66 static BMenuItem* 67 create_application_item(const char* signature, uint32 what) 68 { 69 char name[B_FILE_NAME_LENGTH]; 70 71 BMessage* message = new BMessage(what); 72 message->AddString("signature", signature); 73 74 BMimeType applicationType(signature); 75 if (applicationType.GetShortDescription(name) == B_OK) 76 return new BMenuItem(name, message); 77 78 return new BMenuItem(signature, message); 79 } 80 81 82 // #pragma mark - Public functions 83 84 85 void 86 update_preferred_app_menu(BMenu* menu, BMimeType* type, uint32 what, 87 const char* preferredFrom) 88 { 89 // clear menu (but leave the first entry, ie. "None") 90 91 for (int32 i = menu->CountItems(); i-- > 1;) { 92 delete menu->RemoveItem(i); 93 } 94 95 // fill it again 96 97 menu->ItemAt(0)->SetMarked(true); 98 99 BMessage applications; 100 if (type == NULL || type->GetSupportingApps(&applications) != B_OK) 101 return; 102 103 char preferred[B_MIME_TYPE_LENGTH]; 104 if (type->GetPreferredApp(preferred) != B_OK) 105 preferred[0] = '\0'; 106 107 int32 lastFullSupport; 108 if (applications.FindInt32("be:sub", &lastFullSupport) != B_OK) 109 lastFullSupport = -1; 110 111 BList subList; 112 BList superList; 113 114 const char* signature; 115 int32 i = 0; 116 while (applications.FindString("applications", i, &signature) == B_OK) { 117 BMenuItem* item = create_application_item(signature, what); 118 119 if (i < lastFullSupport) 120 subList.AddItem(item); 121 else 122 superList.AddItem(item); 123 124 i++; 125 } 126 127 // sort lists 128 129 subList.SortItems(compare_menu_items); 130 superList.SortItems(compare_menu_items); 131 132 // add lists to the menu 133 134 if (subList.CountItems() != 0 || superList.CountItems() != 0) 135 menu->AddSeparatorItem(); 136 137 for (int32 i = 0; i < subList.CountItems(); i++) { 138 menu->AddItem((BMenuItem*)subList.ItemAt(i)); 139 } 140 141 // Add type separator 142 if (superList.CountItems() != 0 && subList.CountItems() != 0) 143 menu->AddSeparatorItem(); 144 145 for (int32 i = 0; i < superList.CountItems(); i++) { 146 menu->AddItem((BMenuItem*)superList.ItemAt(i)); 147 } 148 149 // make items unique and select current choice 150 151 bool lastItemSame = false; 152 const char* lastSignature = NULL; 153 BMenuItem* last = NULL; 154 BMenuItem* select = NULL; 155 156 for (int32 index = 0; index < menu->CountItems(); index++) { 157 BMenuItem* item = menu->ItemAt(index); 158 if (item == NULL) 159 continue; 160 161 if (item->Message() == NULL 162 || item->Message()->FindString("signature", &signature) != B_OK) 163 continue; 164 165 if ((preferredFrom == NULL && !strcasecmp(signature, preferred)) 166 || (preferredFrom != NULL 167 && !strcasecmp(signature, preferredFrom))) { 168 select = item; 169 } 170 171 if (last == NULL || strcmp(last->Label(), item->Label())) { 172 if (lastItemSame) 173 add_signature(last, lastSignature); 174 175 lastItemSame = false; 176 last = item; 177 lastSignature = signature; 178 continue; 179 } 180 181 lastItemSame = true; 182 add_signature(last, lastSignature); 183 184 last = item; 185 lastSignature = signature; 186 } 187 188 if (lastItemSame) 189 add_signature(last, lastSignature); 190 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 198 // this file type! 199 BMenuItem* item = create_application_item(preferredFrom 200 ? preferredFrom : preferred, what); 201 202 menu->AddSeparatorItem(); 203 menu->AddItem(item); 204 item->SetMarked(true); 205 } 206 } 207 208 209 status_t 210 retrieve_preferred_app(BMessage* message, bool sameAs, const char* forType, 211 BString& preferredApp) 212 { 213 entry_ref ref; 214 if (message == NULL || message->FindRef("refs", &ref) != B_OK) 215 return B_BAD_VALUE; 216 217 BFile file(&ref, B_READ_ONLY); 218 status_t status = file.InitCheck(); 219 220 char preferred[B_MIME_TYPE_LENGTH]; 221 222 if (status == B_OK) { 223 if (sameAs) { 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) 229 preferred[0] = '\0'; 230 231 if (!preferred[0]) { 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); 237 } 238 } 239 } 240 } else { 241 // get application signature 242 BAppFileInfo appInfo(&file); 243 status = appInfo.InitCheck(); 244 245 if (status == B_OK && appInfo.GetSignature(preferred) != B_OK) 246 preferred[0] = '\0'; 247 } 248 } 249 250 if (status != B_OK) { 251 error_alert(B_TRANSLATE("File could not be opened"), 252 status, B_STOP_ALERT); 253 return status; 254 } 255 256 if (!preferred[0]) { 257 error_alert(sameAs 258 ? B_TRANSLATE("Could not retrieve preferred application of this " 259 "file") 260 : B_TRANSLATE("Could not retrieve application signature")); 261 return B_ERROR; 262 } 263 264 // Check if the application chosen supports this type 265 266 BMimeType mimeType(forType); 267 bool found = false; 268 269 BMessage applications; 270 if (mimeType.GetSupportingApps(&applications) == B_OK 271 && is_application_in_message(applications, preferred)) 272 found = true; 273 274 applications.MakeEmpty(); 275 276 if (!found && mimeType.GetWildcardApps(&applications) == B_OK 277 && is_application_in_message(applications, preferred)) 278 found = true; 279 280 if (!found) { 281 // warn user 282 BMimeType appType(preferred); 283 char description[B_MIME_TYPE_LENGTH]; 284 if (appType.GetShortDescription(description) != B_OK) 285 description[0] = '\0'; 286 287 char warning[512]; 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); 292 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) 298 return B_ERROR; 299 } 300 301 preferredApp = preferred; 302 return B_OK; 303 } 304 305