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