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, const BEntry& entry) 302 : 303 BWindow(_Frame(settings), B_TRANSLATE("Application type"), B_TITLED_WINDOW, 304 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | B_FRAME_EVENTS | B_AUTO_UPDATE_SIZE_LIMITS), 305 fChangedProperties(0) 306 { 307 float padding = be_control_look->DefaultItemSpacing(); 308 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment(); 309 310 BMenuBar* menuBar = new BMenuBar((char*)NULL); 311 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); 312 313 BMenu* menu = new BMenu(B_TRANSLATE("File")); 314 fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"), 315 new BMessage(kMsgSave), 'S'); 316 fSaveMenuItem->SetEnabled(false); 317 menu->AddItem(fSaveMenuItem); 318 BMenuItem* item; 319 menu->AddItem(item = new BMenuItem( 320 B_TRANSLATE("Save into resource file" B_UTF8_ELLIPSIS), NULL)); 321 item->SetEnabled(false); 322 323 menu->AddSeparatorItem(); 324 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 325 new BMessage(B_QUIT_REQUESTED), 'W', B_COMMAND_KEY)); 326 menuBar->AddItem(menu); 327 328 // Signature 329 330 fSignatureControl = new BTextControl("signature", 331 B_TRANSLATE("Signature:"), NULL, new BMessage(kMsgSignatureChanged)); 332 fSignatureControl->SetModificationMessage( 333 new BMessage(kMsgSignatureChanged)); 334 335 // filter out invalid characters that can't be part of a MIME type name 336 BTextView* textView = fSignatureControl->TextView(); 337 textView->SetMaxBytes(B_MIME_TYPE_LENGTH); 338 const char* disallowedCharacters = "<>@,;:\"()[]?= "; 339 for (int32 i = 0; disallowedCharacters[i]; i++) { 340 textView->DisallowChar(disallowedCharacters[i]); 341 } 342 343 // "Application Flags" group 344 345 BBox* flagsBox = new BBox("flagsBox"); 346 347 fFlagsCheckBox = new BCheckBox("flags", B_TRANSLATE("Application flags"), 348 new BMessage(kMsgToggleAppFlags)); 349 fFlagsCheckBox->SetValue(B_CONTROL_ON); 350 351 fSingleLaunchButton = new BRadioButton("single", 352 B_TRANSLATE("Single launch"), new BMessage(kMsgAppFlagsChanged)); 353 354 fMultipleLaunchButton = new BRadioButton("multiple", 355 B_TRANSLATE("Multiple launch"), new BMessage(kMsgAppFlagsChanged)); 356 357 fExclusiveLaunchButton = new BRadioButton("exclusive", 358 B_TRANSLATE("Exclusive launch"), new BMessage(kMsgAppFlagsChanged)); 359 360 fArgsOnlyCheckBox = new BCheckBox("args only", B_TRANSLATE("Args only"), 361 new BMessage(kMsgAppFlagsChanged)); 362 363 fBackgroundAppCheckBox = new BCheckBox("background", 364 B_TRANSLATE("Background app"), new BMessage(kMsgAppFlagsChanged)); 365 366 BLayoutBuilder::Grid<>(flagsBox, 0, 0) 367 .SetInsets(padding, padding * 2, padding, padding) 368 .Add(fSingleLaunchButton, 0, 0).Add(fArgsOnlyCheckBox, 1, 0) 369 .Add(fMultipleLaunchButton, 0, 1).Add(fBackgroundAppCheckBox, 1, 1) 370 .Add(fExclusiveLaunchButton, 0, 2); 371 flagsBox->SetLabel(fFlagsCheckBox); 372 373 // "Icon" group 374 375 BBox* iconBox = new BBox("IconBox"); 376 iconBox->SetLabel(B_TRANSLATE("Icon")); 377 fIconView = new IconView("icon"); 378 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged)); 379 BLayoutBuilder::Group<>(iconBox, B_HORIZONTAL) 380 .SetInsets(padding, padding * 2, padding, padding) 381 .Add(fIconView); 382 383 // "Supported Types" group 384 385 BBox* typeBox = new BBox("typesBox"); 386 typeBox->SetLabel(B_TRANSLATE("Supported types")); 387 388 fTypeListView = new SupportedTypeListView("Suppported Types", 389 B_SINGLE_SELECTION_LIST); 390 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); 391 392 BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView, 393 B_FRAME_EVENTS | B_WILL_DRAW, false, true); 394 395 fAddTypeButton = new BButton("add type", 396 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType)); 397 398 fRemoveTypeButton = new BButton("remove type", B_TRANSLATE("Remove"), 399 new BMessage(kMsgRemoveType)); 400 401 fTypeIconView = new IconView("type icon"); 402 BGroupView* iconHolder = new BGroupView(B_HORIZONTAL); 403 iconHolder->AddChild(fTypeIconView); 404 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged)); 405 406 BLayoutBuilder::Grid<>(typeBox, padding, padding) 407 .SetInsets(padding, padding * 2, padding, padding) 408 .Add(scrollView, 0, 0, 1, 5) 409 .Add(fAddTypeButton, 1, 0, 1, 2) 410 .Add(fRemoveTypeButton, 1, 2, 1, 2) 411 .Add(iconHolder, 2, 1, 1, 2) 412 .SetColumnWeight(0, 3) 413 .SetColumnWeight(1, 2) 414 .SetColumnWeight(2, 1); 415 416 iconHolder->SetExplicitAlignment( 417 BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); 418 419 // "Version Info" group 420 421 BBox* versionBox = new BBox("versionBox"); 422 versionBox->SetLabel(B_TRANSLATE("Version info")); 423 424 fMajorVersionControl = new BTextControl("version major", 425 B_TRANSLATE("Version:"), NULL, new BMessage(kMsgVersionInfoChanged)); 426 _MakeNumberTextControl(fMajorVersionControl); 427 428 fMiddleVersionControl = new BTextControl("version middle", ".", NULL, 429 new BMessage(kMsgVersionInfoChanged)); 430 _MakeNumberTextControl(fMiddleVersionControl); 431 432 fMinorVersionControl = new BTextControl("version minor", ".", NULL, 433 new BMessage(kMsgVersionInfoChanged)); 434 _MakeNumberTextControl(fMinorVersionControl); 435 436 fVarietyMenu = new BPopUpMenu("variety", true, true); 437 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Development"), 438 new BMessage(kMsgVersionInfoChanged))); 439 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Alpha"), 440 new BMessage(kMsgVersionInfoChanged))); 441 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Beta"), 442 new BMessage(kMsgVersionInfoChanged))); 443 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Gamma"), 444 new BMessage(kMsgVersionInfoChanged))); 445 item = new BMenuItem(B_TRANSLATE("Golden master"), 446 new BMessage(kMsgVersionInfoChanged)); 447 fVarietyMenu->AddItem(item); 448 item->SetMarked(true); 449 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Final"), 450 new BMessage(kMsgVersionInfoChanged))); 451 452 BMenuField* varietyField = new BMenuField("", fVarietyMenu); 453 fInternalVersionControl = new BTextControl("version internal", "/", NULL, 454 new BMessage(kMsgVersionInfoChanged)); 455 fShortDescriptionControl = new BTextControl("short description", 456 B_TRANSLATE("Short description:"), NULL, 457 new BMessage(kMsgVersionInfoChanged)); 458 459 // TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says? 460 version_info versionInfo; 461 fShortDescriptionControl->TextView()->SetMaxBytes( 462 sizeof(versionInfo.short_info)); 463 464 BStringView* longLabel = new BStringView(NULL, 465 B_TRANSLATE("Long description:")); 466 longLabel->SetExplicitAlignment(labelAlignment); 467 fLongDescriptionView = new TabFilteringTextView("long desc", 468 kMsgVersionInfoChanged); 469 fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info)); 470 471 scrollView = new BScrollView("desc scrollview", fLongDescriptionView, 472 B_FRAME_EVENTS | B_WILL_DRAW, false, true); 473 474 // Manually set a minimum size for the version text controls 475 // TODO: the same does not work when applied to the layout items 476 float width = be_plain_font->StringWidth("99") + 16; 477 fMajorVersionControl->TextView()->SetExplicitMinSize( 478 BSize(width, B_SIZE_UNSET)); 479 fMiddleVersionControl->TextView()->SetExplicitMinSize( 480 BSize(width, B_SIZE_UNSET)); 481 fMinorVersionControl->TextView()->SetExplicitMinSize( 482 BSize(width, B_SIZE_UNSET)); 483 fInternalVersionControl->TextView()->SetExplicitMinSize( 484 BSize(width, B_SIZE_UNSET)); 485 486 BLayoutBuilder::Grid<>(versionBox, padding / 2, padding / 2) 487 .SetInsets(padding, padding * 2, padding, padding) 488 .AddTextControl(fMajorVersionControl, 0, 0) 489 .Add(fMiddleVersionControl, 2, 0, 2) 490 .Add(fMinorVersionControl, 4, 0, 2) 491 .Add(varietyField, 6, 0, 3) 492 .Add(fInternalVersionControl, 9, 0, 2) 493 .AddTextControl(fShortDescriptionControl, 0, 1, 494 B_ALIGN_HORIZONTAL_UNSET, 1, 10) 495 .Add(longLabel, 0, 2) 496 .Add(scrollView, 1, 2, 10, 3) 497 .SetRowWeight(3, 3); 498 499 // put it all together 500 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 501 .SetInsets(0, 0, 0, 0) 502 .Add(menuBar) 503 .AddGroup(B_VERTICAL, padding) 504 .SetInsets(padding, padding, padding, padding) 505 .Add(fSignatureControl) 506 .AddGroup(B_HORIZONTAL, padding) 507 .Add(flagsBox, 3) 508 .Add(iconBox, 1) 509 .End() 510 .Add(typeBox, 2) 511 .Add(versionBox); 512 513 SetKeyMenuBar(menuBar); 514 515 fSignatureControl->MakeFocus(true); 516 BMimeType::StartWatching(this); 517 _SetTo(entry); 518 519 Layout(false); 520 } 521 522 523 ApplicationTypeWindow::~ApplicationTypeWindow() 524 { 525 BMimeType::StopWatching(this); 526 } 527 528 BRect 529 ApplicationTypeWindow::_Frame(const BMessage& settings) const 530 { 531 BRect rect; 532 if (settings.FindRect("app_type_next_frame", &rect) == B_OK) 533 return rect; 534 535 return BRect(100.0f, 110.0f, 250.0f, 340.0f); 536 } 537 538 BString 539 ApplicationTypeWindow::_Title(const BEntry& entry) 540 { 541 char name[B_FILE_NAME_LENGTH]; 542 if (entry.GetName(name) != B_OK) 543 strcpy(name, "\"-\""); 544 545 BString title = B_TRANSLATE("%1 application type"); 546 title.ReplaceFirst("%1", name); 547 return title; 548 } 549 550 551 void 552 ApplicationTypeWindow::_SetTo(const BEntry& entry) 553 { 554 SetTitle(_Title(entry).String()); 555 fEntry = entry; 556 557 // Retrieve Info 558 559 BFile file(&entry, B_READ_ONLY); 560 if (file.InitCheck() != B_OK) 561 return; 562 563 BAppFileInfo info(&file); 564 if (info.InitCheck() != B_OK) 565 return; 566 567 char signature[B_MIME_TYPE_LENGTH]; 568 if (info.GetSignature(signature) != B_OK) 569 signature[0] = '\0'; 570 571 bool gotFlags = false; 572 uint32 flags; 573 if (info.GetAppFlags(&flags) == B_OK) 574 gotFlags = true; 575 else 576 flags = B_MULTIPLE_LAUNCH; 577 578 version_info versionInfo; 579 if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK) 580 memset(&versionInfo, 0, sizeof(version_info)); 581 582 // Set Controls 583 584 fSignatureControl->SetModificationMessage(NULL); 585 fSignatureControl->SetText(signature); 586 fSignatureControl->SetModificationMessage( 587 new BMessage(kMsgSignatureChanged)); 588 589 // flags 590 591 switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) { 592 case B_SINGLE_LAUNCH: 593 fSingleLaunchButton->SetValue(B_CONTROL_ON); 594 break; 595 596 case B_EXCLUSIVE_LAUNCH: 597 fExclusiveLaunchButton->SetValue(B_CONTROL_ON); 598 break; 599 600 case B_MULTIPLE_LAUNCH: 601 default: 602 fMultipleLaunchButton->SetValue(B_CONTROL_ON); 603 break; 604 } 605 606 fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0); 607 fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0); 608 fFlagsCheckBox->SetValue(gotFlags); 609 610 _UpdateAppFlagsEnabled(); 611 612 // icon 613 614 entry_ref ref; 615 if (entry.GetRef(&ref) == B_OK) 616 fIcon.SetTo(ref); 617 else 618 fIcon.Unset(); 619 620 fIconView->SetModificationMessage(NULL); 621 fIconView->SetTo(&fIcon); 622 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged)); 623 624 // supported types 625 626 BMessage supportedTypes; 627 info.GetSupportedTypes(&supportedTypes); 628 629 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 630 BListItem* item = fTypeListView->RemoveItem(i); 631 delete item; 632 } 633 634 const char* type; 635 for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) { 636 SupportedTypeItem* item = new SupportedTypeItem(type); 637 638 entry_ref ref; 639 if (fEntry.GetRef(&ref) == B_OK) 640 item->SetIcon(ref, type); 641 642 fTypeListView->AddItem(item); 643 } 644 fTypeListView->SortItems(&SupportedTypeItem::Compare); 645 fTypeIconView->SetModificationMessage(NULL); 646 fTypeIconView->SetTo(NULL); 647 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged)); 648 fTypeIconView->SetEnabled(false); 649 fRemoveTypeButton->SetEnabled(false); 650 651 // version info 652 653 char text[256]; 654 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.major); 655 fMajorVersionControl->SetText(text); 656 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.middle); 657 fMiddleVersionControl->SetText(text); 658 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.minor); 659 fMinorVersionControl->SetText(text); 660 661 if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems()) 662 versionInfo.variety = 0; 663 BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety); 664 if (item != NULL) 665 item->SetMarked(true); 666 667 snprintf(text, sizeof(text), "%" B_PRId32, versionInfo.internal); 668 fInternalVersionControl->SetText(text); 669 670 fShortDescriptionControl->SetText(versionInfo.short_info); 671 fLongDescriptionView->SetText(versionInfo.long_info); 672 673 // store original data 674 675 fOriginalInfo.signature = signature; 676 fOriginalInfo.gotFlags = gotFlags; 677 fOriginalInfo.flags = gotFlags ? flags : 0; 678 fOriginalInfo.versionInfo = versionInfo; 679 fOriginalInfo.supportedTypes = _SupportedTypes(); 680 // The list view has the types sorted possibly differently 681 // to the supportedTypes message, so don't use that here, but 682 // get the sorted message instead. 683 fOriginalInfo.iconChanged = false; 684 fOriginalInfo.typeIconsChanged = false; 685 686 fChangedProperties = 0; 687 _CheckSaveMenuItem(0); 688 } 689 690 691 void 692 ApplicationTypeWindow::_UpdateAppFlagsEnabled() 693 { 694 bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF; 695 696 fSingleLaunchButton->SetEnabled(enabled); 697 fMultipleLaunchButton->SetEnabled(enabled); 698 fExclusiveLaunchButton->SetEnabled(enabled); 699 fArgsOnlyCheckBox->SetEnabled(enabled); 700 fBackgroundAppCheckBox->SetEnabled(enabled); 701 } 702 703 704 void 705 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control) 706 { 707 // filter out invalid characters that can't be part of a MIME type name 708 BTextView* textView = control->TextView(); 709 textView->SetMaxBytes(10); 710 711 for (int32 i = 0; i < 256; i++) { 712 if (!isdigit(i)) 713 textView->DisallowChar(i); 714 } 715 } 716 717 718 void 719 ApplicationTypeWindow::_Save() 720 { 721 BFile file; 722 status_t status = file.SetTo(&fEntry, B_READ_WRITE); 723 if (status != B_OK) 724 return; 725 726 BAppFileInfo info(&file); 727 status = info.InitCheck(); 728 if (status != B_OK) 729 return; 730 731 // Retrieve Info 732 733 uint32 flags = 0; 734 bool gotFlags = _Flags(flags); 735 BMessage supportedTypes = _SupportedTypes(); 736 version_info versionInfo = _VersionInfo(); 737 738 // Save 739 740 status = info.SetSignature(fSignatureControl->Text()); 741 if (status == B_OK) { 742 if (gotFlags) 743 status = info.SetAppFlags(flags); 744 else 745 status = info.RemoveAppFlags(); 746 } 747 if (status == B_OK) 748 status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND); 749 if (status == B_OK) 750 fIcon.CopyTo(info, NULL, true); 751 752 // supported types and their icons 753 if (status == B_OK) 754 status = info.SetSupportedTypes(&supportedTypes); 755 756 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 757 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 758 fTypeListView->ItemAt(i)); 759 760 if (item != NULL) 761 item->Icon().CopyTo(info, item->Type(), true); 762 } 763 764 // reset the saved info 765 fOriginalInfo.signature = fSignatureControl->Text(); 766 fOriginalInfo.gotFlags = gotFlags; 767 fOriginalInfo.flags = flags; 768 fOriginalInfo.versionInfo = versionInfo; 769 fOriginalInfo.supportedTypes = supportedTypes; 770 fOriginalInfo.iconChanged = false; 771 fOriginalInfo.typeIconsChanged = false; 772 773 fChangedProperties = 0; 774 _CheckSaveMenuItem(0); 775 } 776 777 778 void 779 ApplicationTypeWindow::_CheckSaveMenuItem(uint32 flags) 780 { 781 fChangedProperties = _NeedsSaving(flags); 782 fSaveMenuItem->SetEnabled(fChangedProperties != 0); 783 } 784 785 786 bool 787 operator!=(const version_info& a, const version_info& b) 788 { 789 return a.major != b.major || a.middle != b.middle || a.minor != b.minor 790 || a.variety != b.variety || a.internal != b.internal 791 || strcmp(a.short_info, b.short_info) != 0 792 || strcmp(a.long_info, b.long_info) != 0; 793 } 794 795 796 uint32 797 ApplicationTypeWindow::_NeedsSaving(uint32 _flags) const 798 { 799 uint32 flags = fChangedProperties; 800 if (_flags & CHECK_SIGNATUR) { 801 if (fOriginalInfo.signature != fSignatureControl->Text()) 802 flags |= CHECK_SIGNATUR; 803 else 804 flags &= ~CHECK_SIGNATUR; 805 } 806 807 if (_flags & CHECK_FLAGS) { 808 uint32 appFlags = 0; 809 bool gotFlags = _Flags(appFlags); 810 if (fOriginalInfo.gotFlags != gotFlags 811 || fOriginalInfo.flags != appFlags) { 812 flags |= CHECK_FLAGS; 813 } else 814 flags &= ~CHECK_FLAGS; 815 } 816 817 if (_flags & CHECK_VERSION) { 818 if (fOriginalInfo.versionInfo != _VersionInfo()) 819 flags |= CHECK_VERSION; 820 else 821 flags &= ~CHECK_VERSION; 822 } 823 824 if (_flags & CHECK_ICON) { 825 if (fOriginalInfo.iconChanged) 826 flags |= CHECK_ICON; 827 else 828 flags &= ~CHECK_ICON; 829 } 830 831 if (_flags & CHECK_TYPES) { 832 if (!fOriginalInfo.supportedTypes.HasSameData(_SupportedTypes())) 833 flags |= CHECK_TYPES; 834 else 835 flags &= ~CHECK_TYPES; 836 } 837 838 if (_flags & CHECK_TYPE_ICONS) { 839 if (fOriginalInfo.typeIconsChanged) 840 flags |= CHECK_TYPE_ICONS; 841 else 842 flags &= ~CHECK_TYPE_ICONS; 843 } 844 845 return flags; 846 } 847 848 849 // #pragma mark - 850 851 852 bool 853 ApplicationTypeWindow::_Flags(uint32& flags) const 854 { 855 flags = 0; 856 if (fFlagsCheckBox->Value() != B_CONTROL_OFF) { 857 if (fSingleLaunchButton->Value() != B_CONTROL_OFF) 858 flags |= B_SINGLE_LAUNCH; 859 else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF) 860 flags |= B_MULTIPLE_LAUNCH; 861 else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF) 862 flags |= B_EXCLUSIVE_LAUNCH; 863 864 if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF) 865 flags |= B_ARGV_ONLY; 866 if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF) 867 flags |= B_BACKGROUND_APP; 868 return true; 869 } 870 return false; 871 } 872 873 874 BMessage 875 ApplicationTypeWindow::_SupportedTypes() const 876 { 877 BMessage supportedTypes; 878 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 879 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 880 fTypeListView->ItemAt(i)); 881 882 if (item != NULL) 883 supportedTypes.AddString("types", item->Type()); 884 } 885 return supportedTypes; 886 } 887 888 889 version_info 890 ApplicationTypeWindow::_VersionInfo() const 891 { 892 version_info versionInfo; 893 versionInfo.major = atol(fMajorVersionControl->Text()); 894 versionInfo.middle = atol(fMiddleVersionControl->Text()); 895 versionInfo.minor = atol(fMinorVersionControl->Text()); 896 versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked()); 897 versionInfo.internal = atol(fInternalVersionControl->Text()); 898 strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(), 899 sizeof(versionInfo.short_info)); 900 strlcpy(versionInfo.long_info, fLongDescriptionView->Text(), 901 sizeof(versionInfo.long_info)); 902 return versionInfo; 903 } 904 905 906 // #pragma mark - 907 908 909 void 910 ApplicationTypeWindow::MessageReceived(BMessage* message) 911 { 912 switch (message->what) { 913 case kMsgToggleAppFlags: 914 _UpdateAppFlagsEnabled(); 915 _CheckSaveMenuItem(CHECK_FLAGS); 916 break; 917 918 case kMsgSignatureChanged: 919 _CheckSaveMenuItem(CHECK_SIGNATUR); 920 break; 921 922 case kMsgAppFlagsChanged: 923 _CheckSaveMenuItem(CHECK_FLAGS); 924 break; 925 926 case kMsgIconChanged: 927 fOriginalInfo.iconChanged = true; 928 _CheckSaveMenuItem(CHECK_ICON); 929 break; 930 931 case kMsgTypeIconsChanged: 932 fOriginalInfo.typeIconsChanged = true; 933 _CheckSaveMenuItem(CHECK_TYPE_ICONS); 934 break; 935 936 case kMsgVersionInfoChanged: 937 _CheckSaveMenuItem(CHECK_VERSION); 938 break; 939 940 case kMsgSave: 941 _Save(); 942 break; 943 944 case kMsgTypeSelected: 945 { 946 int32 index; 947 if (message->FindInt32("index", &index) == B_OK) { 948 SupportedTypeItem* item 949 = (SupportedTypeItem*)fTypeListView->ItemAt(index); 950 951 fTypeIconView->SetModificationMessage(NULL); 952 fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL); 953 fTypeIconView->SetModificationMessage( 954 new BMessage(kMsgTypeIconsChanged)); 955 fTypeIconView->SetEnabled(item != NULL); 956 fRemoveTypeButton->SetEnabled(item != NULL); 957 958 _CheckSaveMenuItem(CHECK_TYPES); 959 } 960 break; 961 } 962 963 case kMsgAddType: 964 { 965 BWindow* window = new TypeListWindow(NULL, 966 kMsgTypeAdded, this); 967 window->Show(); 968 break; 969 } 970 971 case kMsgTypeAdded: 972 { 973 const char* type; 974 if (message->FindString("type", &type) != B_OK) 975 break; 976 977 // check if this type already exists 978 979 SupportedTypeItem* newItem = new SupportedTypeItem(type); 980 int32 insertAt = 0; 981 982 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 983 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 984 fTypeListView->ItemAt(i)); 985 if (item == NULL) 986 continue; 987 988 int compare = strcasecmp(item->Type(), type); 989 if (!compare) { 990 // type does already exist, select it and bail out 991 delete newItem; 992 newItem = NULL; 993 fTypeListView->Select(i); 994 break; 995 } 996 if (compare < 0) 997 insertAt = i + 1; 998 } 999 1000 if (newItem == NULL) 1001 break; 1002 1003 fTypeListView->AddItem(newItem, insertAt); 1004 fTypeListView->Select(insertAt); 1005 1006 _CheckSaveMenuItem(CHECK_TYPES); 1007 break; 1008 } 1009 1010 case kMsgRemoveType: 1011 { 1012 int32 index = fTypeListView->CurrentSelection(); 1013 if (index < 0) 1014 break; 1015 1016 delete fTypeListView->RemoveItem(index); 1017 fTypeIconView->SetModificationMessage(NULL); 1018 fTypeIconView->SetTo(NULL); 1019 fTypeIconView->SetModificationMessage( 1020 new BMessage(kMsgTypeIconsChanged)); 1021 fTypeIconView->SetEnabled(false); 1022 fRemoveTypeButton->SetEnabled(false); 1023 1024 _CheckSaveMenuItem(CHECK_TYPES); 1025 break; 1026 } 1027 1028 case B_SIMPLE_DATA: 1029 { 1030 entry_ref ref; 1031 if (message->FindRef("refs", &ref) != B_OK) 1032 break; 1033 1034 // TODO: add to supported types 1035 break; 1036 } 1037 1038 case B_META_MIME_CHANGED: 1039 const char* type; 1040 int32 which; 1041 if (message->FindString("be:type", &type) != B_OK 1042 || message->FindInt32("be:which", &which) != B_OK) 1043 break; 1044 1045 // TODO: update supported types names 1046 // if (which == B_MIME_TYPE_DELETED) 1047 1048 // _CheckSaveMenuItem(...); 1049 break; 1050 1051 default: 1052 BWindow::MessageReceived(message); 1053 } 1054 } 1055 1056 1057 bool 1058 ApplicationTypeWindow::QuitRequested() 1059 { 1060 if (_NeedsSaving(CHECK_ALL) != 0) { 1061 BAlert* alert = new BAlert(B_TRANSLATE("Save request"), 1062 B_TRANSLATE("Save changes before closing?"), 1063 B_TRANSLATE("Cancel"), B_TRANSLATE("Don't save"), 1064 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 1065 B_WARNING_ALERT); 1066 alert->SetShortcut(0, B_ESCAPE); 1067 alert->SetShortcut(1, 'd'); 1068 alert->SetShortcut(2, 's'); 1069 1070 int32 choice = alert->Go(); 1071 switch (choice) { 1072 case 0: 1073 return false; 1074 case 1: 1075 break; 1076 case 2: 1077 _Save(); 1078 break; 1079 } 1080 } 1081 1082 BMessage update(kMsgSettingsChanged); 1083 update.AddRect("app_type_next_frame", Frame()); 1084 be_app_messenger.SendMessage(&update); 1085 1086 be_app->PostMessage(kMsgTypeWindowClosed); 1087 return true; 1088 } 1089 1090