1 // Copyright 1999, Be Incorporated. All Rights Reserved. 2 // Copyright 2000-2004, Jun Suzuki. All Rights Reserved. 3 // Copyright 2007, 2010 Stephan Aßmus. All Rights Reserved. 4 // Copyright 2010-2013, Haiku, Inc. All Rights Reserved. 5 // This file may be used under the terms of the Be Sample Code License. 6 7 8 #include "MediaConverterWindow.h" 9 10 #include <stdio.h> 11 #include <string.h> 12 13 #include <Alert.h> 14 #include <Application.h> 15 #include <Box.h> 16 #include <Button.h> 17 #include <Catalog.h> 18 #include <ControlLook.h> 19 #include <FilePanel.h> 20 #include <FindDirectory.h> 21 #include <LayoutBuilder.h> 22 #include <Locale.h> 23 #include <Menu.h> 24 #include <MenuBar.h> 25 #include <MenuField.h> 26 #include <MenuItem.h> 27 #include <Path.h> 28 #include <PopUpMenu.h> 29 #include <Roster.h> 30 #include <ScrollBar.h> 31 #include <ScrollView.h> 32 #include <Slider.h> 33 #include <StringView.h> 34 #include <TextControl.h> 35 36 #include "MediaFileInfoView.h" 37 #include "MediaFileListView.h" 38 #include "MessageConstants.h" 39 40 41 #undef B_TRANSLATION_CONTEXT 42 #define B_TRANSLATION_CONTEXT "MediaConverter" 43 #define VERSION "1.3.0" 44 45 46 static const unsigned int kMinSourceWidth = 12; 47 static const unsigned int kQualitySliderWidth = 28; 48 static const unsigned int kDurationWidth = 10; 49 50 51 // #pragma mark - DirectoryFilter 52 53 54 class DirectoryFilter : public BRefFilter { 55 public: 56 DirectoryFilter() {}; 57 virtual bool Filter(const entry_ref* ref, 58 BNode* node, struct stat_beos* st, const char* filetype) 59 { 60 return node->IsDirectory(); 61 } 62 }; 63 64 65 // #pragma mark - FileFormatMenuItem 66 67 68 class FileFormatMenuItem : public BMenuItem { 69 public: 70 FileFormatMenuItem(media_file_format* format); 71 virtual ~FileFormatMenuItem(); 72 73 media_file_format fFileFormat; 74 }; 75 76 77 FileFormatMenuItem::FileFormatMenuItem(media_file_format *format) 78 : 79 BMenuItem(format->pretty_name, new BMessage(FORMAT_SELECT_MESSAGE)) 80 { 81 memcpy(&fFileFormat, format, sizeof(fFileFormat)); 82 } 83 84 85 FileFormatMenuItem::~FileFormatMenuItem() 86 { 87 } 88 89 90 // #pragma mark - CodecMenuItem 91 92 93 class CodecMenuItem : public BMenuItem { 94 public: 95 CodecMenuItem(media_codec_info *ci, uint32 msg_type); 96 virtual ~CodecMenuItem(); 97 98 media_codec_info fCodecInfo; 99 }; 100 101 102 CodecMenuItem::CodecMenuItem(media_codec_info *ci, uint32 msg_type) 103 : 104 BMenuItem(ci->pretty_name, new BMessage(msg_type)) 105 { 106 memcpy(&fCodecInfo, ci, sizeof(fCodecInfo)); 107 } 108 109 110 CodecMenuItem::~CodecMenuItem() 111 { 112 } 113 114 115 // #pragma mark - OutputBox 116 117 118 class OutputBox : public BBox { 119 public: 120 OutputBox(border_style border, BView* child); 121 virtual void FrameResized(float width, float height) 122 { 123 MediaConverterWindow* window 124 = dynamic_cast<MediaConverterWindow*>(Window()); 125 if (window != NULL) 126 window->TruncateOutputFolderPath(); 127 BBox::FrameResized(width, height); 128 } 129 }; 130 131 132 OutputBox::OutputBox(border_style border, BView* child) 133 : 134 BBox(border, child) 135 { 136 } 137 138 139 // #pragma mark - MediaConverterWindow 140 141 142 MediaConverterWindow::MediaConverterWindow(BRect frame) 143 : 144 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("MediaConverter"), B_TITLED_WINDOW_LOOK, 145 B_NORMAL_WINDOW_FEEL, B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE 146 | B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), 147 fVideoQuality(75), 148 fAudioQuality(75), 149 fSaveFilePanel(NULL), 150 fOpenFilePanel(NULL), 151 fOutputDirSpecified(false), 152 fEnabled(true), 153 fConverting(false), 154 fCancelling(false) 155 { 156 BPath outputDir; 157 if (find_directory(B_USER_DIRECTORY, &outputDir) != B_OK) 158 outputDir.SetTo("/boot/home"); 159 fOutputDir.SetTo(outputDir.Path()); 160 161 fMenuBar = new BMenuBar("menubar"); 162 _CreateMenu(); 163 164 float padding = be_control_look->DefaultItemSpacing(); 165 166 fListView = new MediaFileListView(); 167 fListView->SetExplicitMinSize(BSize(padding * kMinSourceWidth, B_SIZE_UNSET)); 168 BScrollView* scroller = new BScrollView(NULL, fListView, 0, false, true); 169 170 // file list view box 171 fSourcesBox = new BBox(B_FANCY_BORDER, scroller); 172 fSourcesBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0)); 173 // fSourcesBox's layout adjusted in _UpdateLabels 174 175 // info box 176 fInfoView = new MediaFileInfoView(); 177 fInfoView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 178 B_ALIGN_VERTICAL_UNSET)); 179 fInfoBox = new BBox(B_FANCY_BORDER, fInfoView); 180 181 // output menu fields 182 fFormatMenu = new BMenuField(NULL, B_TRANSLATE("File format:"), 183 new BPopUpMenu("")); 184 fAudioMenu = new BMenuField(NULL, B_TRANSLATE("Audio encoding:"), 185 new BPopUpMenu("")); 186 fVideoMenu = new BMenuField(NULL, B_TRANSLATE("Video encoding:"), 187 new BPopUpMenu("")); 188 189 // output folder 190 fDestButton = new BButton(B_TRANSLATE("Output folder"), 191 new BMessage(OUTPUT_FOLDER_MESSAGE)); 192 BAlignment labelAlignment(be_control_look->DefaultLabelAlignment()); 193 fOutputFolder = new BStringView(NULL, outputDir.Path()); 194 fOutputFolder->SetExplicitAlignment(labelAlignment); 195 196 // start/end duration 197 fStartDurationTC = new BTextControl(NULL, "0", NULL); 198 BLayoutItem* startDuration = fStartDurationTC->CreateTextViewLayoutItem(); 199 startDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET)); 200 startDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 201 B_ALIGN_VERTICAL_CENTER)); 202 fEndDurationTC = new BTextControl(NULL, "0", NULL); 203 BLayoutItem* endDuration = fEndDurationTC->CreateTextViewLayoutItem(); 204 endDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET)); 205 endDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 206 B_ALIGN_VERTICAL_CENTER)); 207 208 // video quality 209 fVideoQualitySlider = new BSlider("VSlider", "" , 210 new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL); 211 fVideoQualitySlider->SetModificationMessage( 212 new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE)); 213 fVideoQualitySlider->SetValue(fVideoQuality); 214 fVideoQualitySlider->SetEnabled(false); 215 fVideoQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth, 216 B_SIZE_UNSET)); 217 218 // audio quality 219 fAudioQualitySlider = new BSlider("ASlider", "" , 220 new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL); 221 fAudioQualitySlider->SetModificationMessage( 222 new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE)); 223 fAudioQualitySlider->SetValue(fAudioQuality); 224 fAudioQualitySlider->SetEnabled(false); 225 fAudioQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth, 226 B_SIZE_UNSET)); 227 228 // output format box 229 BView* outputGrid = BLayoutBuilder::Grid<>() 230 .Add(fFormatMenu->CreateLabelLayoutItem(), 0, 0) 231 .Add(fFormatMenu->CreateMenuBarLayoutItem(), 1, 0) 232 .Add(fAudioMenu->CreateLabelLayoutItem(), 0, 1) 233 .Add(fAudioMenu->CreateMenuBarLayoutItem(), 1, 1) 234 .Add(fVideoMenu->CreateLabelLayoutItem(), 0, 2) 235 .Add(fVideoMenu->CreateMenuBarLayoutItem(), 1, 2) 236 .Add(fDestButton, 0, 3) 237 .Add(fOutputFolder, 1, 3) 238 .Add(fStartDurationTC->CreateLabelLayoutItem(), 0, 4) 239 .Add(startDuration, 1, 4) 240 .Add(fEndDurationTC->CreateLabelLayoutItem(), 0, 5) 241 .Add(endDuration, 1, 5) 242 .Add(fVideoQualitySlider, 0, 6, 2, 1) 243 .Add(fAudioQualitySlider, 0, 7, 2, 1) 244 .View(); 245 outputGrid->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 246 B_ALIGN_USE_FULL_HEIGHT)); 247 fOutputBox = new OutputBox(B_FANCY_BORDER, outputGrid); 248 fOutputBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0)); 249 // fOutputBox's layout adjusted in _UpdateLabels 250 251 // buttons 252 fPreviewButton = new BButton(B_TRANSLATE("Preview"), 253 new BMessage(PREVIEW_MESSAGE)); 254 fPreviewButton->SetEnabled(false); 255 256 fConvertButton = new BButton(B_TRANSLATE("Convert"), 257 new BMessage(CONVERT_BUTTON_MESSAGE)); 258 259 // Status views 260 fStatus = new BStringView(NULL, NULL); 261 fStatus->SetExplicitAlignment(labelAlignment); 262 fFileStatus = new BStringView(NULL, NULL); 263 fFileStatus->SetExplicitAlignment(labelAlignment); 264 265 SetStatusMessage(""); 266 _UpdateLabels(); 267 268 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 269 .SetInsets(0, 0, 0, 0) 270 .Add(fMenuBar) 271 .AddSplit(B_HORIZONTAL, padding / 2) 272 .SetInsets(padding, padding, padding, padding) 273 .Add(fSourcesBox, 0.4) 274 .AddGroup(B_VERTICAL, padding, 0.6) 275 .Add(fInfoBox) 276 .Add(fOutputBox) 277 .End() 278 .End() 279 .AddGrid(padding, padding) 280 .SetInsets(padding, 0, padding, padding) 281 .Add(fStatus, 0, 0) 282 .Add(fFileStatus, 0, 1) 283 .Add(BSpaceLayoutItem::CreateGlue(), 1, 0) 284 .Add(fPreviewButton, 2, 0) 285 .Add(fConvertButton, 3, 0) 286 .End() 287 ; 288 } 289 290 291 MediaConverterWindow::~MediaConverterWindow() 292 { 293 delete fSaveFilePanel; 294 delete fOpenFilePanel; 295 } 296 297 298 // #pragma mark - 299 300 301 void 302 MediaConverterWindow::MessageReceived(BMessage* msg) 303 { 304 entry_ref inRef; 305 306 char buffer[40]; 307 BEntry inEntry; 308 309 switch (msg->what) { 310 #if B_BEOS_VERSION <= B_BEOS_VERSION_6 311 case B_LANGUAGE_CHANGED: 312 LanguageChanged(); 313 break; 314 #endif 315 316 case INIT_FORMAT_MENUS: 317 BuildFormatMenu(); 318 if (CountSourceFiles() == 0) 319 SetEnabled(false, false); 320 break; 321 322 case B_SIMPLE_DATA: 323 if (msg->WasDropped()) { 324 DetachCurrentMessage(); 325 msg->what = B_REFS_RECEIVED; 326 BMessenger(be_app).SendMessage(msg); 327 delete msg; 328 } 329 break; 330 331 case FORMAT_SELECT_MESSAGE: 332 BuildAudioVideoMenus(); 333 break; 334 case AUDIO_CODEC_SELECT_MESSAGE: 335 break; 336 case VIDEO_CODEC_SELECT_MESSAGE: 337 break; 338 339 case CONVERT_BUTTON_MESSAGE: 340 if (!fConverting) { 341 fConvertButton->SetLabel(B_TRANSLATE("Cancel")); 342 fConverting = true; 343 SetStatusMessage(B_TRANSLATE("Convert")); 344 SetEnabled(false, true); 345 BMessenger(be_app).SendMessage(START_CONVERSION_MESSAGE); 346 } else if (!fCancelling) { 347 fCancelling = true; 348 SetStatusMessage(B_TRANSLATE("Cancelling" B_UTF8_ELLIPSIS)); 349 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE); 350 } 351 break; 352 353 case CONVERSION_DONE_MESSAGE: 354 { 355 SetStatusMessage(fCancelling ? B_TRANSLATE("Conversion cancelled") 356 : B_TRANSLATE("Conversion completed")); 357 fConverting = false; 358 fCancelling = false; 359 bool enable = CountSourceFiles() > 0; 360 SetEnabled(enable, enable); 361 fConvertButton->SetLabel(B_TRANSLATE("Convert")); 362 break; 363 } 364 365 case OUTPUT_FOLDER_MESSAGE: 366 // Execute Save Panel 367 if (fSaveFilePanel == NULL) { 368 BButton* selectThisDir; 369 370 BMessage message(FOLDER_SELECT_MESSAGE); 371 fSaveFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, 372 B_DIRECTORY_NODE, true, &message, NULL, false, true); 373 fSaveFilePanel->SetButtonLabel(B_DEFAULT_BUTTON, 374 B_TRANSLATE("Select")); 375 fSaveFilePanel->SetTarget(this); 376 377 fSaveFilePanel->Window()->Lock(); 378 fSaveFilePanel->Window()->SetTitle( 379 B_TRANSLATE("MediaConverter+:SaveDirectory")); 380 BRect buttonRect 381 = fSaveFilePanel->Window()->ChildAt(0)->FindView( 382 "cancel button")->Frame(); 383 buttonRect.right = buttonRect.left - 20; 384 buttonRect.left = buttonRect.right - 130; 385 selectThisDir = new BButton(buttonRect, NULL, 386 B_TRANSLATE("Select this folder"), 387 new BMessage(SELECT_THIS_DIR_MESSAGE), 388 B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT); 389 selectThisDir->SetTarget(this); 390 fSaveFilePanel->Window()->ChildAt(0)->AddChild(selectThisDir); 391 fSaveFilePanel->Window()->Unlock(); 392 393 fSaveFilePanel->SetRefFilter(new DirectoryFilter); 394 } 395 fSaveFilePanel->Show(); 396 break; 397 398 case FOLDER_SELECT_MESSAGE: 399 // "SELECT" Button at Save Panel Pushed 400 fSaveFilePanel->GetNextSelectedRef(&inRef); 401 inEntry.SetTo(&inRef, true); 402 _SetOutputFolder(inEntry); 403 fOutputDirSpecified = true; 404 break; 405 406 case SELECT_THIS_DIR_MESSAGE: 407 // "THIS DIR" Button at Save Panel Pushed 408 fSaveFilePanel->GetPanelDirectory(&inRef); 409 fSaveFilePanel->Hide(); 410 inEntry.SetTo(&inRef, true); 411 _SetOutputFolder(inEntry); 412 fOutputDirSpecified = true; 413 break; 414 415 case OPEN_FILE_MESSAGE: 416 // Execute Open Panel 417 if (!fOpenFilePanel) { 418 fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, 419 B_FILE_NODE, true, NULL, NULL, false, true); 420 fOpenFilePanel->SetTarget(this); 421 } 422 fOpenFilePanel->Show(); 423 break; 424 425 case B_REFS_RECEIVED: 426 // Media Files Seleced by Open Panel 427 DetachCurrentMessage(); 428 msg->what = B_REFS_RECEIVED; 429 BMessenger(be_app).SendMessage(msg); 430 // fall through 431 432 case B_CANCEL: 433 break; 434 435 case QUIT_MESSAGE: 436 MediaConverterWindow::QuitRequested(); 437 break; 438 439 case PREVIEW_MESSAGE: 440 { 441 // Build the command line to launch the preview application. 442 // TODO: Launch the default app instead of hardcoded MediaPlayer! 443 int32 srcIndex = fListView->CurrentSelection(); 444 BMediaFile* inFile = NULL; 445 status_t status = GetSourceFileAt(srcIndex, &inFile, &inRef); 446 447 const char* argv[3]; 448 BString startPosString; 449 BPath path; 450 451 if (status == B_OK) { 452 argv[0] = "-pos"; 453 // NOTE: -pos argument is currently not supported by Haiku 454 // MediaPlayer. 455 startPosString << fStartDurationTC->Text(); 456 startPosString << "000"; 457 argv[1] = startPosString.String(); 458 459 status = inEntry.SetTo(&inRef); 460 } 461 462 if (status == B_OK) { 463 status = inEntry.GetPath(&path); 464 if (status == B_OK) 465 argv[2] = path.Path(); 466 } 467 468 if (status == B_OK) { 469 status = be_roster->Launch( 470 "application/x-vnd.Haiku-MediaPlayer", 471 3, (char**)argv, NULL); 472 } 473 474 if (status != B_OK && status != B_ALREADY_RUNNING) { 475 BString errorString(B_TRANSLATE("Error launching: %strError%")); 476 errorString.ReplaceFirst("%strError%", strerror(status)); 477 BAlert* alert = new BAlert(B_TRANSLATE("Error"), 478 errorString.String(), B_TRANSLATE("OK")); 479 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 480 alert->Go(); 481 } 482 break; 483 } 484 485 case VIDEO_QUALITY_CHANGED_MESSAGE: 486 { 487 int32 value; 488 msg->FindInt32("be:value", &value); 489 snprintf(buffer, sizeof(buffer), 490 B_TRANSLATE("Video quality: %3d%%"), (int8)value); 491 fVideoQualitySlider->SetLabel(buffer); 492 fVideoQuality = value; 493 break; 494 } 495 496 case AUDIO_QUALITY_CHANGED_MESSAGE: 497 { 498 int32 value; 499 msg->FindInt32("be:value", &value); 500 snprintf(buffer, sizeof(buffer), 501 B_TRANSLATE("Audio quality: %3d%%"), (int8)value); 502 fAudioQualitySlider->SetLabel(buffer); 503 fAudioQuality = value; 504 break; 505 } 506 507 default: 508 BWindow::MessageReceived(msg); 509 } 510 } 511 512 513 bool 514 MediaConverterWindow::QuitRequested() 515 { 516 if (!fConverting) { 517 BMessenger(be_app).SendMessage(B_QUIT_REQUESTED); 518 return true; 519 } else if (!fCancelling) { 520 fCancelling = true; 521 SetStatusMessage(B_TRANSLATE("Cancelling")); 522 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE); 523 } 524 return false; 525 } 526 527 528 // #pragma mark - 529 530 531 void 532 MediaConverterWindow::LanguageChanged() 533 { 534 _DestroyMenu(); 535 _CreateMenu(); 536 _UpdateLabels(); 537 BuildAudioVideoMenus(); 538 Lock(); 539 fInfoView->Invalidate(); 540 Unlock(); 541 } 542 543 544 void 545 MediaConverterWindow::BuildAudioVideoMenus() 546 { 547 BMenu* menu = fAudioMenu->Menu(); 548 BMenuItem* item; 549 // clear out old audio codec menu items 550 while ((item = menu->RemoveItem((int32)0)) != NULL) 551 delete item; 552 553 bool separator = true; 554 555 // get selected file format 556 FileFormatMenuItem* ffmi 557 = (FileFormatMenuItem*)fFormatMenu->Menu()->FindMarked(); 558 media_file_format* mf_format = &(ffmi->fFileFormat); 559 560 media_format format, outfmt; 561 memset(&format, 0, sizeof(format)); 562 media_codec_info codec_info; 563 int32 cookie = 0; 564 CodecMenuItem* cmi; 565 566 // add available audio encoders to menu 567 format.type = B_MEDIA_RAW_AUDIO; 568 format.u.raw_audio = media_raw_audio_format::wildcard; 569 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info) 570 == B_OK) { 571 if (separator) { 572 menu->AddItem(new BMenuItem( 573 B_TRANSLATE_CONTEXT("No audio", "Audio codecs list"), 574 new BMessage(AUDIO_CODEC_SELECT_MESSAGE))); 575 menu->AddSeparatorItem(); 576 separator = false; 577 } 578 579 cmi = new CodecMenuItem(&codec_info, AUDIO_CODEC_SELECT_MESSAGE); 580 menu->AddItem(cmi); 581 // reset media format struct 582 /* 583 format.type = B_MEDIA_RAW_AUDIO; 584 format.u.raw_audio = media_raw_audio_format::wildcard; 585 */ 586 } 587 588 // mark first audio encoder 589 item = menu->ItemAt(0); 590 if (item != NULL) { 591 fAudioMenu->SetEnabled(fEnabled); 592 fAudioQualitySlider->SetEnabled(fEnabled); 593 item->SetMarked(true); 594 ((BInvoker *)item)->Invoke(); 595 } else { 596 item = new BMenuItem( 597 B_TRANSLATE_CONTEXT("None available", "Audio codecs"), 598 NULL); 599 menu->AddItem(item); 600 item->SetMarked(true); 601 fAudioMenu->SetEnabled(false); 602 fAudioQualitySlider->SetEnabled(false); 603 } 604 605 // clear out old video codec menu items 606 menu = fVideoMenu->Menu(); 607 while ((item = menu->RemoveItem((int32)0)) != NULL) 608 delete item; 609 610 separator = true; 611 612 // construct a generic video format. Some of these parameters 613 // seem silly, but are needed for R4.5.x, which is more picky 614 // than subsequent BeOS releases will be. 615 memset(&format, 0, sizeof(format)); 616 format.type = B_MEDIA_RAW_VIDEO; 617 format.u.raw_video.last_active = (uint32)(240 - 1); 618 format.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; 619 format.u.raw_video.display.format = B_RGB32; 620 format.u.raw_video.display.line_width = (int32)320; 621 format.u.raw_video.display.line_count = (int32)240; 622 format.u.raw_video.display.bytes_per_row = 4 * 320; 623 624 // add available video encoders to menu 625 cookie = 0; 626 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info) == B_OK) { 627 if (separator) { 628 menu->AddItem(new BMenuItem( 629 B_TRANSLATE_CONTEXT("No video", "Video codecs list"), 630 new BMessage(VIDEO_CODEC_SELECT_MESSAGE))); 631 menu->AddSeparatorItem(); 632 separator = false; 633 } 634 635 cmi = new CodecMenuItem(&codec_info, VIDEO_CODEC_SELECT_MESSAGE); 636 menu->AddItem(cmi); 637 } 638 639 // mark first video encoder 640 item = menu->ItemAt(0); 641 if (item != NULL) { 642 fVideoMenu->SetEnabled(fEnabled); 643 fVideoQualitySlider->SetEnabled(fEnabled); 644 item->SetMarked(true); 645 ((BInvoker *)item)->Invoke(); 646 } else { 647 item = new BMenuItem( 648 B_TRANSLATE_CONTEXT("None available", "Video codecs"), 649 NULL); 650 menu->AddItem(item); 651 item->SetMarked(true); 652 fVideoMenu->SetEnabled(false); 653 fVideoQualitySlider->SetEnabled(false); 654 } 655 } 656 657 void 658 MediaConverterWindow::GetSelectedFormatInfo(media_file_format** format, 659 media_codec_info** audio, media_codec_info** video) 660 { 661 *audio = NULL; 662 *video = NULL; 663 *format = NULL; 664 665 FileFormatMenuItem *formatItem = 666 dynamic_cast<FileFormatMenuItem *>(fFormatMenu->Menu()->FindMarked()); 667 if (formatItem != NULL) { 668 *format = &(formatItem->fFileFormat); 669 } 670 671 *audio = *video = NULL; 672 CodecMenuItem *codecItem = 673 dynamic_cast<CodecMenuItem *>(fAudioMenu->Menu()->FindMarked()); 674 if (codecItem != NULL) { 675 *audio = &(codecItem->fCodecInfo); 676 } 677 678 codecItem = dynamic_cast<CodecMenuItem *>(fVideoMenu->Menu()->FindMarked()); 679 if (codecItem != NULL) { 680 *video = &(codecItem->fCodecInfo); 681 } 682 } 683 684 685 void 686 MediaConverterWindow::BuildFormatMenu() 687 { 688 BMenu *menu = fFormatMenu->Menu(); 689 BMenuItem *item; 690 // clear out old format menu items 691 while ((item = menu->RemoveItem((int32)0)) != NULL) { 692 delete item; 693 } 694 695 // add menu items for each file format 696 media_file_format mfi; 697 int32 cookie = 0; 698 FileFormatMenuItem *ff_item; 699 while (get_next_file_format(&cookie, &mfi) == B_OK) { 700 ff_item = new FileFormatMenuItem(&mfi); 701 menu->AddItem(ff_item); 702 } 703 704 // mark first item 705 item = menu->ItemAt(0); 706 if (item != NULL) { 707 item->SetMarked(true); 708 ((BInvoker *)item)->Invoke(); 709 } 710 } 711 712 713 void 714 MediaConverterWindow::SetFileMessage(const char *message) 715 { 716 fFileStatus->SetText(message); 717 } 718 719 720 void 721 MediaConverterWindow::SetStatusMessage(const char *message) 722 { 723 fStatus->SetText(message); 724 } 725 726 727 // #pragma mark - 728 729 730 bool 731 MediaConverterWindow::AddSourceFile(BMediaFile* file, const entry_ref& ref) 732 { 733 if (!fListView->AddMediaItem(file, ref)) 734 return false; 735 736 if (!fOutputDirSpecified) { 737 BEntry entry(&ref); 738 entry.GetParent(&entry); 739 _SetOutputFolder(entry); 740 } 741 742 return true; 743 } 744 745 746 void 747 MediaConverterWindow::RemoveSourceFile(int32 index) 748 { 749 delete fListView->RemoveItem(index); 750 fStartDurationTC->SetText("0"); 751 fEndDurationTC->SetText("0"); 752 } 753 754 755 int32 756 MediaConverterWindow::CountSourceFiles() 757 { 758 return fListView->CountItems(); 759 } 760 761 762 status_t 763 MediaConverterWindow::GetSourceFileAt(int32 index, BMediaFile** _file, 764 entry_ref* ref) 765 { 766 MediaFileListItem* item = dynamic_cast<MediaFileListItem*>( 767 fListView->ItemAt(index)); 768 if (item != NULL) { 769 *_file = item->fMediaFile; 770 *ref = item->fRef; 771 return B_OK; 772 } else { 773 return B_ERROR; 774 } 775 } 776 777 778 void 779 MediaConverterWindow::SourceFileSelectionChanged() 780 { 781 int32 selected = fListView->CurrentSelection(); 782 BMediaFile* file = NULL; 783 entry_ref ref; 784 bool enabled = GetSourceFileAt(selected, &file, &ref) == B_OK; 785 786 fPreviewButton->SetEnabled(enabled); 787 fVideoQualitySlider->SetEnabled(enabled); 788 fAudioQualitySlider->SetEnabled(enabled); 789 fStartDurationTC->SetEnabled(enabled); 790 fEndDurationTC->SetEnabled(enabled); 791 792 BString duration; 793 if (enabled) { 794 fInfoView->Update(file, &ref); 795 // HACK: get the fInfoView to update the duration "synchronously" 796 UpdateIfNeeded(); 797 duration << fInfoView->Duration() / 1000; 798 } else 799 duration = "0"; 800 801 // update duration text controls 802 fStartDurationTC->SetText("0"); 803 fEndDurationTC->SetText(duration.String()); 804 } 805 806 807 // #pragma mark - 808 809 810 void 811 MediaConverterWindow::SetEnabled(bool enabled, bool convertEnabled) 812 { 813 fConvertButton->SetEnabled(convertEnabled); 814 if (enabled == fEnabled) 815 return; 816 817 fFormatMenu->SetEnabled(enabled); 818 fAudioMenu->SetEnabled(enabled); 819 fVideoMenu->SetEnabled(enabled); 820 fListView->SetEnabled(enabled); 821 fStartDurationTC->SetEnabled(enabled); 822 fEndDurationTC->SetEnabled(enabled); 823 824 fEnabled = enabled; 825 } 826 827 828 bool 829 MediaConverterWindow::IsEnabled() 830 { 831 return fEnabled; 832 } 833 834 835 const char* 836 MediaConverterWindow::StartDuration() const 837 { 838 return fStartDurationTC->Text(); 839 } 840 841 842 const char* 843 MediaConverterWindow::EndDuration() const 844 { 845 return fEndDurationTC->Text(); 846 } 847 848 849 BDirectory 850 MediaConverterWindow::OutputDirectory() const 851 { 852 return fOutputDir; 853 } 854 855 856 void 857 MediaConverterWindow::SetAudioQualityLabel(const char* label) 858 { 859 fAudioQualitySlider->SetLabel(label); 860 } 861 862 863 void 864 MediaConverterWindow::SetVideoQualityLabel(const char* label) 865 { 866 fVideoQualitySlider->SetLabel(label); 867 } 868 869 870 void 871 MediaConverterWindow::TruncateOutputFolderPath() 872 { 873 BEntry entry; 874 fOutputDir.GetEntry(&entry); 875 BPath path; 876 entry.GetPath(&path); 877 BString pathString(path.Path()); 878 float maxWidth = fVideoMenu->MenuBar()->Frame().Width(); 879 880 fOutputFolder->TruncateString(&pathString, B_TRUNCATE_MIDDLE, maxWidth); 881 fOutputFolder->SetText(pathString.String()); 882 } 883 884 885 // #pragma mark - 886 887 888 void 889 MediaConverterWindow::_UpdateLabels() 890 { 891 if (fSourcesBox != NULL) { 892 fSourcesBox->SetLabel(B_TRANSLATE("Source files")); 893 _UpdateBBoxLayoutInsets(fSourcesBox); 894 } 895 896 if (fInfoBox != NULL) 897 fInfoBox->SetLabel(B_TRANSLATE("File details")); 898 899 if (fOutputBox != NULL) { 900 fOutputBox->SetLabel(B_TRANSLATE("Output format")); 901 _UpdateBBoxLayoutInsets(fOutputBox); 902 } 903 904 if (fConvertButton != NULL) 905 fConvertButton->SetLabel(B_TRANSLATE("Convert")); 906 907 if (fPreviewButton != NULL) 908 fPreviewButton->SetLabel(B_TRANSLATE("Preview")); 909 910 if (fDestButton != NULL) 911 fDestButton->SetLabel(B_TRANSLATE("Output folder")); 912 913 if (fVideoQualitySlider != NULL) { 914 char buffer[40]; 915 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Video quality: %3d%%"), 916 (int8)fVideoQuality); 917 fVideoQualitySlider->SetLabel(buffer); 918 fVideoQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), 919 B_TRANSLATE("High")); 920 } 921 922 if (fAudioQualitySlider != NULL) { 923 char buffer[40]; 924 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Audio quality: %3d%%"), 925 (int8)fAudioQuality); 926 fAudioQualitySlider->SetLabel(buffer); 927 fAudioQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), 928 B_TRANSLATE("High")); 929 } 930 931 if (fStartDurationTC != NULL) 932 fStartDurationTC->SetLabel(B_TRANSLATE("Start [ms]: ")); 933 934 if (fEndDurationTC != NULL) 935 fEndDurationTC->SetLabel(B_TRANSLATE("End [ms]: ")); 936 937 if (fFormatMenu != NULL) 938 fFormatMenu->SetLabel(B_TRANSLATE("File format:")); 939 940 if (fAudioMenu != NULL) 941 fAudioMenu->SetLabel(B_TRANSLATE("Audio encoding:")); 942 943 if (fVideoMenu != NULL) 944 fVideoMenu->SetLabel(B_TRANSLATE("Video encoding:")); 945 946 SetFileMessage(B_TRANSLATE("Drop media files onto this window")); 947 } 948 949 950 void 951 MediaConverterWindow::_UpdateBBoxLayoutInsets(BBox* box) 952 { 953 BTwoDimensionalLayout* layout 954 = dynamic_cast<BTwoDimensionalLayout*>(box->GetLayout()); 955 if (layout != NULL) { 956 float padding = be_control_look->DefaultItemSpacing(); 957 layout->SetInsets(padding, box->TopBorderOffset() + padding, padding, 958 padding); 959 } 960 } 961 962 963 void 964 MediaConverterWindow::_DestroyMenu() 965 { 966 BMenu* Menu; 967 968 while ((Menu = fMenuBar->SubmenuAt(0)) != NULL) { 969 fMenuBar->RemoveItem(Menu); 970 delete Menu; 971 } 972 } 973 974 975 void 976 MediaConverterWindow::_CreateMenu() 977 { 978 BMenuItem* item; 979 BMenu* menu; 980 981 menu = new BMenu(B_TRANSLATE_CONTEXT("File", "Menu")); 982 item = new BMenuItem(B_TRANSLATE_CONTEXT( 983 "Open" B_UTF8_ELLIPSIS, "Menu"), 984 new BMessage(OPEN_FILE_MESSAGE), 'O'); 985 menu->AddItem(item); 986 menu->AddSeparatorItem(); 987 item = new BMenuItem(B_TRANSLATE_CONTEXT("Quit", "Menu"), 988 new BMessage(QUIT_MESSAGE), 'Q'); 989 menu->AddItem(item); 990 991 fMenuBar->AddItem(menu); 992 } 993 994 995 void 996 MediaConverterWindow::_SetOutputFolder(BEntry entry) 997 { 998 fOutputDir.SetTo(&entry); 999 TruncateOutputFolderPath(); 1000 } 1001