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