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