BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / apps / mediaconverter / MediaConverterWindow.cpp
blob7cf8336c843f4985b8c972300d78eeb682d4caac
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.
8 #include "MediaConverterWindow.h"
10 #include <stdio.h>
11 #include <string.h>
12 #include <unistd.h>
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>
37 #include "MediaFileInfoView.h"
38 #include "MediaFileListView.h"
39 #include "MessageConstants.h"
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "MediaConverter"
44 #define VERSION "1.3.0"
47 static const unsigned int kMinSourceWidth = 12;
48 static const unsigned int kQualitySliderWidth = 28;
49 static const unsigned int kDurationWidth = 10;
52 // #pragma mark - DirectoryFilter
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)
61 // ToDo: Fix this properly in Tracker
62 // If you create a folder, then rename it, node is NULL.
63 // The BeBook says: "Note that the function is never sent an
64 // abstract entry, so the node, st, and filetype arguments will
65 // always be valid."
66 return node == NULL ? false : node->IsDirectory();
71 // #pragma mark - FileFormatMenuItem
74 class FileFormatMenuItem : public BMenuItem {
75 public:
76 FileFormatMenuItem(media_file_format* format);
77 virtual ~FileFormatMenuItem();
79 media_file_format fFileFormat;
83 FileFormatMenuItem::FileFormatMenuItem(media_file_format* format)
85 BMenuItem(format->pretty_name, new BMessage(FORMAT_SELECT_MESSAGE))
87 memcpy(&fFileFormat, format, sizeof(fFileFormat));
91 FileFormatMenuItem::~FileFormatMenuItem()
96 // #pragma mark - CodecMenuItem
99 class CodecMenuItem : public BMenuItem {
100 public:
101 CodecMenuItem(media_codec_info* ci, uint32 message_type);
102 virtual ~CodecMenuItem();
104 media_codec_info fCodecInfo;
108 CodecMenuItem::CodecMenuItem(media_codec_info* ci, uint32 message_type)
110 BMenuItem(ci->pretty_name, new BMessage(message_type))
112 memcpy(&fCodecInfo, ci, sizeof(fCodecInfo));
116 CodecMenuItem::~CodecMenuItem()
121 // #pragma mark - OutputBox
124 class OutputBox : public BBox {
125 public:
126 OutputBox(border_style border, BView* child);
127 virtual void FrameResized(float width, float height)
129 MediaConverterWindow* window
130 = dynamic_cast<MediaConverterWindow*>(Window());
131 if (window != NULL)
132 window->TruncateOutputFolderPath();
133 BBox::FrameResized(width, height);
138 OutputBox::OutputBox(border_style border, BView* child)
140 BBox(border, child)
145 // #pragma mark - MediaConverterWindow
148 MediaConverterWindow::MediaConverterWindow(BRect frame)
150 BWindow(frame, B_TRANSLATE_SYSTEM_NAME("MediaConverter"),
151 B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_NOT_ZOOMABLE
152 | B_NOT_V_RESIZABLE | B_ASYNCHRONOUS_CONTROLS
153 | B_AUTO_UPDATE_SIZE_LIMITS),
154 fVideoQuality(75),
155 fAudioQuality(75),
156 fSaveFilePanel(NULL),
157 fOpenFilePanel(NULL),
158 fOutputDirSpecified(false),
159 fEnabled(true),
160 fConverting(false),
161 fCancelling(false)
163 BPath outputDir;
164 if (find_directory(B_USER_DIRECTORY, &outputDir) != B_OK)
165 outputDir.SetTo("/boot/home");
166 fOutputDir.SetTo(outputDir.Path());
168 fMenuBar = new BMenuBar("menubar");
169 _CreateMenu();
171 float padding = be_control_look->DefaultItemSpacing();
173 fListView = new MediaFileListView();
174 fListView->SetExplicitMinSize(BSize(padding * kMinSourceWidth, B_SIZE_UNSET));
175 BScrollView* scroller = new BScrollView(NULL, fListView, 0, false, true);
177 // file list view box
178 fSourcesBox = new BBox(B_FANCY_BORDER, scroller);
179 fSourcesBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0));
180 // fSourcesBox's layout adjusted in _UpdateLabels
182 // info box
183 fInfoView = new MediaFileInfoView();
184 fInfoView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
185 B_ALIGN_VERTICAL_UNSET));
186 fInfoBox = new BBox(B_FANCY_BORDER, fInfoView);
188 // output menu fields
189 fFormatMenu = new BMenuField(NULL, B_TRANSLATE("File format:"),
190 new BPopUpMenu(""));
191 fAudioMenu = new BMenuField(NULL, B_TRANSLATE("Audio encoding:"),
192 new BPopUpMenu(""));
193 fVideoMenu = new BMenuField(NULL, B_TRANSLATE("Video encoding:"),
194 new BPopUpMenu(""));
196 // output folder
197 fDestButton = new BButton(B_TRANSLATE("Output folder"),
198 new BMessage(OUTPUT_FOLDER_MESSAGE));
199 BAlignment labelAlignment(be_control_look->DefaultLabelAlignment());
200 fOutputFolder = new BStringView(NULL, outputDir.Path());
201 fOutputFolder->SetExplicitAlignment(labelAlignment);
203 // start/end duration
204 fStartDurationTC = new BTextControl(NULL, "0", NULL);
205 BLayoutItem* startDuration = fStartDurationTC->CreateTextViewLayoutItem();
206 startDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET));
207 startDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
208 B_ALIGN_VERTICAL_CENTER));
209 fEndDurationTC = new BTextControl(NULL, "0", NULL);
210 BLayoutItem* endDuration = fEndDurationTC->CreateTextViewLayoutItem();
211 endDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET));
212 endDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
213 B_ALIGN_VERTICAL_CENTER));
215 // video quality
216 fVideoQualitySlider = new BSlider("VSlider", "" ,
217 new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL);
218 fVideoQualitySlider->SetModificationMessage(
219 new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE));
220 fVideoQualitySlider->SetValue(fVideoQuality);
221 fVideoQualitySlider->SetEnabled(false);
222 fVideoQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth,
223 B_SIZE_UNSET));
225 // audio quality
226 fAudioQualitySlider = new BSlider("ASlider", "" ,
227 new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL);
228 fAudioQualitySlider->SetModificationMessage(
229 new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE));
230 fAudioQualitySlider->SetValue(fAudioQuality);
231 fAudioQualitySlider->SetEnabled(false);
232 fAudioQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth,
233 B_SIZE_UNSET));
235 // output format box
236 BView* outputGrid = BLayoutBuilder::Grid<>()
237 .Add(fFormatMenu->CreateLabelLayoutItem(), 0, 0)
238 .Add(fFormatMenu->CreateMenuBarLayoutItem(), 1, 0)
239 .Add(fAudioMenu->CreateLabelLayoutItem(), 0, 1)
240 .Add(fAudioMenu->CreateMenuBarLayoutItem(), 1, 1)
241 .Add(fVideoMenu->CreateLabelLayoutItem(), 0, 2)
242 .Add(fVideoMenu->CreateMenuBarLayoutItem(), 1, 2)
243 .Add(fDestButton, 0, 3)
244 .Add(fOutputFolder, 1, 3)
245 .Add(fStartDurationTC->CreateLabelLayoutItem(), 0, 4)
246 .Add(startDuration, 1, 4)
247 .Add(fEndDurationTC->CreateLabelLayoutItem(), 0, 5)
248 .Add(endDuration, 1, 5)
249 .Add(fVideoQualitySlider, 0, 6, 2, 1)
250 .Add(fAudioQualitySlider, 0, 7, 2, 1)
251 .View();
252 outputGrid->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
253 B_ALIGN_USE_FULL_HEIGHT));
254 fOutputBox = new OutputBox(B_FANCY_BORDER, outputGrid);
255 fOutputBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0));
256 // fOutputBox's layout adjusted in _UpdateLabels
258 // buttons
259 fPreviewButton = new BButton(B_TRANSLATE("Preview"),
260 new BMessage(PREVIEW_MESSAGE));
261 fPreviewButton->SetEnabled(false);
263 fConvertButton = new BButton(B_TRANSLATE("Convert"),
264 new BMessage(CONVERT_BUTTON_MESSAGE));
266 // Status views
267 fStatus = new BStringView(NULL, NULL);
268 fStatus->SetExplicitAlignment(labelAlignment);
269 fFileStatus = new BStringView(NULL, NULL);
270 fFileStatus->SetExplicitAlignment(labelAlignment);
272 SetStatusMessage("");
273 _UpdateLabels();
275 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
276 .SetInsets(0, 0, 0, 0)
277 .Add(fMenuBar)
278 .AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
279 .SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
280 B_USE_WINDOW_SPACING, 0)
281 .Add(fSourcesBox)
282 .AddGroup(B_VERTICAL, B_USE_ITEM_SPACING)
283 .Add(fInfoBox)
284 .Add(fOutputBox)
285 .End()
286 .End()
287 .AddGrid(B_USE_ITEM_SPACING)
288 .SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING,
289 B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING)
290 .Add(fStatus, 0, 0)
291 .Add(fFileStatus, 0, 1)
292 .Add(BSpaceLayoutItem::CreateGlue(), 1, 0)
293 .Add(fPreviewButton, 2, 0)
294 .Add(fConvertButton, 3, 0)
295 .End();
299 MediaConverterWindow::~MediaConverterWindow()
301 delete fSaveFilePanel;
302 delete fOpenFilePanel;
306 // #pragma mark -
309 void
310 MediaConverterWindow::MessageReceived(BMessage* message)
312 entry_ref inRef;
314 char buffer[40];
315 BEntry inEntry;
317 switch (message->what) {
318 #if B_BEOS_VERSION <= B_BEOS_VERSION_6
319 case B_LANGUAGE_CHANGED:
320 LanguageChanged();
321 break;
322 #endif
324 case INIT_FORMAT_MENUS:
325 BuildFormatMenu();
326 if (CountSourceFiles() == 0)
327 SetEnabled(false, false);
328 break;
330 case B_SIMPLE_DATA:
331 if (message->WasDropped()) {
332 DetachCurrentMessage();
333 message->what = B_REFS_RECEIVED;
334 BMessenger(be_app).SendMessage(message);
335 delete message;
337 break;
339 case FORMAT_SELECT_MESSAGE:
340 BuildAudioVideoMenus();
341 break;
342 case AUDIO_CODEC_SELECT_MESSAGE:
343 break;
344 case VIDEO_CODEC_SELECT_MESSAGE:
345 break;
347 case CONVERT_BUTTON_MESSAGE:
348 if (!fConverting) {
349 fConvertButton->SetLabel(B_TRANSLATE("Cancel"));
350 fConverting = true;
351 SetStatusMessage(B_TRANSLATE("Convert"));
352 SetEnabled(false, true);
353 BMessenger(be_app).SendMessage(START_CONVERSION_MESSAGE);
354 } else if (!fCancelling) {
355 fCancelling = true;
356 SetStatusMessage(B_TRANSLATE("Cancelling" B_UTF8_ELLIPSIS));
357 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE);
359 break;
361 case CONVERSION_DONE_MESSAGE:
363 SetStatusMessage(fCancelling ? B_TRANSLATE("Conversion cancelled")
364 : B_TRANSLATE("Conversion completed"));
365 fConverting = false;
366 fCancelling = false;
367 bool enable = CountSourceFiles() > 0;
368 SetEnabled(enable, enable);
369 fConvertButton->SetLabel(B_TRANSLATE("Convert"));
370 break;
373 case OUTPUT_FOLDER_MESSAGE:
374 // Execute Save Panel
375 if (fSaveFilePanel == NULL) {
376 BButton* selectThisDir;
378 BMessage folderSelect(FOLDER_SELECT_MESSAGE);
379 fSaveFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL,
380 B_DIRECTORY_NODE, true, &folderSelect, NULL, false, true);
381 fSaveFilePanel->SetButtonLabel(B_DEFAULT_BUTTON,
382 B_TRANSLATE("Select"));
383 fSaveFilePanel->SetTarget(this);
385 fSaveFilePanel->Window()->Lock();
386 fSaveFilePanel->Window()->SetTitle(
387 B_TRANSLATE("MediaConverter+:SaveDirectory"));
388 BRect buttonRect
389 = fSaveFilePanel->Window()->ChildAt(0)->FindView(
390 "cancel button")->Frame();
391 buttonRect.right = buttonRect.left - 20;
392 buttonRect.left = buttonRect.right - 130;
393 selectThisDir = new BButton(buttonRect, NULL,
394 B_TRANSLATE("Select this folder"),
395 new BMessage(SELECT_THIS_DIR_MESSAGE),
396 B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT);
397 selectThisDir->SetTarget(this);
398 fSaveFilePanel->Window()->ChildAt(0)->AddChild(selectThisDir);
399 fSaveFilePanel->Window()->Unlock();
401 fSaveFilePanel->SetRefFilter(new DirectoryFilter);
403 fSaveFilePanel->Show();
404 break;
406 case FOLDER_SELECT_MESSAGE:
407 // "SELECT" Button at Save Panel Pushed
408 fSaveFilePanel->GetNextSelectedRef(&inRef);
409 inEntry.SetTo(&inRef, true);
410 _SetOutputFolder(inEntry);
411 fOutputDirSpecified = true;
412 fSaveFilePanel->Rewind();
413 break;
415 case SELECT_THIS_DIR_MESSAGE:
416 // "THIS DIR" Button at Save Panel Pushed
417 fSaveFilePanel->GetPanelDirectory(&inRef);
418 fSaveFilePanel->Hide();
419 inEntry.SetTo(&inRef, true);
420 _SetOutputFolder(inEntry);
421 fOutputDirSpecified = true;
422 break;
424 case OPEN_FILE_MESSAGE:
425 // Execute Open Panel
426 if (!fOpenFilePanel) {
427 fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL,
428 B_FILE_NODE, true, NULL, NULL, false, true);
429 fOpenFilePanel->SetTarget(this);
431 fOpenFilePanel->Show();
432 break;
434 case B_REFS_RECEIVED:
435 // Media Files Seleced by Open Panel
436 DetachCurrentMessage();
437 message->what = B_REFS_RECEIVED;
438 BMessenger(be_app).SendMessage(message);
439 // fall through
441 case B_CANCEL:
442 break;
444 case QUIT_MESSAGE:
445 MediaConverterWindow::QuitRequested();
446 break;
448 case PREVIEW_MESSAGE:
450 // Build the command line to launch the preview application.
451 // TODO: Launch the default app instead of hardcoded MediaPlayer!
452 int32 srcIndex = fListView->CurrentSelection();
453 BMediaFile* inFile = NULL;
454 status_t status = GetSourceFileAt(srcIndex, &inFile, &inRef);
456 const char* argv[3];
457 BString startPosString;
458 BPath path;
460 if (status == B_OK) {
461 argv[0] = "-pos";
462 // NOTE: -pos argument is currently not supported by Haiku
463 // MediaPlayer.
464 startPosString << fStartDurationTC->Text();
465 startPosString << "000";
466 argv[1] = startPosString.String();
468 status = inEntry.SetTo(&inRef);
471 if (status == B_OK) {
472 status = inEntry.GetPath(&path);
473 if (status == B_OK)
474 argv[2] = path.Path();
477 if (status == B_OK) {
478 status = be_roster->Launch(
479 "application/x-vnd.Haiku-MediaPlayer",
480 3, (char**)argv, NULL);
483 if (status != B_OK && status != B_ALREADY_RUNNING) {
484 BString errorString(B_TRANSLATE("Error launching: %strError%"));
485 errorString.ReplaceFirst("%strError%", strerror(status));
486 BAlert* alert = new BAlert(B_TRANSLATE("Error"),
487 errorString.String(), B_TRANSLATE("OK"));
488 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
489 alert->Go();
491 break;
494 case VIDEO_QUALITY_CHANGED_MESSAGE:
496 int32 value;
497 message->FindInt32("be:value", &value);
498 snprintf(buffer, sizeof(buffer),
499 B_TRANSLATE("Video quality: %3d%%"), (int8)value);
500 fVideoQualitySlider->SetLabel(buffer);
501 fVideoQuality = value;
502 break;
505 case AUDIO_QUALITY_CHANGED_MESSAGE:
507 int32 value;
508 message->FindInt32("be:value", &value);
509 snprintf(buffer, sizeof(buffer),
510 B_TRANSLATE("Audio quality: %3d%%"), (int8)value);
511 fAudioQualitySlider->SetLabel(buffer);
512 fAudioQuality = value;
513 break;
516 default:
517 BWindow::MessageReceived(message);
522 bool
523 MediaConverterWindow::QuitRequested()
525 if (!fConverting) {
526 BMessenger(be_app).SendMessage(B_QUIT_REQUESTED);
527 return true;
528 } else if (!fCancelling) {
529 fCancelling = true;
530 SetStatusMessage(B_TRANSLATE("Cancelling"));
531 BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE);
534 return false;
538 // #pragma mark -
541 void
542 MediaConverterWindow::LanguageChanged()
544 _DestroyMenu();
545 _CreateMenu();
546 _UpdateLabels();
547 BuildAudioVideoMenus();
548 Lock();
549 fInfoView->Invalidate();
550 Unlock();
554 void
555 MediaConverterWindow::BuildAudioVideoMenus()
557 BMenu* menu = fAudioMenu->Menu();
558 BMenuItem* item;
560 // clear out old audio codec menu items
561 while ((item = menu->RemoveItem((int32)0)) != NULL)
562 delete item;
564 bool separator = true;
566 // get selected file format
567 FileFormatMenuItem* ffmi
568 = (FileFormatMenuItem*)fFormatMenu->Menu()->FindMarked();
569 media_file_format* mf_format = &(ffmi->fFileFormat);
571 media_format format, outfmt;
572 memset(&format, 0, sizeof(format));
573 media_codec_info codec_info;
574 int32 cookie = 0;
575 CodecMenuItem* cmi;
577 // add available audio encoders to menu
578 format.type = B_MEDIA_RAW_AUDIO;
579 format.u.raw_audio = media_raw_audio_format::wildcard;
580 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info)
581 == B_OK) {
582 if (separator) {
583 menu->AddItem(new BMenuItem(
584 B_TRANSLATE_CONTEXT("No audio", "Audio codecs list"),
585 new BMessage(AUDIO_CODEC_SELECT_MESSAGE)));
586 menu->AddSeparatorItem();
587 separator = false;
590 cmi = new CodecMenuItem(&codec_info, AUDIO_CODEC_SELECT_MESSAGE);
591 menu->AddItem(cmi);
592 // reset media format struct
594 format.type = B_MEDIA_RAW_AUDIO;
595 format.u.raw_audio = media_raw_audio_format::wildcard;
599 // mark first audio encoder
600 item = menu->ItemAt(0);
601 if (item != NULL) {
602 fAudioMenu->SetEnabled(fEnabled);
603 fAudioQualitySlider->SetEnabled(fEnabled);
604 item->SetMarked(true);
605 ((BInvoker*)item)->Invoke();
606 } else {
607 item = new BMenuItem(
608 B_TRANSLATE_CONTEXT("None available", "Audio codecs"), NULL);
609 menu->AddItem(item);
610 item->SetMarked(true);
611 fAudioMenu->SetEnabled(false);
612 fAudioQualitySlider->SetEnabled(false);
615 // clear out old video codec menu items
616 menu = fVideoMenu->Menu();
617 while ((item = menu->RemoveItem((int32)0)) != NULL)
618 delete item;
620 separator = true;
622 // construct a generic video format. Some of these parameters
623 // seem silly, but are needed for R4.5.x, which is more picky
624 // than subsequent BeOS releases will be.
625 memset(&format, 0, sizeof(format));
626 format.type = B_MEDIA_RAW_VIDEO;
627 format.u.raw_video.last_active = (uint32)(240 - 1);
628 format.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
629 format.u.raw_video.display.format = B_RGB32;
630 format.u.raw_video.display.line_width = (int32)320;
631 format.u.raw_video.display.line_count = (int32)240;
632 format.u.raw_video.display.bytes_per_row = 4 * 320;
634 // add available video encoders to menu
635 cookie = 0;
636 while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info)
637 == B_OK) {
638 if (separator) {
639 menu->AddItem(new BMenuItem(
640 B_TRANSLATE_CONTEXT("No video", "Video codecs list"),
641 new BMessage(VIDEO_CODEC_SELECT_MESSAGE)));
642 menu->AddSeparatorItem();
643 separator = false;
646 cmi = new CodecMenuItem(&codec_info, VIDEO_CODEC_SELECT_MESSAGE);
647 menu->AddItem(cmi);
650 // mark first video encoder
651 item = menu->ItemAt(0);
652 if (item != NULL) {
653 fVideoMenu->SetEnabled(fEnabled);
654 fVideoQualitySlider->SetEnabled(fEnabled);
655 item->SetMarked(true);
656 ((BInvoker*)item)->Invoke();
657 } else {
658 item = new BMenuItem(
659 B_TRANSLATE_CONTEXT("None available", "Video codecs"), NULL);
660 menu->AddItem(item);
661 item->SetMarked(true);
662 fVideoMenu->SetEnabled(false);
663 fVideoQualitySlider->SetEnabled(false);
667 void
668 MediaConverterWindow::GetSelectedFormatInfo(media_file_format** format,
669 media_codec_info** audio, media_codec_info** video)
671 *audio = NULL;
672 *video = NULL;
673 *format = NULL;
675 FileFormatMenuItem* formatItem =
676 dynamic_cast<FileFormatMenuItem*>(fFormatMenu->Menu()->FindMarked());
677 if (formatItem != NULL)
678 *format = &(formatItem->fFileFormat);
680 *audio = *video = NULL;
681 CodecMenuItem* codecItem =
682 dynamic_cast<CodecMenuItem*>(fAudioMenu->Menu()->FindMarked());
683 if (codecItem != NULL)
684 *audio = &(codecItem->fCodecInfo);
686 codecItem = dynamic_cast<CodecMenuItem*>(fVideoMenu->Menu()->FindMarked());
687 if (codecItem != NULL)
688 *video = &(codecItem->fCodecInfo);
692 void
693 MediaConverterWindow::BuildFormatMenu()
695 BMenu* menu = fFormatMenu->Menu();
696 BMenuItem* item;
698 // clear out old format menu items
699 while ((item = menu->RemoveItem((int32)0)) != NULL)
700 delete item;
702 // add menu items for each file format
703 media_file_format mfi;
704 int32 cookie = 0;
705 FileFormatMenuItem* ff_item;
706 while (get_next_file_format(&cookie, &mfi) == B_OK) {
707 if ((mfi.capabilities & media_file_format::B_WRITABLE) == 0)
708 continue;
709 ff_item = new FileFormatMenuItem(&mfi);
710 menu->AddItem(ff_item);
713 // mark first item
714 item = menu->ItemAt(0);
715 if (item != NULL) {
716 item->SetMarked(true);
717 ((BInvoker*)item)->Invoke();
722 void
723 MediaConverterWindow::SetFileMessage(const char* message)
725 fFileStatus->SetText(message);
729 void
730 MediaConverterWindow::SetStatusMessage(const char* message)
732 fStatus->SetText(message);
736 // #pragma mark -
739 bool
740 MediaConverterWindow::AddSourceFile(BMediaFile* file, const entry_ref& ref)
742 if (!fListView->AddMediaItem(file, ref))
743 return false;
745 if (!fOutputDirSpecified) {
746 BEntry entry(&ref);
747 entry.GetParent(&entry);
748 _SetOutputFolder(entry);
751 return true;
755 void
756 MediaConverterWindow::RemoveSourceFile(int32 index)
758 delete fListView->RemoveItem(index);
759 fStartDurationTC->SetText("0");
760 fEndDurationTC->SetText("0");
764 int32
765 MediaConverterWindow::CountSourceFiles()
767 return fListView->CountItems();
771 status_t
772 MediaConverterWindow::GetSourceFileAt(int32 index, BMediaFile** _file,
773 entry_ref* ref)
775 MediaFileListItem* item = dynamic_cast<MediaFileListItem*>(
776 fListView->ItemAt(index));
777 if (item != NULL) {
778 *_file = item->fMediaFile;
779 *ref = item->fRef;
780 return B_OK;
781 } else
782 return B_ERROR;
786 void
787 MediaConverterWindow::SourceFileSelectionChanged()
789 int32 selected = fListView->CurrentSelection();
790 BMediaFile* file = NULL;
791 entry_ref ref;
792 bool enabled = GetSourceFileAt(selected, &file, &ref) == B_OK;
794 fPreviewButton->SetEnabled(enabled);
795 fVideoQualitySlider->SetEnabled(enabled);
796 fAudioQualitySlider->SetEnabled(enabled);
797 fStartDurationTC->SetEnabled(enabled);
798 fEndDurationTC->SetEnabled(enabled);
800 BString duration;
801 if (enabled) {
802 fInfoView->Update(file, &ref);
803 // HACK: get the fInfoView to update the duration "synchronously"
804 UpdateIfNeeded();
805 duration << fInfoView->Duration() / 1000;
806 } else
807 duration = "0";
809 // update duration text controls
810 fStartDurationTC->SetText("0");
811 fEndDurationTC->SetText(duration.String());
815 // #pragma mark -
818 void
819 MediaConverterWindow::SetEnabled(bool enabled, bool convertEnabled)
821 fConvertButton->SetEnabled(convertEnabled);
822 if (enabled == fEnabled)
823 return;
825 fFormatMenu->SetEnabled(enabled);
826 fAudioMenu->SetEnabled(enabled);
827 fVideoMenu->SetEnabled(enabled);
828 fListView->SetEnabled(enabled);
829 fStartDurationTC->SetEnabled(enabled);
830 fEndDurationTC->SetEnabled(enabled);
832 fEnabled = enabled;
836 bool
837 MediaConverterWindow::IsEnabled()
839 return fEnabled;
843 const char*
844 MediaConverterWindow::StartDuration() const
846 return fStartDurationTC->Text();
850 const char*
851 MediaConverterWindow::EndDuration() const
853 return fEndDurationTC->Text();
857 BDirectory
858 MediaConverterWindow::OutputDirectory() const
860 return fOutputDir;
864 void
865 MediaConverterWindow::SetAudioQualityLabel(const char* label)
867 fAudioQualitySlider->SetLabel(label);
871 void
872 MediaConverterWindow::SetVideoQualityLabel(const char* label)
874 fVideoQualitySlider->SetLabel(label);
878 void
879 MediaConverterWindow::TruncateOutputFolderPath()
881 BEntry entry;
882 fOutputDir.GetEntry(&entry);
883 BPath path;
884 entry.GetPath(&path);
885 BString pathString(path.Path());
886 float maxWidth = fVideoMenu->MenuBar()->Frame().Width();
888 fOutputFolder->TruncateString(&pathString, B_TRUNCATE_MIDDLE, maxWidth);
889 fOutputFolder->SetText(pathString.String());
890 if (fOutputFolder->StringWidth(path.Path()) > maxWidth)
891 fOutputFolder->SetToolTip(path.Path());
892 else
893 fOutputFolder->SetToolTip((const char*)NULL);
897 // #pragma mark -
900 void
901 MediaConverterWindow::_UpdateLabels()
903 if (fSourcesBox != NULL) {
904 fSourcesBox->SetLabel(B_TRANSLATE("Source files"));
905 _UpdateBBoxLayoutInsets(fSourcesBox);
908 if (fInfoBox != NULL)
909 fInfoBox->SetLabel(B_TRANSLATE("File details"));
911 if (fOutputBox != NULL) {
912 fOutputBox->SetLabel(B_TRANSLATE("Output format"));
913 _UpdateBBoxLayoutInsets(fOutputBox);
916 if (fConvertButton != NULL)
917 fConvertButton->SetLabel(B_TRANSLATE("Convert"));
919 if (fPreviewButton != NULL)
920 fPreviewButton->SetLabel(B_TRANSLATE("Preview"));
922 if (fDestButton != NULL)
923 fDestButton->SetLabel(B_TRANSLATE("Output folder"));
925 if (fVideoQualitySlider != NULL) {
926 char buffer[40];
927 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Video quality: %3d%%"),
928 (int8)fVideoQuality);
929 fVideoQualitySlider->SetLabel(buffer);
930 fVideoQualitySlider->SetLimitLabels(B_TRANSLATE("Low"),
931 B_TRANSLATE("High"));
934 if (fAudioQualitySlider != NULL) {
935 char buffer[40];
936 snprintf(buffer, sizeof(buffer), B_TRANSLATE("Audio quality: %3d%%"),
937 (int8)fAudioQuality);
938 fAudioQualitySlider->SetLabel(buffer);
939 fAudioQualitySlider->SetLimitLabels(B_TRANSLATE("Low"),
940 B_TRANSLATE("High"));
943 if (fStartDurationTC != NULL)
944 fStartDurationTC->SetLabel(B_TRANSLATE("Start [ms]: "));
946 if (fEndDurationTC != NULL)
947 fEndDurationTC->SetLabel(B_TRANSLATE("End   [ms]: "));
949 if (fFormatMenu != NULL)
950 fFormatMenu->SetLabel(B_TRANSLATE("File format:"));
952 if (fAudioMenu != NULL)
953 fAudioMenu->SetLabel(B_TRANSLATE("Audio encoding:"));
955 if (fVideoMenu != NULL)
956 fVideoMenu->SetLabel(B_TRANSLATE("Video encoding:"));
958 SetFileMessage(B_TRANSLATE("Drop media files onto this window"));
962 void
963 MediaConverterWindow::_UpdateBBoxLayoutInsets(BBox* box)
965 BTwoDimensionalLayout* layout
966 = dynamic_cast<BTwoDimensionalLayout*>(box->GetLayout());
967 if (layout != NULL) {
968 float padding = be_control_look->DefaultItemSpacing();
969 layout->SetInsets(padding, box->TopBorderOffset() + padding, padding,
970 padding);
975 void
976 MediaConverterWindow::_DestroyMenu()
978 BMenu* menu;
980 while ((menu = fMenuBar->SubmenuAt(0)) != NULL) {
981 fMenuBar->RemoveItem(menu);
982 delete menu;
987 void
988 MediaConverterWindow::_CreateMenu()
990 BMenu* menu;
991 BMenuItem* item;
993 menu = new BMenu(B_TRANSLATE_CONTEXT("File", "Menu"));
994 item = new BMenuItem(B_TRANSLATE_CONTEXT("Open" B_UTF8_ELLIPSIS, "Menu"),
995 new BMessage(OPEN_FILE_MESSAGE), 'O');
996 menu->AddItem(item);
997 menu->AddSeparatorItem();
998 item = new BMenuItem(B_TRANSLATE_CONTEXT("Quit", "Menu"),
999 new BMessage(QUIT_MESSAGE), 'Q');
1000 menu->AddItem(item);
1002 fMenuBar->AddItem(menu);
1006 void
1007 MediaConverterWindow::_SetOutputFolder(BEntry entry)
1009 BPath path;
1010 entry.GetPath(&path);
1011 if (access(path.Path(), W_OK) != -1) {
1012 fOutputDir.SetTo(&entry);
1013 } else {
1014 BString errorString(B_TRANSLATE("Error writing to location: %strPath%."
1015 " Defaulting to location: /boot/home"));
1016 errorString.ReplaceFirst("%strPath%", path.Path());
1017 BAlert* alert = new BAlert(B_TRANSLATE("Error"),
1018 errorString.String(), B_TRANSLATE("OK"));
1019 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1020 alert->Go();
1021 fOutputDir.SetTo("/boot/home");
1023 TruncateOutputFolderPath();