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, B_USE_DEFAULT_SPACING) 278 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING, 279 B_USE_WINDOW_SPACING, 0) 280 .Add(fSourcesBox) 281 .AddGroup(B_VERTICAL, B_USE_ITEM_SPACING) 282 .Add(fInfoBox) 283 .Add(fOutputBox) 284 .End() 285 .End() 286 .AddGrid(B_USE_ITEM_SPACING) 287 .SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING, 288 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING) 289 .Add(fStatus, 0, 0) 290 .Add(fFileStatus, 0, 1) 291 .Add(BSpaceLayoutItem::CreateGlue(), 1, 0) 292 .Add(fPreviewButton, 2, 0) 293 .Add(fConvertButton, 3, 0) 294 .End(); 295 } 296 297 298 MediaConverterWindow::~MediaConverterWindow() 299 { 300 delete fSaveFilePanel; 301 delete fOpenFilePanel; 302 } 303 304 305 // #pragma mark - 306 307 308 void 309 MediaConverterWindow::MessageReceived(BMessage* message) 310 { 311 entry_ref inRef; 312 313 char buffer[40]; 314 BEntry inEntry; 315 316 switch (message->what) { 317 #if B_BEOS_VERSION <= B_BEOS_VERSION_6 318 case B_LANGUAGE_CHANGED: 319 LanguageChanged(); 320 break; 321 #endif 322 323 case INIT_FORMAT_MENUS: 324 BuildFormatMenu(); 325 if (CountSourceFiles() == 0) 326 SetEnabled(false, false); 327 break; 328 329 case B_SIMPLE_DATA: 330 if (message->WasDropped()) { 331 DetachCurrentMessage(); 332 message->what = B_REFS_RECEIVED; 333 BMessenger(be_app).SendMessage(message); 334 delete message; 335 } 336 break; 337 338 case FORMAT_SELECT_MESSAGE: 339 BuildAudioVideoMenus(); 340 break; 341 case AUDIO_CODEC_SELECT_MESSAGE: 342 break; 343 case VIDEO_CODEC_SELECT_MESSAGE: 344 break; 345 346 case CONVERT_BUTTON_MESSAGE: 347 if (!fConverting) { 348 fConvertButton->SetLabel(B_TRANSLATE("Cancel")); 349 fConverting = true; 350 SetStatusMessage(B_TRANSLATE("Convert")); 351 SetEnabled(false, true); 352 BMessenger(be_app).SendMessage(START_CONVERSION_MESSAGE); 353 } else if (!fCancelling) { 354 fCancelling = true; 355 SetStatusMessage(B_TRANSLATE("Cancelling" B_UTF8_ELLIPSIS)); 356 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE); 357 } 358 break; 359 360 case CONVERSION_DONE_MESSAGE: 361 { 362 SetStatusMessage(fCancelling ? B_TRANSLATE("Conversion cancelled") 363 : B_TRANSLATE("Conversion completed")); 364 fConverting = false; 365 fCancelling = false; 366 bool enable = CountSourceFiles() > 0; 367 SetEnabled(enable, enable); 368 fConvertButton->SetLabel(B_TRANSLATE("Convert")); 369 break; 370 } 371 372 case OUTPUT_FOLDER_MESSAGE: 373 // Execute Save Panel 374 if (fSaveFilePanel == NULL) { 375 BButton* selectThisDir; 376 377 BMessage folderSelect(FOLDER_SELECT_MESSAGE); 378 fSaveFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, 379 B_DIRECTORY_NODE, true, &folderSelect, NULL, false, true); 380 fSaveFilePanel->SetButtonLabel(B_DEFAULT_BUTTON, 381 B_TRANSLATE("Select")); 382 fSaveFilePanel->SetTarget(this); 383 384 fSaveFilePanel->Window()->Lock(); 385 fSaveFilePanel->Window()->SetTitle( 386 B_TRANSLATE("MediaConverter+:SaveDirectory")); 387 BRect buttonRect 388 = fSaveFilePanel->Window()->ChildAt(0)->FindView( 389 "cancel button")->Frame(); 390 buttonRect.right = buttonRect.left - 20; 391 buttonRect.left = buttonRect.right - 130; 392 selectThisDir = new BButton(buttonRect, NULL, 393 B_TRANSLATE("Select this folder"), 394 new BMessage(SELECT_THIS_DIR_MESSAGE), 395 B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT); 396 selectThisDir->SetTarget(this); 397 fSaveFilePanel->Window()->ChildAt(0)->AddChild(selectThisDir); 398 fSaveFilePanel->Window()->Unlock(); 399 400 fSaveFilePanel->SetRefFilter(new DirectoryFilter); 401 } 402 fSaveFilePanel->Show(); 403 break; 404 405 case FOLDER_SELECT_MESSAGE: 406 // "SELECT" Button at Save Panel Pushed 407 fSaveFilePanel->GetNextSelectedRef(&inRef); 408 inEntry.SetTo(&inRef, true); 409 _SetOutputFolder(inEntry); 410 fOutputDirSpecified = true; 411 fSaveFilePanel->Rewind(); 412 break; 413 414 case SELECT_THIS_DIR_MESSAGE: 415 // "THIS DIR" Button at Save Panel Pushed 416 fSaveFilePanel->GetPanelDirectory(&inRef); 417 fSaveFilePanel->Hide(); 418 inEntry.SetTo(&inRef, true); 419 _SetOutputFolder(inEntry); 420 fOutputDirSpecified = true; 421 break; 422 423 case OPEN_FILE_MESSAGE: 424 // Execute Open Panel 425 if (!fOpenFilePanel) { 426 fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, 427 B_FILE_NODE, true, NULL, NULL, false, true); 428 fOpenFilePanel->SetTarget(this); 429 } 430 fOpenFilePanel->Show(); 431 break; 432 433 case B_REFS_RECEIVED: 434 // Media Files Seleced by Open Panel 435 DetachCurrentMessage(); 436 message->what = B_REFS_RECEIVED; 437 BMessenger(be_app).SendMessage(message); 438 // fall through 439 440 case B_CANCEL: 441 break; 442 443 case QUIT_MESSAGE: 444 MediaConverterWindow::QuitRequested(); 445 break; 446 447 case PREVIEW_MESSAGE: 448 { 449 // Build the command line to launch the preview application. 450 // TODO: Launch the default app instead of hardcoded MediaPlayer! 451 int32 srcIndex = fListView->CurrentSelection(); 452 BMediaFile* inFile = NULL; 453 status_t status = GetSourceFileAt(srcIndex, &inFile, &inRef); 454 455 const char* argv[3]; 456 BString startPosString; 457 BPath path; 458 459 if (status == B_OK) { 460 argv[0] = "-pos"; 461 // NOTE: -pos argument is currently not supported by Haiku 462 // MediaPlayer. 463 startPosString << fStartDurationTC->Text(); 464 startPosString << "000"; 465 argv[1] = startPosString.String(); 466 467 status = inEntry.SetTo(&inRef); 468 } 469 470 if (status == B_OK) { 471 status = inEntry.GetPath(&path); 472 if (status == B_OK) 473 argv[2] = path.Path(); 474 } 475 476 if (status == B_OK) { 477 status = be_roster->Launch( 478 "application/x-vnd.Haiku-MediaPlayer", 479 3, (char**)argv, NULL); 480 } 481 482 if (status != B_OK && status != B_ALREADY_RUNNING) { 483 BString errorString(B_TRANSLATE("Error launching: %strError%")); 484 errorString.ReplaceFirst("%strError%", strerror(status)); 485 BAlert* alert = new BAlert(B_TRANSLATE("Error"), 486 errorString.String(), B_TRANSLATE("OK")); 487 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 488 alert->Go(); 489 } 490 break; 491 } 492 493 case VIDEO_QUALITY_CHANGED_MESSAGE: 494 { 495 int32 value; 496 message->FindInt32("be:value", &value); 497 snprintf(buffer, sizeof(buffer), 498 B_TRANSLATE("Video quality: %3d%%"), (int8)value); 499 fVideoQualitySlider->SetLabel(buffer); 500 fVideoQuality = value; 501 break; 502 } 503 504 case AUDIO_QUALITY_CHANGED_MESSAGE: 505 { 506 int32 value; 507 message->FindInt32("be:value", &value); 508 snprintf(buffer, sizeof(buffer), 509 B_TRANSLATE("Audio quality: %3d%%"), (int8)value); 510 fAudioQualitySlider->SetLabel(buffer); 511 fAudioQuality = value; 512 break; 513 } 514 515 default: 516 BWindow::MessageReceived(message); 517 } 518 } 519 520 521 bool 522 MediaConverterWindow::QuitRequested() 523 { 524 if (!fConverting) { 525 BMessenger(be_app).SendMessage(B_QUIT_REQUESTED); 526 return true; 527 } else if (!fCancelling) { 528 fCancelling = true; 529 SetStatusMessage(B_TRANSLATE("Cancelling")); 530 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE); 531 } 532 533 return false; 534 } 535 536 537 // #pragma mark - 538 539 540 void 541 MediaConverterWindow::LanguageChanged() 542 { 543 _DestroyMenu(); 544 _CreateMenu(); 545 _UpdateLabels(); 546 BuildAudioVideoMenus(); 547 Lock(); 548 fInfoView->Invalidate(); 549 Unlock(); 550 } 551 552 553 void 554 MediaConverterWindow::BuildAudioVideoMenus() 555 { 556 BMenu* menu = fAudioMenu->Menu(); 557 BMenuItem* item; 558 559 // clear out old audio codec menu items 560 while ((item = menu->RemoveItem((int32)0)) != NULL) 561 delete item; 562 563 bool separator = true; 564 565 // get selected file format 566 FileFormatMenuItem* ffmi 567 = (FileFormatMenuItem*)fFormatMenu->Menu()->FindMarked(); 568 media_file_format* mf_format = &(ffmi->fFileFormat); 569 570 media_format format, outfmt; 571 memset(&format, 0, sizeof(format)); 572 media_codec_info codec_info; 573 int32 cookie = 0; 574 CodecMenuItem* cmi; 575 576 // add available audio encoders to menu 577 format.type = B_MEDIA_RAW_AUDIO; 578 format.u.raw_audio = media_raw_audio_format::wildcard; 579 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info) 580 == B_OK) { 581 if (separator) { 582 menu->AddItem(new BMenuItem( 583 B_TRANSLATE_CONTEXT("No audio", "Audio codecs list"), 584 new BMessage(AUDIO_CODEC_SELECT_MESSAGE))); 585 menu->AddSeparatorItem(); 586 separator = false; 587 } 588 589 cmi = new CodecMenuItem(&codec_info, AUDIO_CODEC_SELECT_MESSAGE); 590 menu->AddItem(cmi); 591 // reset media format struct 592 /* 593 format.type = B_MEDIA_RAW_AUDIO; 594 format.u.raw_audio = media_raw_audio_format::wildcard; 595 */ 596 } 597 598 // mark first audio encoder 599 item = menu->ItemAt(0); 600 if (item != NULL) { 601 fAudioMenu->SetEnabled(fEnabled); 602 fAudioQualitySlider->SetEnabled(fEnabled); 603 item->SetMarked(true); 604 ((BInvoker*)item)->Invoke(); 605 } else { 606 item = new BMenuItem( 607 B_TRANSLATE_CONTEXT("None available", "Audio codecs"), NULL); 608 menu->AddItem(item); 609 item->SetMarked(true); 610 fAudioMenu->SetEnabled(false); 611 fAudioQualitySlider->SetEnabled(false); 612 } 613 614 // clear out old video codec menu items 615 menu = fVideoMenu->Menu(); 616 while ((item = menu->RemoveItem((int32)0)) != NULL) 617 delete item; 618 619 separator = true; 620 621 // construct a generic video format. Some of these parameters 622 // seem silly, but are needed for R4.5.x, which is more picky 623 // than subsequent BeOS releases will be. 624 memset(&format, 0, sizeof(format)); 625 format.type = B_MEDIA_RAW_VIDEO; 626 format.u.raw_video.last_active = (uint32)(240 - 1); 627 format.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT; 628 format.u.raw_video.display.format = B_RGB32; 629 format.u.raw_video.display.line_width = (int32)320; 630 format.u.raw_video.display.line_count = (int32)240; 631 format.u.raw_video.display.bytes_per_row = 4 * 320; 632 633 // add available video encoders to menu 634 cookie = 0; 635 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info) 636 == B_OK) { 637 if (separator) { 638 menu->AddItem(new BMenuItem( 639 B_TRANSLATE_CONTEXT("No video", "Video codecs list"), 640 new BMessage(VIDEO_CODEC_SELECT_MESSAGE))); 641 menu->AddSeparatorItem(); 642 separator = false; 643 } 644 645 cmi = new CodecMenuItem(&codec_info, VIDEO_CODEC_SELECT_MESSAGE); 646 menu->AddItem(cmi); 647 } 648 649 // mark first video encoder 650 item = menu->ItemAt(0); 651 if (item != NULL) { 652 fVideoMenu->SetEnabled(fEnabled); 653 fVideoQualitySlider->SetEnabled(fEnabled); 654 item->SetMarked(true); 655 ((BInvoker*)item)->Invoke(); 656 } else { 657 item = new BMenuItem( 658 B_TRANSLATE_CONTEXT("None available", "Video codecs"), NULL); 659 menu->AddItem(item); 660 item->SetMarked(true); 661 fVideoMenu->SetEnabled(false); 662 fVideoQualitySlider->SetEnabled(false); 663 } 664 } 665 666 void 667 MediaConverterWindow::GetSelectedFormatInfo(media_file_format** format, 668 media_codec_info** audio, media_codec_info** video) 669 { 670 *audio = NULL; 671 *video = NULL; 672 *format = NULL; 673 674 FileFormatMenuItem* formatItem = 675 dynamic_cast<FileFormatMenuItem*>(fFormatMenu->Menu()->FindMarked()); 676 if (formatItem != NULL) 677 *format = &(formatItem->fFileFormat); 678 679 *audio = *video = NULL; 680 CodecMenuItem* codecItem = 681 dynamic_cast<CodecMenuItem*>(fAudioMenu->Menu()->FindMarked()); 682 if (codecItem != NULL) 683 *audio = &(codecItem->fCodecInfo); 684 685 codecItem = dynamic_cast<CodecMenuItem*>(fVideoMenu->Menu()->FindMarked()); 686 if (codecItem != NULL) 687 *video = &(codecItem->fCodecInfo); 688 } 689 690 691 void 692 MediaConverterWindow::BuildFormatMenu() 693 { 694 BMenu* menu = fFormatMenu->Menu(); 695 BMenuItem* item; 696 697 // clear out old format menu items 698 while ((item = menu->RemoveItem((int32)0)) != NULL) 699 delete item; 700 701 // add menu items for each file format 702 media_file_format mfi; 703 int32 cookie = 0; 704 FileFormatMenuItem* ff_item; 705 while (get_next_file_format(&cookie, &mfi) == B_OK) { 706 if ((mfi.capabilities & media_file_format::B_WRITABLE) == 0) 707 continue; 708 ff_item = new FileFormatMenuItem(&mfi); 709 menu->AddItem(ff_item); 710 } 711 712 // mark first item 713 item = menu->ItemAt(0); 714 if (item != NULL) { 715 item->SetMarked(true); 716 ((BInvoker*)item)->Invoke(); 717 } 718 } 719 720 721 void 722 MediaConverterWindow::SetFileMessage(const char* message) 723 { 724 fFileStatus->SetText(message); 725 } 726 727 728 void 729 MediaConverterWindow::SetStatusMessage(const char* message) 730 { 731 fStatus->SetText(message); 732 } 733 734 735 // #pragma mark - 736 737 738 bool 739 MediaConverterWindow::AddSourceFile(BMediaFile* file, const entry_ref& ref) 740 { 741 if (!fListView->AddMediaItem(file, ref)) 742 return false; 743 744 if (!fOutputDirSpecified) { 745 BEntry entry(&ref); 746 entry.GetParent(&entry); 747 _SetOutputFolder(entry); 748 } 749 750 return true; 751 } 752 753 754 void 755 MediaConverterWindow::RemoveSourceFile(int32 index) 756 { 757 delete fListView->RemoveItem(index); 758 fStartDurationTC->SetText("0"); 759 fEndDurationTC->SetText("0"); 760 } 761 762 763 int32 764 MediaConverterWindow::CountSourceFiles() 765 { 766 return fListView->CountItems(); 767 } 768 769 770 status_t 771 MediaConverterWindow::GetSourceFileAt(int32 index, BMediaFile** _file, 772 entry_ref* ref) 773 { 774 MediaFileListItem* item = dynamic_cast<MediaFileListItem*>( 775 fListView->ItemAt(index)); 776 if (item != NULL) { 777 *_file = item->fMediaFile; 778 *ref = item->fRef; 779 return B_OK; 780 } else 781 return B_ERROR; 782 } 783 784 785 void 786 MediaConverterWindow::SourceFileSelectionChanged() 787 { 788 int32 selected = fListView->CurrentSelection(); 789 BMediaFile* file = NULL; 790 entry_ref ref; 791 bool enabled = GetSourceFileAt(selected, &file, &ref) == B_OK; 792 793 fPreviewButton->SetEnabled(enabled); 794 fVideoQualitySlider->SetEnabled(enabled); 795 fAudioQualitySlider->SetEnabled(enabled); 796 fStartDurationTC->SetEnabled(enabled); 797 fEndDurationTC->SetEnabled(enabled); 798 799 BString duration; 800 if (enabled) { 801 fInfoView->Update(file, &ref); 802 // HACK: get the fInfoView to update the duration "synchronously" 803 UpdateIfNeeded(); 804 duration << fInfoView->Duration() / 1000; 805 } else 806 duration = "0"; 807 808 // update duration text controls 809 fStartDurationTC->SetText("0"); 810 fEndDurationTC->SetText(duration.String()); 811 } 812 813 814 // #pragma mark - 815 816 817 void 818 MediaConverterWindow::SetEnabled(bool enabled, bool convertEnabled) 819 { 820 fConvertButton->SetEnabled(convertEnabled); 821 if (enabled == fEnabled) 822 return; 823 824 fFormatMenu->SetEnabled(enabled); 825 fAudioMenu->SetEnabled(enabled); 826 fVideoMenu->SetEnabled(enabled); 827 fListView->SetEnabled(enabled); 828 fStartDurationTC->SetEnabled(enabled); 829 fEndDurationTC->SetEnabled(enabled); 830 831 fEnabled = enabled; 832 } 833 834 835 bool 836 MediaConverterWindow::IsEnabled() 837 { 838 return fEnabled; 839 } 840 841 842 const char* 843 MediaConverterWindow::StartDuration() const 844 { 845 return fStartDurationTC->Text(); 846 } 847 848 849 const char* 850 MediaConverterWindow::EndDuration() const 851 { 852 return fEndDurationTC->Text(); 853 } 854 855 856 BDirectory 857 MediaConverterWindow::OutputDirectory() const 858 { 859 return fOutputDir; 860 } 861 862 863 void 864 MediaConverterWindow::SetAudioQualityLabel(const char* label) 865 { 866 fAudioQualitySlider->SetLabel(label); 867 } 868 869 870 void 871 MediaConverterWindow::SetVideoQualityLabel(const char* label) 872 { 873 fVideoQualitySlider->SetLabel(label); 874 } 875 876 877 void 878 MediaConverterWindow::TruncateOutputFolderPath() 879 { 880 BEntry entry; 881 fOutputDir.GetEntry(&entry); 882 BPath path; 883 entry.GetPath(&path); 884 BString pathString(path.Path()); 885 float maxWidth = fVideoMenu->MenuBar()->Frame().Width(); 886 887 fOutputFolder->TruncateString(&pathString, B_TRUNCATE_MIDDLE, maxWidth); 888 fOutputFolder->SetText(pathString.String()); 889 if (fOutputFolder->StringWidth(path.Path()) > maxWidth) 890 fOutputFolder->SetToolTip(path.Path()); 891 else 892 fOutputFolder->SetToolTip((const char*)NULL); 893 } 894 895 896 // #pragma mark - 897 898 899 void 900 MediaConverterWindow::_UpdateLabels() 901 { 902 if (fSourcesBox != NULL) { 903 fSourcesBox->SetLabel(B_TRANSLATE("Source files")); 904 _UpdateBBoxLayoutInsets(fSourcesBox); 905 } 906 907 if (fInfoBox != NULL) 908 fInfoBox->SetLabel(B_TRANSLATE("File details")); 909 910 if (fOutputBox != NULL) { 911 fOutputBox->SetLabel(B_TRANSLATE("Output format")); 912 _UpdateBBoxLayoutInsets(fOutputBox); 913 } 914 915 if (fConvertButton != NULL) 916 fConvertButton->SetLabel(B_TRANSLATE("Convert")); 917 918 if (fPreviewButton != NULL) 919 fPreviewButton->SetLabel(B_TRANSLATE("Preview")); 920 921 if (fDestButton != NULL) 922 fDestButton->SetLabel(B_TRANSLATE("Output folder")); 923 924 if (fVideoQualitySlider != NULL) { 925 char buffer[40]; 926 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Video quality: %3d%%"), 927 (int8)fVideoQuality); 928 fVideoQualitySlider->SetLabel(buffer); 929 fVideoQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), 930 B_TRANSLATE("High")); 931 } 932 933 if (fAudioQualitySlider != NULL) { 934 char buffer[40]; 935 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Audio quality: %3d%%"), 936 (int8)fAudioQuality); 937 fAudioQualitySlider->SetLabel(buffer); 938 fAudioQualitySlider->SetLimitLabels(B_TRANSLATE("Low"), 939 B_TRANSLATE("High")); 940 } 941 942 if (fStartDurationTC != NULL) 943 fStartDurationTC->SetLabel(B_TRANSLATE("Start [ms]: ")); 944 945 if (fEndDurationTC != NULL) 946 fEndDurationTC->SetLabel(B_TRANSLATE("End [ms]: ")); 947 948 if (fFormatMenu != NULL) 949 fFormatMenu->SetLabel(B_TRANSLATE("File format:")); 950 951 if (fAudioMenu != NULL) 952 fAudioMenu->SetLabel(B_TRANSLATE("Audio encoding:")); 953 954 if (fVideoMenu != NULL) 955 fVideoMenu->SetLabel(B_TRANSLATE("Video encoding:")); 956 957 SetFileMessage(B_TRANSLATE("Drop media files onto this window")); 958 } 959 960 961 void 962 MediaConverterWindow::_UpdateBBoxLayoutInsets(BBox* box) 963 { 964 BTwoDimensionalLayout* layout 965 = dynamic_cast<BTwoDimensionalLayout*>(box->GetLayout()); 966 if (layout != NULL) { 967 float padding = be_control_look->DefaultItemSpacing(); 968 layout->SetInsets(padding, box->TopBorderOffset() + padding, padding, 969 padding); 970 } 971 } 972 973 974 void 975 MediaConverterWindow::_DestroyMenu() 976 { 977 BMenu* menu; 978 979 while ((menu = fMenuBar->SubmenuAt(0)) != NULL) { 980 fMenuBar->RemoveItem(menu); 981 delete menu; 982 } 983 } 984 985 986 void 987 MediaConverterWindow::_CreateMenu() 988 { 989 BMenu* menu; 990 BMenuItem* item; 991 992 menu = new BMenu(B_TRANSLATE_CONTEXT("File", "Menu")); 993 item = new BMenuItem(B_TRANSLATE_CONTEXT("Open" B_UTF8_ELLIPSIS, "Menu"), 994 new BMessage(OPEN_FILE_MESSAGE), 'O'); 995 menu->AddItem(item); 996 menu->AddSeparatorItem(); 997 item = new BMenuItem(B_TRANSLATE_CONTEXT("Quit", "Menu"), 998 new BMessage(QUIT_MESSAGE), 'Q'); 999 menu->AddItem(item); 1000 1001 fMenuBar->AddItem(menu); 1002 } 1003 1004 1005 void 1006 MediaConverterWindow::_SetOutputFolder(BEntry entry) 1007 { 1008 fOutputDir.SetTo(&entry); 1009 TruncateOutputFolderPath(); 1010 } 1011