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