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(const BMessage& settings) 236 : BWindow(_Frame(settings), "FileTypes", B_TITLED_WINDOW, 237 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS), 238 fNewTypeWindow(NULL) 239 { 240 bool showIcons; 241 bool showRule; 242 if (settings.FindBool("show_icons", &showIcons) != B_OK) 243 showIcons = true; 244 if (settings.FindBool("show_rule", &showRule) != B_OK) 245 showRule = false; 246 247 // add the menu 248 249 BMenuBar* menuBar = new BMenuBar(BRect(0, 0, 0, 0), NULL); 250 AddChild(menuBar); 251 252 BMenu* menu = new BMenu("File"); 253 BMenuItem* item; 254 menu->AddItem(item = new BMenuItem("New Resource File" B_UTF8_ELLIPSIS, 255 NULL, 'N', B_COMMAND_KEY)); 256 item->SetEnabled(false); 257 258 BMenu* recentsMenu = BRecentFilesList::NewFileListMenu("Open" B_UTF8_ELLIPSIS, 259 NULL, NULL, be_app, 10, false, NULL, kSignature); 260 item = new BMenuItem(recentsMenu, new BMessage(kMsgOpenFilePanel)); 261 item->SetShortcut('O', B_COMMAND_KEY); 262 menu->AddItem(item); 263 menu->AddItem(new BMenuItem("Application Types" B_UTF8_ELLIPSIS, 264 new BMessage(kMsgOpenApplicationTypesWindow))); 265 menu->AddSeparatorItem(); 266 267 menu->AddItem(new BMenuItem("About FileTypes" B_UTF8_ELLIPSIS, 268 new BMessage(B_ABOUT_REQUESTED))); 269 menu->AddSeparatorItem(); 270 271 menu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED), 272 'Q', B_COMMAND_KEY)); 273 menu->SetTargetForItems(be_app); 274 menuBar->AddItem(menu); 275 276 menu = new BMenu("Settings"); 277 item = new BMenuItem("Show Icons in List", new BMessage(kMsgToggleIcons)); 278 item->SetMarked(showIcons); 279 item->SetTarget(this); 280 menu->AddItem(item); 281 282 item = new BMenuItem("Show Recognition Rule", new BMessage(kMsgToggleRule)); 283 item->SetMarked(showRule); 284 item->SetTarget(this); 285 menu->AddItem(item); 286 menuBar->AddItem(menu); 287 288 // MIME Types list 289 290 BRect rect = Bounds(); 291 rect.top = menuBar->Bounds().Height() + 1.0f; 292 BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW); 293 topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 294 AddChild(topView); 295 296 BButton* button = new BButton(rect, "add", "Add" B_UTF8_ELLIPSIS, 297 new BMessage(kMsgAddType), B_FOLLOW_BOTTOM); 298 button->ResizeToPreferred(); 299 button->MoveTo(8.0f, topView->Bounds().bottom - 8.0f - button->Bounds().Height()); 300 topView->AddChild(button); 301 302 rect = button->Frame(); 303 rect.OffsetBy(rect.Width() + 8.0f, 0.0f); 304 fRemoveTypeButton = new BButton(rect, "remove", "Remove", 305 new BMessage(kMsgRemoveType), B_FOLLOW_BOTTOM); 306 fRemoveTypeButton->ResizeToPreferred(); 307 topView->AddChild(fRemoveTypeButton); 308 309 rect.bottom = rect.top - 10.0f; 310 rect.top = 10.0f; 311 rect.left = 10.0f; 312 rect.right -= B_V_SCROLL_BAR_WIDTH + 2.0f; 313 if (rect.right < 180) 314 rect.right = 180; 315 316 fTypeListView = new MimeTypeListView(rect, "typeview", NULL, showIcons, false, 317 B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM); 318 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); 319 320 BScrollView* scrollView = new BScrollView("scrollview", fTypeListView, 321 B_FOLLOW_LEFT | B_FOLLOW_TOP_BOTTOM, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 322 topView->AddChild(scrollView); 323 324 // "Icon" group 325 326 font_height plainHeight; 327 be_plain_font->GetHeight(&plainHeight); 328 float height = ceilf(plainHeight.ascent + plainHeight.descent 329 + plainHeight.leading) + 2.0f; 330 331 BFont font(be_bold_font); 332 float labelWidth = font.StringWidth("Icon"); 333 font_height boldHeight; 334 font.GetHeight(&boldHeight); 335 336 BRect innerRect; 337 fIconView = new TypeIconView(innerRect, "icon", NULL, 338 B_FOLLOW_LEFT | B_FOLLOW_V_CENTER); 339 fIconView->ResizeToPreferred(); 340 341 rect.left = rect.right + 12.0f + B_V_SCROLL_BAR_WIDTH; 342 rect.right = rect.left + max_c(fIconView->Bounds().Width(), labelWidth) + 16.0f; 343 rect.bottom = rect.top + ceilf(boldHeight.ascent) 344 + max_c(fIconView->Bounds().Height(), 345 button->Bounds().Height() * 2.0f + height + 4.0f) + 12.0f; 346 rect.top -= 2.0f; 347 fIconBox = new BBox(rect); 348 fIconBox->SetLabel("Icon"); 349 topView->AddChild(fIconBox); 350 351 innerRect.left = 8.0f; 352 innerRect.top = plainHeight.ascent + 3.0f 353 + (rect.Height() - boldHeight.ascent - fIconView->Bounds().Height()) / 2.0f; 354 if (innerRect.top + fIconView->Bounds().Height() > fIconBox->Bounds().Height() - 6.0f) 355 innerRect.top = fIconBox->Bounds().Height() - 6.0f - fIconView->Bounds().Height(); 356 fIconView->MoveTo(innerRect.LeftTop()); 357 fIconBox->AddChild(fIconView); 358 359 // "File Recognition" group 360 361 BRect rightRect(rect); 362 rightRect.left = rect.right + 8.0f; 363 rightRect.right = topView->Bounds().Width() - 8.0f; 364 fRecognitionBox = new BBox(rightRect, NULL, B_FOLLOW_LEFT_RIGHT); 365 fRecognitionBox->SetLabel("File Recognition"); 366 topView->AddChild(fRecognitionBox); 367 368 innerRect = fRecognitionBox->Bounds().InsetByCopy(8.0f, 4.0f); 369 innerRect.top += ceilf(boldHeight.ascent); 370 fExtensionLabel = new StringView(innerRect, "extension", "Extensions:", NULL); 371 fExtensionLabel->SetAlignment(B_ALIGN_LEFT, B_ALIGN_LEFT); 372 fExtensionLabel->ResizeToPreferred(); 373 fRecognitionBox->AddChild(fExtensionLabel); 374 375 innerRect.top += fExtensionLabel->Bounds().Height() + 2.0f; 376 innerRect.left = innerRect.right - button->StringWidth("Remove") - 16.0f; 377 innerRect.bottom = innerRect.top + button->Bounds().Height(); 378 fAddExtensionButton = new BButton(innerRect, "add ext", "Add" B_UTF8_ELLIPSIS, 379 new BMessage(kMsgAddExtension), B_FOLLOW_RIGHT); 380 fRecognitionBox->AddChild(fAddExtensionButton); 381 382 innerRect.OffsetBy(0, innerRect.Height() + 4.0f); 383 fRemoveExtensionButton = new BButton(innerRect, "remove ext", "Remove", 384 new BMessage(kMsgRemoveExtension), B_FOLLOW_RIGHT); 385 fRecognitionBox->AddChild(fRemoveExtensionButton); 386 387 innerRect.right = innerRect.left - 10.0f - B_V_SCROLL_BAR_WIDTH; 388 innerRect.left = 10.0f; 389 innerRect.top = fAddExtensionButton->Frame().top + 2.0f; 390 innerRect.bottom = innerRect.bottom - 2.0f; 391 // take scrollview border into account 392 fExtensionListView = new BListView(innerRect, "listview ext", 393 B_SINGLE_SELECTION_LIST, B_FOLLOW_LEFT_RIGHT); 394 fExtensionListView->SetSelectionMessage(new BMessage(kMsgExtensionSelected)); 395 fExtensionListView->SetInvocationMessage(new BMessage(kMsgExtensionInvoked)); 396 397 scrollView = new BScrollView("scrollview ext", fExtensionListView, 398 B_FOLLOW_LEFT_RIGHT, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 399 fRecognitionBox->AddChild(scrollView); 400 401 innerRect.left = 8.0f; 402 innerRect.top = innerRect.bottom + 10.0f; 403 innerRect.right = fRecognitionBox->Bounds().right - 8.0f; 404 innerRect.bottom = innerRect.top + 20.0f; 405 fRuleControl = new BTextControl(innerRect, "rule", "Rule:", "", 406 new BMessage(kMsgRuleEntered), B_FOLLOW_LEFT_RIGHT); 407 //fRuleControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 408 fRuleControl->SetDivider(fRuleControl->StringWidth(fRuleControl->Label()) + 6.0f); 409 fRuleControl->Hide(); 410 fRecognitionBox->AddChild(fRuleControl); 411 412 // "Description" group 413 414 rect.top = rect.bottom + 8.0f; 415 rect.bottom = rect.top + ceilf(boldHeight.ascent) + 24.0f; 416 rect.right = rightRect.right; 417 fDescriptionBox = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); 418 fDescriptionBox->SetLabel("Description"); 419 topView->AddChild(fDescriptionBox); 420 421 innerRect = fDescriptionBox->Bounds().InsetByCopy(8.0f, 6.0f); 422 innerRect.top += ceilf(boldHeight.ascent); 423 innerRect.bottom = innerRect.top + button->Bounds().Height(); 424 fInternalNameView = new StringView(innerRect, "internal", "Internal Name:", "", 425 B_FOLLOW_LEFT_RIGHT); 426 labelWidth = fInternalNameView->StringWidth(fInternalNameView->Label()) + 2.0f; 427 fInternalNameView->SetDivider(labelWidth); 428 fInternalNameView->SetEnabled(false); 429 fInternalNameView->ResizeToPreferred(); 430 fDescriptionBox->AddChild(fInternalNameView); 431 432 innerRect.OffsetBy(0, fInternalNameView->Bounds().Height() + 5.0f); 433 fTypeNameControl = new BTextControl(innerRect, "type", "Type Name:", "", 434 new BMessage(kMsgTypeEntered), B_FOLLOW_LEFT_RIGHT); 435 fTypeNameControl->SetDivider(labelWidth); 436 fTypeNameControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 437 fDescriptionBox->ResizeBy(0, fInternalNameView->Bounds().Height() 438 + fTypeNameControl->Bounds().Height() * 2.0f); 439 fDescriptionBox->AddChild(fTypeNameControl); 440 441 innerRect.OffsetBy(0, fTypeNameControl->Bounds().Height() + 5.0f); 442 fDescriptionControl = new BTextControl(innerRect, "description", "Description:", "", 443 new BMessage(kMsgDescriptionEntered), B_FOLLOW_LEFT_RIGHT); 444 fDescriptionControl->SetDivider(labelWidth); 445 fDescriptionControl->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 446 fDescriptionBox->AddChild(fDescriptionControl); 447 448 // "Preferred Application" group 449 450 rect = fDescriptionBox->Frame(); 451 rect.top = rect.bottom + 8.0f; 452 rect.bottom = rect.top + ceilf(boldHeight.ascent) 453 + button->Bounds().Height() + 14.0f; 454 fPreferredBox = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); 455 fPreferredBox->SetLabel("Preferred Application"); 456 topView->AddChild(fPreferredBox); 457 458 innerRect = fPreferredBox->Bounds().InsetByCopy(8.0f, 6.0f); 459 innerRect.top += ceilf(boldHeight.ascent); 460 innerRect.left = innerRect.right - button->StringWidth("Same As" B_UTF8_ELLIPSIS) - 24.0f; 461 innerRect.bottom = innerRect.top + button->Bounds().Height(); 462 fSameAsButton = new BButton(innerRect, "same as", "Same As" B_UTF8_ELLIPSIS, 463 new BMessage(kMsgSamePreferredAppAs), B_FOLLOW_RIGHT); 464 fPreferredBox->AddChild(fSameAsButton); 465 466 innerRect.OffsetBy(-innerRect.Width() - 6.0f, 0.0f); 467 fSelectButton = new BButton(innerRect, "select", "Select" B_UTF8_ELLIPSIS, 468 new BMessage(kMsgSelectPreferredApp), B_FOLLOW_RIGHT); 469 fPreferredBox->AddChild(fSelectButton); 470 471 menu = new BPopUpMenu("preferred"); 472 menu->AddItem(item = new BMenuItem("None", new BMessage(kMsgPreferredAppChosen))); 473 item->SetMarked(true); 474 475 innerRect.right = innerRect.left - 6.0f; 476 innerRect.left = 8.0f; 477 BView* constrainingView = new BView(innerRect, NULL, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW); 478 constrainingView->SetViewColor(topView->ViewColor()); 479 480 fPreferredField = new BMenuField(innerRect.OffsetToCopy(B_ORIGIN), "preferred", 481 NULL, menu); 482 float width; 483 fPreferredField->GetPreferredSize(&width, &height); 484 fPreferredField->ResizeTo(innerRect.Width(), height); 485 fPreferredField->MoveBy(0.0f, (innerRect.Height() - height) / 2.0f); 486 constrainingView->AddChild(fPreferredField); 487 // we embed the menu field in another view to make it behave like 488 // we want so that it can't obscure other elements with larger 489 // labels 490 491 fPreferredBox->AddChild(constrainingView); 492 493 // "Extra Attributes" group 494 495 rect.top = rect.bottom + 8.0f; 496 rect.bottom = topView->Bounds().Height() - 8.0f; 497 fAttributeBox = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP_BOTTOM); 498 fAttributeBox->SetLabel("Extra Attributes"); 499 topView->AddChild(fAttributeBox); 500 501 innerRect = fAttributeBox->Bounds().InsetByCopy(8.0f, 6.0f); 502 innerRect.top += ceilf(boldHeight.ascent); 503 innerRect.left = innerRect.right - button->StringWidth("Remove") - 16.0f; 504 innerRect.bottom = innerRect.top + button->Bounds().Height(); 505 fAddAttributeButton = new BButton(innerRect, "add attr", "Add" B_UTF8_ELLIPSIS, 506 new BMessage(kMsgAddAttribute), B_FOLLOW_RIGHT); 507 fAttributeBox->AddChild(fAddAttributeButton); 508 509 innerRect.OffsetBy(0, innerRect.Height() + 4.0f); 510 fRemoveAttributeButton = new BButton(innerRect, "remove attr", "Remove", 511 new BMessage(kMsgRemoveAttribute), B_FOLLOW_RIGHT); 512 fAttributeBox->AddChild(fRemoveAttributeButton); 513 /* 514 innerRect.OffsetBy(0, innerRect.Height() + 4.0f); 515 button = new BButton(innerRect, "push attr", "Push Up", 516 new BMessage(kMsgRemoveAttribute), B_FOLLOW_RIGHT); 517 fAttributeBox->AddChild(button); 518 */ 519 innerRect.right = innerRect.left - 10.0f - B_V_SCROLL_BAR_WIDTH; 520 innerRect.left = 10.0f; 521 innerRect.top = 8.0f + ceilf(boldHeight.ascent); 522 innerRect.bottom = fAttributeBox->Bounds().bottom - 10.0f; 523 // take scrollview border into account 524 fAttributeListView = new AttributeListView(innerRect, "listview attr", 525 B_FOLLOW_ALL); 526 fAttributeListView->SetSelectionMessage(new BMessage(kMsgAttributeSelected)); 527 fAttributeListView->SetInvocationMessage(new BMessage(kMsgAttributeInvoked)); 528 529 scrollView = new BScrollView("scrollview attr", fAttributeListView, 530 B_FOLLOW_ALL, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 531 fAttributeBox->AddChild(scrollView); 532 533 SetSizeLimits(rightRect.left + 72.0f + font.StringWidth("jpg") 534 + font.StringWidth(fRecognitionBox->Label()), 32767.0f, 535 rect.top + 2.0f * button->Bounds().Height() + boldHeight.ascent 536 + 32.0f + menuBar->Bounds().Height(), 32767.0f); 537 538 _SetType(NULL); 539 _ShowSnifferRule(showRule); 540 541 BMimeType::StartWatching(this); 542 } 543 544 545 FileTypesWindow::~FileTypesWindow() 546 { 547 BMimeType::StopWatching(this); 548 } 549 550 551 BRect 552 FileTypesWindow::_Frame(const BMessage& settings) const 553 { 554 BRect rect; 555 if (settings.FindRect("file_types_frame", &rect) == B_OK) 556 return rect; 557 558 return BRect(80.0f, 80.0f, 600.0f, 480.0f); 559 } 560 561 562 void 563 FileTypesWindow::_ShowSnifferRule(bool show) 564 { 565 if (fRuleControl->IsHidden() == !show) 566 return; 567 568 float minWidth, maxWidth, minHeight, maxHeight; 569 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 570 571 float diff = fRuleControl->Bounds().Height() + 8.0f; 572 573 if (!show) { 574 fRuleControl->Hide(); 575 diff = -diff; 576 } 577 578 // adjust other controls to make space or take it again 579 580 fIconBox->ResizeBy(0.0f, diff); 581 fRecognitionBox->ResizeBy(0.0f, diff); 582 fDescriptionBox->MoveBy(0.0f, diff); 583 fPreferredBox->MoveBy(0.0f, diff); 584 fAttributeBox->MoveBy(0.0f, diff); 585 fAttributeBox->ResizeBy(0.0f, -diff); 586 587 if (show) 588 fRuleControl->Show(); 589 590 SetSizeLimits(minWidth, maxWidth, minHeight + diff, maxHeight); 591 } 592 593 594 void 595 FileTypesWindow::_UpdateExtensions(BMimeType* type) 596 { 597 // clear list 598 599 for (int32 i = fExtensionListView->CountItems(); i-- > 0;) { 600 delete fExtensionListView->ItemAt(i); 601 } 602 fExtensionListView->MakeEmpty(); 603 604 // fill it again 605 606 if (type == NULL) 607 return; 608 609 BMessage extensions; 610 if (type->GetFileExtensions(&extensions) != B_OK) 611 return; 612 613 const char* extension; 614 int32 i = 0; 615 while (extensions.FindString("extensions", i++, &extension) == B_OK) { 616 char dotExtension[B_FILE_NAME_LENGTH]; 617 snprintf(dotExtension, B_FILE_NAME_LENGTH, ".%s", extension); 618 619 fExtensionListView->AddItem(new BStringItem(dotExtension)); 620 } 621 } 622 623 624 void 625 FileTypesWindow::_AdoptPreferredApplication(BMessage* message, bool sameAs) 626 { 627 if (fCurrentType.Type() == NULL) 628 return; 629 630 BString preferred; 631 if (retrieve_preferred_app(message, sameAs, fCurrentType.Type(), preferred) != B_OK) 632 return; 633 634 status_t status = fCurrentType.SetPreferredApp(preferred.String()); 635 if (status != B_OK) 636 error_alert("Could not set preferred application", status); 637 } 638 639 640 void 641 FileTypesWindow::_UpdatePreferredApps(BMimeType* type) 642 { 643 update_preferred_app_menu(fPreferredField->Menu(), type, kMsgPreferredAppChosen); 644 } 645 646 647 void 648 FileTypesWindow::_UpdateIcon(BMimeType* type) 649 { 650 fIconView->SetTo(type); 651 } 652 653 654 void 655 FileTypesWindow::_SetType(BMimeType* type, int32 forceUpdate) 656 { 657 bool enabled = type != NULL; 658 659 // update controls 660 661 if (type != NULL) { 662 if (fCurrentType == *type) { 663 if (!forceUpdate) 664 return; 665 } else 666 forceUpdate = B_EVERYTHING_CHANGED; 667 668 if (&fCurrentType != type) 669 fCurrentType.SetTo(type->Type()); 670 671 fInternalNameView->SetText(type->Type()); 672 673 char description[B_MIME_TYPE_LENGTH]; 674 675 if ((forceUpdate & B_SHORT_DESCRIPTION_CHANGED) != 0) { 676 if (type->GetShortDescription(description) != B_OK) 677 description[0] = '\0'; 678 fTypeNameControl->SetText(description); 679 } 680 681 if ((forceUpdate & B_LONG_DESCRIPTION_CHANGED) != 0) { 682 if (type->GetLongDescription(description) != B_OK) 683 description[0] = '\0'; 684 fDescriptionControl->SetText(description); 685 } 686 687 if ((forceUpdate & B_SNIFFER_RULE_CHANGED) != 0) { 688 BString rule; 689 if (type->GetSnifferRule(&rule) != B_OK) 690 rule = ""; 691 fRuleControl->SetText(rule.String()); 692 } 693 } else { 694 fCurrentType.Unset(); 695 fInternalNameView->SetText(NULL); 696 fTypeNameControl->SetText(NULL); 697 fDescriptionControl->SetText(NULL); 698 fRuleControl->SetText(NULL); 699 fPreferredField->Menu()->ItemAt(0)->SetMarked(true); 700 } 701 702 if ((forceUpdate & B_FILE_EXTENSIONS_CHANGED) != 0) 703 _UpdateExtensions(type); 704 705 if ((forceUpdate & B_PREFERRED_APP_CHANGED) != 0) 706 _UpdatePreferredApps(type); 707 708 if ((forceUpdate & (B_ICON_CHANGED | B_PREFERRED_APP_CHANGED)) != 0) 709 _UpdateIcon(type); 710 711 if ((forceUpdate & B_ATTR_INFO_CHANGED) != 0) 712 fAttributeListView->SetTo(type); 713 714 // enable/disable controls 715 716 fIconView->SetEnabled(enabled); 717 718 fInternalNameView->SetEnabled(enabled); 719 fTypeNameControl->SetEnabled(enabled); 720 fDescriptionControl->SetEnabled(enabled); 721 fPreferredField->SetEnabled(enabled); 722 723 fRemoveTypeButton->SetEnabled(enabled); 724 725 fSelectButton->SetEnabled(enabled); 726 fSameAsButton->SetEnabled(enabled); 727 728 fExtensionLabel->SetEnabled(enabled); 729 fAddExtensionButton->SetEnabled(enabled); 730 fRemoveExtensionButton->SetEnabled(false); 731 fRuleControl->SetEnabled(enabled); 732 733 fAddAttributeButton->SetEnabled(enabled); 734 fRemoveAttributeButton->SetEnabled(false); 735 } 736 737 738 void 739 FileTypesWindow::PlaceSubWindow(BWindow* window) 740 { 741 window->MoveTo(Frame().left + (Frame().Width() - window->Frame().Width()) / 2.0f, 742 Frame().top + (Frame().Height() - window->Frame().Height()) / 2.0f); 743 } 744 745 746 void 747 FileTypesWindow::MessageReceived(BMessage* message) 748 { 749 switch (message->what) { 750 case B_SIMPLE_DATA: 751 type_code type; 752 if (message->GetInfo("refs", &type) == B_OK 753 && type == B_REF_TYPE) { 754 be_app->PostMessage(message); 755 } 756 break; 757 758 case kMsgToggleIcons: 759 { 760 BMenuItem* item; 761 if (message->FindPointer("source", (void **)&item) != B_OK) 762 break; 763 764 item->SetMarked(!fTypeListView->IsShowingIcons()); 765 fTypeListView->ShowIcons(item->IsMarked()); 766 767 // update settings 768 BMessage update(kMsgSettingsChanged); 769 update.AddBool("show_icons", item->IsMarked()); 770 be_app_messenger.SendMessage(&update); 771 break; 772 } 773 774 case kMsgToggleRule: 775 { 776 BMenuItem* item; 777 if (message->FindPointer("source", (void **)&item) != B_OK) 778 break; 779 780 item->SetMarked(fRuleControl->IsHidden()); 781 _ShowSnifferRule(item->IsMarked()); 782 783 // update settings 784 BMessage update(kMsgSettingsChanged); 785 update.AddBool("show_rule", item->IsMarked()); 786 be_app_messenger.SendMessage(&update); 787 break; 788 } 789 790 case kMsgTypeSelected: 791 { 792 int32 index; 793 if (message->FindInt32("index", &index) == B_OK) { 794 MimeTypeItem* item = (MimeTypeItem*)fTypeListView->ItemAt(index); 795 if (item != NULL) { 796 BMimeType type(item->Type()); 797 _SetType(&type); 798 } else 799 _SetType(NULL); 800 } 801 break; 802 } 803 804 case kMsgAddType: 805 { 806 if (fNewTypeWindow == NULL) { 807 fNewTypeWindow = new NewFileTypeWindow(this, fCurrentType.Type()); 808 fNewTypeWindow->Show(); 809 } else 810 fNewTypeWindow->Activate(); 811 break; 812 } 813 case kMsgNewTypeWindowClosed: 814 fNewTypeWindow = NULL; 815 break; 816 817 case kMsgRemoveType: 818 { 819 if (fCurrentType.Type() == NULL) 820 break; 821 822 BAlert* alert; 823 if (fCurrentType.IsSupertypeOnly()) { 824 alert = new BPrivate::OverrideAlert("FileTypes Request", 825 "Removing a super type cannot be reverted.\n" 826 "All file types that belong to this super type " 827 "will be lost!\n\n" 828 "Are you sure you want to do this? To remove the whole " 829 "group, hold down the Shift key and press \"Remove\".", 830 "Remove", B_SHIFT_KEY, "Cancel", 0, NULL, 0, 831 B_WIDTH_AS_USUAL, B_STOP_ALERT); 832 } else { 833 alert = new BAlert("FileTypes Request", 834 "Removing a file type cannot be reverted.\n" 835 "Are you sure you want to remove it?", 836 "Remove", "Cancel", NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 837 } 838 if (alert->Go()) 839 break; 840 841 status_t status = fCurrentType.Delete(); 842 if (status != B_OK) 843 fprintf(stderr, "Could not remove file type: %s\n", strerror(status)); 844 break; 845 } 846 847 case kMsgSelectNewType: 848 { 849 const char* type; 850 if (message->FindString("type", &type) == B_OK) 851 fTypeListView->SelectNewType(type); 852 break; 853 } 854 855 // File Recognition group 856 857 case kMsgExtensionSelected: 858 { 859 int32 index; 860 if (message->FindInt32("index", &index) == B_OK) { 861 BStringItem* item = (BStringItem*)fExtensionListView->ItemAt(index); 862 fRemoveExtensionButton->SetEnabled(item != NULL); 863 } 864 break; 865 } 866 867 case kMsgExtensionInvoked: 868 { 869 if (fCurrentType.Type() == NULL) 870 break; 871 872 int32 index; 873 if (message->FindInt32("index", &index) == B_OK) { 874 BStringItem* item = (BStringItem*)fExtensionListView->ItemAt(index); 875 if (item == NULL) 876 break; 877 878 BWindow* window = new ExtensionWindow(this, fCurrentType, item->Text()); 879 window->Show(); 880 } 881 break; 882 } 883 884 case kMsgAddExtension: 885 { 886 if (fCurrentType.Type() == NULL) 887 break; 888 889 BWindow* window = new ExtensionWindow(this, fCurrentType, NULL); 890 window->Show(); 891 break; 892 } 893 894 case kMsgRemoveExtension: 895 { 896 int32 index = fExtensionListView->CurrentSelection(); 897 if (index < 0 || fCurrentType.Type() == NULL) 898 break; 899 900 BMessage extensions; 901 if (fCurrentType.GetFileExtensions(&extensions) == B_OK) { 902 extensions.RemoveData("extensions", index); 903 fCurrentType.SetFileExtensions(&extensions); 904 } 905 break; 906 } 907 908 case kMsgRuleEntered: 909 { 910 // check rule 911 BString parseError; 912 if (BMimeType::CheckSnifferRule(fRuleControl->Text(), &parseError) != B_OK) { 913 parseError.Prepend("Recognition rule is not valid:\n\n"); 914 error_alert(parseError.String()); 915 } else 916 fCurrentType.SetSnifferRule(fRuleControl->Text()); 917 break; 918 } 919 920 // Description group 921 922 case kMsgTypeEntered: 923 { 924 fCurrentType.SetShortDescription(fTypeNameControl->Text()); 925 break; 926 } 927 928 case kMsgDescriptionEntered: 929 { 930 fCurrentType.SetLongDescription(fDescriptionControl->Text()); 931 break; 932 } 933 934 // Preferred Application group 935 936 case kMsgPreferredAppChosen: 937 { 938 const char* signature; 939 if (message->FindString("signature", &signature) != B_OK) 940 signature = NULL; 941 942 fCurrentType.SetPreferredApp(signature); 943 break; 944 } 945 946 case kMsgSelectPreferredApp: 947 { 948 BMessage panel(kMsgOpenFilePanel); 949 panel.AddString("title", "Select Preferred Application"); 950 panel.AddInt32("message", kMsgPreferredAppOpened); 951 panel.AddMessenger("target", this); 952 953 be_app_messenger.SendMessage(&panel); 954 break; 955 } 956 case kMsgPreferredAppOpened: 957 _AdoptPreferredApplication(message, false); 958 break; 959 960 case kMsgSamePreferredAppAs: 961 { 962 BMessage panel(kMsgOpenFilePanel); 963 panel.AddString("title", "Select Same Preferred Application As"); 964 panel.AddInt32("message", kMsgSamePreferredAppAsOpened); 965 panel.AddMessenger("target", this); 966 967 be_app_messenger.SendMessage(&panel); 968 break; 969 } 970 case kMsgSamePreferredAppAsOpened: 971 _AdoptPreferredApplication(message, true); 972 break; 973 974 // Extra Attributes group 975 976 case kMsgAttributeSelected: 977 { 978 int32 index; 979 if (message->FindInt32("index", &index) == B_OK) { 980 AttributeItem* item = (AttributeItem*)fAttributeListView->ItemAt(index); 981 fRemoveAttributeButton->SetEnabled(item != NULL); 982 } 983 break; 984 } 985 986 case kMsgAttributeInvoked: 987 { 988 if (fCurrentType.Type() == NULL) 989 break; 990 991 int32 index; 992 if (message->FindInt32("index", &index) == B_OK) { 993 AttributeItem* item = (AttributeItem*)fAttributeListView->ItemAt(index); 994 if (item == NULL) 995 break; 996 997 BWindow* window = new AttributeWindow(this, fCurrentType, 998 item); 999 window->Show(); 1000 } 1001 break; 1002 } 1003 1004 case kMsgAddAttribute: 1005 { 1006 if (fCurrentType.Type() == NULL) 1007 break; 1008 1009 BWindow* window = new AttributeWindow(this, fCurrentType, NULL); 1010 window->Show(); 1011 break; 1012 } 1013 1014 case kMsgRemoveAttribute: 1015 { 1016 int32 index = fAttributeListView->CurrentSelection(); 1017 if (index < 0 || fCurrentType.Type() == NULL) 1018 break; 1019 1020 BMessage attributes; 1021 if (fCurrentType.GetAttrInfo(&attributes) == B_OK) { 1022 const char* kAttributeNames[] = { 1023 "attr:public_name", "attr:name", "attr:type", 1024 "attr:editable", "attr:viewable", "attr:extra", 1025 "attr:alignment", "attr:width", "attr:display_as" 1026 }; 1027 1028 for (uint32 i = 0; i < 1029 sizeof(kAttributeNames) / sizeof(kAttributeNames[0]); i++) { 1030 attributes.RemoveData(kAttributeNames[i], index); 1031 } 1032 1033 fCurrentType.SetAttrInfo(&attributes); 1034 } 1035 break; 1036 } 1037 1038 case B_META_MIME_CHANGED: 1039 { 1040 const char* type; 1041 int32 which; 1042 if (message->FindString("be:type", &type) != B_OK 1043 || message->FindInt32("be:which", &which) != B_OK) 1044 break; 1045 1046 if (fCurrentType.Type() == NULL) 1047 break; 1048 1049 if (!strcasecmp(fCurrentType.Type(), type)) { 1050 if (which != B_MIME_TYPE_DELETED) 1051 _SetType(&fCurrentType, which); 1052 else 1053 _SetType(NULL); 1054 } else { 1055 // this change could still affect our current type 1056 1057 if (which == B_MIME_TYPE_DELETED 1058 || which == B_PREFERRED_APP_CHANGED 1059 #ifdef __HAIKU__ 1060 || which == B_SUPPORTED_TYPES_CHANGED 1061 #endif 1062 || which == B_ICON_FOR_TYPE_CHANGED) { 1063 _UpdatePreferredApps(&fCurrentType); 1064 _UpdateIcon(&fCurrentType); 1065 } 1066 } 1067 break; 1068 } 1069 1070 default: 1071 BWindow::MessageReceived(message); 1072 } 1073 } 1074 1075 1076 bool 1077 FileTypesWindow::QuitRequested() 1078 { 1079 BMessage update(kMsgSettingsChanged); 1080 update.AddRect("file_types_frame", Frame()); 1081 be_app_messenger.SendMessage(&update); 1082 1083 be_app->PostMessage(kMsgTypesWindowClosed); 1084 return true; 1085 } 1086 1087 1088