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 "AttributeListView.h" 8 #include "AttributeWindow.h" 9 #include "DropTargetListView.h" 10 #include "ExtensionWindow.h" 11 #include "FileTypes.h" 12 #include "FileTypesWindow.h" 13 #include "IconView.h" 14 #include "MimeTypeListView.h" 15 #include "NewFileTypeWindow.h" 16 #include "PreferredAppMenu.h" 17 #include "StringView.h" 18 19 #include <Alignment.h> 20 #include <AppFileInfo.h> 21 #include <Application.h> 22 #include <Bitmap.h> 23 #include <Box.h> 24 #include <Button.h> 25 #include <Catalog.h> 26 #include <ControlLook.h> 27 #include <GridLayoutBuilder.h> 28 #include <GroupLayoutBuilder.h> 29 #include <LayoutBuilder.h> 30 #include <ListView.h> 31 #include <Locale.h> 32 #include <MenuBar.h> 33 #include <MenuField.h> 34 #include <MenuItem.h> 35 #include <Mime.h> 36 #include <NodeInfo.h> 37 #include <OutlineListView.h> 38 #include <PopUpMenu.h> 39 #include <ScrollView.h> 40 #include <SpaceLayoutItem.h> 41 #include <SplitView.h> 42 #include <TextControl.h> 43 44 #include <OverrideAlert.h> 45 #include <be_apps/Tracker/RecentItems.h> 46 47 #include <stdio.h> 48 #include <stdlib.h> 49 50 51 #undef B_TRANSLATE_CONTEXT 52 #define B_TRANSLATE_CONTEXT "FileTypes Window" 53 54 55 const uint32 kMsgTypeSelected = 'typs'; 56 const uint32 kMsgAddType = 'atyp'; 57 const uint32 kMsgRemoveType = 'rtyp'; 58 59 const uint32 kMsgExtensionSelected = 'exts'; 60 const uint32 kMsgExtensionInvoked = 'exti'; 61 const uint32 kMsgAddExtension = 'aext'; 62 const uint32 kMsgRemoveExtension = 'rext'; 63 const uint32 kMsgRuleEntered = 'rule'; 64 65 const uint32 kMsgAttributeSelected = 'atrs'; 66 const uint32 kMsgAttributeInvoked = 'atri'; 67 const uint32 kMsgAddAttribute = 'aatr'; 68 const uint32 kMsgRemoveAttribute = 'ratr'; 69 const uint32 kMsgMoveUpAttribute = 'muat'; 70 const uint32 kMsgMoveDownAttribute = 'mdat'; 71 72 const uint32 kMsgPreferredAppChosen = 'papc'; 73 const uint32 kMsgSelectPreferredApp = 'slpa'; 74 const uint32 kMsgSamePreferredAppAs = 'spaa'; 75 76 const uint32 kMsgPreferredAppOpened = 'paOp'; 77 const uint32 kMsgSamePreferredAppAsOpened = 'spaO'; 78 79 const uint32 kMsgTypeEntered = 'type'; 80 const uint32 kMsgDescriptionEntered = 'dsce'; 81 82 const uint32 kMsgToggleIcons = 'tgic'; 83 const uint32 kMsgToggleRule = 'tgrl'; 84 85 86 static const char* kAttributeNames[] = { 87 "attr:public_name", 88 "attr:name", 89 "attr:type", 90 "attr:editable", 91 "attr:viewable", 92 "attr:extra", 93 "attr:alignment", 94 "attr:width", 95 "attr:display_as" 96 }; 97 98 99 class TypeIconView : public IconView { 100 public: 101 TypeIconView(const char* name); 102 virtual ~TypeIconView(); 103 104 virtual void Draw(BRect updateRect); 105 virtual void GetPreferredSize(float* _width, float* _height); 106 107 protected: 108 virtual BRect BitmapRect() const; 109 }; 110 111 112 class ExtensionListView : public DropTargetListView { 113 public: 114 ExtensionListView(const char* name, 115 list_view_type type = B_SINGLE_SELECTION_LIST, 116 uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE); 117 virtual ~ExtensionListView(); 118 119 virtual void MessageReceived(BMessage* message); 120 virtual bool AcceptsDrag(const BMessage* message); 121 122 void SetType(BMimeType* type); 123 124 private: 125 BMimeType fType; 126 }; 127 128 129 // #pragma mark - 130 131 132 TypeIconView::TypeIconView(const char* name) 133 : IconView(name) 134 { 135 ShowEmptyFrame(false); 136 SetIconSize(48); 137 } 138 139 140 TypeIconView::~TypeIconView() 141 { 142 } 143 144 145 void 146 TypeIconView::Draw(BRect updateRect) 147 { 148 if (!IsEnabled()) 149 return; 150 151 IconView::Draw(updateRect); 152 153 const char* text = NULL; 154 155 switch (IconSource()) { 156 case kNoIcon: 157 text = B_TRANSLATE("no icon"); 158 break; 159 case kApplicationIcon: 160 text = B_TRANSLATE("(from application)"); 161 break; 162 case kSupertypeIcon: 163 text = B_TRANSLATE("(from super type)"); 164 break; 165 166 default: 167 return; 168 } 169 170 SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 171 B_DISABLED_LABEL_TINT)); 172 SetLowColor(ViewColor()); 173 174 font_height fontHeight; 175 GetFontHeight(&fontHeight); 176 177 float y = fontHeight.ascent; 178 if (IconSource() == kNoIcon) { 179 // center text in the middle of the icon 180 y += (IconSize() - fontHeight.ascent - fontHeight.descent) / 2.0f; 181 } else 182 y += IconSize() + 3.0f; 183 184 DrawString(text, BPoint(ceilf((Bounds().Width() - StringWidth(text)) / 2.0f), 185 ceilf(y))); 186 } 187 188 189 void 190 TypeIconView::GetPreferredSize(float* _width, float* _height) 191 { 192 if (_width) { 193 float a = StringWidth(B_TRANSLATE("(from application)")); 194 float b = StringWidth(B_TRANSLATE("(from super type)")); 195 float width = max_c(a, b); 196 if (width < IconSize()) 197 width = IconSize(); 198 199 *_width = ceilf(width); 200 } 201 202 if (_height) { 203 font_height fontHeight; 204 GetFontHeight(&fontHeight); 205 206 *_height = IconSize() + 3.0f + ceilf(fontHeight.ascent 207 + fontHeight.descent); 208 } 209 } 210 211 212 BRect 213 TypeIconView::BitmapRect() const 214 { 215 if (IconSource() == kNoIcon) { 216 // this also defines the drop target area 217 font_height fontHeight; 218 GetFontHeight(&fontHeight); 219 220 float width = StringWidth(B_TRANSLATE("no icon")) + 8.0f; 221 float height = ceilf(fontHeight.ascent + fontHeight.descent) + 6.0f; 222 float x = (Bounds().Width() - width) / 2.0f; 223 float y = ceilf((IconSize() - fontHeight.ascent - fontHeight.descent) 224 / 2.0f) - 3.0f; 225 226 return BRect(x, y, x + width, y + height); 227 } 228 229 float x = (Bounds().Width() - IconSize()) / 2.0f; 230 return BRect(x, 0.0f, x + IconSize() - 1, IconSize() - 1); 231 } 232 233 234 // #pragma mark - 235 236 237 ExtensionListView::ExtensionListView(const char* name, 238 list_view_type type, uint32 flags) 239 : 240 DropTargetListView(name, type, flags) 241 { 242 } 243 244 245 ExtensionListView::~ExtensionListView() 246 { 247 } 248 249 250 void 251 ExtensionListView::MessageReceived(BMessage* message) 252 { 253 if (message->WasDropped() && AcceptsDrag(message)) { 254 // create extension list 255 BList list; 256 entry_ref ref; 257 for (int32 index = 0; message->FindRef("refs", index, &ref) == B_OK; 258 index++) { 259 const char* point = strchr(ref.name, '.'); 260 if (point != NULL && point[1]) 261 list.AddItem(strdup(++point)); 262 } 263 264 merge_extensions(fType, list); 265 266 // delete extension list 267 for (int32 index = list.CountItems(); index-- > 0;) { 268 free(list.ItemAt(index)); 269 } 270 } else 271 DropTargetListView::MessageReceived(message); 272 } 273 274 275 bool 276 ExtensionListView::AcceptsDrag(const BMessage* message) 277 { 278 if (fType.Type() == NULL) 279 return false; 280 281 int32 count = 0; 282 entry_ref ref; 283 284 for (int32 index = 0; message->FindRef("refs", index, &ref) == B_OK; 285 index++) { 286 const char* point = strchr(ref.name, '.'); 287 if (point != NULL && point[1]) 288 count++; 289 } 290 291 return count > 0; 292 } 293 294 295 void 296 ExtensionListView::SetType(BMimeType* type) 297 { 298 if (type != NULL) 299 fType.SetTo(type->Type()); 300 else 301 fType.Unset(); 302 } 303 304 305 // #pragma mark - 306 307 308 FileTypesWindow::FileTypesWindow(const BMessage& settings) 309 : 310 BWindow(_Frame(settings), B_TRANSLATE_SYSTEM_NAME("FileTypes"), 311 B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS 312 | B_AUTO_UPDATE_SIZE_LIMITS), 313 fNewTypeWindow(NULL) 314 { 315 bool showIcons; 316 bool showRule; 317 if (settings.FindBool("show_icons", &showIcons) != B_OK) 318 showIcons = true; 319 if (settings.FindBool("show_rule", &showRule) != B_OK) 320 showRule = false; 321 322 float padding = be_control_look->DefaultItemSpacing(); 323 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment(); 324 BAlignment fullAlignment(B_ALIGN_USE_FULL_WIDTH, B_ALIGN_USE_FULL_HEIGHT); 325 326 // add the menu 327 BMenuBar* menuBar = new BMenuBar(""); 328 329 BMenu* menu = new BMenu(B_TRANSLATE("File")); 330 BMenuItem* item = new BMenuItem( 331 B_TRANSLATE("New resource file" B_UTF8_ELLIPSIS), NULL, 'N', 332 B_COMMAND_KEY); 333 item->SetEnabled(false); 334 menu->AddItem(item); 335 336 BMenu* recentsMenu = BRecentFilesList::NewFileListMenu( 337 B_TRANSLATE("Open" B_UTF8_ELLIPSIS), NULL, NULL, 338 be_app, 10, false, NULL, kSignature); 339 item = new BMenuItem(recentsMenu, new BMessage(kMsgOpenFilePanel)); 340 item->SetShortcut('O', B_COMMAND_KEY); 341 menu->AddItem(item); 342 343 menu->AddItem(new BMenuItem( 344 B_TRANSLATE("Application types" B_UTF8_ELLIPSIS), 345 new BMessage(kMsgOpenApplicationTypesWindow))); 346 menu->AddSeparatorItem(); 347 348 menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"), 349 new BMessage(B_QUIT_REQUESTED), 'Q', B_COMMAND_KEY)); 350 menu->SetTargetForItems(be_app); 351 menuBar->AddItem(menu); 352 353 menu = new BMenu(B_TRANSLATE("Settings")); 354 item = new BMenuItem(B_TRANSLATE("Show icons in list"), 355 new BMessage(kMsgToggleIcons)); 356 item->SetMarked(showIcons); 357 item->SetTarget(this); 358 menu->AddItem(item); 359 360 item = new BMenuItem(B_TRANSLATE("Show recognition rule"), 361 new BMessage(kMsgToggleRule)); 362 item->SetMarked(showRule); 363 item->SetTarget(this); 364 menu->AddItem(item); 365 menuBar->AddItem(menu); 366 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); 367 368 // MIME Types list 369 BButton* addTypeButton = new BButton("add", 370 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType)); 371 372 fRemoveTypeButton = new BButton("remove", B_TRANSLATE("Remove"), 373 new BMessage(kMsgRemoveType) ); 374 375 fTypeListView = new MimeTypeListView("typeview", NULL, showIcons, false); 376 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); 377 fTypeListView->SetExplicitMinSize(BSize(200, B_SIZE_UNSET)); 378 379 BScrollView* typeListScrollView = new BScrollView("scrollview", 380 fTypeListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 381 382 // "Icon" group 383 384 fIconView = new TypeIconView("icon"); 385 fIconBox = new BBox("Icon BBox"); 386 fIconBox->SetLabel(B_TRANSLATE("Icon")); 387 fIconBox->AddChild(BGroupLayoutBuilder(B_VERTICAL, padding) 388 .Add(BSpaceLayoutItem::CreateGlue(), 1) 389 .Add(fIconView, 3) 390 .Add(BSpaceLayoutItem::CreateGlue(), 1) 391 .SetInsets(padding, padding, padding, padding) 392 .TopView()); 393 394 // "File Recognition" group 395 396 fRecognitionBox = new BBox("Recognition Box"); 397 fRecognitionBox->SetLabel(B_TRANSLATE("File recognition")); 398 fRecognitionBox->SetExplicitAlignment(fullAlignment); 399 400 fExtensionLabel = new StringView(B_TRANSLATE("Extensions:"), NULL); 401 fExtensionLabel->LabelView()->SetExplicitAlignment(labelAlignment); 402 403 fAddExtensionButton = new BButton("add ext", 404 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddExtension)); 405 fAddExtensionButton->SetExplicitMaxSize( 406 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 407 408 fRemoveExtensionButton = new BButton("remove ext", B_TRANSLATE("Remove"), 409 new BMessage(kMsgRemoveExtension)); 410 411 fExtensionListView = new ExtensionListView("listview ext", 412 B_SINGLE_SELECTION_LIST); 413 fExtensionListView->SetSelectionMessage( 414 new BMessage(kMsgExtensionSelected)); 415 fExtensionListView->SetInvocationMessage( 416 new BMessage(kMsgExtensionInvoked)); 417 418 BScrollView* scrollView = new BScrollView("scrollview ext", 419 fExtensionListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 420 421 fRuleControl = new BTextControl("rule", B_TRANSLATE("Rule:"), "", 422 new BMessage(kMsgRuleEntered)); 423 fRuleControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 424 fRuleControl->Hide(); 425 426 BGridView* recognitionBoxGrid = new BGridView(padding, padding / 2); 427 BGridLayoutBuilder(recognitionBoxGrid) 428 .Add(fExtensionLabel->LabelView(), 0, 0) 429 .Add(scrollView, 0, 1, 2, 3) 430 .Add(fAddExtensionButton, 2, 1) 431 .Add(fRemoveExtensionButton, 2, 2) 432 .Add(fRuleControl, 0, 4, 3, 1) 433 .SetInsets(padding, padding, padding, padding); 434 435 recognitionBoxGrid->SetExplicitAlignment(fullAlignment); 436 fRecognitionBox->AddChild(recognitionBoxGrid); 437 438 // "Description" group 439 440 fDescriptionBox = new BBox("description BBox"); 441 fDescriptionBox->SetLabel(B_TRANSLATE("Description")); 442 fDescriptionBox->SetExplicitAlignment(fullAlignment); 443 444 fInternalNameView = new StringView(B_TRANSLATE("Internal name:"), NULL); 445 fInternalNameView->SetEnabled(false); 446 fTypeNameControl = new BTextControl("type", B_TRANSLATE("Type name:"), "", 447 new BMessage(kMsgTypeEntered)); 448 fDescriptionControl = new BTextControl("description", 449 B_TRANSLATE("Description:"), "", new BMessage(kMsgDescriptionEntered)); 450 451 fDescriptionBox->AddChild(BGridLayoutBuilder(padding / 2, padding / 2) 452 .Add(fInternalNameView->LabelView(), 0, 0) 453 .Add(fInternalNameView->TextView(), 1, 0) 454 .Add(fTypeNameControl->CreateLabelLayoutItem(), 0, 1) 455 .Add(fTypeNameControl->CreateTextViewLayoutItem(), 1, 1, 2) 456 .Add(fDescriptionControl->CreateLabelLayoutItem(), 0, 2) 457 .Add(fDescriptionControl->CreateTextViewLayoutItem(), 1, 2, 2) 458 .SetInsets(padding, padding, padding, padding) 459 .View()); 460 461 // "Preferred Application" group 462 463 fPreferredBox = new BBox("preferred BBox"); 464 fPreferredBox->SetLabel(B_TRANSLATE("Preferred application")); 465 466 menu = new BPopUpMenu("preferred"); 467 menu->AddItem(item = new BMenuItem(B_TRANSLATE("None"), 468 new BMessage(kMsgPreferredAppChosen))); 469 item->SetMarked(true); 470 fPreferredField = new BMenuField("preferred", (char*)NULL, menu); 471 472 fSelectButton = new BButton("select", 473 B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 474 new BMessage(kMsgSelectPreferredApp)); 475 476 fSameAsButton = new BButton("same as", 477 B_TRANSLATE("Same as" B_UTF8_ELLIPSIS), 478 new BMessage(kMsgSamePreferredAppAs)); 479 480 fPreferredBox->AddChild(BGroupLayoutBuilder(B_HORIZONTAL, padding) 481 .Add(fPreferredField) 482 .Add(fSelectButton) 483 .Add(fSameAsButton) 484 .SetInsets(padding, padding, padding, padding) 485 .TopView()); 486 487 // "Extra Attributes" group 488 489 fAttributeBox = new BBox("Attribute Box"); 490 fAttributeBox->SetLabel(B_TRANSLATE("Extra attributes")); 491 492 fAddAttributeButton = new BButton("add attr", 493 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddAttribute)); 494 fAddAttributeButton->SetExplicitMaxSize( 495 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 496 497 fRemoveAttributeButton = new BButton("remove attr", B_TRANSLATE("Remove"), 498 new BMessage(kMsgRemoveAttribute)); 499 fRemoveAttributeButton->SetExplicitMaxSize( 500 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 501 502 fMoveUpAttributeButton = new BButton("move up attr", B_TRANSLATE("Move up"), 503 new BMessage(kMsgMoveUpAttribute)); 504 fMoveUpAttributeButton->SetExplicitMaxSize( 505 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 506 fMoveDownAttributeButton = new BButton("move down attr", 507 B_TRANSLATE("Move down"), new BMessage(kMsgMoveDownAttribute)); 508 fMoveDownAttributeButton->SetExplicitMaxSize( 509 BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); 510 511 fAttributeListView = new AttributeListView("listview attr"); 512 fAttributeListView->SetSelectionMessage( 513 new BMessage(kMsgAttributeSelected)); 514 fAttributeListView->SetInvocationMessage( 515 new BMessage(kMsgAttributeInvoked)); 516 517 BScrollView* attributesScroller = new BScrollView("scrollview attr", 518 fAttributeListView, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 519 520 fAttributeBox->AddChild(BGroupLayoutBuilder(B_HORIZONTAL, padding) 521 .Add(attributesScroller, 1.0f) 522 .AddGroup(B_VERTICAL, padding / 2, 0.0f) 523 .Add(fAddAttributeButton) 524 .Add(fRemoveAttributeButton) 525 .AddStrut(padding) 526 .Add(fMoveUpAttributeButton) 527 .Add(fMoveDownAttributeButton) 528 .AddGlue() 529 .End() 530 .SetInsets(padding, padding, padding, padding) 531 .TopView()); 532 533 fMainSplitView = new BSplitView(B_HORIZONTAL, floorf(padding / 2)); 534 535 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 536 .SetInsets(0, 0, 0, 0) 537 .Add(menuBar) 538 .AddGroup(B_HORIZONTAL, 0) 539 .SetInsets(padding, padding, padding, padding) 540 .AddSplit(fMainSplitView) 541 .AddGroup(B_VERTICAL, padding) 542 .Add(typeListScrollView) 543 .AddGroup(B_HORIZONTAL, padding) 544 .Add(addTypeButton) 545 .Add(fRemoveTypeButton) 546 .AddGlue() 547 .End() 548 .End() 549 // Right side 550 .AddGroup(B_VERTICAL, padding) 551 .AddGroup(B_HORIZONTAL, padding) 552 .Add(fIconBox, 1) 553 .Add(fRecognitionBox, 3) 554 .End() 555 .Add(fDescriptionBox) 556 .Add(fPreferredBox) 557 .Add(fAttributeBox, 5); 558 559 _SetType(NULL); 560 _ShowSnifferRule(showRule); 561 562 float leftWeight; 563 float rightWeight; 564 if (settings.FindFloat("left_split_weight", &leftWeight) != B_OK 565 || settings.FindFloat("right_split_weight", &rightWeight) != B_OK) { 566 leftWeight = 0.2; 567 rightWeight = 1.0 - leftWeight; 568 } 569 fMainSplitView->SetItemWeight(0, leftWeight, false); 570 fMainSplitView->SetItemWeight(1, rightWeight, true); 571 572 BMimeType::StartWatching(this); 573 } 574 575 576 FileTypesWindow::~FileTypesWindow() 577 { 578 BMimeType::StopWatching(this); 579 } 580 581 582 void 583 FileTypesWindow::MessageReceived(BMessage* message) 584 { 585 switch (message->what) { 586 case B_SIMPLE_DATA: 587 { 588 type_code type; 589 if (message->GetInfo("refs", &type) == B_OK 590 && type == B_REF_TYPE) { 591 be_app->PostMessage(message); 592 } 593 break; 594 } 595 596 case kMsgToggleIcons: 597 { 598 BMenuItem* item; 599 if (message->FindPointer("source", (void **)&item) != B_OK) 600 break; 601 602 item->SetMarked(!fTypeListView->IsShowingIcons()); 603 fTypeListView->ShowIcons(item->IsMarked()); 604 605 // update settings 606 BMessage update(kMsgSettingsChanged); 607 update.AddBool("show_icons", item->IsMarked()); 608 be_app_messenger.SendMessage(&update); 609 break; 610 } 611 612 case kMsgToggleRule: 613 { 614 BMenuItem* item; 615 if (message->FindPointer("source", (void **)&item) != B_OK) 616 break; 617 618 item->SetMarked(fRuleControl->IsHidden()); 619 _ShowSnifferRule(item->IsMarked()); 620 621 // update settings 622 BMessage update(kMsgSettingsChanged); 623 update.AddBool("show_rule", item->IsMarked()); 624 be_app_messenger.SendMessage(&update); 625 break; 626 } 627 628 case kMsgTypeSelected: 629 { 630 int32 index; 631 if (message->FindInt32("index", &index) == B_OK) { 632 MimeTypeItem* item 633 = (MimeTypeItem*)fTypeListView->ItemAt(index); 634 if (item != NULL) { 635 BMimeType type(item->Type()); 636 _SetType(&type); 637 } else 638 _SetType(NULL); 639 } 640 break; 641 } 642 643 case kMsgAddType: 644 if (fNewTypeWindow == NULL) { 645 fNewTypeWindow 646 = new NewFileTypeWindow(this, fCurrentType.Type()); 647 fNewTypeWindow->Show(); 648 } else 649 fNewTypeWindow->Activate(); 650 break; 651 652 case kMsgNewTypeWindowClosed: 653 fNewTypeWindow = NULL; 654 break; 655 656 case kMsgRemoveType: 657 { 658 if (fCurrentType.Type() == NULL) 659 break; 660 661 BAlert* alert; 662 if (fCurrentType.IsSupertypeOnly()) { 663 alert = new BPrivate::OverrideAlert( 664 B_TRANSLATE("FileTypes request"), 665 B_TRANSLATE("Removing a super type cannot be reverted.\n" 666 "All file types that belong to this super type " 667 "will be lost!\n\n" 668 "Are you sure you want to do this? To remove the whole " 669 "group, hold down the Shift key and press \"Remove\"."), 670 B_TRANSLATE("Remove"), B_SHIFT_KEY, B_TRANSLATE("Cancel"), 671 0, NULL, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 672 } else { 673 alert = new BAlert(B_TRANSLATE("FileTypes request"), 674 B_TRANSLATE("Removing a file type cannot be reverted.\n" 675 "Are you sure you want to remove it?"), 676 B_TRANSLATE("Remove"), B_TRANSLATE("Cancel"), 677 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 678 } 679 if (alert->Go()) 680 break; 681 682 status_t status = fCurrentType.Delete(); 683 if (status != B_OK) { 684 fprintf(stderr, B_TRANSLATE( 685 "Could not remove file type: %s\n"), strerror(status)); 686 } 687 break; 688 } 689 690 case kMsgSelectNewType: 691 { 692 const char* type; 693 if (message->FindString("type", &type) == B_OK) 694 fTypeListView->SelectNewType(type); 695 break; 696 } 697 698 // File Recognition group 699 700 case kMsgExtensionSelected: 701 { 702 int32 index; 703 if (message->FindInt32("index", &index) == B_OK) { 704 BStringItem* item 705 = (BStringItem*)fExtensionListView->ItemAt(index); 706 fRemoveExtensionButton->SetEnabled(item != NULL); 707 } 708 break; 709 } 710 711 case kMsgExtensionInvoked: 712 { 713 if (fCurrentType.Type() == NULL) 714 break; 715 716 int32 index; 717 if (message->FindInt32("index", &index) == B_OK) { 718 BStringItem* item 719 = (BStringItem*)fExtensionListView->ItemAt(index); 720 if (item == NULL) 721 break; 722 723 BWindow* window 724 = new ExtensionWindow(this, fCurrentType, item->Text()); 725 window->Show(); 726 } 727 break; 728 } 729 730 case kMsgAddExtension: 731 { 732 if (fCurrentType.Type() == NULL) 733 break; 734 735 BWindow* window = new ExtensionWindow(this, fCurrentType, NULL); 736 window->Show(); 737 break; 738 } 739 740 case kMsgRemoveExtension: 741 { 742 int32 index = fExtensionListView->CurrentSelection(); 743 if (index < 0 || fCurrentType.Type() == NULL) 744 break; 745 746 BMessage extensions; 747 if (fCurrentType.GetFileExtensions(&extensions) == B_OK) { 748 extensions.RemoveData("extensions", index); 749 fCurrentType.SetFileExtensions(&extensions); 750 } 751 break; 752 } 753 754 case kMsgRuleEntered: 755 { 756 // check rule 757 BString parseError; 758 if (BMimeType::CheckSnifferRule(fRuleControl->Text(), 759 &parseError) != B_OK) { 760 parseError.Prepend( 761 B_TRANSLATE("Recognition rule is not valid:\n\n")); 762 error_alert(parseError.String()); 763 } else 764 fCurrentType.SetSnifferRule(fRuleControl->Text()); 765 break; 766 } 767 768 // Description group 769 770 case kMsgTypeEntered: 771 { 772 fCurrentType.SetShortDescription(fTypeNameControl->Text()); 773 break; 774 } 775 776 case kMsgDescriptionEntered: 777 { 778 fCurrentType.SetLongDescription(fDescriptionControl->Text()); 779 break; 780 } 781 782 // Preferred Application group 783 784 case kMsgPreferredAppChosen: 785 { 786 const char* signature; 787 if (message->FindString("signature", &signature) != B_OK) 788 signature = NULL; 789 790 fCurrentType.SetPreferredApp(signature); 791 break; 792 } 793 794 case kMsgSelectPreferredApp: 795 { 796 BMessage panel(kMsgOpenFilePanel); 797 panel.AddString("title", 798 B_TRANSLATE("Select preferred application")); 799 panel.AddInt32("message", kMsgPreferredAppOpened); 800 panel.AddMessenger("target", this); 801 802 be_app_messenger.SendMessage(&panel); 803 break; 804 } 805 case kMsgPreferredAppOpened: 806 _AdoptPreferredApplication(message, false); 807 break; 808 809 case kMsgSamePreferredAppAs: 810 { 811 BMessage panel(kMsgOpenFilePanel); 812 panel.AddString("title", 813 B_TRANSLATE("Select same preferred application as")); 814 panel.AddInt32("message", kMsgSamePreferredAppAsOpened); 815 panel.AddMessenger("target", this); 816 817 be_app_messenger.SendMessage(&panel); 818 break; 819 } 820 case kMsgSamePreferredAppAsOpened: 821 _AdoptPreferredApplication(message, true); 822 break; 823 824 // Extra Attributes group 825 826 case kMsgAttributeSelected: 827 { 828 int32 index; 829 if (message->FindInt32("index", &index) == B_OK) { 830 AttributeItem* item 831 = (AttributeItem*)fAttributeListView->ItemAt(index); 832 fRemoveAttributeButton->SetEnabled(item != NULL); 833 fMoveUpAttributeButton->SetEnabled(index > 0); 834 fMoveDownAttributeButton->SetEnabled(index >= 0 835 && index < fAttributeListView->CountItems() - 1); 836 } 837 break; 838 } 839 840 case kMsgAttributeInvoked: 841 { 842 if (fCurrentType.Type() == NULL) 843 break; 844 845 int32 index; 846 if (message->FindInt32("index", &index) == B_OK) { 847 AttributeItem* item 848 = (AttributeItem*)fAttributeListView->ItemAt(index); 849 if (item == NULL) 850 break; 851 852 BWindow* window = new AttributeWindow(this, fCurrentType, 853 item); 854 window->Show(); 855 } 856 break; 857 } 858 859 case kMsgAddAttribute: 860 { 861 if (fCurrentType.Type() == NULL) 862 break; 863 864 BWindow* window = new AttributeWindow(this, fCurrentType, NULL); 865 window->Show(); 866 break; 867 } 868 869 case kMsgRemoveAttribute: 870 { 871 int32 index = fAttributeListView->CurrentSelection(); 872 if (index < 0 || fCurrentType.Type() == NULL) 873 break; 874 875 BMessage attributes; 876 if (fCurrentType.GetAttrInfo(&attributes) == B_OK) { 877 for (uint32 i = 0; i < 878 sizeof(kAttributeNames) / sizeof(kAttributeNames[0]); 879 i++) { 880 attributes.RemoveData(kAttributeNames[i], index); 881 } 882 883 fCurrentType.SetAttrInfo(&attributes); 884 } 885 break; 886 } 887 888 case kMsgMoveUpAttribute: 889 { 890 int32 index = fAttributeListView->CurrentSelection(); 891 if (index < 1 || fCurrentType.Type() == NULL) 892 break; 893 894 _MoveUpAttributeIndex(index); 895 break; 896 } 897 898 case kMsgMoveDownAttribute: 899 { 900 int32 index = fAttributeListView->CurrentSelection(); 901 if (index < 0 || index == fAttributeListView->CountItems() - 1 902 || fCurrentType.Type() == NULL) { 903 break; 904 } 905 906 _MoveUpAttributeIndex(index + 1); 907 break; 908 } 909 910 case B_META_MIME_CHANGED: 911 { 912 const char* type; 913 int32 which; 914 if (message->FindString("be:type", &type) != B_OK 915 || message->FindInt32("be:which", &which) != B_OK) 916 break; 917 918 if (fCurrentType.Type() == NULL) 919 break; 920 921 if (!strcasecmp(fCurrentType.Type(), type)) { 922 if (which != B_MIME_TYPE_DELETED) 923 _SetType(&fCurrentType, which); 924 else 925 _SetType(NULL); 926 } else { 927 // this change could still affect our current type 928 929 if (which == B_MIME_TYPE_DELETED 930 || which == B_SUPPORTED_TYPES_CHANGED 931 || which == B_PREFERRED_APP_CHANGED) { 932 _UpdatePreferredApps(&fCurrentType); 933 } 934 } 935 break; 936 } 937 938 default: 939 BWindow::MessageReceived(message); 940 } 941 } 942 943 944 void 945 FileTypesWindow::SelectType(const char* type) 946 { 947 fTypeListView->SelectType(type); 948 } 949 950 951 bool 952 FileTypesWindow::QuitRequested() 953 { 954 BMessage update(kMsgSettingsChanged); 955 update.AddRect("file_types_frame", Frame()); 956 update.AddFloat("left_split_weight", fMainSplitView->ItemWeight(0L)); 957 update.AddFloat("right_split_weight", fMainSplitView->ItemWeight(1)); 958 be_app_messenger.SendMessage(&update); 959 960 be_app->PostMessage(kMsgTypesWindowClosed); 961 return true; 962 } 963 964 965 void 966 FileTypesWindow::PlaceSubWindow(BWindow* window) 967 { 968 window->MoveTo(Frame().left + (Frame().Width() - window->Frame().Width()) 969 / 2.0f, Frame().top + (Frame().Height() - window->Frame().Height()) 970 / 2.0f); 971 } 972 973 974 // #pragma mark - private 975 976 977 BRect 978 FileTypesWindow::_Frame(const BMessage& settings) const 979 { 980 BRect rect; 981 if (settings.FindRect("file_types_frame", &rect) == B_OK) 982 return rect; 983 984 return BRect(80.0f, 80.0f, 0.0f, 0.0f); 985 } 986 987 988 void 989 FileTypesWindow::_ShowSnifferRule(bool show) 990 { 991 if (fRuleControl->IsHidden() == !show) 992 return; 993 994 if (!show) 995 fRuleControl->Hide(); 996 else 997 fRuleControl->Show(); 998 } 999 1000 1001 void 1002 FileTypesWindow::_UpdateExtensions(BMimeType* type) 1003 { 1004 // clear list 1005 1006 for (int32 i = fExtensionListView->CountItems(); i-- > 0;) { 1007 delete fExtensionListView->ItemAt(i); 1008 } 1009 fExtensionListView->MakeEmpty(); 1010 1011 // fill it again 1012 1013 if (type == NULL) 1014 return; 1015 1016 BMessage extensions; 1017 if (type->GetFileExtensions(&extensions) != B_OK) 1018 return; 1019 1020 const char* extension; 1021 int32 i = 0; 1022 while (extensions.FindString("extensions", i++, &extension) == B_OK) { 1023 char dotExtension[B_FILE_NAME_LENGTH]; 1024 snprintf(dotExtension, B_FILE_NAME_LENGTH, ".%s", extension); 1025 1026 fExtensionListView->AddItem(new BStringItem(dotExtension)); 1027 } 1028 } 1029 1030 1031 void 1032 FileTypesWindow::_AdoptPreferredApplication(BMessage* message, bool sameAs) 1033 { 1034 if (fCurrentType.Type() == NULL) 1035 return; 1036 1037 BString preferred; 1038 if (retrieve_preferred_app(message, sameAs, fCurrentType.Type(), preferred) 1039 != B_OK) { 1040 return; 1041 } 1042 1043 status_t status = fCurrentType.SetPreferredApp(preferred.String()); 1044 if (status != B_OK) 1045 error_alert(B_TRANSLATE("Could not set preferred application"), 1046 status); 1047 } 1048 1049 1050 void 1051 FileTypesWindow::_UpdatePreferredApps(BMimeType* type) 1052 { 1053 update_preferred_app_menu(fPreferredField->Menu(), type, 1054 kMsgPreferredAppChosen); 1055 } 1056 1057 1058 void 1059 FileTypesWindow::_UpdateIcon(BMimeType* type) 1060 { 1061 if (type != NULL) 1062 fIconView->SetTo(*type); 1063 else 1064 fIconView->Unset(); 1065 } 1066 1067 1068 void 1069 FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate) 1070 { 1071 bool enabled = type != NULL; 1072 1073 // update controls 1074 1075 if (type != NULL) { 1076 if (fCurrentType == *type) { 1077 if (!forceUpdate) 1078 return; 1079 } else 1080 forceUpdate = B_EVERYTHING_CHANGED; 1081 1082 if (&fCurrentType != type) 1083 fCurrentType.SetTo(type->Type()); 1084 1085 fInternalNameView->SetText(type->Type()); 1086 1087 char description[B_MIME_TYPE_LENGTH]; 1088 1089 if ((forceUpdate & B_SHORT_DESCRIPTION_CHANGED) != 0) { 1090 if (type->GetShortDescription(description) != B_OK) 1091 description[0] = '\0'; 1092 fTypeNameControl->SetText(description); 1093 } 1094 1095 if ((forceUpdate & B_LONG_DESCRIPTION_CHANGED) != 0) { 1096 if (type->GetLongDescription(description) != B_OK) 1097 description[0] = '\0'; 1098 fDescriptionControl->SetText(description); 1099 } 1100 1101 if ((forceUpdate & B_SNIFFER_RULE_CHANGED) != 0) { 1102 BString rule; 1103 if (type->GetSnifferRule(&rule) != B_OK) 1104 rule = ""; 1105 fRuleControl->SetText(rule.String()); 1106 } 1107 1108 fExtensionListView->SetType(&fCurrentType); 1109 } else { 1110 fCurrentType.Unset(); 1111 fInternalNameView->SetText(NULL); 1112 fTypeNameControl->SetText(NULL); 1113 fDescriptionControl->SetText(NULL); 1114 fRuleControl->SetText(NULL); 1115 fPreferredField->Menu()->ItemAt(0)->SetMarked(true); 1116 fExtensionListView->SetType(NULL); 1117 fAttributeListView->SetTo(NULL); 1118 } 1119 1120 if ((forceUpdate & B_FILE_EXTENSIONS_CHANGED) != 0) 1121 _UpdateExtensions(type); 1122 1123 if ((forceUpdate & B_PREFERRED_APP_CHANGED) != 0) 1124 _UpdatePreferredApps(type); 1125 1126 if ((forceUpdate & (B_ICON_CHANGED | B_PREFERRED_APP_CHANGED)) != 0) 1127 _UpdateIcon(type); 1128 1129 if ((forceUpdate & B_ATTR_INFO_CHANGED) != 0) 1130 fAttributeListView->SetTo(type); 1131 1132 // enable/disable controls 1133 1134 fIconView->SetEnabled(enabled); 1135 1136 fInternalNameView->SetEnabled(enabled); 1137 fTypeNameControl->SetEnabled(enabled); 1138 fDescriptionControl->SetEnabled(enabled); 1139 fPreferredField->SetEnabled(enabled); 1140 1141 fRemoveTypeButton->SetEnabled(enabled); 1142 1143 fSelectButton->SetEnabled(enabled); 1144 fSameAsButton->SetEnabled(enabled); 1145 1146 fExtensionLabel->SetEnabled(enabled); 1147 fAddExtensionButton->SetEnabled(enabled); 1148 fRemoveExtensionButton->SetEnabled(false); 1149 fRuleControl->SetEnabled(enabled); 1150 1151 fAddAttributeButton->SetEnabled(enabled); 1152 fRemoveAttributeButton->SetEnabled(false); 1153 fMoveUpAttributeButton->SetEnabled(false); 1154 fMoveDownAttributeButton->SetEnabled(false); 1155 } 1156 1157 1158 void 1159 FileTypesWindow::_MoveUpAttributeIndex(int32 index) 1160 { 1161 BMessage attributes; 1162 if (fCurrentType.GetAttrInfo(&attributes) != B_OK) 1163 return; 1164 1165 // Iterate over all known attribute fields, and for each field, 1166 // iterate over all fields of the same name and build a copy 1167 // of the attributes message with the field at the given index swapped 1168 // with the previous field. 1169 BMessage resortedAttributes; 1170 for (uint32 i = 0; i < 1171 sizeof(kAttributeNames) / sizeof(kAttributeNames[0]); 1172 i++) { 1173 1174 type_code type; 1175 int32 count; 1176 bool isFixedSize; 1177 if (attributes.GetInfo(kAttributeNames[i], &type, &count, 1178 &isFixedSize) != B_OK) { 1179 // Apparently the message does not contain this name, 1180 // so just ignore this attribute name. 1181 // NOTE: This shows that the attribute description is 1182 // too fragile. It would have been better to pack each 1183 // attribute description into a separate BMessage. 1184 continue; 1185 } 1186 1187 for (int32 j = 0; j < count; j++) { 1188 const void* data; 1189 ssize_t size; 1190 int32 originalIndex; 1191 if (j == index - 1) 1192 originalIndex = j + 1; 1193 else if (j == index) 1194 originalIndex = j - 1; 1195 else 1196 originalIndex = j; 1197 attributes.FindData(kAttributeNames[i], type, 1198 originalIndex, &data, &size); 1199 if (j == 0) { 1200 resortedAttributes.AddData(kAttributeNames[i], type, 1201 data, size, isFixedSize); 1202 } else { 1203 resortedAttributes.AddData(kAttributeNames[i], type, 1204 data, size); 1205 } 1206 } 1207 } 1208 1209 // Setting it directly on the type will trigger an update of the GUI as 1210 // well. TODO: FileTypes is heavily descructive, it should use an 1211 // Undo/Redo stack. 1212 fCurrentType.SetAttrInfo(&resortedAttributes); 1213 } 1214 1215 1216