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(const BMessage& settings, BPoint offset, 302 const BEntry& entry) 303 : 304 BWindow(_Frame(settings).OffsetBySelf(offset), 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, 5) 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, B_SIZE_UNSET)); 487 fMiddleVersionControl->TextView()->SetExplicitMinSize( 488 BSize(width, B_SIZE_UNSET)); 489 fMinorVersionControl->TextView()->SetExplicitMinSize( 490 BSize(width, B_SIZE_UNSET)); 491 fInternalVersionControl->TextView()->SetExplicitMinSize( 492 BSize(width, B_SIZE_UNSET)); 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 B_ALIGN_HORIZONTAL_UNSET, 1, 10) 503 .Add(longLabel, 0, 2) 504 .Add(scrollView, 1, 2, 10, 3) 505 .SetRowWeight(3, 3); 506 507 // put it all together 508 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 509 .SetInsets(0, 0, 0, 0) 510 .Add(menuBar) 511 .AddGroup(B_VERTICAL, padding) 512 .SetInsets(padding, padding, padding, padding) 513 .Add(fSignatureControl) 514 .AddGroup(B_HORIZONTAL, padding) 515 .Add(flagsBox, 3) 516 .Add(iconBox, 1) 517 .End() 518 .Add(typeBox, 2) 519 .Add(versionBox); 520 521 SetKeyMenuBar(menuBar); 522 523 fSignatureControl->MakeFocus(true); 524 BMimeType::StartWatching(this); 525 _SetTo(entry); 526 } 527 528 529 ApplicationTypeWindow::~ApplicationTypeWindow() 530 { 531 BMimeType::StopWatching(this); 532 } 533 534 BRect 535 ApplicationTypeWindow::_Frame(const BMessage& settings) const 536 { 537 BRect rect; 538 if (settings.FindRect("app_type_frame", &rect) == B_OK) 539 return rect; 540 541 return BRect(100.0f, 110.0f, 250.0f, 340.0f); 542 } 543 544 BString 545 ApplicationTypeWindow::_Title(const BEntry& entry) 546 { 547 char name[B_FILE_NAME_LENGTH]; 548 if (entry.GetName(name) != B_OK) 549 strcpy(name, "\"-\""); 550 551 BString title = B_TRANSLATE("%1 application type"); 552 title.ReplaceFirst("%1", name); 553 return title; 554 } 555 556 557 void 558 ApplicationTypeWindow::_SetTo(const BEntry& entry) 559 { 560 SetTitle(_Title(entry).String()); 561 fEntry = entry; 562 563 // Retrieve Info 564 565 BFile file(&entry, B_READ_ONLY); 566 if (file.InitCheck() != B_OK) 567 return; 568 569 BAppFileInfo info(&file); 570 if (info.InitCheck() != B_OK) 571 return; 572 573 char signature[B_MIME_TYPE_LENGTH]; 574 if (info.GetSignature(signature) != B_OK) 575 signature[0] = '\0'; 576 577 bool gotFlags = false; 578 uint32 flags; 579 if (info.GetAppFlags(&flags) == B_OK) 580 gotFlags = true; 581 else 582 flags = B_MULTIPLE_LAUNCH; 583 584 version_info versionInfo; 585 if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK) 586 memset(&versionInfo, 0, sizeof(version_info)); 587 588 // Set Controls 589 590 fSignatureControl->SetModificationMessage(NULL); 591 fSignatureControl->SetText(signature); 592 fSignatureControl->SetModificationMessage( 593 new BMessage(kMsgSignatureChanged)); 594 595 // flags 596 597 switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) { 598 case B_SINGLE_LAUNCH: 599 fSingleLaunchButton->SetValue(B_CONTROL_ON); 600 break; 601 602 case B_EXCLUSIVE_LAUNCH: 603 fExclusiveLaunchButton->SetValue(B_CONTROL_ON); 604 break; 605 606 case B_MULTIPLE_LAUNCH: 607 default: 608 fMultipleLaunchButton->SetValue(B_CONTROL_ON); 609 break; 610 } 611 612 fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0); 613 fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0); 614 fFlagsCheckBox->SetValue(gotFlags); 615 616 _UpdateAppFlagsEnabled(); 617 618 // icon 619 620 entry_ref ref; 621 if (entry.GetRef(&ref) == B_OK) 622 fIcon.SetTo(ref); 623 else 624 fIcon.Unset(); 625 626 fIconView->SetModificationMessage(NULL); 627 fIconView->SetTo(&fIcon); 628 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged)); 629 630 // supported types 631 632 BMessage supportedTypes; 633 info.GetSupportedTypes(&supportedTypes); 634 635 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 636 BListItem* item = fTypeListView->RemoveItem(i); 637 delete item; 638 } 639 640 const char* type; 641 for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) { 642 SupportedTypeItem* item = new SupportedTypeItem(type); 643 644 entry_ref ref; 645 if (fEntry.GetRef(&ref) == B_OK) 646 item->SetIcon(ref, type); 647 648 fTypeListView->AddItem(item); 649 } 650 fTypeListView->SortItems(&SupportedTypeItem::Compare); 651 fTypeIconView->SetModificationMessage(NULL); 652 fTypeIconView->SetTo(NULL); 653 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged)); 654 fTypeIconView->SetEnabled(false); 655 fRemoveTypeButton->SetEnabled(false); 656 657 // version info 658 659 char text[256]; 660 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.major); 661 fMajorVersionControl->SetText(text); 662 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.middle); 663 fMiddleVersionControl->SetText(text); 664 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.minor); 665 fMinorVersionControl->SetText(text); 666 667 if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems()) 668 versionInfo.variety = 0; 669 BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety); 670 if (item != NULL) 671 item->SetMarked(true); 672 673 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.internal); 674 fInternalVersionControl->SetText(text); 675 676 fShortDescriptionControl->SetText(versionInfo.short_info); 677 fLongDescriptionView->SetText(versionInfo.long_info); 678 679 // store original data 680 681 fOriginalInfo.signature = signature; 682 fOriginalInfo.gotFlags = gotFlags; 683 fOriginalInfo.flags = gotFlags ? flags : 0; 684 fOriginalInfo.versionInfo = versionInfo; 685 fOriginalInfo.supportedTypes = _SupportedTypes(); 686 // The list view has the types sorted possibly differently 687 // to the supportedTypes message, so don't use that here, but 688 // get the sorted message instead. 689 fOriginalInfo.iconChanged = false; 690 fOriginalInfo.typeIconsChanged = false; 691 692 fChangedProperties = 0; 693 _CheckSaveMenuItem(0); 694 } 695 696 697 void 698 ApplicationTypeWindow::_UpdateAppFlagsEnabled() 699 { 700 bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF; 701 702 fSingleLaunchButton->SetEnabled(enabled); 703 fMultipleLaunchButton->SetEnabled(enabled); 704 fExclusiveLaunchButton->SetEnabled(enabled); 705 fArgsOnlyCheckBox->SetEnabled(enabled); 706 fBackgroundAppCheckBox->SetEnabled(enabled); 707 } 708 709 710 void 711 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control) 712 { 713 // filter out invalid characters that can't be part of a MIME type name 714 BTextView* textView = control->TextView(); 715 textView->SetMaxBytes(10); 716 717 for (int32 i = 0; i < 256; i++) { 718 if (!isdigit(i)) 719 textView->DisallowChar(i); 720 } 721 } 722 723 724 void 725 ApplicationTypeWindow::_Save() 726 { 727 BFile file; 728 status_t status = file.SetTo(&fEntry, B_READ_WRITE); 729 if (status != B_OK) 730 return; 731 732 BAppFileInfo info(&file); 733 status = info.InitCheck(); 734 if (status != B_OK) 735 return; 736 737 // Retrieve Info 738 739 uint32 flags = 0; 740 bool gotFlags = _Flags(flags); 741 BMessage supportedTypes = _SupportedTypes(); 742 version_info versionInfo = _VersionInfo(); 743 744 // Save 745 746 status = info.SetSignature(fSignatureControl->Text()); 747 if (status == B_OK) { 748 if (gotFlags) 749 status = info.SetAppFlags(flags); 750 else 751 status = info.RemoveAppFlags(); 752 } 753 if (status == B_OK) 754 status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND); 755 if (status == B_OK) 756 fIcon.CopyTo(info, NULL, true); 757 758 // supported types and their icons 759 if (status == B_OK) 760 status = info.SetSupportedTypes(&supportedTypes); 761 762 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 763 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 764 fTypeListView->ItemAt(i)); 765 766 if (item != NULL) 767 item->Icon().CopyTo(info, item->Type(), true); 768 } 769 770 // reset the saved info 771 fOriginalInfo.signature = fSignatureControl->Text(); 772 fOriginalInfo.gotFlags = gotFlags; 773 fOriginalInfo.flags = flags; 774 fOriginalInfo.versionInfo = versionInfo; 775 fOriginalInfo.supportedTypes = supportedTypes; 776 fOriginalInfo.iconChanged = false; 777 fOriginalInfo.typeIconsChanged = false; 778 779 fChangedProperties = 0; 780 _CheckSaveMenuItem(0); 781 } 782 783 784 void 785 ApplicationTypeWindow::_CheckSaveMenuItem(uint32 flags) 786 { 787 fChangedProperties = _NeedsSaving(flags); 788 fSaveMenuItem->SetEnabled(fChangedProperties != 0); 789 } 790 791 792 bool 793 operator!=(const version_info& a, const version_info& b) 794 { 795 return a.major != b.major || a.middle != b.middle || a.minor != b.minor 796 || a.variety != b.variety || a.internal != b.internal 797 || strcmp(a.short_info, b.short_info) != 0 798 || strcmp(a.long_info, b.long_info) != 0; 799 } 800 801 802 uint32 803 ApplicationTypeWindow::_NeedsSaving(uint32 _flags) const 804 { 805 uint32 flags = fChangedProperties; 806 if (_flags & CHECK_SIGNATUR) { 807 if (fOriginalInfo.signature != fSignatureControl->Text()) 808 flags |= CHECK_SIGNATUR; 809 else 810 flags &= ~CHECK_SIGNATUR; 811 } 812 813 if (_flags & CHECK_FLAGS) { 814 uint32 appFlags = 0; 815 bool gotFlags = _Flags(appFlags); 816 if (fOriginalInfo.gotFlags != gotFlags 817 || fOriginalInfo.flags != appFlags) { 818 flags |= CHECK_FLAGS; 819 } else 820 flags &= ~CHECK_FLAGS; 821 } 822 823 if (_flags & CHECK_VERSION) { 824 if (fOriginalInfo.versionInfo != _VersionInfo()) 825 flags |= CHECK_VERSION; 826 else 827 flags &= ~CHECK_VERSION; 828 } 829 830 if (_flags & CHECK_ICON) { 831 if (fOriginalInfo.iconChanged) 832 flags |= CHECK_ICON; 833 else 834 flags &= ~CHECK_ICON; 835 } 836 837 if (_flags & CHECK_TYPES) { 838 if (!fOriginalInfo.supportedTypes.HasSameData(_SupportedTypes())) 839 flags |= CHECK_TYPES; 840 else 841 flags &= ~CHECK_TYPES; 842 } 843 844 if (_flags & CHECK_TYPE_ICONS) { 845 if (fOriginalInfo.typeIconsChanged) 846 flags |= CHECK_TYPE_ICONS; 847 else 848 flags &= ~CHECK_TYPE_ICONS; 849 } 850 851 return flags; 852 } 853 854 855 // #pragma mark - 856 857 858 bool 859 ApplicationTypeWindow::_Flags(uint32& flags) const 860 { 861 flags = 0; 862 if (fFlagsCheckBox->Value() != B_CONTROL_OFF) { 863 if (fSingleLaunchButton->Value() != B_CONTROL_OFF) 864 flags |= B_SINGLE_LAUNCH; 865 else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF) 866 flags |= B_MULTIPLE_LAUNCH; 867 else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF) 868 flags |= B_EXCLUSIVE_LAUNCH; 869 870 if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF) 871 flags |= B_ARGV_ONLY; 872 if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF) 873 flags |= B_BACKGROUND_APP; 874 return true; 875 } 876 return false; 877 } 878 879 880 BMessage 881 ApplicationTypeWindow::_SupportedTypes() const 882 { 883 BMessage supportedTypes; 884 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 885 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 886 fTypeListView->ItemAt(i)); 887 888 if (item != NULL) 889 supportedTypes.AddString("types", item->Type()); 890 } 891 return supportedTypes; 892 } 893 894 895 version_info 896 ApplicationTypeWindow::_VersionInfo() const 897 { 898 version_info versionInfo; 899 versionInfo.major = atol(fMajorVersionControl->Text()); 900 versionInfo.middle = atol(fMiddleVersionControl->Text()); 901 versionInfo.minor = atol(fMinorVersionControl->Text()); 902 versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked()); 903 versionInfo.internal = atol(fInternalVersionControl->Text()); 904 strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(), 905 sizeof(versionInfo.short_info)); 906 strlcpy(versionInfo.long_info, fLongDescriptionView->Text(), 907 sizeof(versionInfo.long_info)); 908 return versionInfo; 909 } 910 911 912 // #pragma mark - 913 914 915 void 916 ApplicationTypeWindow::MessageReceived(BMessage* message) 917 { 918 switch (message->what) { 919 case kMsgToggleAppFlags: 920 _UpdateAppFlagsEnabled(); 921 _CheckSaveMenuItem(CHECK_FLAGS); 922 break; 923 924 case kMsgSignatureChanged: 925 _CheckSaveMenuItem(CHECK_SIGNATUR); 926 break; 927 928 case kMsgAppFlagsChanged: 929 _CheckSaveMenuItem(CHECK_FLAGS); 930 break; 931 932 case kMsgIconChanged: 933 fOriginalInfo.iconChanged = true; 934 _CheckSaveMenuItem(CHECK_ICON); 935 break; 936 937 case kMsgTypeIconsChanged: 938 fOriginalInfo.typeIconsChanged = true; 939 _CheckSaveMenuItem(CHECK_TYPE_ICONS); 940 break; 941 942 case kMsgVersionInfoChanged: 943 _CheckSaveMenuItem(CHECK_VERSION); 944 break; 945 946 case kMsgSave: 947 _Save(); 948 break; 949 950 case kMsgTypeSelected: 951 { 952 int32 index; 953 if (message->FindInt32("index", &index) == B_OK) { 954 SupportedTypeItem* item 955 = (SupportedTypeItem*)fTypeListView->ItemAt(index); 956 957 fTypeIconView->SetModificationMessage(NULL); 958 fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL); 959 fTypeIconView->SetModificationMessage( 960 new BMessage(kMsgTypeIconsChanged)); 961 fTypeIconView->SetEnabled(item != NULL); 962 fRemoveTypeButton->SetEnabled(item != NULL); 963 964 _CheckSaveMenuItem(CHECK_TYPES); 965 } 966 break; 967 } 968 969 case kMsgAddType: 970 { 971 BWindow* window = new TypeListWindow(NULL, 972 kMsgTypeAdded, this); 973 window->Show(); 974 break; 975 } 976 977 case kMsgTypeAdded: 978 { 979 const char* type; 980 if (message->FindString("type", &type) != B_OK) 981 break; 982 983 // check if this type already exists 984 985 SupportedTypeItem* newItem = new SupportedTypeItem(type); 986 int32 insertAt = 0; 987 988 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 989 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 990 fTypeListView->ItemAt(i)); 991 if (item == NULL) 992 continue; 993 994 int compare = strcasecmp(item->Type(), type); 995 if (!compare) { 996 // type does already exist, select it and bail out 997 delete newItem; 998 newItem = NULL; 999 fTypeListView->Select(i); 1000 break; 1001 } 1002 if (compare < 0) 1003 insertAt = i + 1; 1004 } 1005 1006 if (newItem == NULL) 1007 break; 1008 1009 fTypeListView->AddItem(newItem, insertAt); 1010 fTypeListView->Select(insertAt); 1011 1012 _CheckSaveMenuItem(CHECK_TYPES); 1013 break; 1014 } 1015 1016 case kMsgRemoveType: 1017 { 1018 int32 index = fTypeListView->CurrentSelection(); 1019 if (index < 0) 1020 break; 1021 1022 delete fTypeListView->RemoveItem(index); 1023 fTypeIconView->SetModificationMessage(NULL); 1024 fTypeIconView->SetTo(NULL); 1025 fTypeIconView->SetModificationMessage( 1026 new BMessage(kMsgTypeIconsChanged)); 1027 fTypeIconView->SetEnabled(false); 1028 fRemoveTypeButton->SetEnabled(false); 1029 1030 _CheckSaveMenuItem(CHECK_TYPES); 1031 break; 1032 } 1033 1034 case B_SIMPLE_DATA: 1035 { 1036 entry_ref ref; 1037 if (message->FindRef("refs", &ref) != B_OK) 1038 break; 1039 1040 // TODO: add to supported types 1041 break; 1042 } 1043 1044 case B_META_MIME_CHANGED: 1045 const char* type; 1046 int32 which; 1047 if (message->FindString("be:type", &type) != B_OK 1048 || message->FindInt32("be:which", &which) != B_OK) 1049 break; 1050 1051 // TODO: update supported types names 1052 // if (which == B_MIME_TYPE_DELETED) 1053 1054 // _CheckSaveMenuItem(...); 1055 break; 1056 1057 default: 1058 BWindow::MessageReceived(message); 1059 } 1060 } 1061 1062 1063 bool 1064 ApplicationTypeWindow::QuitRequested() 1065 { 1066 if (_NeedsSaving(CHECK_ALL) != 0) { 1067 BAlert* alert = new BAlert(B_TRANSLATE("Save request"), 1068 B_TRANSLATE("Save changes before closing?"), 1069 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"), 1070 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 1071 B_WARNING_ALERT); 1072 alert->SetShortcut(0, B_ESCAPE); 1073 alert->SetShortcut(1, 'd'); 1074 alert->SetShortcut(2, 's'); 1075 1076 int32 choice = alert->Go(); 1077 switch (choice) { 1078 case 0: 1079 return false; 1080 case 1: 1081 break; 1082 case 2: 1083 _Save(); 1084 break; 1085 } 1086 } 1087 1088 BMessage update(kMsgSettingsChanged); 1089 update.AddRect("app_type_frame", Frame()); 1090 be_app_messenger.SendMessage(&update); 1091 1092 be_app->PostMessage(kMsgTypeWindowClosed); 1093 return true; 1094 } 1095 1096