1 /* 2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "FileTypes.h" 8 #include "FileTypeWindow.h" 9 #include "IconView.h" 10 #include "PreferredAppMenu.h" 11 #include "TypeListWindow.h" 12 13 #include <Application.h> 14 #include <Bitmap.h> 15 #include <Box.h> 16 #include <Button.h> 17 #include <Catalog.h> 18 #include <ControlLook.h> 19 #include <File.h> 20 #include <LayoutBuilder.h> 21 #include <Locale.h> 22 #include <MenuField.h> 23 #include <MenuItem.h> 24 #include <Mime.h> 25 #include <NodeInfo.h> 26 #include <PopUpMenu.h> 27 #include <SpaceLayoutItem.h> 28 #include <TextControl.h> 29 30 #include <stdio.h> 31 32 33 #undef B_TRANSLATION_CONTEXT 34 #define B_TRANSLATION_CONTEXT "FileType Window" 35 36 37 const uint32 kMsgTypeEntered = 'type'; 38 const uint32 kMsgSelectType = 'sltp'; 39 const uint32 kMsgTypeSelected = 'tpsd'; 40 const uint32 kMsgSameTypeAs = 'stpa'; 41 const uint32 kMsgSameTypeAsOpened = 'stpO'; 42 43 const uint32 kMsgPreferredAppChosen = 'papc'; 44 const uint32 kMsgSelectPreferredApp = 'slpa'; 45 const uint32 kMsgSamePreferredAppAs = 'spaa'; 46 const uint32 kMsgPreferredAppOpened = 'paOp'; 47 const uint32 kMsgSamePreferredAppAsOpened = 'spaO'; 48 49 50 FileTypeWindow::FileTypeWindow(BPoint position, const BMessage& refs) 51 : 52 BWindow(BRect(0.0f, 0.0f, 300.0f, 200.0f).OffsetBySelf(position), 53 B_TRANSLATE("File type"), B_TITLED_WINDOW, 54 B_NOT_V_RESIZABLE | B_NOT_ZOOMABLE 55 | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS) 56 { 57 float padding = be_control_look->DefaultItemSpacing(); 58 59 // "File Type" group 60 BBox* fileTypeBox = new BBox("file type BBox"); 61 fileTypeBox->SetLabel(B_TRANSLATE("File type")); 62 63 fTypeControl = new BTextControl("type", NULL, "Type Control", 64 new BMessage(kMsgTypeEntered)); 65 66 // filter out invalid characters that can't be part of a MIME type name 67 BTextView* textView = fTypeControl->TextView(); 68 const char* disallowedCharacters = "<>@,;:\"()[]?="; 69 for (int32 i = 0; disallowedCharacters[i]; i++) { 70 textView->DisallowChar(disallowedCharacters[i]); 71 } 72 73 fSelectTypeButton = new BButton("select type", 74 B_TRANSLATE("Select" B_UTF8_ELLIPSIS), new BMessage(kMsgSelectType)); 75 76 fSameTypeAsButton = new BButton("same type as", 77 B_TRANSLATE_COMMENT("Same as" B_UTF8_ELLIPSIS, 78 "The same TYPE as ..."), new BMessage(kMsgSameTypeAs)); 79 80 BLayoutBuilder::Grid<>(fileTypeBox, padding, padding / 2) 81 .SetInsets(padding, padding * 2, padding, padding) 82 .Add(fTypeControl, 0, 0, 3, 1) 83 .Add(fSelectTypeButton, 0, 1) 84 .Add(fSameTypeAsButton, 1, 1); 85 86 // "Icon" group 87 88 BBox* iconBox = new BBox("icon BBox"); 89 iconBox->SetLabel(B_TRANSLATE("Icon")); 90 fIconView = new IconView("icon"); 91 BLayoutBuilder::Group<>(iconBox, B_HORIZONTAL) 92 .SetInsets(padding, padding * 2, padding, padding) 93 .Add(fIconView); 94 95 // "Preferred Application" group 96 97 BBox* preferredBox = new BBox("preferred BBox"); 98 preferredBox->SetLabel(B_TRANSLATE("Preferred application")); 99 100 BMenu* menu = new BPopUpMenu("preferred"); 101 BMenuItem* item; 102 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Default application"), 103 new BMessage(kMsgPreferredAppChosen))); 104 item->SetMarked(true); 105 106 fPreferredField = new BMenuField("preferred", NULL, menu); 107 108 fSelectAppButton = new BButton("select app", 109 B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 110 new BMessage(kMsgSelectPreferredApp)); 111 112 fSameAppAsButton = new BButton("same app as", 113 B_TRANSLATE_COMMENT("Same as" B_UTF8_ELLIPSIS, 114 "The same APPLICATION as ..."), 115 new BMessage(kMsgSamePreferredAppAs)); 116 117 BLayoutBuilder::Grid<>(preferredBox, padding, padding / 2) 118 .SetInsets(padding, padding * 2, padding, padding) 119 .Add(fPreferredField, 0, 0, 2, 1) 120 .Add(fSelectAppButton, 0, 1) 121 .Add(fSameAppAsButton, 1, 1); 122 123 BLayoutBuilder::Grid<>(this) 124 .SetInsets(padding) 125 .Add(fileTypeBox, 0, 0, 2, 1) 126 .Add(preferredBox, 0, 1, 1, 1) 127 .Add(iconBox, 1, 1, 1, 1); 128 129 fTypeControl->MakeFocus(true); 130 BMimeType::StartWatching(this); 131 _SetTo(refs); 132 } 133 134 135 FileTypeWindow::~FileTypeWindow() 136 { 137 BMimeType::StopWatching(this); 138 } 139 140 141 BString 142 FileTypeWindow::_Title(const BMessage& refs) 143 { 144 BString title; 145 146 entry_ref ref; 147 if (refs.FindRef("refs", 1, &ref) == B_OK) { 148 bool same = false; 149 BEntry entry, parent; 150 if (entry.SetTo(&ref) == B_OK 151 && entry.GetParent(&parent) == B_OK) { 152 same = true; 153 154 // Check if all entries have the same parent directory 155 for (int32 i = 0; refs.FindRef("refs", i, &ref) == B_OK; i++) { 156 BEntry directory; 157 if (entry.SetTo(&ref) == B_OK 158 && entry.GetParent(&directory) == B_OK) { 159 if (directory != parent) { 160 same = false; 161 break; 162 } 163 } 164 } 165 } 166 167 char name[B_FILE_NAME_LENGTH]; 168 if (same && parent.GetName(name) == B_OK) { 169 char buffer[512]; 170 snprintf(buffer, sizeof(buffer), 171 B_TRANSLATE("Multiple files from \"%s\" file type"), name); 172 title = buffer; 173 } else 174 title = B_TRANSLATE("[Multiple files] file types"); 175 } else if (refs.FindRef("refs", 0, &ref) == B_OK) { 176 char buffer[512]; 177 snprintf(buffer, sizeof(buffer), B_TRANSLATE("%s file type"), ref.name); 178 title = buffer; 179 } 180 181 return title; 182 } 183 184 185 void 186 FileTypeWindow::_SetTo(const BMessage& refs) 187 { 188 SetTitle(_Title(refs).String()); 189 190 // get common info and icons 191 192 fCommonPreferredApp = ""; 193 fCommonType = ""; 194 entry_ref ref; 195 for (int32 i = 0; refs.FindRef("refs", i, &ref) == B_OK; i++) { 196 BNode node(&ref); 197 if (node.InitCheck() != B_OK) 198 continue; 199 200 BNodeInfo info(&node); 201 if (info.InitCheck() != B_OK) 202 continue; 203 204 // TODO: watch entries? 205 206 entry_ref* copiedRef = new entry_ref(ref); 207 fEntries.AddItem(copiedRef); 208 209 char type[B_MIME_TYPE_LENGTH]; 210 if (info.GetType(type) != B_OK) 211 type[0] = '\0'; 212 213 if (i > 0) { 214 if (fCommonType != type) 215 fCommonType = ""; 216 } else 217 fCommonType = type; 218 219 char preferredApp[B_MIME_TYPE_LENGTH]; 220 if (info.GetPreferredApp(preferredApp) != B_OK) 221 preferredApp[0] = '\0'; 222 223 if (i > 0) { 224 if (fCommonPreferredApp != preferredApp) 225 fCommonPreferredApp = ""; 226 } else 227 fCommonPreferredApp = preferredApp; 228 229 if (i == 0) 230 fIconView->SetTo(ref); 231 } 232 233 fTypeControl->SetText(fCommonType.String()); 234 _UpdatePreferredApps(); 235 236 fIconView->ShowIconHeap(fEntries.CountItems() != 1); 237 } 238 239 240 void 241 FileTypeWindow::_AdoptType(BMessage* message) 242 { 243 entry_ref ref; 244 if (message == NULL || message->FindRef("refs", &ref) != B_OK) 245 return; 246 247 BNode node(&ref); 248 status_t status = node.InitCheck(); 249 250 char type[B_MIME_TYPE_LENGTH]; 251 252 if (status == B_OK) { 253 // get type from file 254 BNodeInfo nodeInfo(&node); 255 status = nodeInfo.InitCheck(); 256 if (status == B_OK) { 257 if (nodeInfo.GetType(type) != B_OK) 258 type[0] = '\0'; 259 } 260 } 261 262 if (status != B_OK) { 263 error_alert(B_TRANSLATE("Could not open file"), status); 264 return; 265 } 266 267 fCommonType = type; 268 fTypeControl->SetText(type); 269 _AdoptType(); 270 } 271 272 273 void 274 FileTypeWindow::_AdoptType() 275 { 276 for (int32 i = 0; i < fEntries.CountItems(); i++) { 277 BNode node(fEntries.ItemAt(i)); 278 BNodeInfo info(&node); 279 if (node.InitCheck() != B_OK 280 || info.InitCheck() != B_OK) 281 continue; 282 283 info.SetType(fCommonType.String()); 284 } 285 } 286 287 288 void 289 FileTypeWindow::_AdoptPreferredApp(BMessage* message, bool sameAs) 290 { 291 if (retrieve_preferred_app(message, sameAs, fCommonType.String(), 292 fCommonPreferredApp) == B_OK) { 293 _AdoptPreferredApp(); 294 _UpdatePreferredApps(); 295 } 296 } 297 298 299 void 300 FileTypeWindow::_AdoptPreferredApp() 301 { 302 for (int32 i = 0; i < fEntries.CountItems(); i++) { 303 BNode node(fEntries.ItemAt(i)); 304 if (fCommonPreferredApp.Length() == 0) { 305 node.RemoveAttr("BEOS:PREF_APP"); 306 continue; 307 } 308 309 BNodeInfo info(&node); 310 if (node.InitCheck() != B_OK 311 || info.InitCheck() != B_OK) 312 continue; 313 314 info.SetPreferredApp(fCommonPreferredApp.String()); 315 } 316 } 317 318 319 void 320 FileTypeWindow::_UpdatePreferredApps() 321 { 322 BMimeType type(fCommonType.String()); 323 update_preferred_app_menu(fPreferredField->Menu(), &type, 324 kMsgPreferredAppChosen, fCommonPreferredApp.String()); 325 } 326 327 328 void 329 FileTypeWindow::MessageReceived(BMessage* message) 330 { 331 switch (message->what) { 332 // File Type group 333 334 case kMsgTypeEntered: 335 fCommonType = fTypeControl->Text(); 336 _AdoptType(); 337 break; 338 339 case kMsgSelectType: 340 { 341 BWindow* window = new TypeListWindow(fCommonType.String(), 342 kMsgTypeSelected, this); 343 window->Show(); 344 break; 345 } 346 case kMsgTypeSelected: 347 { 348 const char* type; 349 if (message->FindString("type", &type) == B_OK) { 350 fCommonType = type; 351 fTypeControl->SetText(type); 352 _AdoptType(); 353 } 354 break; 355 } 356 357 case kMsgSameTypeAs: 358 { 359 BMessage panel(kMsgOpenFilePanel); 360 panel.AddString("title", B_TRANSLATE("Select same type as")); 361 panel.AddInt32("message", kMsgSameTypeAsOpened); 362 panel.AddMessenger("target", this); 363 panel.AddBool("allowDirs", true); 364 365 be_app_messenger.SendMessage(&panel); 366 break; 367 } 368 case kMsgSameTypeAsOpened: 369 _AdoptType(message); 370 break; 371 372 // Preferred Application group 373 374 case kMsgPreferredAppChosen: 375 { 376 const char* signature; 377 if (message->FindString("signature", &signature) == B_OK) 378 fCommonPreferredApp = signature; 379 else 380 fCommonPreferredApp = ""; 381 382 _AdoptPreferredApp(); 383 break; 384 } 385 386 case kMsgSelectPreferredApp: 387 { 388 BMessage panel(kMsgOpenFilePanel); 389 panel.AddString("title", 390 B_TRANSLATE("Select preferred application")); 391 panel.AddInt32("message", kMsgPreferredAppOpened); 392 panel.AddMessenger("target", this); 393 394 be_app_messenger.SendMessage(&panel); 395 break; 396 } 397 case kMsgPreferredAppOpened: 398 _AdoptPreferredApp(message, false); 399 break; 400 401 case kMsgSamePreferredAppAs: 402 { 403 BMessage panel(kMsgOpenFilePanel); 404 panel.AddString("title", 405 B_TRANSLATE("Select same preferred application as")); 406 panel.AddInt32("message", kMsgSamePreferredAppAsOpened); 407 panel.AddMessenger("target", this); 408 panel.AddBool("allowDirs", true); 409 410 be_app_messenger.SendMessage(&panel); 411 break; 412 } 413 case kMsgSamePreferredAppAsOpened: 414 _AdoptPreferredApp(message, true); 415 break; 416 417 // Other 418 419 case B_SIMPLE_DATA: 420 { 421 entry_ref ref; 422 if (message->FindRef("refs", &ref) != B_OK) 423 break; 424 425 BFile file(&ref, B_READ_ONLY); 426 if (is_application(file)) 427 _AdoptPreferredApp(message, false); 428 else 429 _AdoptType(message); 430 break; 431 } 432 433 case B_META_MIME_CHANGED: 434 const char* type; 435 int32 which; 436 if (message->FindString("be:type", &type) != B_OK 437 || message->FindInt32("be:which", &which) != B_OK) 438 break; 439 440 if (which == B_MIME_TYPE_DELETED 441 || which == B_SUPPORTED_TYPES_CHANGED) { 442 _UpdatePreferredApps(); 443 } 444 break; 445 446 default: 447 BWindow::MessageReceived(message); 448 } 449 } 450 451 452 bool 453 FileTypeWindow::QuitRequested() 454 { 455 be_app->PostMessage(kMsgTypeWindowClosed); 456 return true; 457 } 458 459