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 #include <unistd.h> 13 14 #include <Alert.h> 15 #include <Application.h> 16 #include <Box.h> 17 #include <Button.h> 18 #include <Catalog.h> 19 #include <ControlLook.h> 20 #include <FilePanel.h> 21 #include <FindDirectory.h> 22 #include <LayoutBuilder.h> 23 #include <Locale.h> 24 #include <Menu.h> 25 #include <MenuBar.h> 26 #include <MenuField.h> 27 #include <MenuItem.h> 28 #include <Path.h> 29 #include <PopUpMenu.h> 30 #include <Roster.h> 31 #include <ScrollBar.h> 32 #include <ScrollView.h> 33 #include <Slider.h> 34 #include <StringView.h> 35 #include <TextControl.h> 36 37 #include "MediaFileInfoView.h" 38 #include "MediaFileListView.h" 39 #include "MessageConstants.h" 40 41 42 #undef B_TRANSLATION_CONTEXT 43 #define B_TRANSLATION_CONTEXT "MediaConverter" 44 #define VERSION "1.3.0" 45 46 47 static const unsigned int kMinSourceWidth = 12; 48 static const unsigned int kQualitySliderWidth = 28; 49 static const unsigned int kDurationWidth = 10; 50 51 52 // #pragma mark - DirectoryFilter 53 54 55 class DirectoryFilter : public BRefFilter { 56 public: 57 DirectoryFilter() {}; 58 virtual bool Filter(const entry_ref* ref, 59 BNode* node, struct stat_beos* st, const char* filetype) 60 { 61 return node->IsDirectory(); 62 } 63 }; 64 65 66 // #pragma mark - FileFormatMenuItem 67 68 69 class FileFormatMenuItem : public BMenuItem { 70 public: 71 FileFormatMenuItem(media_file_format* format); 72 virtual ~FileFormatMenuItem(); 73 74 media_file_format fFileFormat; 75 }; 76 77 78 FileFormatMenuItem::FileFormatMenuItem(media_file_format* format) 79 : 80 BMenuItem(format->pretty_name, new BMessage(FORMAT_SELECT_MESSAGE)) 81 { 82 memcpy(&fFileFormat, format, sizeof(fFileFormat)); 83 } 84 85 86 FileFormatMenuItem::~FileFormatMenuItem() 87 { 88 } 89 90 91 // #pragma mark - CodecMenuItem 92 93 94 class CodecMenuItem : public BMenuItem { 95 public: 96 CodecMenuItem(media_codec_info* ci, uint32 message_type); 97 virtual ~CodecMenuItem(); 98 99 media_codec_info fCodecInfo; 100 }; 101 102 103 CodecMenuItem::CodecMenuItem(media_codec_info* ci, uint32 message_type) 104 : 105 BMenuItem(ci->pretty_name, new BMessage(message_type)) 106 { 107 memcpy(&fCodecInfo, ci, sizeof(fCodecInfo)); 108 } 109 110 111 CodecMenuItem::~CodecMenuItem() 112 { 113 } 114 115 116 // #pragma mark - OutputBox 117 118 119 class OutputBox : public BBox { 120 public: 121 OutputBox(border_style border, BView* child); 122 virtual void FrameResized(float width, float height) 123 { 124 MediaConverterWindow* window 125 = dynamic_cast<MediaConverterWindow*>(Window()); 126 if (window != NULL) 127 window->TruncateOutputFolderPath(); 128 BBox::FrameResized(width, height); 129 } 130 }; 131 132 133 OutputBox::OutputBox(border_style border, BView* child) 134 : 135 BBox(border, child) 136 { 137 } 138 139 140 // #pragma mark - MediaConverterWindow 141 142 143 MediaConverterWindow::MediaConverterWindow(BRect frame) 144 : 145 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("MediaConverter"), 146 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_NOT_ZOOMABLE 147 | B_NOT_V_RESIZABLE | B_ASYNCHRONOUS_CONTROLS 148 | B_AUTO_UPDATE_SIZE_LIMITS), 149 fVideoQuality(75), 150 fAudioQuality(75), 151 fSaveFilePanel(NULL), 152 fOpenFilePanel(NULL), 153 fOutputDirSpecified(false), 154 fEnabled(true), 155 fConverting(false), 156 fCancelling(false) 157 { 158 BPath outputDir; 159 if (find_directory(B_USER_DIRECTORY, &outputDir) != B_OK) 160 outputDir.SetTo("/boot/home"); 161 fOutputDir.SetTo(outputDir.Path()); 162 163 fMenuBar = new BMenuBar("menubar"); 164 _CreateMenu(); 165 166 float padding = be_control_look->DefaultItemSpacing(); 167 168 fListView = new MediaFileListView(); 169 fListView->SetExplicitMinSize(BSize(padding * kMinSourceWidth, B_SIZE_UNSET)); 170 BScrollView* scroller = new BScrollView(NULL, fListView, 0, false, true); 171 172 // file list view box 173 fSourcesBox = new BBox(B_FANCY_BORDER, scroller); 174 fSourcesBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0)); 175 // fSourcesBox's layout adjusted in _UpdateLabels 176 177 // info box 178 fInfoView = new MediaFileInfoView(); 179 fInfoView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 180 B_ALIGN_VERTICAL_UNSET)); 181 fInfoBox = new BBox(B_FANCY_BORDER, fInfoView); 182 183 // output menu fields 184 fFormatMenu = new BMenuField(NULL, B_TRANSLATE("File format:"), 185 new BPopUpMenu("")); 186 fAudioMenu = new BMenuField(NULL, B_TRANSLATE("Audio encoding:"), 187 new BPopUpMenu("")); 188 fVideoMenu = new BMenuField(NULL, B_TRANSLATE("Video encoding:"), 189 new BPopUpMenu("")); 190 191 // output folder 192 fDestButton = new BButton(B_TRANSLATE("Output folder"), 193 new BMessage(OUTPUT_FOLDER_MESSAGE)); 194 BAlignment labelAlignment(be_control_look->DefaultLabelAlignment()); 195 fOutputFolder = new BStringView(NULL, outputDir.Path()); 196 fOutputFolder->SetExplicitAlignment(labelAlignment); 197 198 // start/end duration 199 fStartDurationTC = new BTextControl(NULL, "0", NULL); 200 BLayoutItem* startDuration = fStartDurationTC->CreateTextViewLayoutItem(); 201 startDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET)); 202 startDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 203 B_ALIGN_VERTICAL_CENTER)); 204 fEndDurationTC = new BTextControl(NULL, "0", NULL); 205 BLayoutItem* endDuration = fEndDurationTC->CreateTextViewLayoutItem(); 206 endDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET)); 207 endDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, 208 B_ALIGN_VERTICAL_CENTER)); 209 210 // video quality 211 fVideoQualitySlider = new BSlider("VSlider", "" , 212 new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL); 213 fVideoQualitySlider->SetModificationMessage( 214 new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE)); 215 fVideoQualitySlider->SetValue(fVideoQuality); 216 fVideoQualitySlider->SetEnabled(false); 217 fVideoQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth, 218 B_SIZE_UNSET)); 219 220 // audio quality 221 fAudioQualitySlider = new BSlider("ASlider", "" , 222 new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL); 223 fAudioQualitySlider->SetModificationMessage( 224 new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE)); 225 fAudioQualitySlider->SetValue(fAudioQuality); 226 fAudioQualitySlider->SetEnabled(false); 227 fAudioQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth, 228 B_SIZE_UNSET)); 229 230 // output format box 231 BView* outputGrid = BLayoutBuilder::Grid<>() 232 .Add(fFormatMenu->CreateLabelLayoutItem(), 0, 0) 233 .Add(fFormatMenu->CreateMenuBarLayoutItem(), 1, 0) 234 .Add(fAudioMenu->CreateLabelLayoutItem(), 0, 1) 235 .Add(fAudioMenu->CreateMenuBarLayoutItem(), 1, 1) 236 .Add(fVideoMenu->CreateLabelLayoutItem(), 0, 2) 237 .Add(fVideoMenu->CreateMenuBarLayoutItem(), 1, 2) 238 .Add(fDestButton, 0, 3) 239 .Add(fOutputFolder, 1, 3) 240 .Add(fStartDurationTC->CreateLabelLayoutItem(), 0, 4) 241 .Add(startDuration, 1, 4) 242 .Add(fEndDurationTC->CreateLabelLayoutItem(), 0, 5) 243 .Add(endDuration, 1, 5) 244 .Add(fVideoQualitySlider, 0, 6, 2, 1) 245 .Add(fAudioQualitySlider, 0, 7, 2, 1) 246 .View(); 247 outputGrid->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH, 248 B_ALIGN_USE_FULL_HEIGHT)); 249 fOutputBox = new OutputBox(B_FANCY_BORDER, outputGrid); 250 fOutputBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0)); 251 // fOutputBox's layout adjusted in _UpdateLabels 252 253 // buttons 254 fPreviewButton = new BButton(B_TRANSLATE("Preview"), 255 new BMessage(PREVIEW_MESSAGE)); 256 fPreviewButton->SetEnabled(false); 257 258 fConvertButton = new BButton(B_TRANSLATE("Convert"), 259 new BMessage(CONVERT_BUTTON_MESSAGE)); 260 261 // Status views 262 fStatus = new BStringView(NULL, NULL); 263 fStatus->SetExplicitAlignment(labelAlignment); 264 fFileStatus = new BStringView(NULL, NULL); 265 fFileStatus->SetExplicitAlignment(labelAlignment); 266 267 SetStatusMessage(""); 268 _UpdateLabels(); 269 270 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 271 .SetInsets(0, 0, 0, 0) 272 .Add(fMenuBar) 273 .AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING) 274 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, 275 B_USE_WINDOW_SPACING, 0) 276 .Add(fSourcesBox) 277 .AddGroup(B_VERTICAL, B_USE_ITEM_SPACING) 278 .Add(fInfoBox) 279 .Add(fOutputBox) 280 .End() 281 .End() 282 .AddGrid(B_USE_ITEM_SPACING) 283 .SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING, 284 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING) 285 .Add(fStatus, 0, 0) 286 .Add(fFileStatus, 0, 1) 287 .Add(BSpaceLayoutItem::CreateGlue(), 1, 0) 288 .Add(fPreviewButton, 2, 0) 289 .Add(fConvertButton, 3, 0) 290 .End(); 291 } 292 293 294 MediaConverterWindow::~MediaConverterWindow() 295 { 296 delete fSaveFilePanel; 297 delete fOpenFilePanel; 298 } 299 300 301 // #pragma mark - 302 303 304 void 305 MediaConverterWindow::MessageReceived(BMessage* message) 306 { 307 entry_ref inRef; 308 309 char buffer[40]; 310 BEntry inEntry; 311 312 switch (message->what) { 313 #if B_BEOS_VERSION <= B_BEOS_VERSION_6 314 case B_LANGUAGE_CHANGED: 315 LanguageChanged(); 316 break; 317 #endif 318 319 case INIT_FORMAT_MENUS: 320 BuildFormatMenu(); 321 if (CountSourceFiles() == 0) 322 SetEnabled(false, false); 323 break; 324 325 case B_SIMPLE_DATA: 326 if (message->WasDropped()) { 327 DetachCurrentMessage(); 328 message->what = B_REFS_RECEIVED; 329 BMessenger(be_app).SendMessage(message); 330 delete message; 331 } 332 break; 333 334 case FORMAT_SELECT_MESSAGE: 335 BuildAudioVideoMenus(); 336 break; 337 case AUDIO_CODEC_SELECT_MESSAGE: 338 break; 339 case VIDEO_CODEC_SELECT_MESSAGE: 340 break; 341 342 case CONVERT_BUTTON_MESSAGE: 343 if (!fConverting) { 344 fConvertButton->SetLabel(B_TRANSLATE("Cancel")); 345 fConverting = true; 346 SetStatusMessage(B_TRANSLATE("Convert")); 347 SetEnabled(false, true); 348 BMessenger(be_app).SendMessage(START_CONVERSION_MESSAGE); 349 } else if (!fCancelling) { 350 fCancelling = true; 351 SetStatusMessage(B_TRANSLATE("Cancelling" B_UTF8_ELLIPSIS)); 352 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE); 353 } 354 break; 355 356 case CONVERSION_DONE_MESSAGE: 357 { 358 SetStatusMessage(fCancelling ? B_TRANSLATE("Conversion cancelled") 359 : B_TRANSLATE("Conversion completed")); 360 fConverting = false; 361 fCancelling = false; 362 bool enable = CountSourceFiles() > 0; 363 SetEnabled(enable, enable); 364 fConvertButton->SetLabel(B_TRANSLATE("Convert")); 365 break; 366 } 367 368 case OUTPUT_FOLDER_MESSAGE: 369 // Execute Save Panel 370 if (fSaveFilePanel == NULL) { 371 BButton* selectThisDir; 372 373 BMessage folderSelect(FOLDER_SELECT_MESSAGE); 374 fSaveFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, 375 B_DIRECTORY_NODE, true, &folderSelect, NULL, false, true); 376 fSaveFilePanel->SetButtonLabel(B_DEFAULT_BUTTON, 377 B_TRANSLATE("Select")); 378 fSaveFilePanel->SetTarget(this); 379 380 fSaveFilePanel->Window()->Lock(); 381 fSaveFilePanel->Window()->SetTitle( 382 B_TRANSLATE("MediaConverter+:SaveDirectory")); 383 BRect buttonRect 384 = fSaveFilePanel->Window()->ChildAt(0)->FindView( 385 "cancel button")->Frame(); 386 buttonRect.right = buttonRect.left - 20; 387 buttonRect.left = buttonRect.right - 130; 388 selectThisDir = new BButton(buttonRect, NULL, 389 B_TRANSLATE("Select this folder"), 390 new BMessage(SELECT_THIS_DIR_MESSAGE), 391 B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT); 392 selectThisDir->SetTarget(this); 393 fSaveFilePanel->Window()->ChildAt(0)->AddChild(selectThisDir); 394 fSaveFilePanel->Window()->Unlock(); 395 396 fSaveFilePanel->SetRefFilter(new DirectoryFilter); 397 } 398 fSaveFilePanel->Show(); 399 break; 400 401 case FOLDER_SELECT_MESSAGE: 402 // "SELECT" Button at Save Panel Pushed 403 fSaveFilePanel->GetNextSelectedRef(&inRef); 404 inEntry.SetTo(&inRef, true); 405 _SetOutputFolder(inEntry); 406 fOutputDirSpecified = true; 407 fSaveFilePanel->Rewind(); 408 break; 409 410 case SELECT_THIS_DIR_MESSAGE: 411 // "THIS DIR" Button at Save Panel Pushed 412 fSaveFilePanel->GetPanelDirectory(&inRef); 413 fSaveFilePanel->Hide(); 414 inEntry.SetTo(&inRef, true); 415 _SetOutputFolder(inEntry); 416 fOutputDirSpecified = true; 417 break; 418 419 case OPEN_FILE_MESSAGE: 420 // Execute Open Panel 421 if (!fOpenFilePanel) { 422 fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, 423 B_FILE_NODE, true, NULL, NULL, false, true); 424 fOpenFilePanel->SetTarget(this); 425 } 426 fOpenFilePanel->Show(); 427 break; 428 429 case B_REFS_RECEIVED: 430 // Media Files Seleced by Open Panel 431 DetachCurrentMessage(); 432 message->what = B_REFS_RECEIVED; 433 BMessenger(be_app).SendMessage(message); 434 // fall through 435 436 case B_CANCEL: 437 break; 438 439 case QUIT_MESSAGE: 440 MediaConverterWindow::QuitRequested(); 441 break; 442 443 case PREVIEW_MESSAGE: 444 { 445 // Build the command line to launch the preview application. 446 // TODO: Launch the default app instead of hardcoded MediaPlayer! 447 int32 srcIndex = fListView->CurrentSelection(); 448 BMediaFile* inFile = NULL; 449 status_t status = GetSourceFileAt(srcIndex, &inFile, &inRef); 450 451 const char* argv[3]; 452 BString startPosString; 453 BPath path; 454 455 if (status == B_OK) { 456 argv[0] = "-pos"; 457 // NOTE: -pos argument is currently not supported by Haiku 458 // MediaPlayer. 459 startPosString << fStartDurationTC->Text(); 460 startPosString << "000"; 461 argv[1] = startPosString.String(); 462 463 status = inEntry.SetTo(&inRef); 464 } 465 466 if (status == B_OK) { 467 status = inEntry.GetPath(&path); 468 if (status == B_OK) 469 argv[2] = path.Path(); 470 } 471 472 if (status == B_OK) { 473 status = be_roster->Launch( 474 "application/x-vnd.Haiku-MediaPlayer", 475 3, (char**)argv, NULL); 476 } 477 478 if (status != B_OK && status != B_ALREADY_RUNNING) { 479 BString errorString(B_TRANSLATE("Error launching: %strError%")); 480 errorString.ReplaceFirst("%strError%", strerror(status)); 481 BAlert* alert = new BAlert(B_TRANSLATE("Error"), 482 errorString.String(), B_TRANSLATE("OK")); 483 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 484 alert->Go(); 485 } 486 break; 487 } 488 489 case VIDEO_QUALITY_CHANGED_MESSAGE: 490 { 491 int32 value; 492 message->FindInt32("be:value", &value); 493 snprintf(buffer, sizeof(buffer), 494 B_TRANSLATE("Video quality: %3d%%"), (int8)value); 495 fVideoQualitySlider->SetLabel(buffer); 496 fVideoQuality = value; 497 break; 498 } 499 500 case AUDIO_QUALITY_CHANGED_MESSAGE: 501 { 502 int32 value; 503 message->FindInt32("be:value", &value); 504 snprintf(buffer, sizeof(buffer), 505 B_TRANSLATE("Audio quality: %3d%%"), (int8)value); 506 fAudioQualitySlider->SetLabel(buffer); 507 fAudioQuality = value; 508 break; 509 } 510 511 default: 512 BWindow::MessageReceived(message); 513 } 514 } 515 516 517 bool 518 MediaConverterWindow::QuitRequested() 519 { 520 if (!fConverting) { 521 BMessenger(be_app).SendMessage(B_QUIT_REQUESTED); 522 return true; 523 } else if (!fCancelling) { 524 fCancelling = true; 525 SetStatusMessage(B_TRANSLATE("Cancelling")); 526 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE); 527 } 528 529 return false; 530 } 531 532 533 // #pragma mark - 534 535 536 void 537 MediaConverterWindow::LanguageChanged() 538 { 539 _DestroyMenu(); 540 _CreateMenu(); 541 _UpdateLabels(); 542 BuildAudioVideoMenus(); 543 Lock(); 544 fInfoView->Invalidate(); 545 Unlock(); 546 } 547 548 549 void 550 MediaConverterWindow::BuildAudioVideoMenus() 551 { 552 BMenu* menu = fAudioMenu->Menu(); 553 BMenuItem* item; 554 555 // clear out old audio codec menu items 556 while ((item = menu->RemoveItem((int32)0)) != NULL) 557 delete item; 558 559 bool separator = true; 560 561 // get selected file format 562 FileFormatMenuItem* ffmi 563 = (FileFormatMenuItem*)fFormatMenu->Menu()->FindMarked(); 564 media_file_format* mf_format = &(ffmi->fFileFormat); 565 566 media_format format, outfmt; 567 format.Clear(); 568 media_codec_info codec_info; 569 int32 cookie = 0; 570 CodecMenuItem* cmi; 571 572 // add available audio encoders to menu 573 format.type = B_MEDIA_RAW_AUDIO; 574 format.u.raw_audio = media_raw_audio_format::wildcard; 575 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info) 576 == B_OK) { 577 if (separator) { 578 menu->AddItem(new BMenuItem( 579 B_TRANSLATE_CONTEXT("No audio", "Audio codecs list"), 580 new BMessage(AUDIO_CODEC_SELECT_MESSAGE))); 581 menu->AddSeparatorItem(); 582 separator = false; 583 } 584 585 cmi = new CodecMenuItem(&codec_info, AUDIO_CODEC_SELECT_MESSAGE); 586 menu->AddItem(cmi); 587 // reset media format struct 588 /* 589 format.type = B_MEDIA_RAW_AUDIO; 590 format.u.raw_audio = media_raw_audio_format::wildcard; 591 */ 592 } 593 594 // mark first audio encoder 595 item = menu->ItemAt(0); 596 if (item != NULL) { 597 fAudioMenu->SetEnabled(fEnabled); 598 fAudioQualitySlider->SetEnabled(fEnabled); 599 item->SetMarked(true); 600 ((BInvoker*)item)->Invoke(); 601 } else { 602 item = new BMenuItem( 603 B_TRANSLATE_CONTEXT("None available", "Audio codecs"), NULL); 604 menu->AddItem(item); 605 item->SetMarked(true); 606 fAudioMenu->SetEnabled(false); 607 fAudioQualitySlider->SetEnabled(false); 608 } 609 610 // clear out old video codec menu items 611 menu = fVideoMenu->Menu(); 612 while ((item = menu->RemoveItem((int32)0)) != NULL) 613 delete item; 614 615 separator = true; 616 617 // construct a generic video format. Some of these parameters 618 // seem silly, but are needed for R4.5.x, which is more picky 619 // than subsequent BeOS releases will be. 620 format.Clear(); 621 format.type = B_MEDIA_RAW_VIDEO; 622 format.u.raw_video.last_active = (uint32)(240 - 1); 623 format.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; 624 format.u.raw_video.display.format = B_RGB32; 625 format.u.raw_video.display.line_width = (int32)320; 626 format.u.raw_video.display.line_count = (int32)240; 627 format.u.raw_video.display.bytes_per_row = 4 * 320; 628 629 // add available video encoders to menu 630 cookie = 0; 631 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info) 632 == B_OK) { 633 if (separator) { 634 menu->AddItem(new BMenuItem( 635 B_TRANSLATE_CONTEXT("No video", "Video codecs list"), 636 new BMessage(VIDEO_CODEC_SELECT_MESSAGE))); 637 menu->AddSeparatorItem(); 638 separator = false; 639 } 640 641 cmi = new CodecMenuItem(&codec_info, VIDEO_CODEC_SELECT_MESSAGE); 642 menu->AddItem(cmi); 643 } 644 645 // mark first video encoder 646 item = menu->ItemAt(0); 647 if (item != NULL) { 648 fVideoMenu->SetEnabled(fEnabled); 649 fVideoQualitySlider->SetEnabled(fEnabled); 650 item->SetMarked(true); 651 ((BInvoker*)item)->Invoke(); 652 } else { 653 item = new BMenuItem( 654 B_TRANSLATE_CONTEXT("None available", "Video codecs"), NULL); 655 menu->AddItem(item); 656 item->SetMarked(true); 657 fVideoMenu->SetEnabled(false); 658 fVideoQualitySlider->SetEnabled(false); 659 } 660 } 661 662 void 663 MediaConverterWindow::GetSelectedFormatInfo(media_file_format** format, 664 media_codec_info** audio, media_codec_info** video) 665 { 666 *audio = NULL; 667 *video = NULL; 668 *format = NULL; 669 670 FileFormatMenuItem* formatItem = 671 dynamic_cast<FileFormatMenuItem*>(fFormatMenu->Menu()->FindMarked()); 672 if (formatItem != NULL) 673 *format = &(formatItem->fFileFormat); 674 675 *audio = *video = NULL; 676 CodecMenuItem* codecItem = 677 dynamic_cast<CodecMenuItem*>(fAudioMenu->Menu()->FindMarked()); 678 if (codecItem != NULL) 679 *audio = &(codecItem->fCodecInfo); 680 681 codecItem = dynamic_cast<CodecMenuItem*>(fVideoMenu->Menu()->FindMarked()); 682 if (codecItem != NULL) 683 *video = &(codecItem->fCodecInfo); 684 } 685 686 687 void 688 MediaConverterWindow::BuildFormatMenu() 689 { 690 BMenu* menu = fFormatMenu->Menu(); 691 BMenuItem* item; 692 693 // clear out old format menu items 694 while ((item = menu->RemoveItem((int32)0)) != NULL) 695 delete item; 696 697 // add menu items for each file format 698 media_file_format mfi; 699 int32 cookie = 0; 700 FileFormatMenuItem* ff_item; 701 while (get_next_file_format(&cookie, &mfi) == B_OK) { 702 if ((mfi.capabilities & media_file_format::B_WRITABLE) == 0) 703 continue; 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 BPath path; 1005 entry.GetPath(&path); 1006 if (access(path.Path(), W_OK) != -1) { 1007 fOutputDir.SetTo(&entry); 1008 } else { 1009 BString errorString(B_TRANSLATE("Error writing to location: %strPath%." 1010 " Defaulting to location: /boot/home")); 1011 errorString.ReplaceFirst("%strPath%", path.Path()); 1012 BAlert* alert = new BAlert(B_TRANSLATE("Error"), 1013 errorString.String(), B_TRANSLATE("OK")); 1014 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1015 alert->Go(); 1016 fOutputDir.SetTo("/boot/home"); 1017 } 1018 TruncateOutputFolderPath(); 1019 } 1020