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