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