xref: /haiku/src/apps/mediaconverter/MediaConverterWindow.cpp (revision e5d65858f2361fe0552495b61620c84dcee6bc00)
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 
48 class DirectoryFilter : public BRefFilter {
49 public:
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 
62 class FileFormatMenuItem : public BMenuItem {
63 public:
64 	FileFormatMenuItem(media_file_format* format);
65 	virtual ~FileFormatMenuItem();
66 
67 	media_file_format fFileFormat;
68 };
69 
70 
71 FileFormatMenuItem::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 
79 FileFormatMenuItem::~FileFormatMenuItem()
80 {
81 }
82 
83 
84 // #pragma mark - CodecMenuItem
85 
86 
87 class 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 
96 CodecMenuItem::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 
104 CodecMenuItem::~CodecMenuItem()
105 {
106 }
107 
108 
109 // #pragma mark - MediaConverterWindow
110 
111 
112 MediaConverterWindow::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 
245 MediaConverterWindow::~MediaConverterWindow()
246 {
247 	delete fSaveFilePanel;
248 	delete fOpenFilePanel;
249 }
250 
251 
252 // #pragma mark -
253 
254 
255 void
256 MediaConverterWindow::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 
467 bool
468 MediaConverterWindow::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 
485 void
486 MediaConverterWindow::LanguageChanged()
487 {
488 	_DestroyMenu();
489 	_CreateMenu();
490 	_UpdateLabels();
491 	BuildAudioVideoMenus();
492 	Lock();
493 	fInfoView->Invalidate();
494 	Unlock();
495 }
496 
497 
498 void
499 MediaConverterWindow::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 
611 void
612 MediaConverterWindow::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 
639 void
640 MediaConverterWindow::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 
667 void
668 MediaConverterWindow::SetFileMessage(const char *message)
669 {
670 	fFileStatus->SetText(message);
671 }
672 
673 
674 void
675 MediaConverterWindow::SetStatusMessage(const char *message)
676 {
677 	fStatus->SetText(message);
678 }
679 
680 
681 // #pragma mark -
682 
683 
684 bool
685 MediaConverterWindow::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 
700 void
701 MediaConverterWindow::RemoveSourceFile(int32 index)
702 {
703 	delete fListView->RemoveItem(index);
704 	fStartDurationTC->SetText("0");
705 	fEndDurationTC->SetText("0");
706 }
707 
708 
709 int32
710 MediaConverterWindow::CountSourceFiles()
711 {
712 	return fListView->CountItems();
713 }
714 
715 
716 status_t
717 MediaConverterWindow::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 
732 void
733 MediaConverterWindow::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 
764 void
765 MediaConverterWindow::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 
782 bool
783 MediaConverterWindow::IsEnabled()
784 {
785 	return fEnabled;
786 }
787 
788 
789 const char*
790 MediaConverterWindow::StartDuration() const
791 {
792 	return fStartDurationTC->Text();
793 }
794 
795 
796 const char*
797 MediaConverterWindow::EndDuration() const
798 {
799 	return fEndDurationTC->Text();
800 }
801 
802 
803 BDirectory
804 MediaConverterWindow::OutputDirectory() const
805 {
806 	return fOutputDir;
807 }
808 
809 
810 void
811 MediaConverterWindow::SetAudioQualityLabel(const char* label)
812 {
813 	fAudioQualitySlider->SetLabel(label);
814 }
815 
816 
817 void
818 MediaConverterWindow::SetVideoQualityLabel(const char* label)
819 {
820 	fVideoQualitySlider->SetLabel(label);
821 }
822 
823 
824 // #pragma mark -
825 
826 
827 void
828 MediaConverterWindow::_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 
889 void
890 MediaConverterWindow::_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 
902 void
903 MediaConverterWindow::_DestroyMenu()
904 {
905 	BMenu* Menu;
906 
907 	while ((Menu = fMenuBar->SubmenuAt(0)) != NULL) {
908 		fMenuBar->RemoveItem(Menu);
909 		delete Menu;
910 	}
911 }
912 
913 
914 void
915 MediaConverterWindow::_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 
934 void
935 MediaConverterWindow::_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