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