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