xref: /haiku/src/apps/mediaconverter/MediaConverterWindow.cpp (revision a9adf1a6b6c1e5dd7e275504b7d6a4f93b8618b8)
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 // #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_NOT_V_RESIZABLE
117 		| B_ASYNCHRONOUS_CONTROLS | 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 	float padding = be_control_look->DefaultItemSpacing();
136 
137 	fListView = new MediaFileListView();
138 	fListView->SetExplicitMinSize(BSize(padding * 10, B_SIZE_UNSET));
139 	BScrollView* scroller = new BScrollView(NULL, fListView, 0, false, true);
140 
141 	// file list view box
142 	fSourcesBox = new BBox(B_FANCY_BORDER, scroller);
143 	fSourcesBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0));
144 	// We give fSourcesBox a layout to provide insets for the sources list
145 	// said insets are adjusted in _UpdateLabels
146 
147 	fInfoView = new MediaFileInfoView();
148 	fInfoBox = new BBox(B_FANCY_BORDER, fInfoView);
149 	fInfoBox->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
150 			B_ALIGN_USE_FULL_HEIGHT));
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 				BAlert* alert = new BAlert(B_TRANSLATE("Error"),
433 					errorString.String(), B_TRANSLATE("OK"));
434 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
435 				alert->Go();
436 			}
437 			break;
438 		}
439 
440 		case VIDEO_QUALITY_CHANGED_MESSAGE:
441 		{
442 			int32 value;
443 			msg->FindInt32("be:value", &value);
444 			snprintf(buffer, sizeof(buffer),
445 				B_TRANSLATE("Video quality: %3d%%"), (int8)value);
446 			fVideoQualitySlider->SetLabel(buffer);
447 			fVideoQuality = value;
448 			break;
449 		}
450 
451 		case AUDIO_QUALITY_CHANGED_MESSAGE:
452 		{
453 			int32 value;
454 			msg->FindInt32("be:value", &value);
455 			snprintf(buffer, sizeof(buffer),
456 				B_TRANSLATE("Audio quality: %3d%%"), (int8)value);
457 			fAudioQualitySlider->SetLabel(buffer);
458 			fAudioQuality = value;
459 			break;
460 		}
461 
462 		default:
463 			BWindow::MessageReceived(msg);
464 	}
465 }
466 
467 
468 bool
469 MediaConverterWindow::QuitRequested()
470 {
471 	if (!fConverting) {
472 		BMessenger(be_app).SendMessage(B_QUIT_REQUESTED);
473 		return true;
474 	} else if (!fCancelling) {
475 		fCancelling = true;
476 		SetStatusMessage(B_TRANSLATE("Cancelling"));
477 		BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE);
478 	}
479 	return false;
480 }
481 
482 
483 // #pragma mark -
484 
485 
486 void
487 MediaConverterWindow::LanguageChanged()
488 {
489 	_DestroyMenu();
490 	_CreateMenu();
491 	_UpdateLabels();
492 	BuildAudioVideoMenus();
493 	Lock();
494 	fInfoView->Invalidate();
495 	Unlock();
496 }
497 
498 
499 void
500 MediaConverterWindow::BuildAudioVideoMenus()
501 {
502 	BMenu* menu = fAudioMenu->Menu();
503 	BMenuItem* item;
504 	// clear out old audio codec menu items
505 	while ((item = menu->RemoveItem((int32)0)) != NULL)
506 		delete item;
507 
508 	bool separator = true;
509 
510 	// get selected file format
511 	FileFormatMenuItem* ffmi
512 		= (FileFormatMenuItem*)fFormatMenu->Menu()->FindMarked();
513 	media_file_format* mf_format = &(ffmi->fFileFormat);
514 
515 	media_format format, outfmt;
516 	memset(&format, 0, sizeof(format));
517 	media_codec_info codec_info;
518 	int32 cookie = 0;
519 	CodecMenuItem* cmi;
520 
521 	// add available audio encoders to menu
522 	format.type = B_MEDIA_RAW_AUDIO;
523 	format.u.raw_audio = media_raw_audio_format::wildcard;
524 	while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info)
525 		== B_OK) {
526 		if (separator) {
527 			menu->AddItem(new BMenuItem(
528 				B_TRANSLATE_CONTEXT("No audio", "Audio codecs list"),
529 				new BMessage(AUDIO_CODEC_SELECT_MESSAGE)));
530 			menu->AddSeparatorItem();
531 			separator = false;
532 		}
533 
534 		cmi = new CodecMenuItem(&codec_info, AUDIO_CODEC_SELECT_MESSAGE);
535 		menu->AddItem(cmi);
536 		// reset media format struct
537 /*
538 		format.type = B_MEDIA_RAW_AUDIO;
539 		format.u.raw_audio = media_raw_audio_format::wildcard;
540 */
541 	}
542 
543 	// mark first audio encoder
544 	item = menu->ItemAt(0);
545 	if (item != NULL) {
546 		fAudioMenu->SetEnabled(fEnabled);
547 		fAudioQualitySlider->SetEnabled(fEnabled);
548 		item->SetMarked(true);
549 		((BInvoker *)item)->Invoke();
550 	} else {
551 		item = new BMenuItem(
552 			B_TRANSLATE_CONTEXT("None available", "Audio codecs"),
553 			NULL);
554 		menu->AddItem(item);
555 		item->SetMarked(true);
556 		fAudioMenu->SetEnabled(false);
557 		fAudioQualitySlider->SetEnabled(false);
558 	}
559 
560 	// clear out old video codec menu items
561 	menu = fVideoMenu->Menu();
562 	while ((item = menu->RemoveItem((int32)0)) != NULL)
563 		delete item;
564 
565 	separator = true;
566 
567 	// construct a generic video format.  Some of these parameters
568 	// seem silly, but are needed for R4.5.x, which is more picky
569 	// than subsequent BeOS releases will be.
570 	memset(&format, 0, sizeof(format));
571 	format.type = B_MEDIA_RAW_VIDEO;
572 	format.u.raw_video.last_active = (uint32)(240 - 1);
573 	format.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
574 	format.u.raw_video.display.format = B_RGB32;
575 	format.u.raw_video.display.line_width = (int32)320;
576 	format.u.raw_video.display.line_count = (int32)240;
577 	format.u.raw_video.display.bytes_per_row = 4 * 320;
578 
579 	// add available video encoders to menu
580 	cookie = 0;
581 	while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info) == B_OK) {
582 		if (separator) {
583 			menu->AddItem(new BMenuItem(
584 				B_TRANSLATE_CONTEXT("No video", "Video codecs list"),
585 				new BMessage(VIDEO_CODEC_SELECT_MESSAGE)));
586 			menu->AddSeparatorItem();
587 			separator = false;
588 		}
589 
590 		cmi = new CodecMenuItem(&codec_info, VIDEO_CODEC_SELECT_MESSAGE);
591 		menu->AddItem(cmi);
592 	}
593 
594 	// mark first video encoder
595 	item = menu->ItemAt(0);
596 	if (item != NULL) {
597 		fVideoMenu->SetEnabled(fEnabled);
598 		fVideoQualitySlider->SetEnabled(fEnabled);
599 		item->SetMarked(true);
600 		((BInvoker *)item)->Invoke();
601 	} else {
602 		item = new BMenuItem(
603 			B_TRANSLATE_CONTEXT("None available", "Video codecs"),
604 			NULL);
605 		menu->AddItem(item);
606 		item->SetMarked(true);
607 		fVideoMenu->SetEnabled(false);
608 		fVideoQualitySlider->SetEnabled(false);
609 	}
610 }
611 
612 void
613 MediaConverterWindow::GetSelectedFormatInfo(media_file_format** format,
614 	media_codec_info** audio, media_codec_info** video)
615 {
616 	*audio = NULL;
617 	*video = NULL;
618 	*format = NULL;
619 
620 	FileFormatMenuItem *formatItem =
621 		dynamic_cast<FileFormatMenuItem *>(fFormatMenu->Menu()->FindMarked());
622 	if (formatItem != NULL) {
623 		*format = &(formatItem->fFileFormat);
624 	}
625 
626 	*audio = *video = NULL;
627 	CodecMenuItem *codecItem =
628 		dynamic_cast<CodecMenuItem *>(fAudioMenu->Menu()->FindMarked());
629 	if (codecItem != NULL) {
630 		*audio =  &(codecItem->fCodecInfo);
631 	}
632 
633 	codecItem = dynamic_cast<CodecMenuItem *>(fVideoMenu->Menu()->FindMarked());
634 	if (codecItem != NULL) {
635 		*video =  &(codecItem->fCodecInfo);
636 	}
637 }
638 
639 
640 void
641 MediaConverterWindow::BuildFormatMenu()
642 {
643 	BMenu *menu = fFormatMenu->Menu();
644 	BMenuItem *item;
645 	// clear out old format menu items
646 	while ((item = menu->RemoveItem((int32)0)) != NULL) {
647 		delete item;
648 	}
649 
650 	// add menu items for each file format
651 	media_file_format mfi;
652 	int32 cookie = 0;
653 	FileFormatMenuItem *ff_item;
654 	while (get_next_file_format(&cookie, &mfi) == B_OK) {
655 		ff_item = new FileFormatMenuItem(&mfi);
656 		menu->AddItem(ff_item);
657 	}
658 
659 	// mark first item
660 	item = menu->ItemAt(0);
661 	if (item != NULL) {
662 		item->SetMarked(true);
663 		((BInvoker *)item)->Invoke();
664 	}
665 }
666 
667 
668 void
669 MediaConverterWindow::SetFileMessage(const char *message)
670 {
671 	fFileStatus->SetText(message);
672 }
673 
674 
675 void
676 MediaConverterWindow::SetStatusMessage(const char *message)
677 {
678 	fStatus->SetText(message);
679 }
680 
681 
682 // #pragma mark -
683 
684 
685 bool
686 MediaConverterWindow::AddSourceFile(BMediaFile* file, const entry_ref& ref)
687 {
688 	if (!fListView->AddMediaItem(file, ref))
689 		return false;
690 
691 	if (!fOutputDirSpecified) {
692 		BEntry entry(&ref);
693 		entry.GetParent(&entry);
694 		_SetOutputFolder(entry);
695 	}
696 
697 	return true;
698 }
699 
700 
701 void
702 MediaConverterWindow::RemoveSourceFile(int32 index)
703 {
704 	delete fListView->RemoveItem(index);
705 	fStartDurationTC->SetText("0");
706 	fEndDurationTC->SetText("0");
707 }
708 
709 
710 int32
711 MediaConverterWindow::CountSourceFiles()
712 {
713 	return fListView->CountItems();
714 }
715 
716 
717 status_t
718 MediaConverterWindow::GetSourceFileAt(int32 index, BMediaFile** _file,
719 	entry_ref* ref)
720 {
721 	MediaFileListItem* item = dynamic_cast<MediaFileListItem*>(
722 		fListView->ItemAt(index));
723 	if (item != NULL) {
724 		*_file = item->fMediaFile;
725 		*ref = item->fRef;
726 		return B_OK;
727 	} else {
728 		return B_ERROR;
729 	}
730 }
731 
732 
733 void
734 MediaConverterWindow::SourceFileSelectionChanged()
735 {
736 	int32 selected = fListView->CurrentSelection();
737 	BMediaFile* file = NULL;
738 	entry_ref ref;
739 	bool enabled = GetSourceFileAt(selected, &file, &ref) == B_OK;
740 
741 	fPreviewButton->SetEnabled(enabled);
742 	fVideoQualitySlider->SetEnabled(enabled);
743 	fAudioQualitySlider->SetEnabled(enabled);
744 	fStartDurationTC->SetEnabled(enabled);
745 	fEndDurationTC->SetEnabled(enabled);
746 
747 	BString duration;
748 	if (enabled) {
749 		fInfoView->Update(file, &ref);
750 		// HACK: get the fInfoView to update the duration "synchronously"
751 		UpdateIfNeeded();
752 		duration << fInfoView->Duration() / 1000;
753 	} else
754 		duration = "0";
755 
756 	// update duration text controls
757 	fStartDurationTC->SetText("0");
758 	fEndDurationTC->SetText(duration.String());
759 }
760 
761 
762 // #pragma mark -
763 
764 
765 void
766 MediaConverterWindow::SetEnabled(bool enabled, bool convertEnabled)
767 {
768 	fConvertButton->SetEnabled(convertEnabled);
769 	if (enabled == fEnabled)
770 			return;
771 
772 	fFormatMenu->SetEnabled(enabled);
773 	fAudioMenu->SetEnabled(enabled);
774 	fVideoMenu->SetEnabled(enabled);
775 	fListView->SetEnabled(enabled);
776 	fStartDurationTC->SetEnabled(enabled);
777 	fEndDurationTC->SetEnabled(enabled);
778 
779 	fEnabled = enabled;
780 }
781 
782 
783 bool
784 MediaConverterWindow::IsEnabled()
785 {
786 	return fEnabled;
787 }
788 
789 
790 const char*
791 MediaConverterWindow::StartDuration() const
792 {
793 	return fStartDurationTC->Text();
794 }
795 
796 
797 const char*
798 MediaConverterWindow::EndDuration() const
799 {
800 	return fEndDurationTC->Text();
801 }
802 
803 
804 BDirectory
805 MediaConverterWindow::OutputDirectory() const
806 {
807 	return fOutputDir;
808 }
809 
810 
811 void
812 MediaConverterWindow::SetAudioQualityLabel(const char* label)
813 {
814 	fAudioQualitySlider->SetLabel(label);
815 }
816 
817 
818 void
819 MediaConverterWindow::SetVideoQualityLabel(const char* label)
820 {
821 	fVideoQualitySlider->SetLabel(label);
822 }
823 
824 
825 // #pragma mark -
826 
827 
828 void
829 MediaConverterWindow::_UpdateLabels()
830 {
831 	if (fSourcesBox != NULL) {
832 		fSourcesBox->SetLabel(B_TRANSLATE("Source files"));
833 		_UpdateBBoxLayoutInsets(fSourcesBox);
834 	}
835 
836 	if (fInfoBox != NULL)
837 		fInfoBox->SetLabel(B_TRANSLATE("File details"));
838 
839 	if (fOutputBox != NULL) {
840 		fOutputBox->SetLabel(B_TRANSLATE("Output format"));
841 		_UpdateBBoxLayoutInsets(fOutputBox);
842 	}
843 
844 	if (fConvertButton != NULL)
845 		fConvertButton->SetLabel(B_TRANSLATE("Convert"));
846 
847 	if (fPreviewButton != NULL)
848 		fPreviewButton->SetLabel(B_TRANSLATE("Preview"));
849 
850 	if (fDestButton != NULL)
851 		fDestButton->SetLabel(B_TRANSLATE("Output folder"));
852 
853 	if (fVideoQualitySlider != NULL) {
854 		char buffer[40];
855 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Video quality: %3d%%"),
856 			(int8)fVideoQuality);
857 		fVideoQualitySlider->SetLabel(buffer);
858 		fVideoQualitySlider->SetLimitLabels(B_TRANSLATE("Low"),
859 			B_TRANSLATE("High"));
860 	}
861 
862 	if (fAudioQualitySlider != NULL) {
863 		char buffer[40];
864 		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Audio quality: %3d%%"),
865 			(int8)fAudioQuality);
866 		fAudioQualitySlider->SetLabel(buffer);
867 		fAudioQualitySlider->SetLimitLabels(B_TRANSLATE("Low"),
868 			B_TRANSLATE("High"));
869 	}
870 
871 	if (fStartDurationTC != NULL)
872 		fStartDurationTC->SetLabel(B_TRANSLATE("Start [ms]: "));
873 
874 	if (fEndDurationTC != NULL)
875 		fEndDurationTC->SetLabel(B_TRANSLATE("End   [ms]: "));
876 
877 	if (fFormatMenu != NULL)
878 		fFormatMenu->SetLabel(B_TRANSLATE("File format:"));
879 
880 	if (fAudioMenu != NULL)
881 		fAudioMenu->SetLabel(B_TRANSLATE("Audio encoding:"));
882 
883 	if (fVideoMenu != NULL)
884 		fVideoMenu->SetLabel(B_TRANSLATE("Video encoding:"));
885 
886 	SetFileMessage(B_TRANSLATE("Drop media files onto this window"));
887 }
888 
889 
890 void
891 MediaConverterWindow::_UpdateBBoxLayoutInsets(BBox* box)
892 {
893 	BTwoDimensionalLayout* layout
894 		= dynamic_cast<BTwoDimensionalLayout*>(box->GetLayout());
895 	if (layout != NULL) {
896 		float padding = be_control_look->DefaultItemSpacing();
897 		layout->SetInsets(padding, box->TopBorderOffset() + padding, padding,
898 			padding);
899 	}
900 }
901 
902 
903 void
904 MediaConverterWindow::_DestroyMenu()
905 {
906 	BMenu* Menu;
907 
908 	while ((Menu = fMenuBar->SubmenuAt(0)) != NULL) {
909 		fMenuBar->RemoveItem(Menu);
910 		delete Menu;
911 	}
912 }
913 
914 
915 void
916 MediaConverterWindow::_CreateMenu()
917 {
918 	BMenuItem* item;
919 	BMenu* menu;
920 
921 	menu = new BMenu(B_TRANSLATE_CONTEXT("File", "Menu"));
922 	item = new BMenuItem(B_TRANSLATE_CONTEXT(
923 		"Open" B_UTF8_ELLIPSIS, "Menu"),
924 		new BMessage(OPEN_FILE_MESSAGE), 'O');
925 	menu->AddItem(item);
926 	menu->AddSeparatorItem();
927 	item = new BMenuItem(B_TRANSLATE_CONTEXT("Quit", "Menu"),
928 		new BMessage(QUIT_MESSAGE), 'Q');
929 	menu->AddItem(item);
930 
931 	fMenuBar->AddItem(menu);
932 }
933 
934 
935 void
936 MediaConverterWindow::_SetOutputFolder(BEntry entry)
937 {
938 	BPath path;
939 	entry.GetPath(&path);
940 	fOutputFolder->SetText(path.Path());
941 	fOutputFolder->ResizeToPreferred();
942 	fOutputDir.SetTo(path.Path());
943 }
944 
945 
946