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