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