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