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