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