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