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("Same As" B_UTF8_ELLIPSIS) - 24.0f; 499 innerRect.bottom = innerRect.top + button->Bounds().Height(); 500 fSameAsButton = new BButton(innerRect, "same as", "Same As" B_UTF8_ELLIPSIS, 501 new BMessage(kMsgSamePreferredAppAs), B_FOLLOW_RIGHT); 502 fPreferredBox->AddChild(fSameAsButton); 503 504 innerRect.OffsetBy(-innerRect.Width() - 6.0f, 0.0f); 505 fSelectButton = new BButton(innerRect, "select", "Select" B_UTF8_ELLIPSIS, 506 new BMessage(kMsgSelectPreferredApp), B_FOLLOW_RIGHT); 507 fPreferredBox->AddChild(fSelectButton); 508 509 menu = new BPopUpMenu("preferred"); 510 menu->AddItem(item = new BMenuItem("None", new BMessage(kMsgPreferredAppChosen))); 511 item->SetMarked(true); 512 513 innerRect.right = innerRect.left - 6.0f; 514 innerRect.left = 8.0f; 515 BView* constrainingView = new BView(innerRect, NULL, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW); 516 constrainingView->SetViewColor(topView->ViewColor()); 517 518 fPreferredField = new BMenuField(innerRect.OffsetToCopy(B_ORIGIN), "preferred", 519 NULL, menu); 520 float width; 521 fPreferredField->GetPreferredSize(&width, &height); 522 fPreferredField->ResizeTo(innerRect.Width(), height); 523 fPreferredField->MoveBy(0.0f, (innerRect.Height() - height) / 2.0f); 524 constrainingView->AddChild(fPreferredField); 525 // we embed the menu field in another view to make it behave like 526 // we want so that it can't obscure other elements with larger 527 // labels 528 529 fPreferredBox->AddChild(constrainingView); 530 531 // "Extra Attributes" group 532 533 rect.top = rect.bottom + 8.0f; 534 rect.bottom = topView->Bounds().Height() - 8.0f; 535 fAttributeBox = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP_BOTTOM); 536 fAttributeBox->SetLabel("Extra Attributes"); 537 topView->AddChild(fAttributeBox); 538 539 innerRect = fAttributeBox->Bounds().InsetByCopy(8.0f, 6.0f); 540 innerRect.top += ceilf(boldHeight.ascent); 541 innerRect.left = innerRect.right - button->StringWidth("Remove") - 16.0f; 542 innerRect.bottom = innerRect.top + button->Bounds().Height(); 543 fAddAttributeButton = new BButton(innerRect, "add attr", "Add" B_UTF8_ELLIPSIS, 544 new BMessage(kMsgAddAttribute), B_FOLLOW_RIGHT); 545 fAttributeBox->AddChild(fAddAttributeButton); 546 547 innerRect.OffsetBy(0, innerRect.Height() + 4.0f); 548 fRemoveAttributeButton = new BButton(innerRect, "remove attr", "Remove", 549 new BMessage(kMsgRemoveAttribute), B_FOLLOW_RIGHT); 550 fAttributeBox->AddChild(fRemoveAttributeButton); 551 /* 552 innerRect.OffsetBy(0, innerRect.Height() + 4.0f); 553 button = new BButton(innerRect, "push attr", "Push Up", 554 new BMessage(kMsgRemoveAttribute), B_FOLLOW_RIGHT); 555 fAttributeBox->AddChild(button); 556 */ 557 innerRect.right = innerRect.left - 10.0f - B_V_SCROLL_BAR_WIDTH; 558 innerRect.left = 10.0f; 559 innerRect.top = 8.0f + ceilf(boldHeight.ascent); 560 innerRect.bottom = fAttributeBox->Bounds().bottom - 10.0f; 561 // take scrollview border into account 562 fAttributeListView = new AttributeListView(innerRect, "listview attr", 563 B_FOLLOW_ALL); 564 fAttributeListView->SetSelectionMessage(new BMessage(kMsgAttributeSelected)); 565 fAttributeListView->SetInvocationMessage(new BMessage(kMsgAttributeInvoked)); 566 567 scrollView = new BScrollView("scrollview attr", fAttributeListView, 568 B_FOLLOW_ALL, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 569 fAttributeBox->AddChild(scrollView); 570 571 SetSizeLimits(rightRect.left + 72.0f + font.StringWidth("jpg") 572 + font.StringWidth(fRecognitionBox->Label()), 32767.0f, 573 rect.top + 2.0f * button->Bounds().Height() + boldHeight.ascent 574 + 32.0f + menuBar->Bounds().Height(), 32767.0f); 575 576 _SetType(NULL); 577 _ShowSnifferRule(showRule); 578 579 BMimeType::StartWatching(this); 580 } 581 582 583 FileTypesWindow::~FileTypesWindow() 584 { 585 BMimeType::StopWatching(this); 586 } 587 588 589 BRect 590 FileTypesWindow::_Frame(const BMessage& settings) const 591 { 592 BRect rect; 593 if (settings.FindRect("file_types_frame", &rect) == B_OK) 594 return rect; 595 596 return BRect(80.0f, 80.0f, 600.0f, 480.0f); 597 } 598 599 600 void 601 FileTypesWindow::_ShowSnifferRule(bool show) 602 { 603 if (fRuleControl->IsHidden() == !show) 604 return; 605 606 float minWidth, maxWidth, minHeight, maxHeight; 607 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 608 609 float diff = fRuleControl->Bounds().Height() + 8.0f; 610 611 if (!show) { 612 fRuleControl->Hide(); 613 diff = -diff; 614 } 615 616 // adjust other controls to make space or take it again 617 618 fIconBox->ResizeBy(0.0f, diff); 619 fRecognitionBox->ResizeBy(0.0f, diff); 620 fDescriptionBox->MoveBy(0.0f, diff); 621 fPreferredBox->MoveBy(0.0f, diff); 622 fAttributeBox->MoveBy(0.0f, diff); 623 fAttributeBox->ResizeBy(0.0f, -diff); 624 625 if (show) 626 fRuleControl->Show(); 627 628 SetSizeLimits(minWidth, maxWidth, minHeight + diff, maxHeight); 629 } 630 631 632 void 633 FileTypesWindow::_UpdateExtensions(BMimeType* type) 634 { 635 // clear list 636 637 for (int32 i = fExtensionListView->CountItems(); i-- > 0;) { 638 delete fExtensionListView->ItemAt(i); 639 } 640 fExtensionListView->MakeEmpty(); 641 642 // fill it again 643 644 if (type == NULL) 645 return; 646 647 BMessage extensions; 648 if (type->GetFileExtensions(&extensions) != B_OK) 649 return; 650 651 const char* extension; 652 int32 i = 0; 653 while (extensions.FindString("extensions", i++, &extension) == B_OK) { 654 char dotExtension[B_FILE_NAME_LENGTH]; 655 snprintf(dotExtension, B_FILE_NAME_LENGTH, ".%s", extension); 656 657 fExtensionListView->AddItem(new BStringItem(dotExtension)); 658 } 659 } 660 661 662 void 663 FileTypesWindow::_AdoptPreferredApplication(BMessage* message, bool sameAs) 664 { 665 if (fCurrentType.Type() == NULL) 666 return; 667 668 BString preferred; 669 if (retrieve_preferred_app(message, sameAs, fCurrentType.Type(), preferred) != B_OK) 670 return; 671 672 status_t status = fCurrentType.SetPreferredApp(preferred.String()); 673 if (status != B_OK) 674 error_alert("Could not set preferred application", status); 675 } 676 677 678 void 679 FileTypesWindow::_UpdatePreferredApps(BMimeType* type) 680 { 681 update_preferred_app_menu(fPreferredField->Menu(), type, kMsgPreferredAppChosen); 682 } 683 684 685 void 686 FileTypesWindow::_UpdateIcon(BMimeType* type) 687 { 688 if (type != NULL) 689 fIconView->SetTo(*type); 690 else 691 fIconView->Unset(); 692 } 693 694 695 void 696 FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate) 697 { 698 bool enabled = type != NULL; 699 700 // update controls 701 702 if (type != NULL) { 703 if (fCurrentType == *type) { 704 if (!forceUpdate) 705 return; 706 } else 707 forceUpdate = B_EVERYTHING_CHANGED; 708 709 if (&fCurrentType != type) 710 fCurrentType.SetTo(type->Type()); 711 712 fInternalNameView->SetText(type->Type()); 713 714 char description[B_MIME_TYPE_LENGTH]; 715 716 if ((forceUpdate & B_SHORT_DESCRIPTION_CHANGED) != 0) { 717 if (type->GetShortDescription(description) != B_OK) 718 description[0] = '\0'; 719 fTypeNameControl->SetText(description); 720 } 721 722 if ((forceUpdate & B_LONG_DESCRIPTION_CHANGED) != 0) { 723 if (type->GetLongDescription(description) != B_OK) 724 description[0] = '\0'; 725 fDescriptionControl->SetText(description); 726 } 727 728 if ((forceUpdate & B_SNIFFER_RULE_CHANGED) != 0) { 729 BString rule; 730 if (type->GetSnifferRule(&rule) != B_OK) 731 rule = ""; 732 fRuleControl->SetText(rule.String()); 733 } 734 735 fExtensionListView->SetType(&fCurrentType); 736 } else { 737 fCurrentType.Unset(); 738 fInternalNameView->SetText(NULL); 739 fTypeNameControl->SetText(NULL); 740 fDescriptionControl->SetText(NULL); 741 fRuleControl->SetText(NULL); 742 fPreferredField->Menu()->ItemAt(0)->SetMarked(true); 743 fExtensionListView->SetType(NULL); 744 } 745 746 if ((forceUpdate & B_FILE_EXTENSIONS_CHANGED) != 0) 747 _UpdateExtensions(type); 748 749 if ((forceUpdate & B_PREFERRED_APP_CHANGED) != 0) 750 _UpdatePreferredApps(type); 751 752 if ((forceUpdate & (B_ICON_CHANGED | B_PREFERRED_APP_CHANGED)) != 0) 753 _UpdateIcon(type); 754 755 if ((forceUpdate & B_ATTR_INFO_CHANGED) != 0) 756 fAttributeListView->SetTo(type); 757 758 // enable/disable controls 759 760 fIconView->SetEnabled(enabled); 761 762 fInternalNameView->SetEnabled(enabled); 763 fTypeNameControl->SetEnabled(enabled); 764 fDescriptionControl->SetEnabled(enabled); 765 fPreferredField->SetEnabled(enabled); 766 767 fRemoveTypeButton->SetEnabled(enabled); 768 769 fSelectButton->SetEnabled(enabled); 770 fSameAsButton->SetEnabled(enabled); 771 772 fExtensionLabel->SetEnabled(enabled); 773 fAddExtensionButton->SetEnabled(enabled); 774 fRemoveExtensionButton->SetEnabled(false); 775 fRuleControl->SetEnabled(enabled); 776 777 fAddAttributeButton->SetEnabled(enabled); 778 fRemoveAttributeButton->SetEnabled(false); 779 } 780 781 782 void 783 FileTypesWindow::PlaceSubWindow(BWindow* window) 784 { 785 window->MoveTo(Frame().left + (Frame().Width() - window->Frame().Width()) / 2.0f, 786 Frame().top + (Frame().Height() - window->Frame().Height()) / 2.0f); 787 } 788 789 790 void 791 FileTypesWindow::MessageReceived(BMessage* message) 792 { 793 switch (message->what) { 794 case B_SIMPLE_DATA: 795 type_code type; 796 if (message->GetInfo("refs", &type) == B_OK 797 && type == B_REF_TYPE) { 798 be_app->PostMessage(message); 799 } 800 break; 801 802 case kMsgToggleIcons: 803 { 804 BMenuItem* item; 805 if (message->FindPointer("source", (void **)&item) != B_OK) 806 break; 807 808 item->SetMarked(!fTypeListView->IsShowingIcons()); 809 fTypeListView->ShowIcons(item->IsMarked()); 810 811 // update settings 812 BMessage update(kMsgSettingsChanged); 813 update.AddBool("show_icons", item->IsMarked()); 814 be_app_messenger.SendMessage(&update); 815 break; 816 } 817 818 case kMsgToggleRule: 819 { 820 BMenuItem* item; 821 if (message->FindPointer("source", (void **)&item) != B_OK) 822 break; 823 824 item->SetMarked(fRuleControl->IsHidden()); 825 _ShowSnifferRule(item->IsMarked()); 826 827 // update settings 828 BMessage update(kMsgSettingsChanged); 829 update.AddBool("show_rule", item->IsMarked()); 830 be_app_messenger.SendMessage(&update); 831 break; 832 } 833 834 case kMsgTypeSelected: 835 { 836 int32 index; 837 if (message->FindInt32("index", &index) == B_OK) { 838 MimeTypeItem* item = (MimeTypeItem*)fTypeListView->ItemAt(index); 839 if (item != NULL) { 840 BMimeType type(item->Type()); 841 _SetType(&type); 842 } else 843 _SetType(NULL); 844 } 845 break; 846 } 847 848 case kMsgAddType: 849 { 850 if (fNewTypeWindow == NULL) { 851 fNewTypeWindow = new NewFileTypeWindow(this, fCurrentType.Type()); 852 fNewTypeWindow->Show(); 853 } else 854 fNewTypeWindow->Activate(); 855 break; 856 } 857 case kMsgNewTypeWindowClosed: 858 fNewTypeWindow = NULL; 859 break; 860 861 case kMsgRemoveType: 862 { 863 if (fCurrentType.Type() == NULL) 864 break; 865 866 BAlert* alert; 867 if (fCurrentType.IsSupertypeOnly()) { 868 alert = new BPrivate::OverrideAlert("FileTypes Request", 869 "Removing a super type cannot be reverted.\n" 870 "All file types that belong to this super type " 871 "will be lost!\n\n" 872 "Are you sure you want to do this? To remove the whole " 873 "group, hold down the Shift key and press \"Remove\".", 874 "Remove", B_SHIFT_KEY, "Cancel", 0, NULL, 0, 875 B_WIDTH_AS_USUAL, B_STOP_ALERT); 876 } else { 877 alert = new BAlert("FileTypes Request", 878 "Removing a file type cannot be reverted.\n" 879 "Are you sure you want to remove it?", 880 "Remove", "Cancel", NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 881 } 882 if (alert->Go()) 883 break; 884 885 status_t status = fCurrentType.Delete(); 886 if (status != B_OK) 887 fprintf(stderr, "Could not remove file type: %s\n", strerror(status)); 888 break; 889 } 890 891 case kMsgSelectNewType: 892 { 893 const char* type; 894 if (message->FindString("type", &type) == B_OK) 895 fTypeListView->SelectNewType(type); 896 break; 897 } 898 899 // File Recognition group 900 901 case kMsgExtensionSelected: 902 { 903 int32 index; 904 if (message->FindInt32("index", &index) == B_OK) { 905 BStringItem* item = (BStringItem*)fExtensionListView->ItemAt(index); 906 fRemoveExtensionButton->SetEnabled(item != NULL); 907 } 908 break; 909 } 910 911 case kMsgExtensionInvoked: 912 { 913 if (fCurrentType.Type() == NULL) 914 break; 915 916 int32 index; 917 if (message->FindInt32("index", &index) == B_OK) { 918 BStringItem* item = (BStringItem*)fExtensionListView->ItemAt(index); 919 if (item == NULL) 920 break; 921 922 BWindow* window = new ExtensionWindow(this, fCurrentType, item->Text()); 923 window->Show(); 924 } 925 break; 926 } 927 928 case kMsgAddExtension: 929 { 930 if (fCurrentType.Type() == NULL) 931 break; 932 933 BWindow* window = new ExtensionWindow(this, fCurrentType, NULL); 934 window->Show(); 935 break; 936 } 937 938 case kMsgRemoveExtension: 939 { 940 int32 index = fExtensionListView->CurrentSelection(); 941 if (index < 0 || fCurrentType.Type() == NULL) 942 break; 943 944 BMessage extensions; 945 if (fCurrentType.GetFileExtensions(&extensions) == B_OK) { 946 extensions.RemoveData("extensions", index); 947 fCurrentType.SetFileExtensions(&extensions); 948 } 949 break; 950 } 951 952 case kMsgRuleEntered: 953 { 954 // check rule 955 BString parseError; 956 if (BMimeType::CheckSnifferRule(fRuleControl->Text(), &parseError) != B_OK) { 957 parseError.Prepend("Recognition rule is not valid:\n\n"); 958 error_alert(parseError.String()); 959 } else 960 fCurrentType.SetSnifferRule(fRuleControl->Text()); 961 break; 962 } 963 964 // Description group 965 966 case kMsgTypeEntered: 967 { 968 fCurrentType.SetShortDescription(fTypeNameControl->Text()); 969 break; 970 } 971 972 case kMsgDescriptionEntered: 973 { 974 fCurrentType.SetLongDescription(fDescriptionControl->Text()); 975 break; 976 } 977 978 // Preferred Application group 979 980 case kMsgPreferredAppChosen: 981 { 982 const char* signature; 983 if (message->FindString("signature", &signature) != B_OK) 984 signature = NULL; 985 986 fCurrentType.SetPreferredApp(signature); 987 break; 988 } 989 990 case kMsgSelectPreferredApp: 991 { 992 BMessage panel(kMsgOpenFilePanel); 993 panel.AddString("title", "Select Preferred Application"); 994 panel.AddInt32("message", kMsgPreferredAppOpened); 995 panel.AddMessenger("target", this); 996 997 be_app_messenger.SendMessage(&panel); 998 break; 999 } 1000 case kMsgPreferredAppOpened: 1001 _AdoptPreferredApplication(message, false); 1002 break; 1003 1004 case kMsgSamePreferredAppAs: 1005 { 1006 BMessage panel(kMsgOpenFilePanel); 1007 panel.AddString("title", "Select Same Preferred Application As"); 1008 panel.AddInt32("message", kMsgSamePreferredAppAsOpened); 1009 panel.AddMessenger("target", this); 1010 1011 be_app_messenger.SendMessage(&panel); 1012 break; 1013 } 1014 case kMsgSamePreferredAppAsOpened: 1015 _AdoptPreferredApplication(message, true); 1016 break; 1017 1018 // Extra Attributes group 1019 1020 case kMsgAttributeSelected: 1021 { 1022 int32 index; 1023 if (message->FindInt32("index", &index) == B_OK) { 1024 AttributeItem* item = (AttributeItem*)fAttributeListView->ItemAt(index); 1025 fRemoveAttributeButton->SetEnabled(item != NULL); 1026 } 1027 break; 1028 } 1029 1030 case kMsgAttributeInvoked: 1031 { 1032 if (fCurrentType.Type() == NULL) 1033 break; 1034 1035 int32 index; 1036 if (message->FindInt32("index", &index) == B_OK) { 1037 AttributeItem* item = (AttributeItem*)fAttributeListView->ItemAt(index); 1038 if (item == NULL) 1039 break; 1040 1041 BWindow* window = new AttributeWindow(this, fCurrentType, 1042 item); 1043 window->Show(); 1044 } 1045 break; 1046 } 1047 1048 case kMsgAddAttribute: 1049 { 1050 if (fCurrentType.Type() == NULL) 1051 break; 1052 1053 BWindow* window = new AttributeWindow(this, fCurrentType, NULL); 1054 window->Show(); 1055 break; 1056 } 1057 1058 case kMsgRemoveAttribute: 1059 { 1060 int32 index = fAttributeListView->CurrentSelection(); 1061 if (index < 0 || fCurrentType.Type() == NULL) 1062 break; 1063 1064 BMessage attributes; 1065 if (fCurrentType.GetAttrInfo(&attributes) == B_OK) { 1066 const char* kAttributeNames[] = { 1067 "attr:public_name", "attr:name", "attr:type", 1068 "attr:editable", "attr:viewable", "attr:extra", 1069 "attr:alignment", "attr:width", "attr:display_as" 1070 }; 1071 1072 for (uint32 i = 0; i < 1073 sizeof(kAttributeNames) / sizeof(kAttributeNames[0]); i++) { 1074 attributes.RemoveData(kAttributeNames[i], index); 1075 } 1076 1077 fCurrentType.SetAttrInfo(&attributes); 1078 } 1079 break; 1080 } 1081 1082 case B_META_MIME_CHANGED: 1083 { 1084 const char* type; 1085 int32 which; 1086 if (message->FindString("be:type", &type) != B_OK 1087 || message->FindInt32("be:which", &which) != B_OK) 1088 break; 1089 1090 if (fCurrentType.Type() == NULL) 1091 break; 1092 1093 if (!strcasecmp(fCurrentType.Type(), type)) { 1094 if (which != B_MIME_TYPE_DELETED) 1095 _SetType(&fCurrentType, which); 1096 else 1097 _SetType(NULL); 1098 } else { 1099 // this change could still affect our current type 1100 1101 if (which == B_MIME_TYPE_DELETED 1102 #ifdef __HAIKU__ 1103 || which == B_SUPPORTED_TYPES_CHANGED 1104 #endif 1105 || which == B_PREFERRED_APP_CHANGED) 1106 _UpdatePreferredApps(&fCurrentType); 1107 } 1108 break; 1109 } 1110 1111 default: 1112 BWindow::MessageReceived(message); 1113 } 1114 } 1115 1116 1117 bool 1118 FileTypesWindow::QuitRequested() 1119 { 1120 BMessage update(kMsgSettingsChanged); 1121 update.AddRect("file_types_frame", Frame()); 1122 be_app_messenger.SendMessage(&update); 1123 1124 be_app->PostMessage(kMsgTypesWindowClosed); 1125 return true; 1126 } 1127 1128 1129