1 /* 2 * Copyright 2006-2010, Axel Dörfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ApplicationTypeWindow.h" 8 #include "DropTargetListView.h" 9 #include "FileTypes.h" 10 #include "IconView.h" 11 #include "PreferredAppMenu.h" 12 #include "StringView.h" 13 #include "TypeListWindow.h" 14 15 #include <Application.h> 16 #include <Bitmap.h> 17 #include <Box.h> 18 #include <Button.h> 19 #include <Catalog.h> 20 #include <CheckBox.h> 21 #include <ControlLook.h> 22 #include <File.h> 23 #include <GroupView.h> 24 #include <LayoutBuilder.h> 25 #include <ListView.h> 26 #include <Locale.h> 27 #include <MenuBar.h> 28 #include <MenuField.h> 29 #include <MenuItem.h> 30 #include <Mime.h> 31 #include <NodeInfo.h> 32 #include <PopUpMenu.h> 33 #include <RadioButton.h> 34 #include <Roster.h> 35 #include <ScrollView.h> 36 #include <StringView.h> 37 #include <TextControl.h> 38 39 #include <ctype.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 44 45 #undef B_TRANSLATION_CONTEXT 46 #define B_TRANSLATION_CONTEXT "Application Type Window" 47 48 49 const uint32 kMsgSave = 'save'; 50 const uint32 kMsgSignatureChanged = 'sgch'; 51 const uint32 kMsgToggleAppFlags = 'tglf'; 52 const uint32 kMsgAppFlagsChanged = 'afch'; 53 54 const uint32 kMsgIconChanged = 'icch'; 55 const uint32 kMsgTypeIconsChanged = 'tich'; 56 57 const uint32 kMsgTypeSelected = 'tpsl'; 58 const uint32 kMsgAddType = 'adtp'; 59 const uint32 kMsgTypeAdded = 'tpad'; 60 const uint32 kMsgRemoveType = 'rmtp'; 61 const uint32 kMsgTypeRemoved = 'tprm'; 62 63 64 //! TextView that filters the tab key to be able to tab-navigate while editing 65 class TabFilteringTextView : public BTextView { 66 public: 67 TabFilteringTextView(const char* name); 68 virtual ~TabFilteringTextView(); 69 70 virtual void KeyDown(const char* bytes, int32 count); 71 virtual void TargetedByScrollView(BScrollView* scroller); 72 virtual void MakeFocus(bool focused = true); 73 74 private: 75 BScrollView* fScrollView; 76 }; 77 78 79 class SupportedTypeItem : public BStringItem { 80 public: 81 SupportedTypeItem(const char* type); 82 virtual ~SupportedTypeItem(); 83 84 const char* Type() const { return fType.String(); } 85 ::Icon& Icon() { return fIcon; } 86 87 void SetIcon(::Icon* icon); 88 void SetIcon(entry_ref& ref, const char* type); 89 90 static int Compare(const void* _a, const void* _b); 91 92 private: 93 BString fType; 94 ::Icon fIcon; 95 }; 96 97 98 class SupportedTypeListView : public DropTargetListView { 99 public: 100 SupportedTypeListView(const char* name, 101 list_view_type 102 type = B_SINGLE_SELECTION_LIST, 103 uint32 flags = B_WILL_DRAW 104 | B_FRAME_EVENTS | B_NAVIGABLE); 105 virtual ~SupportedTypeListView(); 106 107 virtual void MessageReceived(BMessage* message); 108 virtual bool AcceptsDrag(const BMessage* message); 109 }; 110 111 112 // #pragma mark - 113 114 115 TabFilteringTextView::TabFilteringTextView(const char* name) 116 : 117 BTextView(name, B_WILL_DRAW | B_PULSE_NEEDED | B_NAVIGABLE), 118 fScrollView(NULL) 119 { 120 } 121 122 123 TabFilteringTextView::~TabFilteringTextView() 124 { 125 } 126 127 128 void 129 TabFilteringTextView::KeyDown(const char* bytes, int32 count) 130 { 131 if (bytes[0] == B_TAB) 132 BView::KeyDown(bytes, count); 133 else 134 BTextView::KeyDown(bytes, count); 135 } 136 137 138 void 139 TabFilteringTextView::TargetedByScrollView(BScrollView* scroller) 140 { 141 fScrollView = scroller; 142 } 143 144 145 void 146 TabFilteringTextView::MakeFocus(bool focused) 147 { 148 BTextView::MakeFocus(focused); 149 150 if (fScrollView) 151 fScrollView->SetBorderHighlighted(focused); 152 } 153 154 155 // #pragma mark - 156 157 158 SupportedTypeItem::SupportedTypeItem(const char* type) 159 : BStringItem(type), 160 fType(type) 161 { 162 BMimeType mimeType(type); 163 164 char description[B_MIME_TYPE_LENGTH]; 165 if (mimeType.GetShortDescription(description) == B_OK && description[0]) 166 SetText(description); 167 } 168 169 170 SupportedTypeItem::~SupportedTypeItem() 171 { 172 } 173 174 175 void 176 SupportedTypeItem::SetIcon(::Icon* icon) 177 { 178 if (icon != NULL) 179 fIcon = *icon; 180 else 181 fIcon.Unset(); 182 } 183 184 185 void 186 SupportedTypeItem::SetIcon(entry_ref& ref, const char* type) 187 { 188 fIcon.SetTo(ref, type); 189 } 190 191 192 /*static*/ 193 int 194 SupportedTypeItem::Compare(const void* _a, const void* _b) 195 { 196 const SupportedTypeItem* a = *(const SupportedTypeItem**)_a; 197 const SupportedTypeItem* b = *(const SupportedTypeItem**)_b; 198 199 int compare = strcasecmp(a->Text(), b->Text()); 200 if (compare != 0) 201 return compare; 202 203 return strcasecmp(a->Type(), b->Type()); 204 } 205 206 207 // #pragma mark - 208 209 210 SupportedTypeListView::SupportedTypeListView(const char* name, 211 list_view_type type, uint32 flags) 212 : 213 DropTargetListView(name, type, flags) 214 { 215 } 216 217 218 SupportedTypeListView::~SupportedTypeListView() 219 { 220 } 221 222 223 void 224 SupportedTypeListView::MessageReceived(BMessage* message) 225 { 226 if (message->WasDropped() && AcceptsDrag(message)) { 227 // Add unique types 228 entry_ref ref; 229 for (int32 index = 0; message->FindRef("refs", index++, &ref) == B_OK; ) { 230 BNode node(&ref); 231 BNodeInfo info(&node); 232 if (node.InitCheck() != B_OK || info.InitCheck() != B_OK) 233 continue; 234 235 // TODO: we could identify the file in case it doesn't have a type... 236 char type[B_MIME_TYPE_LENGTH]; 237 if (info.GetType(type) != B_OK) 238 continue; 239 240 // check if that type is already in our list 241 bool found = false; 242 for (int32 i = CountItems(); i-- > 0;) { 243 SupportedTypeItem* item = (SupportedTypeItem*)ItemAt(i); 244 if (!strcmp(item->Text(), type)) { 245 found = true; 246 break; 247 } 248 } 249 250 if (!found) { 251 // add type 252 AddItem(new SupportedTypeItem(type)); 253 } 254 } 255 256 SortItems(&SupportedTypeItem::Compare); 257 } else 258 DropTargetListView::MessageReceived(message); 259 } 260 261 262 bool 263 SupportedTypeListView::AcceptsDrag(const BMessage* message) 264 { 265 type_code type; 266 return message->GetInfo("refs", &type) == B_OK && type == B_REF_TYPE; 267 } 268 269 270 // #pragma mark - 271 272 273 ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, 274 const BEntry& entry) 275 : 276 BWindow(BRect(0.0f, 0.0f, 250.0f, 340.0f).OffsetBySelf(position), 277 B_TRANSLATE("Application type"), B_TITLED_WINDOW, 278 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | 279 B_FRAME_EVENTS | B_AUTO_UPDATE_SIZE_LIMITS), 280 fChangedProperties(0) 281 { 282 float padding = be_control_look->DefaultItemSpacing(); 283 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment(); 284 285 BMenuBar* menuBar = new BMenuBar((char*)NULL); 286 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); 287 288 BMenu* menu = new BMenu(B_TRANSLATE("File")); 289 fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"), 290 new BMessage(kMsgSave), 'S'); 291 fSaveMenuItem->SetEnabled(false); 292 menu->AddItem(fSaveMenuItem); 293 BMenuItem* item; 294 menu->AddItem(item = new BMenuItem( 295 B_TRANSLATE("Save into resource file" B_UTF8_ELLIPSIS), NULL)); 296 item->SetEnabled(false); 297 298 menu->AddSeparatorItem(); 299 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 300 new BMessage(B_QUIT_REQUESTED), 'W', B_COMMAND_KEY)); 301 menuBar->AddItem(menu); 302 303 // Signature 304 305 fSignatureControl = new BTextControl(B_TRANSLATE("Signature:"), NULL, 306 new BMessage(kMsgSignatureChanged)); 307 fSignatureControl->SetModificationMessage( 308 new BMessage(kMsgSignatureChanged)); 309 310 // filter out invalid characters that can't be part of a MIME type name 311 BTextView* textView = fSignatureControl->TextView(); 312 textView->SetMaxBytes(B_MIME_TYPE_LENGTH); 313 const char* disallowedCharacters = "<>@,;:\"()[]?= "; 314 for (int32 i = 0; disallowedCharacters[i]; i++) { 315 textView->DisallowChar(disallowedCharacters[i]); 316 } 317 318 // "Application Flags" group 319 320 BBox* flagsBox = new BBox("flagsBox"); 321 322 fFlagsCheckBox = new BCheckBox("flags", B_TRANSLATE("Application flags"), 323 new BMessage(kMsgToggleAppFlags)); 324 fFlagsCheckBox->SetValue(B_CONTROL_ON); 325 326 fSingleLaunchButton = new BRadioButton("single", 327 B_TRANSLATE("Single launch"), new BMessage(kMsgAppFlagsChanged)); 328 329 fMultipleLaunchButton = new BRadioButton("multiple", 330 B_TRANSLATE("Multiple launch"), new BMessage(kMsgAppFlagsChanged)); 331 332 fExclusiveLaunchButton = new BRadioButton("exclusive", 333 B_TRANSLATE("Exclusive launch"), new BMessage(kMsgAppFlagsChanged)); 334 335 fArgsOnlyCheckBox = new BCheckBox("args only", B_TRANSLATE("Args only"), 336 new BMessage(kMsgAppFlagsChanged)); 337 338 fBackgroundAppCheckBox = new BCheckBox("background", 339 B_TRANSLATE("Background app"), new BMessage(kMsgAppFlagsChanged)); 340 341 BLayoutBuilder::Grid<>(flagsBox, 0, 0) 342 .SetInsets(padding, padding * 2, padding, padding) 343 .Add(fSingleLaunchButton, 0, 0).Add(fArgsOnlyCheckBox, 1, 0) 344 .Add(fMultipleLaunchButton, 0, 1).Add(fBackgroundAppCheckBox, 1, 1) 345 .Add(fExclusiveLaunchButton, 0, 2); 346 flagsBox->SetLabel(fFlagsCheckBox); 347 348 // "Icon" group 349 350 BBox* iconBox = new BBox("IconBox"); 351 iconBox->SetLabel(B_TRANSLATE("Icon")); 352 fIconView = new IconView("icon"); 353 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged)); 354 BLayoutBuilder::Group<>(iconBox, B_HORIZONTAL) 355 .SetInsets(padding, padding * 2, padding, padding) 356 .Add(fIconView); 357 358 // "Supported Types" group 359 360 BBox* typeBox = new BBox("typesBox"); 361 typeBox->SetLabel(B_TRANSLATE("Supported types")); 362 363 fTypeListView = new SupportedTypeListView("Suppported Types", 364 B_SINGLE_SELECTION_LIST); 365 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); 366 367 BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView, 368 B_FRAME_EVENTS | B_WILL_DRAW, false, true); 369 370 fAddTypeButton = new BButton("add type", 371 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType)); 372 373 fRemoveTypeButton = new BButton("remove type", B_TRANSLATE("Remove"), 374 new BMessage(kMsgRemoveType)); 375 376 fTypeIconView = new IconView("type icon"); 377 BGroupView* iconHolder = new BGroupView(B_HORIZONTAL); 378 iconHolder->AddChild(fTypeIconView); 379 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged)); 380 381 BLayoutBuilder::Grid<>(typeBox, padding, padding) 382 .SetInsets(padding, padding * 2, padding, padding) 383 .Add(scrollView, 0, 0, 1, 4) 384 .Add(fAddTypeButton, 1, 0, 1, 2) 385 .Add(fRemoveTypeButton, 1, 2, 1, 2) 386 .Add(iconHolder, 2, 1, 1, 2) 387 .SetColumnWeight(0, 3) 388 .SetColumnWeight(1, 2) 389 .SetColumnWeight(2, 1); 390 391 iconHolder->SetExplicitAlignment( 392 BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); 393 394 // "Version Info" group 395 396 BBox* versionBox = new BBox("versionBox"); 397 versionBox->SetLabel(B_TRANSLATE("Version info")); 398 399 fMajorVersionControl = new BTextControl(B_TRANSLATE("Version:"), 400 NULL, NULL); 401 _MakeNumberTextControl(fMajorVersionControl); 402 403 fMiddleVersionControl = new BTextControl(".", NULL, NULL); 404 _MakeNumberTextControl(fMiddleVersionControl); 405 406 fMinorVersionControl = new BTextControl(".", NULL, NULL); 407 _MakeNumberTextControl(fMinorVersionControl); 408 409 fVarietyMenu = new BPopUpMenu("variety", true, true); 410 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Development"), NULL)); 411 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Alpha"), NULL)); 412 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Beta"), NULL)); 413 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Gamma"), NULL)); 414 item = new BMenuItem(B_TRANSLATE("Golden master"), NULL); 415 fVarietyMenu->AddItem(item); 416 item->SetMarked(true); 417 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Final"), NULL)); 418 419 BMenuField* varietyField = new BMenuField("", fVarietyMenu); 420 fInternalVersionControl = new BTextControl("/", NULL, NULL); 421 fShortDescriptionControl = 422 new BTextControl(B_TRANSLATE("Short description:"), NULL, NULL); 423 424 // TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says? 425 version_info versionInfo; 426 fShortDescriptionControl->TextView()->SetMaxBytes( 427 sizeof(versionInfo.short_info)); 428 429 BStringView* longLabel = new BStringView(NULL, 430 B_TRANSLATE("Long description:")); 431 longLabel->SetExplicitAlignment(labelAlignment); 432 fLongDescriptionView = new TabFilteringTextView("long desc"); 433 fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info)); 434 435 scrollView = new BScrollView("desc scrollview", fLongDescriptionView, 436 B_FRAME_EVENTS | B_WILL_DRAW, false, true); 437 438 // TODO: remove workaround (bug #5678) 439 BSize minScrollSize = scrollView->ScrollBar(B_VERTICAL)->MinSize(); 440 minScrollSize.width += fLongDescriptionView->MinSize().width; 441 scrollView->SetExplicitMinSize(minScrollSize); 442 443 // Manually set a minimum size for the version text controls 444 // TODO: the same does not work when applied to the layout items 445 float width = be_plain_font->StringWidth("99") + 16; 446 fMajorVersionControl->TextView()->SetExplicitMinSize( 447 BSize(width, fMajorVersionControl->MinSize().height)); 448 fMiddleVersionControl->TextView()->SetExplicitMinSize( 449 BSize(width, fMiddleVersionControl->MinSize().height)); 450 fMinorVersionControl->TextView()->SetExplicitMinSize( 451 BSize(width, fMinorVersionControl->MinSize().height)); 452 fInternalVersionControl->TextView()->SetExplicitMinSize( 453 BSize(width, fInternalVersionControl->MinSize().height)); 454 455 BLayoutBuilder::Grid<>(versionBox, padding / 2, padding / 2) 456 .SetInsets(padding, padding * 2, padding, padding) 457 .Add(fMajorVersionControl->CreateLabelLayoutItem(), 0, 0) 458 .Add(fMajorVersionControl->CreateTextViewLayoutItem(), 1, 0) 459 .Add(fMiddleVersionControl, 2, 0, 2) 460 .Add(fMinorVersionControl, 4, 0, 2) 461 .Add(varietyField, 6, 0, 3) 462 .Add(fInternalVersionControl, 9, 0, 2) 463 .Add(fShortDescriptionControl->CreateLabelLayoutItem(), 0, 1) 464 .Add(fShortDescriptionControl->CreateTextViewLayoutItem(), 1, 1, 10) 465 .Add(longLabel, 0, 2) 466 .Add(scrollView, 1, 2, 10, 3) 467 .SetRowWeight(3, 3); 468 469 // put it all together 470 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 471 .SetInsets(0, 0, 0, 0) 472 .Add(menuBar) 473 .AddGroup(B_VERTICAL, padding) 474 .SetInsets(padding, padding, padding, padding) 475 .Add(fSignatureControl) 476 .AddGroup(B_HORIZONTAL, padding) 477 .Add(flagsBox, 3) 478 .Add(iconBox, 1) 479 .End() 480 .Add(typeBox) 481 .Add(versionBox); 482 483 SetKeyMenuBar(menuBar); 484 485 fSignatureControl->MakeFocus(true); 486 BMimeType::StartWatching(this); 487 _SetTo(entry); 488 } 489 490 491 ApplicationTypeWindow::~ApplicationTypeWindow() 492 { 493 BMimeType::StopWatching(this); 494 } 495 496 497 BString 498 ApplicationTypeWindow::_Title(const BEntry& entry) 499 { 500 char name[B_FILE_NAME_LENGTH]; 501 if (entry.GetName(name) != B_OK) 502 strcpy(name, "\"-\""); 503 504 BString title = B_TRANSLATE("%1 application type"); 505 title.ReplaceFirst("%1", name); 506 return title; 507 } 508 509 510 void 511 ApplicationTypeWindow::_SetTo(const BEntry& entry) 512 { 513 SetTitle(_Title(entry).String()); 514 fEntry = entry; 515 516 // Retrieve Info 517 518 BFile file(&entry, B_READ_ONLY); 519 if (file.InitCheck() != B_OK) 520 return; 521 522 BAppFileInfo info(&file); 523 if (info.InitCheck() != B_OK) 524 return; 525 526 char signature[B_MIME_TYPE_LENGTH]; 527 if (info.GetSignature(signature) != B_OK) 528 signature[0] = '\0'; 529 530 bool gotFlags = false; 531 uint32 flags; 532 if (info.GetAppFlags(&flags) == B_OK) 533 gotFlags = true; 534 else 535 flags = B_MULTIPLE_LAUNCH; 536 537 version_info versionInfo; 538 if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK) 539 memset(&versionInfo, 0, sizeof(version_info)); 540 541 // Set Controls 542 543 fSignatureControl->SetModificationMessage(NULL); 544 fSignatureControl->SetText(signature); 545 fSignatureControl->SetModificationMessage( 546 new BMessage(kMsgSignatureChanged)); 547 548 // flags 549 550 switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) { 551 case B_SINGLE_LAUNCH: 552 fSingleLaunchButton->SetValue(B_CONTROL_ON); 553 break; 554 555 case B_EXCLUSIVE_LAUNCH: 556 fExclusiveLaunchButton->SetValue(B_CONTROL_ON); 557 break; 558 559 case B_MULTIPLE_LAUNCH: 560 default: 561 fMultipleLaunchButton->SetValue(B_CONTROL_ON); 562 break; 563 } 564 565 fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0); 566 fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0); 567 fFlagsCheckBox->SetValue(gotFlags); 568 569 _UpdateAppFlagsEnabled(); 570 571 // icon 572 573 entry_ref ref; 574 if (entry.GetRef(&ref) == B_OK) 575 fIcon.SetTo(ref); 576 else 577 fIcon.Unset(); 578 579 fIconView->SetModificationMessage(NULL); 580 fIconView->SetTo(&fIcon); 581 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged)); 582 583 // supported types 584 585 BMessage supportedTypes; 586 info.GetSupportedTypes(&supportedTypes); 587 588 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 589 BListItem* item = fTypeListView->RemoveItem(i); 590 delete item; 591 } 592 593 const char* type; 594 for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) { 595 SupportedTypeItem* item = new SupportedTypeItem(type); 596 597 entry_ref ref; 598 if (fEntry.GetRef(&ref) == B_OK) 599 item->SetIcon(ref, type); 600 601 fTypeListView->AddItem(item); 602 } 603 fTypeListView->SortItems(&SupportedTypeItem::Compare); 604 fTypeIconView->SetModificationMessage(NULL); 605 fTypeIconView->SetTo(NULL); 606 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged)); 607 fTypeIconView->SetEnabled(false); 608 fRemoveTypeButton->SetEnabled(false); 609 610 // version info 611 612 char text[256]; 613 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.major); 614 fMajorVersionControl->SetText(text); 615 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.middle); 616 fMiddleVersionControl->SetText(text); 617 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.minor); 618 fMinorVersionControl->SetText(text); 619 620 if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems()) 621 versionInfo.variety = 0; 622 BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety); 623 if (item != NULL) 624 item->SetMarked(true); 625 626 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.internal); 627 fInternalVersionControl->SetText(text); 628 629 fShortDescriptionControl->SetText(versionInfo.short_info); 630 fLongDescriptionView->SetText(versionInfo.long_info); 631 632 // store original data 633 634 fOriginalInfo.signature = signature; 635 fOriginalInfo.gotFlags = gotFlags; 636 fOriginalInfo.flags = gotFlags ? flags : 0; 637 fOriginalInfo.versionInfo = versionInfo; 638 fOriginalInfo.supportedTypes = _SupportedTypes(); 639 // The list view has the types sorted possibly differently 640 // to the supportedTypes message, so don't use that here, but 641 // get the sorted message instead. 642 fOriginalInfo.iconChanged = false; 643 fOriginalInfo.typeIconsChanged = false; 644 645 fChangedProperties = 0; 646 _CheckSaveMenuItem(0); 647 } 648 649 650 void 651 ApplicationTypeWindow::_UpdateAppFlagsEnabled() 652 { 653 bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF; 654 655 fSingleLaunchButton->SetEnabled(enabled); 656 fMultipleLaunchButton->SetEnabled(enabled); 657 fExclusiveLaunchButton->SetEnabled(enabled); 658 fArgsOnlyCheckBox->SetEnabled(enabled); 659 fBackgroundAppCheckBox->SetEnabled(enabled); 660 } 661 662 663 void 664 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control) 665 { 666 // filter out invalid characters that can't be part of a MIME type name 667 BTextView* textView = control->TextView(); 668 textView->SetMaxBytes(10); 669 670 for (int32 i = 0; i < 256; i++) { 671 if (!isdigit(i)) 672 textView->DisallowChar(i); 673 } 674 } 675 676 677 void 678 ApplicationTypeWindow::_Save() 679 { 680 BFile file; 681 status_t status = file.SetTo(&fEntry, B_READ_WRITE); 682 if (status != B_OK) 683 return; 684 685 BAppFileInfo info(&file); 686 status = info.InitCheck(); 687 if (status != B_OK) 688 return; 689 690 // Retrieve Info 691 692 uint32 flags = 0; 693 bool gotFlags = _Flags(flags); 694 BMessage supportedTypes = _SupportedTypes(); 695 version_info versionInfo = _VersionInfo(); 696 697 // Save 698 699 status = info.SetSignature(fSignatureControl->Text()); 700 if (status == B_OK) { 701 if (gotFlags) 702 status = info.SetAppFlags(flags); 703 else 704 status = info.RemoveAppFlags(); 705 } 706 if (status == B_OK) 707 status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND); 708 if (status == B_OK) 709 fIcon.CopyTo(info, NULL, true); 710 711 // supported types and their icons 712 if (status == B_OK) 713 status = info.SetSupportedTypes(&supportedTypes); 714 715 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 716 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 717 fTypeListView->ItemAt(i)); 718 719 if (item != NULL) 720 item->Icon().CopyTo(info, item->Type(), true); 721 } 722 723 // reset the saved info 724 fOriginalInfo.signature = fSignatureControl->Text(); 725 fOriginalInfo.gotFlags = gotFlags; 726 fOriginalInfo.flags = flags; 727 fOriginalInfo.versionInfo = versionInfo; 728 fOriginalInfo.supportedTypes = supportedTypes; 729 fOriginalInfo.iconChanged = false; 730 fOriginalInfo.typeIconsChanged = false; 731 732 fChangedProperties = 0; 733 _CheckSaveMenuItem(0); 734 } 735 736 737 void 738 ApplicationTypeWindow::_CheckSaveMenuItem(uint32 flags) 739 { 740 fChangedProperties = _NeedsSaving(flags); 741 fSaveMenuItem->SetEnabled(fChangedProperties != 0); 742 } 743 744 745 bool 746 operator!=(const version_info& a, const version_info& b) 747 { 748 return a.major != b.major || a.middle != b.middle || a.minor != b.minor 749 || a.variety != b.variety || a.internal != b.internal 750 || strcmp(a.short_info, b.short_info) != 0 751 || strcmp(a.long_info, b.long_info) != 0; 752 } 753 754 755 uint32 756 ApplicationTypeWindow::_NeedsSaving(uint32 _flags) const 757 { 758 uint32 flags = fChangedProperties; 759 if (_flags & CHECK_SIGNATUR) { 760 if (fOriginalInfo.signature != fSignatureControl->Text()) 761 flags |= CHECK_SIGNATUR; 762 else 763 flags &= ~CHECK_SIGNATUR; 764 } 765 766 if (_flags & CHECK_FLAGS) { 767 uint32 appFlags = 0; 768 bool gotFlags = _Flags(appFlags); 769 if (fOriginalInfo.gotFlags != gotFlags 770 || fOriginalInfo.flags != appFlags) { 771 flags |= CHECK_FLAGS; 772 } else 773 flags &= ~CHECK_FLAGS; 774 } 775 776 if (_flags & CHECK_VERSION) { 777 if (fOriginalInfo.versionInfo != _VersionInfo()) 778 flags |= CHECK_VERSION; 779 else 780 flags &= ~CHECK_VERSION; 781 } 782 783 if (_flags & CHECK_ICON) { 784 if (fOriginalInfo.iconChanged) 785 flags |= CHECK_ICON; 786 else 787 flags &= ~CHECK_ICON; 788 } 789 790 if (_flags & CHECK_TYPES) { 791 if (!fOriginalInfo.supportedTypes.HasSameData(_SupportedTypes())) 792 flags |= CHECK_TYPES; 793 else 794 flags &= ~CHECK_TYPES; 795 } 796 797 if (_flags & CHECK_TYPE_ICONS) { 798 if (fOriginalInfo.typeIconsChanged) 799 flags |= CHECK_TYPE_ICONS; 800 else 801 flags &= ~CHECK_TYPE_ICONS; 802 } 803 804 return flags; 805 } 806 807 808 // #pragma mark - 809 810 811 bool 812 ApplicationTypeWindow::_Flags(uint32& flags) const 813 { 814 flags = 0; 815 if (fFlagsCheckBox->Value() != B_CONTROL_OFF) { 816 if (fSingleLaunchButton->Value() != B_CONTROL_OFF) 817 flags |= B_SINGLE_LAUNCH; 818 else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF) 819 flags |= B_MULTIPLE_LAUNCH; 820 else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF) 821 flags |= B_EXCLUSIVE_LAUNCH; 822 823 if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF) 824 flags |= B_ARGV_ONLY; 825 if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF) 826 flags |= B_BACKGROUND_APP; 827 return true; 828 } 829 return false; 830 } 831 832 833 BMessage 834 ApplicationTypeWindow::_SupportedTypes() const 835 { 836 BMessage supportedTypes; 837 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 838 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 839 fTypeListView->ItemAt(i)); 840 841 if (item != NULL) 842 supportedTypes.AddString("types", item->Type()); 843 } 844 return supportedTypes; 845 } 846 847 848 version_info 849 ApplicationTypeWindow::_VersionInfo() const 850 { 851 version_info versionInfo; 852 versionInfo.major = atol(fMajorVersionControl->Text()); 853 versionInfo.middle = atol(fMiddleVersionControl->Text()); 854 versionInfo.minor = atol(fMinorVersionControl->Text()); 855 versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked()); 856 versionInfo.internal = atol(fInternalVersionControl->Text()); 857 strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(), 858 sizeof(versionInfo.short_info)); 859 strlcpy(versionInfo.long_info, fLongDescriptionView->Text(), 860 sizeof(versionInfo.long_info)); 861 return versionInfo; 862 } 863 864 865 // #pragma mark - 866 867 868 void 869 ApplicationTypeWindow::FrameResized(float width, float height) 870 { 871 // This works around a flaw of BTextView 872 fLongDescriptionView->SetTextRect(fLongDescriptionView->Bounds()); 873 } 874 875 876 void 877 ApplicationTypeWindow::MessageReceived(BMessage* message) 878 { 879 switch (message->what) { 880 case kMsgToggleAppFlags: 881 _UpdateAppFlagsEnabled(); 882 _CheckSaveMenuItem(CHECK_FLAGS); 883 break; 884 885 case kMsgSignatureChanged: 886 _CheckSaveMenuItem(CHECK_SIGNATUR); 887 break; 888 889 case kMsgAppFlagsChanged: 890 _CheckSaveMenuItem(CHECK_FLAGS); 891 break; 892 893 case kMsgIconChanged: 894 fOriginalInfo.iconChanged = true; 895 _CheckSaveMenuItem(CHECK_ICON); 896 break; 897 898 case kMsgTypeIconsChanged: 899 fOriginalInfo.typeIconsChanged = true; 900 _CheckSaveMenuItem(CHECK_TYPE_ICONS); 901 break; 902 903 case kMsgSave: 904 _Save(); 905 break; 906 907 case kMsgTypeSelected: 908 { 909 int32 index; 910 if (message->FindInt32("index", &index) == B_OK) { 911 SupportedTypeItem* item 912 = (SupportedTypeItem*)fTypeListView->ItemAt(index); 913 914 fTypeIconView->SetModificationMessage(NULL); 915 fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL); 916 fTypeIconView->SetModificationMessage( 917 new BMessage(kMsgTypeIconsChanged)); 918 fTypeIconView->SetEnabled(item != NULL); 919 fRemoveTypeButton->SetEnabled(item != NULL); 920 921 _CheckSaveMenuItem(CHECK_TYPES); 922 } 923 break; 924 } 925 926 case kMsgAddType: 927 { 928 BWindow* window = new TypeListWindow(NULL, 929 kMsgTypeAdded, this); 930 window->Show(); 931 break; 932 } 933 934 case kMsgTypeAdded: 935 { 936 const char* type; 937 if (message->FindString("type", &type) != B_OK) 938 break; 939 940 // check if this type already exists 941 942 SupportedTypeItem* newItem = new SupportedTypeItem(type); 943 int32 insertAt = 0; 944 945 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 946 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 947 fTypeListView->ItemAt(i)); 948 if (item == NULL) 949 continue; 950 951 int compare = strcasecmp(item->Type(), type); 952 if (!compare) { 953 // type does already exist, select it and bail out 954 delete newItem; 955 newItem = NULL; 956 fTypeListView->Select(i); 957 break; 958 } 959 if (compare < 0) 960 insertAt = i + 1; 961 } 962 963 if (newItem == NULL) 964 break; 965 966 fTypeListView->AddItem(newItem, insertAt); 967 fTypeListView->Select(insertAt); 968 969 _CheckSaveMenuItem(CHECK_TYPES); 970 break; 971 } 972 973 case kMsgRemoveType: 974 { 975 int32 index = fTypeListView->CurrentSelection(); 976 if (index < 0) 977 break; 978 979 delete fTypeListView->RemoveItem(index); 980 fTypeIconView->SetModificationMessage(NULL); 981 fTypeIconView->SetTo(NULL); 982 fTypeIconView->SetModificationMessage( 983 new BMessage(kMsgTypeIconsChanged)); 984 fTypeIconView->SetEnabled(false); 985 fRemoveTypeButton->SetEnabled(false); 986 987 _CheckSaveMenuItem(CHECK_TYPES); 988 break; 989 } 990 991 case B_SIMPLE_DATA: 992 { 993 entry_ref ref; 994 if (message->FindRef("refs", &ref) != B_OK) 995 break; 996 997 // TODO: add to supported types 998 break; 999 } 1000 1001 case B_META_MIME_CHANGED: 1002 const char* type; 1003 int32 which; 1004 if (message->FindString("be:type", &type) != B_OK 1005 || message->FindInt32("be:which", &which) != B_OK) 1006 break; 1007 1008 // TODO: update supported types names 1009 // if (which == B_MIME_TYPE_DELETED) 1010 1011 // _CheckSaveMenuItem(...); 1012 break; 1013 1014 default: 1015 BWindow::MessageReceived(message); 1016 } 1017 } 1018 1019 1020 bool 1021 ApplicationTypeWindow::QuitRequested() 1022 { 1023 if (_NeedsSaving(CHECK_ALL) != 0) { 1024 BAlert* alert = new BAlert(B_TRANSLATE("Save request"), 1025 B_TRANSLATE("Save changes before closing?"), 1026 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"), 1027 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 1028 B_WARNING_ALERT); 1029 alert->SetShortcut(0, B_ESCAPE); 1030 alert->SetShortcut(1, 'd'); 1031 alert->SetShortcut(2, 's'); 1032 1033 int32 choice = alert->Go(); 1034 switch (choice) { 1035 case 0: 1036 return false; 1037 case 1: 1038 break; 1039 case 2: 1040 _Save(); 1041 break; 1042 } 1043 } 1044 1045 be_app->PostMessage(kMsgTypeWindowClosed); 1046 return true; 1047 } 1048 1049