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