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 <LayoutBuilder.h> 27 #include <ListView.h> 28 #include <Locale.h> 29 #include <MenuBar.h> 30 #include <MenuField.h> 31 #include <MenuItem.h> 32 #include <Mime.h> 33 #include <NodeInfo.h> 34 #include <PopUpMenu.h> 35 #include <RadioButton.h> 36 #include <Roster.h> 37 #include <ScrollView.h> 38 #include <StringView.h> 39 #include <TextControl.h> 40 41 #include <ctype.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 46 47 #undef B_TRANSLATE_CONTEXT 48 #define B_TRANSLATE_CONTEXT "Application Type Window" 49 50 51 const uint32 kMsgSave = 'save'; 52 const uint32 kMsgSignatureChanged = 'sgch'; 53 const uint32 kMsgToggleAppFlags = 'tglf'; 54 const uint32 kMsgAppFlagsChanged = 'afch'; 55 56 const uint32 kMsgIconChanged = 'icch'; 57 const uint32 kMsgTypeIconsChanged = 'tich'; 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 virtual ~TabFilteringTextView(); 71 virtual void KeyDown(const char* bytes, int32 count); 72 }; 73 74 75 TabFilteringTextView::TabFilteringTextView(const char* name) 76 : BTextView(name, B_WILL_DRAW | B_PULSE_NEEDED) 77 { 78 } 79 80 81 TabFilteringTextView::~TabFilteringTextView() 82 { 83 } 84 85 86 void 87 TabFilteringTextView::KeyDown(const char* bytes, int32 count) 88 { 89 if (bytes[0] == B_TAB) 90 BView::KeyDown(bytes, count); 91 else 92 BTextView::KeyDown(bytes, count); 93 } 94 95 96 class SupportedTypeItem : public BStringItem { 97 public: 98 SupportedTypeItem(const char* type); 99 ~SupportedTypeItem(); 100 101 const char* Type() const { return fType.String(); } 102 ::Icon& Icon() { return fIcon; } 103 void SetIcon(::Icon* icon); 104 void SetIcon(entry_ref& ref, const char* type); 105 106 static int Compare(const void* _a, const void* _b); 107 108 private: 109 BString fType; 110 ::Icon fIcon; 111 }; 112 113 class SupportedTypeListView : public DropTargetListView { 114 public: 115 SupportedTypeListView(const char* name, 116 list_view_type type = B_SINGLE_SELECTION_LIST, 117 uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE); 118 virtual ~SupportedTypeListView(); 119 120 virtual void MessageReceived(BMessage* message); 121 virtual bool AcceptsDrag(const BMessage* message); 122 }; 123 124 125 SupportedTypeItem::SupportedTypeItem(const char* type) 126 : BStringItem(type), 127 fType(type) 128 { 129 BMimeType mimeType(type); 130 131 char description[B_MIME_TYPE_LENGTH]; 132 if (mimeType.GetShortDescription(description) == B_OK && description[0]) 133 SetText(description); 134 } 135 136 137 SupportedTypeItem::~SupportedTypeItem() 138 { 139 } 140 141 142 void 143 SupportedTypeItem::SetIcon(::Icon* icon) 144 { 145 if (icon != NULL) 146 fIcon = *icon; 147 else 148 fIcon.Unset(); 149 } 150 151 152 void 153 SupportedTypeItem::SetIcon(entry_ref& ref, const char* type) 154 { 155 fIcon.SetTo(ref, type); 156 } 157 158 159 /*static*/ 160 int 161 SupportedTypeItem::Compare(const void* _a, const void* _b) 162 { 163 const SupportedTypeItem* a = *(const SupportedTypeItem**)_a; 164 const SupportedTypeItem* b = *(const SupportedTypeItem**)_b; 165 166 int compare = strcasecmp(a->Text(), b->Text()); 167 if (compare != 0) 168 return compare; 169 170 return strcasecmp(a->Type(), b->Type()); 171 } 172 173 174 // #pragma mark - 175 176 177 SupportedTypeListView::SupportedTypeListView(const char* name, 178 list_view_type type, uint32 flags) 179 : 180 DropTargetListView(name, type, flags) 181 { 182 } 183 184 185 SupportedTypeListView::~SupportedTypeListView() 186 { 187 } 188 189 190 void 191 SupportedTypeListView::MessageReceived(BMessage* message) 192 { 193 if (message->WasDropped() && AcceptsDrag(message)) { 194 // Add unique types 195 entry_ref ref; 196 for (int32 index = 0; message->FindRef("refs", index++, &ref) == B_OK; ) { 197 BNode node(&ref); 198 BNodeInfo info(&node); 199 if (node.InitCheck() != B_OK || info.InitCheck() != B_OK) 200 continue; 201 202 // TODO: we could identify the file in case it doesn't have a type... 203 char type[B_MIME_TYPE_LENGTH]; 204 if (info.GetType(type) != B_OK) 205 continue; 206 207 // check if that type is already in our list 208 bool found = false; 209 for (int32 i = CountItems(); i-- > 0;) { 210 SupportedTypeItem* item = (SupportedTypeItem*)ItemAt(i); 211 if (!strcmp(item->Text(), type)) { 212 found = true; 213 break; 214 } 215 } 216 217 if (!found) { 218 // add type 219 AddItem(new SupportedTypeItem(type)); 220 } 221 } 222 223 SortItems(&SupportedTypeItem::Compare); 224 } else 225 DropTargetListView::MessageReceived(message); 226 } 227 228 229 bool 230 SupportedTypeListView::AcceptsDrag(const BMessage* message) 231 { 232 type_code type; 233 return message->GetInfo("refs", &type) == B_OK && type == B_REF_TYPE; 234 } 235 236 237 // #pragma mark - 238 239 240 ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, 241 const BEntry& entry) 242 : 243 BWindow(BRect(0.0f, 0.0f, 250.0f, 340.0f).OffsetBySelf(position), 244 B_TRANSLATE("Application type"), B_TITLED_WINDOW, 245 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS | 246 B_FRAME_EVENTS | B_AUTO_UPDATE_SIZE_LIMITS), 247 fChangedProperties(0) 248 { 249 float padding = be_control_look->DefaultItemSpacing(); 250 BAlignment labelAlignment = be_control_look->DefaultLabelAlignment(); 251 252 BMenuBar* menuBar = new BMenuBar((char*)NULL); 253 menuBar->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); 254 255 BMenu* menu = new BMenu(B_TRANSLATE("File")); 256 fSaveMenuItem = new BMenuItem(B_TRANSLATE("Save"), 257 new BMessage(kMsgSave), 'S'); 258 fSaveMenuItem->SetEnabled(false); 259 menu->AddItem(fSaveMenuItem); 260 BMenuItem* item; 261 menu->AddItem(item = new BMenuItem( 262 B_TRANSLATE("Save into resource file" B_UTF8_ELLIPSIS), NULL)); 263 item->SetEnabled(false); 264 265 menu->AddSeparatorItem(); 266 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 267 new BMessage(B_QUIT_REQUESTED), 'W', B_COMMAND_KEY)); 268 menuBar->AddItem(menu); 269 270 // Signature 271 272 fSignatureControl = new BTextControl(B_TRANSLATE("Signature:"), NULL, 273 new BMessage(kMsgSignatureChanged)); 274 fSignatureControl->SetModificationMessage( 275 new BMessage(kMsgSignatureChanged)); 276 277 // filter out invalid characters that can't be part of a MIME type name 278 BTextView* textView = fSignatureControl->TextView(); 279 textView->SetMaxBytes(B_MIME_TYPE_LENGTH); 280 const char* disallowedCharacters = "<>@,;:\"()[]?="; 281 for (int32 i = 0; disallowedCharacters[i]; i++) { 282 textView->DisallowChar(disallowedCharacters[i]); 283 } 284 285 // "Application Flags" group 286 287 BBox* flagsBox = new BBox("flagsBox"); 288 289 fFlagsCheckBox = new BCheckBox("flags", B_TRANSLATE("Application flags"), 290 new BMessage(kMsgToggleAppFlags)); 291 fFlagsCheckBox->SetValue(B_CONTROL_ON); 292 293 fSingleLaunchButton = new BRadioButton("single", 294 B_TRANSLATE("Single launch"), new BMessage(kMsgAppFlagsChanged)); 295 296 fMultipleLaunchButton = new BRadioButton("multiple", 297 B_TRANSLATE("Multiple launch"), new BMessage(kMsgAppFlagsChanged)); 298 299 fExclusiveLaunchButton = new BRadioButton("exclusive", 300 B_TRANSLATE("Exclusive launch"), new BMessage(kMsgAppFlagsChanged)); 301 302 fArgsOnlyCheckBox = new BCheckBox("args only", B_TRANSLATE("Args only"), 303 new BMessage(kMsgAppFlagsChanged)); 304 305 fBackgroundAppCheckBox = new BCheckBox("background", 306 B_TRANSLATE("Background app"), new BMessage(kMsgAppFlagsChanged)); 307 308 flagsBox->AddChild(BGridLayoutBuilder(0, 0) 309 .Add(fSingleLaunchButton, 0, 0).Add(fArgsOnlyCheckBox, 1, 0) 310 .Add(fMultipleLaunchButton, 0, 1).Add(fBackgroundAppCheckBox, 1, 1) 311 .Add(fExclusiveLaunchButton, 0, 2) 312 .SetInsets(padding, padding / 2, padding, padding) 313 .View()); 314 flagsBox->SetLabel(fFlagsCheckBox); 315 316 // "Icon" group 317 318 BBox* iconBox = new BBox("IconBox"); 319 iconBox->SetLabel(B_TRANSLATE("Icon")); 320 fIconView = new IconView("icon"); 321 fIconView->SetModificationMessage(new BMessage(kMsgIconChanged)); 322 iconBox->AddChild(BGroupLayoutBuilder(B_HORIZONTAL) 323 .Add(fIconView) 324 .SetInsets(padding, padding, padding, padding) 325 .TopView()); 326 327 // "Supported Types" group 328 329 BBox* typeBox = new BBox("typesBox"); 330 typeBox->SetLabel(B_TRANSLATE("Supported types")); 331 332 fTypeListView = new SupportedTypeListView("Suppported Types", 333 B_SINGLE_SELECTION_LIST); 334 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); 335 336 BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView, 337 B_FRAME_EVENTS | B_WILL_DRAW, false, true); 338 339 fAddTypeButton = new BButton("add type", 340 B_TRANSLATE("Add" B_UTF8_ELLIPSIS), new BMessage(kMsgAddType)); 341 342 fRemoveTypeButton = new BButton("remove type", B_TRANSLATE("Remove"), 343 new BMessage(kMsgRemoveType)); 344 345 fTypeIconView = new IconView("type icon"); 346 BGroupView* iconHolder = new BGroupView(B_HORIZONTAL); 347 iconHolder->AddChild(fTypeIconView); 348 fTypeIconView->SetModificationMessage(new BMessage(kMsgTypeIconsChanged)); 349 350 typeBox->AddChild(BGridLayoutBuilder(padding, padding) 351 .Add(scrollView, 0, 0, 1, 4) 352 .Add(fAddTypeButton, 1, 0, 1, 2) 353 .Add(fRemoveTypeButton, 1, 2, 1, 2) 354 .Add(iconHolder, 2, 1, 1, 2) 355 .SetInsets(padding, padding / 2, padding, padding) 356 .SetColumnWeight(0, 3) 357 .SetColumnWeight(1, 2) 358 .SetColumnWeight(2, 1) 359 .View()); 360 iconHolder->SetExplicitAlignment( 361 BAlignment(B_ALIGN_CENTER, B_ALIGN_MIDDLE)); 362 363 // "Version Info" group 364 365 BBox* versionBox = new BBox("versionBox"); 366 versionBox->SetLabel(B_TRANSLATE("Version info")); 367 368 fMajorVersionControl = new BTextControl(B_TRANSLATE("Version:"), 369 NULL, NULL); 370 _MakeNumberTextControl(fMajorVersionControl); 371 372 fMiddleVersionControl = new BTextControl(".", NULL, NULL); 373 _MakeNumberTextControl(fMiddleVersionControl); 374 375 fMinorVersionControl = new BTextControl(".", NULL, NULL); 376 _MakeNumberTextControl(fMinorVersionControl); 377 378 fVarietyMenu = new BPopUpMenu("variety", true, true); 379 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Development"), NULL)); 380 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Alpha"), NULL)); 381 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Beta"), NULL)); 382 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Gamma"), NULL)); 383 item = new BMenuItem(B_TRANSLATE("Golden master"), NULL); 384 fVarietyMenu->AddItem(item); 385 item->SetMarked(true); 386 fVarietyMenu->AddItem(new BMenuItem(B_TRANSLATE("Final"), NULL)); 387 388 BMenuField* varietyField = new BMenuField("", fVarietyMenu); 389 fInternalVersionControl = new BTextControl("/", NULL, NULL); 390 fShortDescriptionControl = 391 new BTextControl(B_TRANSLATE("Short description:"), NULL, NULL); 392 393 // TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says? 394 version_info versionInfo; 395 fShortDescriptionControl->TextView()->SetMaxBytes( 396 sizeof(versionInfo.short_info)); 397 398 BStringView* longLabel = new BStringView(NULL, 399 B_TRANSLATE("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 / 2, padding / 2) 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 / 2, padding, padding) 424 .SetRowWeight(3, 3) 425 .View()); 426 427 // put it all together 428 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 429 .SetInsets(0, 0, 0, 0) 430 .Add(menuBar) 431 .AddGroup(B_VERTICAL, padding) 432 .SetInsets(padding, padding, padding, padding) 433 .Add(fSignatureControl) 434 .AddGroup(B_HORIZONTAL, padding) 435 .Add(flagsBox, 3) 436 .Add(iconBox, 1) 437 .End() 438 .Add(typeBox) 439 .Add(versionBox); 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 = B_TRANSLATE("%1 application type"); 463 title.ReplaceFirst("%1", name); 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(B_TRANSLATE("Save request"), 983 B_TRANSLATE("Do you want to save the changes?"), 984 B_TRANSLATE("Quit, don't save"), B_TRANSLATE("Cancel"), 985 B_TRANSLATE("Save"), B_WIDTH_AS_USUAL, B_WARNING_ALERT); 986 int32 choice = alert->Go(); 987 switch (choice) { 988 case 0: 989 break; 990 case 1: 991 return false; 992 case 2: 993 _Save(); 994 break; 995 } 996 } 997 998 be_app->PostMessage(kMsgTypeWindowClosed); 999 return true; 1000 } 1001 1002