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() 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, 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, 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) 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 .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