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 ff_item = new FileFormatMenuItem(&mfi); 705 menu->AddItem(ff_item); 706 } 707 708 // mark first item 709 item = menu->ItemAt(0); 710 if (item != NULL) { 711 item->SetMarked(true); 712 ((BInvoker*)item)->Invoke(); 713 } 714 } 715 716 717 void 718 MediaConverterWindow::SetFileMessage(const char* message) 719 { 720 fFileStatus->SetText(message); 721 } 722 723 724 void 725 MediaConverterWindow::SetStatusMessage(const char* message) 726 { 727 fStatus->SetText(message); 728 } 729 730 731 // #pragma mark - 732 733 734 bool 735 MediaConverterWindow::AddSourceFile(BMediaFile* file, const entry_ref& ref) 736 { 737 if (!fListView->AddMediaItem(file, ref)) 738 return false; 739 740 if (!fOutputDirSpecified) { 741 BEntry entry(&ref); 742 entry.GetParent(&entry); 743 _SetOutputFolder(entry); 744 } 745 746 return true; 747 } 748 749 750 void 751 MediaConverterWindow::RemoveSourceFile(int32 index) 752 { 753 delete fListView->RemoveItem(index); 754 fStartDurationTC->SetText("0"); 755 fEndDurationTC->SetText("0"); 756 } 757 758 759 int32 760 MediaConverterWindow::CountSourceFiles() 761 { 762 return fListView->CountItems(); 763 } 764 765 766 status_t 767 MediaConverterWindow::GetSourceFileAt(int32 index, BMediaFile** _file, 768 entry_ref* ref) 769 { 770 MediaFileListItem* item = dynamic_cast<MediaFileListItem*>( 771 fListView->ItemAt(index)); 772 if (item != NULL) { 773 *_file = item->fMediaFile; 774 *ref = item->fRef; 775 return B_OK; 776 } else 777 return B_ERROR; 778 } 779 780 781 void 782 MediaConverterWindow::SourceFileSelectionChanged() 783 { 784 int32 selected = fListView->CurrentSelection(); 785 BMediaFile* file = NULL; 786 entry_ref ref; 787 bool enabled = GetSourceFileAt(selected, &file, &ref) == B_OK; 788 789 fPreviewButton->SetEnabled(enabled); 790 fVideoQualitySlider->SetEnabled(enabled); 791 fAudioQualitySlider->SetEnabled(enabled); 792 fStartDurationTC->SetEnabled(enabled); 793 fEndDurationTC->SetEnabled(enabled); 794 795 BString duration; 796 if (enabled) { 797 fInfoView->Update(file, &ref); 798 // HACK: get the fInfoView to update the duration "synchronously" 799 UpdateIfNeeded(); 800 duration << fInfoView->Duration() / 1000; 801 } else 802 duration = "0"; 803 804 // update duration text controls 805 fStartDurationTC->SetText("0"); 806 fEndDurationTC->SetText(duration.String()); 807 } 808 809 810 // #pragma mark - 811 812 813 void 814 MediaConverterWindow::SetEnabled(bool enabled, bool convertEnabled) 815 { 816 fConvertButton->SetEnabled(convertEnabled); 817 if (enabled == fEnabled) 818 return; 819 820 fFormatMenu->SetEnabled(enabled); 821 fAudioMenu->SetEnabled(enabled); 822 fVideoMenu->SetEnabled(enabled); 823 fListView->SetEnabled(enabled); 824 fStartDurationTC->SetEnabled(enabled); 825 fEndDurationTC->SetEnabled(enabled); 826 827 fEnabled = enabled; 828 } 829 830 831 bool 832 MediaConverterWindow::IsEnabled() 833 { 834 return fEnabled; 835 } 836 837 838 const char* 839 MediaConverterWindow::StartDuration() const 840 { 841 return fStartDurationTC->Text(); 842 } 843 844 845 const char* 846 MediaConverterWindow::EndDuration() const 847 { 848 return fEndDurationTC->Text(); 849 } 850 851 852 BDirectory 853 MediaConverterWindow::OutputDirectory() const 854 { 855 return fOutputDir; 856 } 857 858 859 void 860 MediaConverterWindow::SetAudioQualityLabel(const char* label) 861 { 862 fAudioQualitySlider->SetLabel(label); 863 } 864 865 866 void 867 MediaConverterWindow::SetVideoQualityLabel(const char* label) 868 { 869 fVideoQualitySlider->SetLabel(label); 870 } 871 872 873 void 874 MediaConverterWindow::TruncateOutputFolderPath() 875 { 876 BEntry entry; 877 fOutputDir.GetEntry(&entry); 878 BPath path; 879 entry.GetPath(&path); 880 BString pathString(path.Path()); 881 float maxWidth = fVideoMenu->MenuBar()->Frame().Width(); 882 883 fOutputFolder->TruncateString(&pathString, B_TRUNCATE_MIDDLE, maxWidth); 884 fOutputFolder->SetText(pathString.String()); 885 if (fOutputFolder->StringWidth(path.Path()) > maxWidth) 886 fOutputFolder->SetToolTip(path.Path()); 887 else 888 fOutputFolder->SetToolTip((const char*)NULL); 889 } 890 891 892 // #pragma mark - 893 894 895 void 896 MediaConverterWindow::_UpdateLabels() 897 { 898 if (fSourcesBox != NULL) { 899 fSourcesBox->SetLabel(B_TRANSLATE("Source files")); 900 _UpdateBBoxLayoutInsets(fSourcesBox); 901 } 902 903 if (fInfoBox != NULL) 904 fInfoBox->SetLabel(B_TRANSLATE("File details")); 905 906 if (fOutputBox != NULL) { 907 fOutputBox->SetLabel(B_TRANSLATE("Output format")); 908 _UpdateBBoxLayoutInsets(fOutputBox); 909 } 910 911 if (fConvertButton != NULL) 912 fConvertButton->SetLabel(B_TRANSLATE("Convert")); 913 914 if (fPreviewButton != NULL) 915 fPreviewButton->SetLabel(B_TRANSLATE("Preview")); 916 917 if (fDestButton != NULL) 918 fDestButton->SetLabel(B_TRANSLATE("Output folder")); 919 920 if (fVideoQualitySlider != NULL) { 921 char buffer[40]; 922 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Video quality: %3d%%"), 923 (int8)fVideoQuality); 924 fVideoQualitySlider->SetLabel(buffer); 925 fVideoQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), 926 B_TRANSLATE("High")); 927 } 928 929 if (fAudioQualitySlider != NULL) { 930 char buffer[40]; 931 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Audio quality: %3d%%"), 932 (int8)fAudioQuality); 933 fAudioQualitySlider->SetLabel(buffer); 934 fAudioQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), 935 B_TRANSLATE("High")); 936 } 937 938 if (fStartDurationTC != NULL) 939 fStartDurationTC->SetLabel(B_TRANSLATE("Start [ms]: ")); 940 941 if (fEndDurationTC != NULL) 942 fEndDurationTC->SetLabel(B_TRANSLATE("End [ms]: ")); 943 944 if (fFormatMenu != NULL) 945 fFormatMenu->SetLabel(B_TRANSLATE("File format:")); 946 947 if (fAudioMenu != NULL) 948 fAudioMenu->SetLabel(B_TRANSLATE("Audio encoding:")); 949 950 if (fVideoMenu != NULL) 951 fVideoMenu->SetLabel(B_TRANSLATE("Video encoding:")); 952 953 SetFileMessage(B_TRANSLATE("Drop media files onto this window")); 954 } 955 956 957 void 958 MediaConverterWindow::_UpdateBBoxLayoutInsets(BBox* box) 959 { 960 BTwoDimensionalLayout* layout 961 = dynamic_cast<BTwoDimensionalLayout*>(box->GetLayout()); 962 if (layout != NULL) { 963 float padding = be_control_look->DefaultItemSpacing(); 964 layout->SetInsets(padding, box->TopBorderOffset() + padding, padding, 965 padding); 966 } 967 } 968 969 970 void 971 MediaConverterWindow::_DestroyMenu() 972 { 973 BMenu* menu; 974 975 while ((menu = fMenuBar->SubmenuAt(0)) != NULL) { 976 fMenuBar->RemoveItem(menu); 977 delete menu; 978 } 979 } 980 981 982 void 983 MediaConverterWindow::_CreateMenu() 984 { 985 BMenu* menu; 986 BMenuItem* item; 987 988 menu = new BMenu(B_TRANSLATE_CONTEXT("File", "Menu")); 989 item = new BMenuItem(B_TRANSLATE_CONTEXT("Open" B_UTF8_ELLIPSIS, "Menu"), 990 new BMessage(OPEN_FILE_MESSAGE), 'O'); 991 menu->AddItem(item); 992 menu->AddSeparatorItem(); 993 item = new BMenuItem(B_TRANSLATE_CONTEXT("Quit", "Menu"), 994 new BMessage(QUIT_MESSAGE), 'Q'); 995 menu->AddItem(item); 996 997 fMenuBar->AddItem(menu); 998 } 999 1000 1001 void 1002 MediaConverterWindow::_SetOutputFolder(BEntry entry) 1003 { 1004 fOutputDir.SetTo(&entry); 1005 TruncateOutputFolderPath(); 1006 } 1007