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