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