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