1 /* 2 * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "ApplicationTypeWindow.h" 8 #include "DropTargetListView.h" 9 #include "FileTypes.h" 10 #include "IconView.h" 11 #include "PreferredAppMenu.h" 12 #include "StringView.h" 13 #include "TypeListWindow.h" 14 15 #include <AppFileInfo.h> 16 #include <Application.h> 17 #include <Bitmap.h> 18 #include <Box.h> 19 #include <Button.h> 20 #include <CheckBox.h> 21 #include <File.h> 22 #include <ListView.h> 23 #include <MenuBar.h> 24 #include <MenuField.h> 25 #include <MenuItem.h> 26 #include <Mime.h> 27 #include <NodeInfo.h> 28 #include <PopUpMenu.h> 29 #include <RadioButton.h> 30 #include <Roster.h> 31 #include <ScrollView.h> 32 #include <TextControl.h> 33 34 #include <ctype.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 40 const uint32 kMsgSave = 'save'; 41 const uint32 kMsgFlagsChanged = 'flgc'; 42 43 const uint32 kMsgTypeSelected = 'tpsl'; 44 const uint32 kMsgAddType = 'adtp'; 45 const uint32 kMsgTypeAdded = 'tpad'; 46 const uint32 kMsgRemoveType = 'rmtp'; 47 48 49 class SupportedTypeItem : public BStringItem { 50 public: 51 SupportedTypeItem(const char* type); 52 ~SupportedTypeItem(); 53 54 const char* Type() const { return fType.String(); } 55 ::Icon& Icon() { return fIcon; } 56 void SetIcon(::Icon* icon); 57 void SetIcon(entry_ref& ref, const char* type); 58 59 static int Compare(const void* _a, const void* _b); 60 61 private: 62 BString fType; 63 ::Icon fIcon; 64 }; 65 66 class SupportedTypeListView : public DropTargetListView { 67 public: 68 SupportedTypeListView(BRect frame, const char* name, 69 list_view_type type = B_SINGLE_SELECTION_LIST, 70 uint32 resizeMask = B_FOLLOW_LEFT | B_FOLLOW_TOP, 71 uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE); 72 virtual ~SupportedTypeListView(); 73 74 virtual void MessageReceived(BMessage* message); 75 virtual bool AcceptsDrag(const BMessage* message); 76 }; 77 78 79 SupportedTypeItem::SupportedTypeItem(const char* type) 80 : BStringItem(type), 81 fType(type) 82 { 83 BMimeType mimeType(type); 84 85 char description[B_MIME_TYPE_LENGTH]; 86 if (mimeType.GetShortDescription(description) == B_OK && description[0]) 87 SetText(description); 88 } 89 90 91 SupportedTypeItem::~SupportedTypeItem() 92 { 93 } 94 95 96 void 97 SupportedTypeItem::SetIcon(::Icon* icon) 98 { 99 if (icon != NULL) 100 fIcon = *icon; 101 else 102 fIcon.Unset(); 103 } 104 105 106 void 107 SupportedTypeItem::SetIcon(entry_ref& ref, const char* type) 108 { 109 fIcon.SetTo(ref, type); 110 } 111 112 113 /*static*/ 114 int 115 SupportedTypeItem::Compare(const void* _a, const void* _b) 116 { 117 const SupportedTypeItem* a = *(const SupportedTypeItem**)_a; 118 const SupportedTypeItem* b = *(const SupportedTypeItem**)_b; 119 120 int compare = strcasecmp(a->Text(), b->Text()); 121 if (compare != 0) 122 return compare; 123 124 return strcasecmp(a->Type(), b->Type()); 125 } 126 127 128 // #pragma mark - 129 130 131 SupportedTypeListView::SupportedTypeListView(BRect frame, const char* name, 132 list_view_type type, uint32 resizeMask, uint32 flags) 133 : DropTargetListView(frame, name, type, resizeMask, flags) 134 { 135 } 136 137 138 SupportedTypeListView::~SupportedTypeListView() 139 { 140 } 141 142 143 void 144 SupportedTypeListView::MessageReceived(BMessage* message) 145 { 146 if (message->WasDropped() && AcceptsDrag(message)) { 147 // Add unique types 148 entry_ref ref; 149 for (int32 index = 0; message->FindRef("refs", index++, &ref) == B_OK; ) { 150 BNode node(&ref); 151 BNodeInfo info(&node); 152 if (node.InitCheck() != B_OK || info.InitCheck() != B_OK) 153 continue; 154 155 // TODO: we could identify the file in case it doesn't have a type... 156 char type[B_MIME_TYPE_LENGTH]; 157 if (info.GetType(type) != B_OK) 158 continue; 159 160 // check if that type is already in our list 161 bool found = false; 162 for (int32 i = CountItems(); i-- > 0;) { 163 SupportedTypeItem* item = (SupportedTypeItem*)ItemAt(i); 164 if (!strcmp(item->Text(), type)) { 165 found = true; 166 break; 167 } 168 } 169 170 if (!found) { 171 // add type 172 AddItem(new SupportedTypeItem(type)); 173 } 174 } 175 176 SortItems(&SupportedTypeItem::Compare); 177 } else 178 DropTargetListView::MessageReceived(message); 179 } 180 181 182 bool 183 SupportedTypeListView::AcceptsDrag(const BMessage* message) 184 { 185 type_code type; 186 return message->GetInfo("refs", &type) == B_OK && type == B_REF_TYPE; 187 } 188 189 190 // #pragma mark - 191 192 193 ApplicationTypeWindow::ApplicationTypeWindow(BPoint position, const BEntry& entry) 194 : BWindow(BRect(0.0f, 0.0f, 250.0f, 340.0f).OffsetBySelf(position), 195 "Application Type", B_TITLED_WINDOW, 196 B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS) 197 { 198 // add the menu 199 200 BMenuBar* menuBar = new BMenuBar(BRect(0, 0, 0, 0), NULL); 201 AddChild(menuBar); 202 203 BMenu* menu = new BMenu("File"); 204 menu->AddItem(new BMenuItem("Save", new BMessage(kMsgSave), 'S', B_COMMAND_KEY)); 205 BMenuItem* item; 206 menu->AddItem(item = new BMenuItem("Save Into Resource File" B_UTF8_ELLIPSIS, 207 NULL)); 208 item->SetEnabled(false); 209 210 menu->AddSeparatorItem(); 211 menu->AddItem(new BMenuItem("Close", new BMessage(B_QUIT_REQUESTED), 212 'W', B_COMMAND_KEY)); 213 menuBar->AddItem(menu); 214 215 // Top view and signature 216 217 BRect rect = Bounds(); 218 rect.top = menuBar->Bounds().Height() + 1.0f; 219 BView* topView = new BView(rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW); 220 topView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 221 AddChild(topView); 222 223 rect = topView->Bounds().InsetByCopy(8.0f, 8.0f); 224 fSignatureControl = new BTextControl(rect, "signature", "Signature:", NULL, 225 NULL, B_FOLLOW_LEFT_RIGHT); 226 fSignatureControl->SetDivider(fSignatureControl->StringWidth( 227 fSignatureControl->Label()) + 4.0f); 228 float width, height; 229 fSignatureControl->GetPreferredSize(&width, &height); 230 fSignatureControl->ResizeTo(rect.Width(), height); 231 topView->AddChild(fSignatureControl); 232 233 // filter out invalid characters that can't be part of a MIME type name 234 BTextView* textView = fSignatureControl->TextView(); 235 textView->SetMaxBytes(B_MIME_TYPE_LENGTH); 236 const char* disallowedCharacters = "<>@,;:\"()[]?="; 237 for (int32 i = 0; disallowedCharacters[i]; i++) { 238 textView->DisallowChar(disallowedCharacters[i]); 239 } 240 241 // "Application Flags" group 242 243 BFont font(be_bold_font); 244 font_height fontHeight; 245 font.GetHeight(&fontHeight); 246 247 width = font.StringWidth("Icon") + 16.0f; 248 if (width < B_LARGE_ICON + 16.0f) 249 width = B_LARGE_ICON + 16.0f; 250 251 rect.top = fSignatureControl->Frame().bottom + 4.0f; 252 rect.bottom = rect.top + 100.0f; 253 rect.right -= width + 8.0f; 254 BBox* box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); 255 topView->AddChild(box); 256 257 fFlagsCheckBox = new BCheckBox(rect, "flags", "Application Flags", 258 new BMessage(kMsgFlagsChanged)); 259 fFlagsCheckBox->SetValue(B_CONTROL_ON); 260 fFlagsCheckBox->ResizeToPreferred(); 261 box->SetLabel(fFlagsCheckBox); 262 263 rect.top = fFlagsCheckBox->Bounds().Height() + 4.0f; 264 fSingleLaunchButton = new BRadioButton(rect, "single", "Single Launch", NULL); 265 fSingleLaunchButton->ResizeToPreferred(); 266 box->AddChild(fSingleLaunchButton); 267 268 rect.OffsetBy(0.0f, fSingleLaunchButton->Bounds().Height() + 0.0f); 269 fMultipleLaunchButton = new BRadioButton(rect, "multiple", "Multiple Launch", NULL); 270 fMultipleLaunchButton->ResizeToPreferred(); 271 box->AddChild(fMultipleLaunchButton); 272 273 rect.OffsetBy(0.0f, fSingleLaunchButton->Bounds().Height() + 0.0f); 274 fExclusiveLaunchButton = new BRadioButton(rect, "exclusive", "Exclusive Launch", NULL); 275 fExclusiveLaunchButton->ResizeToPreferred(); 276 box->AddChild(fExclusiveLaunchButton); 277 278 rect.top = fSingleLaunchButton->Frame().top; 279 rect.left = fExclusiveLaunchButton->Frame().right + 4.0f; 280 fArgsOnlyCheckBox = new BCheckBox(rect, "args only", "Args Only", NULL); 281 fArgsOnlyCheckBox->ResizeToPreferred(); 282 box->AddChild(fArgsOnlyCheckBox); 283 284 rect.top += fArgsOnlyCheckBox->Bounds().Height(); 285 fBackgroundAppCheckBox = new BCheckBox(rect, "background", "Background App", NULL); 286 fBackgroundAppCheckBox->ResizeToPreferred(); 287 box->AddChild(fBackgroundAppCheckBox); 288 289 box->ResizeTo(box->Bounds().Width(), fExclusiveLaunchButton->Frame().bottom + 8.0f); 290 291 // "Icon" group 292 293 rect = box->Frame(); 294 #ifdef __HAIKU__ 295 rect.top += box->TopBorderOffset(); 296 #endif 297 rect.left = rect.right + 8.0f; 298 rect.right += width + 8.0f; 299 float iconBoxWidth = rect.Width(); 300 box = new BBox(rect, NULL, B_FOLLOW_RIGHT | B_FOLLOW_TOP); 301 box->SetLabel("Icon"); 302 #ifdef __HAIKU__ 303 box->MoveBy(0.0f, -box->TopBorderOffset()); 304 box->ResizeBy(0.0f, box->TopBorderOffset()); 305 #endif 306 topView->AddChild(box); 307 308 rect = BRect(8.0f, 0.0f, 7.0f + B_LARGE_ICON, B_LARGE_ICON - 1.0f); 309 #ifdef __HAIKU__ 310 rect.OffsetBy(0.0f, (box->Bounds().Height() + box->TopBorderOffset() 311 - rect.Height()) / 2.0f); 312 #else 313 rect.OffsetBy(0.0f, (box->Bounds().Height() - rect.Height()) / 2.0f); 314 #endif 315 if (rect.top < fontHeight.ascent + fontHeight.descent + 4.0f) 316 rect.top = fontHeight.ascent + fontHeight.descent + 4.0f; 317 fIconView = new IconView(rect, "icon"); 318 box->AddChild(fIconView); 319 320 // "Supported Types" group 321 322 rect.top = box->Frame().bottom + 8.0f; 323 rect.bottom = rect.top + box->Bounds().Height(); 324 rect.left = 8.0f; 325 rect.right = Bounds().Width() - 8.0f; 326 BBox* typeBox = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); 327 typeBox->SetLabel("Supported Types"); 328 topView->AddChild(typeBox); 329 330 rect = typeBox->Bounds().InsetByCopy(8.0f, 6.0f); 331 rect.top += ceilf(fontHeight.ascent); 332 fAddTypeButton = new BButton(rect, "add type", "Add" B_UTF8_ELLIPSIS, 333 new BMessage(kMsgAddType), B_FOLLOW_RIGHT); 334 fAddTypeButton->ResizeToPreferred(); 335 fAddTypeButton->MoveBy(rect.right - fAddTypeButton->Bounds().Width() 336 - B_LARGE_ICON - 16.0f, 0.0f); 337 typeBox->AddChild(fAddTypeButton); 338 339 rect = fAddTypeButton->Frame(); 340 rect.OffsetBy(0, rect.Height() + 4.0f); 341 fRemoveTypeButton = new BButton(rect, "remove type", "Remove", 342 new BMessage(kMsgRemoveType), B_FOLLOW_RIGHT); 343 typeBox->AddChild(fRemoveTypeButton); 344 345 rect.right = rect.left - 10.0f - B_V_SCROLL_BAR_WIDTH; 346 rect.left = 10.0f; 347 rect.top = 8.0f + ceilf(fontHeight.ascent); 348 rect.bottom -= 2.0f; 349 // take scrollview border into account 350 fTypeListView = new SupportedTypeListView(rect, "type listview", 351 B_SINGLE_SELECTION_LIST, B_FOLLOW_ALL); 352 fTypeListView->SetSelectionMessage(new BMessage(kMsgTypeSelected)); 353 354 BScrollView* scrollView = new BScrollView("type scrollview", fTypeListView, 355 B_FOLLOW_ALL, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 356 357 typeBox->ResizeTo(typeBox->Bounds().Width(), fRemoveTypeButton->Frame().bottom + 8.0f); 358 typeBox->AddChild(scrollView); 359 360 rect.left = fRemoveTypeButton->Frame().right + 8.0f; 361 #ifdef __HAIKU__ 362 rect.top = (box->Bounds().Height() + box->TopBorderOffset() - B_LARGE_ICON) / 2.0f; 363 #else 364 rect.top = (box->Bounds().Height() - B_LARGE_ICON) / 2.0f; 365 #endif 366 rect.right = rect.left + B_LARGE_ICON - 1.0f; 367 rect.bottom = rect.top + B_LARGE_ICON - 1.0f; 368 fTypeIconView = new IconView(rect, "type icon", B_FOLLOW_RIGHT | B_FOLLOW_TOP); 369 typeBox->AddChild(fTypeIconView); 370 371 // "Version Info" group 372 373 rect.top = typeBox->Frame().bottom + 8.0f; 374 rect.bottom = rect.top + typeBox->Bounds().Height(); 375 rect.left = 8.0f; 376 rect.right = Bounds().Width() - 8.0f; 377 box = new BBox(rect, NULL, B_FOLLOW_LEFT_RIGHT); 378 // the resizing mode will later also be set to B_FOLLOW_BOTTOM 379 box->SetLabel("Version Info"); 380 topView->AddChild(box); 381 382 BMenuField* menuField; 383 #if 0 384 BPopUpMenu *popUpMenu = new BPopUpMenu("version info", true, true); 385 item = new BMenuItem("Version Info", NULL); 386 item->SetMarked(true); 387 popUpMenu->AddItem(item); 388 item = new BMenuItem("System Version Info", NULL); 389 popUpMenu->AddItem(item); 390 391 menuField = new BMenuField(BRect(0, 0, 100, 15), 392 "version kind", NULL, popUpMenu, true); 393 menuField->ResizeToPreferred(); 394 box->SetLabel(menuField); 395 #endif 396 397 rect.top = 4.0f + ceilf(fontHeight.ascent + fontHeight.descent); 398 rect.bottom = rect.top + height; 399 fMajorVersionControl = new BTextControl(rect, "major", "Version:", NULL, 400 NULL); 401 fMajorVersionControl->SetDivider(fMajorVersionControl->StringWidth( 402 fMajorVersionControl->Label()) + 4.0f); 403 fMajorVersionControl->GetPreferredSize(&width, &height); 404 width = 12.0f + fMajorVersionControl->StringWidth("99"); 405 fMajorVersionControl->ResizeTo(fMajorVersionControl->Divider() + width, height); 406 _MakeNumberTextControl(fMajorVersionControl); 407 box->AddChild(fMajorVersionControl); 408 409 rect.left = fMajorVersionControl->Frame().right + 1.0f; 410 fMiddleVersionControl = new BTextControl(rect, "middle", ".", NULL, 411 NULL); 412 fMiddleVersionControl->SetDivider(fMiddleVersionControl->StringWidth( 413 fMiddleVersionControl->Label()) + 4.0f); 414 fMiddleVersionControl->ResizeTo(fMiddleVersionControl->Divider() + width, height); 415 _MakeNumberTextControl(fMiddleVersionControl); 416 box->AddChild(fMiddleVersionControl); 417 418 rect.left = fMiddleVersionControl->Frame().right + 1.0f; 419 fMinorVersionControl = new BTextControl(rect, "middle", ".", NULL, 420 NULL); 421 fMinorVersionControl->SetDivider(fMinorVersionControl->StringWidth( 422 fMinorVersionControl->Label()) + 4.0f); 423 fMinorVersionControl->ResizeTo(fMinorVersionControl->Divider() + width, height); 424 _MakeNumberTextControl(fMinorVersionControl); 425 box->AddChild(fMinorVersionControl); 426 427 fVarietyMenu = new BPopUpMenu("variety", true, true); 428 fVarietyMenu->AddItem(new BMenuItem("Development", NULL)); 429 fVarietyMenu->AddItem(new BMenuItem("Alpha", NULL)); 430 fVarietyMenu->AddItem(new BMenuItem("Beta", NULL)); 431 fVarietyMenu->AddItem(new BMenuItem("Gamma", NULL)); 432 fVarietyMenu->AddItem(item = new BMenuItem("Golden Master", NULL)); 433 item->SetMarked(true); 434 fVarietyMenu->AddItem(new BMenuItem("Final", NULL)); 435 436 rect.top--; 437 // BMenuField oddity 438 rect.left = fMinorVersionControl->Frame().right + 6.0f; 439 menuField = new BMenuField(rect, 440 "variety", NULL, fVarietyMenu, true); 441 menuField->ResizeToPreferred(); 442 box->AddChild(menuField); 443 444 rect.top++; 445 rect.left = menuField->Frame().right; 446 rect.right = rect.left + 30.0f; 447 fInternalVersionControl = new BTextControl(rect, "internal", "/", NULL, 448 NULL); 449 fInternalVersionControl->SetDivider(fInternalVersionControl->StringWidth( 450 fInternalVersionControl->Label()) + 4.0f); 451 fInternalVersionControl->ResizeTo(fInternalVersionControl->Divider() + width, height); 452 box->AddChild(fInternalVersionControl); 453 454 rect = box->Bounds().InsetByCopy(8.0f, 0.0f); 455 rect.top = fInternalVersionControl->Frame().bottom + 8.0f; 456 fShortDescriptionControl = new BTextControl(rect, "short desc", "Short Description:", 457 NULL, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); 458 float labelWidth = fShortDescriptionControl->StringWidth( 459 fShortDescriptionControl->Label()) + 4.0f; 460 fShortDescriptionControl->SetDivider(labelWidth); 461 fShortDescriptionControl->GetPreferredSize(&width, &height); 462 fShortDescriptionControl->ResizeTo(rect.Width(), height); 463 464 // TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says? 465 version_info versionInfo; 466 fShortDescriptionControl->TextView()->SetMaxBytes(sizeof(versionInfo.short_info)); 467 box->AddChild(fShortDescriptionControl); 468 469 rect.OffsetBy(0.0f, fShortDescriptionControl->Bounds().Height() + 5.0f); 470 rect.right = rect.left + labelWidth; 471 StringView* label = new StringView(rect, NULL, "Long Description:", NULL); 472 label->SetDivider(labelWidth); 473 box->AddChild(label); 474 475 rect.left = rect.right + 3.0f; 476 rect.top += 1.0f; 477 rect.right = box->Bounds().Width() - 10.0f - B_V_SCROLL_BAR_WIDTH; 478 rect.bottom = rect.top + fShortDescriptionControl->Bounds().Height() * 3.0f - 1.0f; 479 fLongDescriptionView = new BTextView(rect, "long desc", 480 rect.OffsetToCopy(B_ORIGIN), B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS); 481 fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info)); 482 483 scrollView = new BScrollView("desc scrollview", fLongDescriptionView, 484 B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 485 box->ResizeTo(box->Bounds().Width(), scrollView->Frame().bottom + 8.0f); 486 box->AddChild(scrollView); 487 488 // Adjust window size and limits 489 490 width = fInternalVersionControl->Frame().right + 16.0f; 491 float minWidth = fBackgroundAppCheckBox->Frame().right + iconBoxWidth + 32.0f; 492 if (width > minWidth) 493 minWidth = width; 494 495 ResizeTo(Bounds().Width() > minWidth ? Bounds().Width() : minWidth, 496 box->Frame().bottom + topView->Frame().top + 8.0f); 497 SetSizeLimits(minWidth, 32767.0f, Bounds().Height(), 32767.0f); 498 typeBox->SetResizingMode(B_FOLLOW_ALL); 499 box->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM); 500 501 fSignatureControl->MakeFocus(true); 502 503 BMimeType::StartWatching(this); 504 _SetTo(entry); 505 } 506 507 508 ApplicationTypeWindow::~ApplicationTypeWindow() 509 { 510 BMimeType::StopWatching(this); 511 } 512 513 514 BString 515 ApplicationTypeWindow::_Title(const BEntry& entry) 516 { 517 char name[B_FILE_NAME_LENGTH]; 518 if (entry.GetName(name) != B_OK) 519 strcpy(name, "\"-\""); 520 521 BString title(name); 522 title.Append(" Application Type"); 523 return title; 524 } 525 526 527 void 528 ApplicationTypeWindow::_SetTo(const BEntry& entry) 529 { 530 SetTitle(_Title(entry).String()); 531 fEntry = entry; 532 533 // Retrieve Info 534 535 BFile file(&entry, B_READ_ONLY); 536 if (file.InitCheck() != B_OK) 537 return; 538 539 BAppFileInfo info(&file); 540 if (info.InitCheck() != B_OK) 541 return; 542 543 char signature[B_MIME_TYPE_LENGTH]; 544 if (info.GetSignature(signature) != B_OK) 545 signature[0] = '\0'; 546 547 bool gotFlags = false; 548 uint32 flags; 549 if (info.GetAppFlags(&flags) == B_OK) 550 gotFlags = true; 551 else 552 flags = B_MULTIPLE_LAUNCH; 553 554 BMessage supportedTypes; 555 info.GetSupportedTypes(&supportedTypes); 556 557 version_info versionInfo; 558 if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK) 559 memset(&versionInfo, 0, sizeof(version_info)); 560 561 // Set Controls 562 563 fSignatureControl->SetText(signature); 564 565 // flags 566 567 switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) { 568 case B_SINGLE_LAUNCH: 569 fSingleLaunchButton->SetValue(B_CONTROL_ON); 570 break; 571 572 case B_EXCLUSIVE_LAUNCH: 573 fExclusiveLaunchButton->SetValue(B_CONTROL_ON); 574 break; 575 576 case B_MULTIPLE_LAUNCH: 577 default: 578 fMultipleLaunchButton->SetValue(B_CONTROL_ON); 579 break; 580 } 581 582 fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0); 583 fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0); 584 fFlagsCheckBox->SetValue(gotFlags); 585 586 _UpdateAppFlagsEnabled(); 587 588 // icon 589 590 entry_ref ref; 591 if (entry.GetRef(&ref) == B_OK) 592 fIcon.SetTo(ref); 593 else 594 fIcon.Unset(); 595 596 fIconView->SetTo(&fIcon); 597 598 // supported types 599 600 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 601 BListItem* item = fTypeListView->RemoveItem(i); 602 delete item; 603 } 604 605 const char* type; 606 for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) { 607 SupportedTypeItem* item = new SupportedTypeItem(type); 608 609 entry_ref ref; 610 if (fEntry.GetRef(&ref) == B_OK) 611 item->SetIcon(ref, type); 612 613 fTypeListView->AddItem(item); 614 } 615 fTypeListView->SortItems(&SupportedTypeItem::Compare); 616 fTypeIconView->SetTo(NULL); 617 fTypeIconView->SetEnabled(false); 618 fRemoveTypeButton->SetEnabled(false); 619 620 // version info 621 622 char text[256]; 623 snprintf(text, sizeof(text), "%ld", versionInfo.major); 624 fMajorVersionControl->SetText(text); 625 snprintf(text, sizeof(text), "%ld", versionInfo.middle); 626 fMiddleVersionControl->SetText(text); 627 snprintf(text, sizeof(text), "%ld", versionInfo.minor); 628 fMinorVersionControl->SetText(text); 629 630 if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems()) 631 versionInfo.variety = 0; 632 BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety); 633 if (item != NULL) 634 item->SetMarked(true); 635 636 snprintf(text, sizeof(text), "%ld", versionInfo.internal); 637 fInternalVersionControl->SetText(text); 638 639 fShortDescriptionControl->SetText(versionInfo.short_info); 640 fLongDescriptionView->SetText(versionInfo.long_info); 641 } 642 643 644 void 645 ApplicationTypeWindow::_UpdateAppFlagsEnabled() 646 { 647 bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF; 648 649 fSingleLaunchButton->SetEnabled(enabled); 650 fMultipleLaunchButton->SetEnabled(enabled); 651 fExclusiveLaunchButton->SetEnabled(enabled); 652 fArgsOnlyCheckBox->SetEnabled(enabled); 653 fBackgroundAppCheckBox->SetEnabled(enabled); 654 } 655 656 657 void 658 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control) 659 { 660 // filter out invalid characters that can't be part of a MIME type name 661 BTextView* textView = control->TextView(); 662 textView->SetMaxBytes(10); 663 664 for (int32 i = 0; i < 256; i++) { 665 if (!isdigit(i)) 666 textView->DisallowChar(i); 667 } 668 } 669 670 671 void 672 ApplicationTypeWindow::_Save() 673 { 674 BFile file; 675 status_t status = file.SetTo(&fEntry, B_READ_WRITE); 676 if (status != B_OK) 677 return; 678 679 BAppFileInfo info(&file); 680 status = info.InitCheck(); 681 if (status != B_OK) 682 return; 683 684 // Retrieve Info 685 686 uint32 flags = 0; 687 if (fFlagsCheckBox->Value() != B_CONTROL_OFF) { 688 if (fSingleLaunchButton->Value() != B_CONTROL_OFF) 689 flags |= B_SINGLE_LAUNCH; 690 else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF) 691 flags |= B_MULTIPLE_LAUNCH; 692 else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF) 693 flags |= B_EXCLUSIVE_LAUNCH; 694 695 if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF) 696 flags |= B_ARGV_ONLY; 697 if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF) 698 flags |= B_BACKGROUND_APP; 699 } 700 701 BMessage supportedTypes; 702 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 703 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 704 fTypeListView->ItemAt(i)); 705 706 supportedTypes.AddString("types", item->Type()); 707 } 708 709 version_info versionInfo; 710 versionInfo.major = atol(fMajorVersionControl->Text()); 711 versionInfo.middle = atol(fMiddleVersionControl->Text()); 712 versionInfo.minor = atol(fMinorVersionControl->Text()); 713 versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked()); 714 versionInfo.internal = atol(fInternalVersionControl->Text()); 715 strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(), 716 sizeof(versionInfo.short_info)); 717 strlcpy(versionInfo.long_info, fLongDescriptionView->Text(), 718 sizeof(versionInfo.long_info)); 719 720 // Save 721 722 status = info.SetSignature(fSignatureControl->Text()); 723 if (status == B_OK) 724 status = info.SetAppFlags(flags); 725 if (status == B_OK) 726 status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND); 727 if (status == B_OK) 728 fIcon.CopyTo(info, NULL, true); 729 730 // supported types and their icons 731 if (status == B_OK) 732 status = info.SetSupportedTypes(&supportedTypes); 733 734 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 735 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 736 fTypeListView->ItemAt(i)); 737 738 item->Icon().CopyTo(info, item->Type(), true); 739 } 740 } 741 742 743 void 744 ApplicationTypeWindow::FrameResized(float width, float height) 745 { 746 // This works around a flaw of BTextView 747 fLongDescriptionView->SetTextRect(fLongDescriptionView->Bounds()); 748 } 749 750 751 void 752 ApplicationTypeWindow::MessageReceived(BMessage* message) 753 { 754 switch (message->what) { 755 case kMsgFlagsChanged: 756 _UpdateAppFlagsEnabled(); 757 break; 758 759 case kMsgSave: 760 _Save(); 761 break; 762 763 case kMsgTypeSelected: 764 { 765 int32 index; 766 if (message->FindInt32("index", &index) == B_OK) { 767 SupportedTypeItem* item = (SupportedTypeItem*)fTypeListView->ItemAt(index); 768 769 fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL); 770 fTypeIconView->SetEnabled(item != NULL); 771 fRemoveTypeButton->SetEnabled(item != NULL); 772 } 773 break; 774 } 775 776 case kMsgAddType: 777 { 778 BWindow* window = new TypeListWindow(NULL, 779 kMsgTypeAdded, this); 780 window->Show(); 781 break; 782 } 783 784 case kMsgTypeAdded: 785 { 786 const char* type; 787 if (message->FindString("type", &type) != B_OK) 788 break; 789 790 // check if this type already exists 791 792 SupportedTypeItem* newItem = new SupportedTypeItem(type); 793 int32 insertAt = 0; 794 795 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 796 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 797 fTypeListView->ItemAt(i)); 798 if (item == NULL) 799 continue; 800 801 int compare = strcasecmp(item->Type(), type); 802 if (!compare) { 803 // type does already exist, select it and bail out 804 delete newItem; 805 newItem = NULL; 806 fTypeListView->Select(i); 807 break; 808 } 809 if (compare < 0) 810 insertAt = i + 1; 811 } 812 813 if (newItem == NULL) 814 break; 815 816 fTypeListView->AddItem(newItem, insertAt); 817 fTypeListView->Select(insertAt); 818 break; 819 } 820 821 case kMsgRemoveType: 822 { 823 int32 index = fTypeListView->CurrentSelection(); 824 if (index < 0) 825 break; 826 827 delete fTypeListView->RemoveItem(index); 828 fTypeIconView->SetTo(NULL); 829 fTypeIconView->SetEnabled(false); 830 fRemoveTypeButton->SetEnabled(false); 831 break; 832 } 833 834 case B_SIMPLE_DATA: 835 { 836 entry_ref ref; 837 if (message->FindRef("refs", &ref) != B_OK) 838 break; 839 840 // TODO: add to supported types 841 break; 842 } 843 844 case B_META_MIME_CHANGED: 845 const char* type; 846 int32 which; 847 if (message->FindString("be:type", &type) != B_OK 848 || message->FindInt32("be:which", &which) != B_OK) 849 break; 850 851 // TODO: update supported types names 852 // if (which == B_MIME_TYPE_DELETED) 853 break; 854 855 default: 856 BWindow::MessageReceived(message); 857 } 858 } 859 860 861 bool 862 ApplicationTypeWindow::QuitRequested() 863 { 864 be_app->PostMessage(kMsgTypeWindowClosed); 865 return true; 866 } 867 868