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 fMajorVersionControl = new BTextControl(rect, "major", "Version:", NULL, 399 NULL); 400 fMajorVersionControl->SetDivider(fMajorVersionControl->StringWidth( 401 fMajorVersionControl->Label()) + 4.0f); 402 fMajorVersionControl->GetPreferredSize(&width, &height); 403 width = 12.0f + fMajorVersionControl->StringWidth("99"); 404 fMajorVersionControl->ResizeTo(fMajorVersionControl->Divider() + width, height); 405 _MakeNumberTextControl(fMajorVersionControl); 406 box->AddChild(fMajorVersionControl); 407 408 rect.left = fMajorVersionControl->Frame().right + 1.0f; 409 fMiddleVersionControl = new BTextControl(rect, "middle", ".", NULL, 410 NULL); 411 fMiddleVersionControl->SetDivider(fMiddleVersionControl->StringWidth( 412 fMiddleVersionControl->Label()) + 4.0f); 413 fMiddleVersionControl->ResizeTo(fMiddleVersionControl->Divider() + width, height); 414 _MakeNumberTextControl(fMiddleVersionControl); 415 box->AddChild(fMiddleVersionControl); 416 417 rect.left = fMiddleVersionControl->Frame().right + 1.0f; 418 fMinorVersionControl = new BTextControl(rect, "middle", ".", NULL, 419 NULL); 420 fMinorVersionControl->SetDivider(fMinorVersionControl->StringWidth( 421 fMinorVersionControl->Label()) + 4.0f); 422 fMinorVersionControl->ResizeTo(fMinorVersionControl->Divider() + width, height); 423 _MakeNumberTextControl(fMinorVersionControl); 424 box->AddChild(fMinorVersionControl); 425 426 fVarietyMenu = new BPopUpMenu("variety", true, true); 427 fVarietyMenu->AddItem(new BMenuItem("Development", NULL)); 428 fVarietyMenu->AddItem(new BMenuItem("Alpha", NULL)); 429 fVarietyMenu->AddItem(new BMenuItem("Beta", NULL)); 430 fVarietyMenu->AddItem(new BMenuItem("Gamma", NULL)); 431 fVarietyMenu->AddItem(item = new BMenuItem("Golden Master", NULL)); 432 item->SetMarked(true); 433 fVarietyMenu->AddItem(new BMenuItem("Final", NULL)); 434 435 rect.top--; 436 // BMenuField oddity 437 rect.left = fMinorVersionControl->Frame().right + 6.0f; 438 menuField = new BMenuField(rect, 439 "variety", NULL, fVarietyMenu, true); 440 menuField->ResizeToPreferred(); 441 box->AddChild(menuField); 442 443 rect.top++; 444 rect.left = menuField->Frame().right; 445 rect.right = rect.left + 30.0f; 446 fInternalVersionControl = new BTextControl(rect, "internal", "/", NULL, 447 NULL); 448 fInternalVersionControl->SetDivider(fInternalVersionControl->StringWidth( 449 fInternalVersionControl->Label()) + 4.0f); 450 fInternalVersionControl->ResizeTo(fInternalVersionControl->Divider() + width, height); 451 box->AddChild(fInternalVersionControl); 452 453 rect = box->Bounds().InsetByCopy(8.0f, 0.0f); 454 rect.top = fInternalVersionControl->Frame().bottom + 8.0f; 455 fShortDescriptionControl = new BTextControl(rect, "short desc", "Short Description:", 456 NULL, NULL, B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP); 457 float labelWidth = fShortDescriptionControl->StringWidth( 458 fShortDescriptionControl->Label()) + 4.0f; 459 fShortDescriptionControl->SetDivider(labelWidth); 460 fShortDescriptionControl->GetPreferredSize(&width, &height); 461 fShortDescriptionControl->ResizeTo(rect.Width(), height); 462 463 // TODO: workaround for a GCC 4.1.0 bug? Or is that really what the standard says? 464 version_info versionInfo; 465 fShortDescriptionControl->TextView()->SetMaxBytes(sizeof(versionInfo.short_info)); 466 box->AddChild(fShortDescriptionControl); 467 468 rect.OffsetBy(0.0f, fShortDescriptionControl->Bounds().Height() + 5.0f); 469 rect.right = rect.left + labelWidth; 470 StringView* label = new StringView(rect, NULL, "Long Description:", NULL); 471 label->SetDivider(labelWidth); 472 box->AddChild(label); 473 474 rect.left = rect.right + 3.0f; 475 rect.top += 1.0f; 476 rect.right = box->Bounds().Width() - 10.0f - B_V_SCROLL_BAR_WIDTH; 477 rect.bottom = rect.top + fShortDescriptionControl->Bounds().Height() * 3.0f - 1.0f; 478 fLongDescriptionView = new BTextView(rect, "long desc", 479 rect.OffsetToCopy(B_ORIGIN), B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS); 480 fLongDescriptionView->SetMaxBytes(sizeof(versionInfo.long_info)); 481 482 scrollView = new BScrollView("desc scrollview", fLongDescriptionView, 483 B_FOLLOW_LEFT_RIGHT | B_FOLLOW_TOP, B_FRAME_EVENTS | B_WILL_DRAW, false, true); 484 box->ResizeTo(box->Bounds().Width(), scrollView->Frame().bottom + 8.0f); 485 box->AddChild(scrollView); 486 487 // Adjust window size and limits 488 489 width = fInternalVersionControl->Frame().right + 16.0f; 490 float minWidth = fBackgroundAppCheckBox->Frame().right + iconBoxWidth + 32.0f; 491 if (width > minWidth) 492 minWidth = width; 493 494 ResizeTo(Bounds().Width() > minWidth ? Bounds().Width() : minWidth, 495 box->Frame().bottom + topView->Frame().top + 8.0f); 496 SetSizeLimits(minWidth, 32767.0f, Bounds().Height(), 32767.0f); 497 typeBox->SetResizingMode(B_FOLLOW_ALL); 498 box->SetResizingMode(B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM); 499 500 fSignatureControl->MakeFocus(true); 501 502 BMimeType::StartWatching(this); 503 _SetTo(entry); 504 } 505 506 507 ApplicationTypeWindow::~ApplicationTypeWindow() 508 { 509 BMimeType::StopWatching(this); 510 } 511 512 513 BString 514 ApplicationTypeWindow::_Title(const BEntry& entry) 515 { 516 char name[B_FILE_NAME_LENGTH]; 517 if (entry.GetName(name) != B_OK) 518 strcpy(name, "\"-\""); 519 520 BString title(name); 521 title.Append(" Application Type"); 522 return title; 523 } 524 525 526 void 527 ApplicationTypeWindow::_SetTo(const BEntry& entry) 528 { 529 SetTitle(_Title(entry).String()); 530 fEntry = entry; 531 532 // Retrieve Info 533 534 BFile file(&entry, B_READ_ONLY); 535 if (file.InitCheck() != B_OK) 536 return; 537 538 BAppFileInfo info(&file); 539 if (info.InitCheck() != B_OK) 540 return; 541 542 char signature[B_MIME_TYPE_LENGTH]; 543 if (info.GetSignature(signature) != B_OK) 544 signature[0] = '\0'; 545 546 bool gotFlags = false; 547 uint32 flags; 548 if (info.GetAppFlags(&flags) == B_OK) 549 gotFlags = true; 550 else 551 flags = B_MULTIPLE_LAUNCH; 552 553 BMessage supportedTypes; 554 info.GetSupportedTypes(&supportedTypes); 555 556 version_info versionInfo; 557 if (info.GetVersionInfo(&versionInfo, B_APP_VERSION_KIND) != B_OK) 558 memset(&versionInfo, 0, sizeof(version_info)); 559 560 // Set Controls 561 562 fSignatureControl->SetText(signature); 563 564 // flags 565 566 switch (flags & (B_SINGLE_LAUNCH | B_MULTIPLE_LAUNCH | B_EXCLUSIVE_LAUNCH)) { 567 case B_SINGLE_LAUNCH: 568 fSingleLaunchButton->SetValue(B_CONTROL_ON); 569 break; 570 571 case B_EXCLUSIVE_LAUNCH: 572 fExclusiveLaunchButton->SetValue(B_CONTROL_ON); 573 break; 574 575 case B_MULTIPLE_LAUNCH: 576 default: 577 fMultipleLaunchButton->SetValue(B_CONTROL_ON); 578 break; 579 } 580 581 fArgsOnlyCheckBox->SetValue((flags & B_ARGV_ONLY) != 0); 582 fBackgroundAppCheckBox->SetValue((flags & B_BACKGROUND_APP) != 0); 583 fFlagsCheckBox->SetValue(gotFlags); 584 585 _UpdateAppFlagsEnabled(); 586 587 // icon 588 589 entry_ref ref; 590 if (entry.GetRef(&ref) == B_OK) 591 fIcon.SetTo(ref); 592 else 593 fIcon.Unset(); 594 595 fIconView->SetTo(&fIcon); 596 597 // supported types 598 599 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 600 BListItem* item = fTypeListView->RemoveItem(i); 601 delete item; 602 } 603 604 const char* type; 605 for (int32 i = 0; supportedTypes.FindString("types", i, &type) == B_OK; i++) { 606 SupportedTypeItem* item = new SupportedTypeItem(type); 607 608 entry_ref ref; 609 if (fEntry.GetRef(&ref) == B_OK) 610 item->SetIcon(ref, type); 611 612 fTypeListView->AddItem(item); 613 } 614 fTypeListView->SortItems(&SupportedTypeItem::Compare); 615 fTypeIconView->SetTo(NULL); 616 fTypeIconView->SetEnabled(false); 617 fRemoveTypeButton->SetEnabled(false); 618 619 // version info 620 621 char text[256]; 622 snprintf(text, sizeof(text), "%ld", versionInfo.major); 623 fMajorVersionControl->SetText(text); 624 snprintf(text, sizeof(text), "%ld", versionInfo.middle); 625 fMiddleVersionControl->SetText(text); 626 snprintf(text, sizeof(text), "%ld", versionInfo.minor); 627 fMinorVersionControl->SetText(text); 628 629 if (versionInfo.variety >= (uint32)fVarietyMenu->CountItems()) 630 versionInfo.variety = 0; 631 BMenuItem* item = fVarietyMenu->ItemAt(versionInfo.variety); 632 if (item != NULL) 633 item->SetMarked(true); 634 635 snprintf(text, sizeof(text), "%ld", versionInfo.internal); 636 fInternalVersionControl->SetText(text); 637 638 fShortDescriptionControl->SetText(versionInfo.short_info); 639 fLongDescriptionView->SetText(versionInfo.long_info); 640 } 641 642 643 void 644 ApplicationTypeWindow::_UpdateAppFlagsEnabled() 645 { 646 bool enabled = fFlagsCheckBox->Value() != B_CONTROL_OFF; 647 648 fSingleLaunchButton->SetEnabled(enabled); 649 fMultipleLaunchButton->SetEnabled(enabled); 650 fExclusiveLaunchButton->SetEnabled(enabled); 651 fArgsOnlyCheckBox->SetEnabled(enabled); 652 fBackgroundAppCheckBox->SetEnabled(enabled); 653 } 654 655 656 void 657 ApplicationTypeWindow::_MakeNumberTextControl(BTextControl* control) 658 { 659 // filter out invalid characters that can't be part of a MIME type name 660 BTextView* textView = control->TextView(); 661 textView->SetMaxBytes(10); 662 663 for (int32 i = 0; i < 256; i++) { 664 if (!isdigit(i)) 665 textView->DisallowChar(i); 666 } 667 } 668 669 670 void 671 ApplicationTypeWindow::_Save() 672 { 673 BFile file; 674 status_t status = file.SetTo(&fEntry, B_READ_WRITE); 675 if (status != B_OK) 676 return; 677 678 BAppFileInfo info(&file); 679 status = info.InitCheck(); 680 if (status != B_OK) 681 return; 682 683 // Retrieve Info 684 685 uint32 flags = 0; 686 if (fFlagsCheckBox->Value() != B_CONTROL_OFF) { 687 if (fSingleLaunchButton->Value() != B_CONTROL_OFF) 688 flags |= B_SINGLE_LAUNCH; 689 else if (fMultipleLaunchButton->Value() != B_CONTROL_OFF) 690 flags |= B_MULTIPLE_LAUNCH; 691 else if (fExclusiveLaunchButton->Value() != B_CONTROL_OFF) 692 flags |= B_EXCLUSIVE_LAUNCH; 693 694 if (fArgsOnlyCheckBox->Value() != B_CONTROL_OFF) 695 flags |= B_ARGV_ONLY; 696 if (fBackgroundAppCheckBox->Value() != B_CONTROL_OFF) 697 flags |= B_BACKGROUND_APP; 698 } 699 700 BMessage supportedTypes; 701 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 702 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 703 fTypeListView->ItemAt(i)); 704 705 supportedTypes.AddString("types", item->Type()); 706 } 707 708 version_info versionInfo; 709 versionInfo.major = atol(fMajorVersionControl->Text()); 710 versionInfo.middle = atol(fMiddleVersionControl->Text()); 711 versionInfo.minor = atol(fMinorVersionControl->Text()); 712 versionInfo.variety = fVarietyMenu->IndexOf(fVarietyMenu->FindMarked()); 713 versionInfo.internal = atol(fInternalVersionControl->Text()); 714 strlcpy(versionInfo.short_info, fShortDescriptionControl->Text(), 715 sizeof(versionInfo.short_info)); 716 strlcpy(versionInfo.long_info, fLongDescriptionView->Text(), 717 sizeof(versionInfo.long_info)); 718 719 // Save 720 721 status = info.SetSignature(fSignatureControl->Text()); 722 if (status == B_OK) 723 status = info.SetAppFlags(flags); 724 if (status == B_OK) 725 status = info.SetVersionInfo(&versionInfo, B_APP_VERSION_KIND); 726 if (status == B_OK) 727 fIcon.CopyTo(info, NULL, true); 728 729 // supported types and their icons 730 if (status == B_OK) 731 status = info.SetSupportedTypes(&supportedTypes); 732 733 for (int32 i = 0; i < fTypeListView->CountItems(); i++) { 734 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 735 fTypeListView->ItemAt(i)); 736 737 item->Icon().CopyTo(info, item->Type(), true); 738 } 739 } 740 741 742 void 743 ApplicationTypeWindow::FrameResized(float width, float height) 744 { 745 // This works around a flaw of BTextView 746 fLongDescriptionView->SetTextRect(fLongDescriptionView->Bounds()); 747 } 748 749 750 void 751 ApplicationTypeWindow::MessageReceived(BMessage* message) 752 { 753 switch (message->what) { 754 case kMsgFlagsChanged: 755 _UpdateAppFlagsEnabled(); 756 break; 757 758 case kMsgSave: 759 _Save(); 760 break; 761 762 case kMsgTypeSelected: 763 { 764 int32 index; 765 if (message->FindInt32("index", &index) == B_OK) { 766 SupportedTypeItem* item = (SupportedTypeItem*)fTypeListView->ItemAt(index); 767 768 fTypeIconView->SetTo(item != NULL ? &item->Icon() : NULL); 769 fTypeIconView->SetEnabled(item != NULL); 770 fRemoveTypeButton->SetEnabled(item != NULL); 771 } 772 break; 773 } 774 775 case kMsgAddType: 776 { 777 BWindow* window = new TypeListWindow(NULL, 778 kMsgTypeAdded, this); 779 window->Show(); 780 break; 781 } 782 783 case kMsgTypeAdded: 784 { 785 const char* type; 786 if (message->FindString("type", &type) != B_OK) 787 break; 788 789 // check if this type already exists 790 791 SupportedTypeItem* newItem = new SupportedTypeItem(type); 792 int32 insertAt = 0; 793 794 for (int32 i = fTypeListView->CountItems(); i-- > 0;) { 795 SupportedTypeItem* item = dynamic_cast<SupportedTypeItem*>( 796 fTypeListView->ItemAt(i)); 797 if (item == NULL) 798 continue; 799 800 int compare = strcasecmp(item->Type(), type); 801 if (!compare) { 802 // type does already exist, select it and bail out 803 delete newItem; 804 newItem = NULL; 805 fTypeListView->Select(i); 806 break; 807 } 808 if (compare < 0) 809 insertAt = i + 1; 810 } 811 812 if (newItem == NULL) 813 break; 814 815 fTypeListView->AddItem(newItem, insertAt); 816 fTypeListView->Select(insertAt); 817 break; 818 } 819 820 case kMsgRemoveType: 821 { 822 int32 index = fTypeListView->CurrentSelection(); 823 if (index < 0) 824 break; 825 826 delete fTypeListView->RemoveItem(index); 827 fTypeIconView->SetTo(NULL); 828 fTypeIconView->SetEnabled(false); 829 fRemoveTypeButton->SetEnabled(false); 830 break; 831 } 832 833 case B_SIMPLE_DATA: 834 { 835 entry_ref ref; 836 if (message->FindRef("refs", &ref) != B_OK) 837 break; 838 839 // TODO: add to supported types 840 break; 841 } 842 843 case B_META_MIME_CHANGED: 844 const char* type; 845 int32 which; 846 if (message->FindString("be:type", &type) != B_OK 847 || message->FindInt32("be:which", &which) != B_OK) 848 break; 849 850 // TODO: update supported types names 851 // if (which == B_MIME_TYPE_DELETED) 852 break; 853 854 default: 855 BWindow::MessageReceived(message); 856 } 857 } 858 859 860 bool 861 ApplicationTypeWindow::QuitRequested() 862 { 863 be_app->PostMessage(kMsgTypeWindowClosed); 864 return true; 865 } 866 867