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