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