// Copyright 1999, Be Incorporated. All Rights Reserved.
// Copyright 2000-2004, Jun Suzuki. All Rights Reserved.
// Copyright 2007, 2010 Stephan Aßmus. All Rights Reserved.
// Copyright 2010-2013, Haiku, Inc. All Rights Reserved.
// This file may be used under the terms of the Be Sample Code License.
 
 
#include "MediaConverterWindow.h"
 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
 
#include <Alert.h>
#include <Application.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <ControlLook.h>
#include <FilePanel.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <Locale.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuField.h>
#include <MenuItem.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <Roster.h>
#include <ScrollBar.h>
#include <ScrollView.h>
#include <Slider.h>
#include <StringView.h>
#include <TextControl.h>
 
#include "MediaFileInfoView.h"
#include "MediaFileListView.h"
#include "MessageConstants.h"
 
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MediaConverter"
#define VERSION "1.3.0"
 
 
static const unsigned int kMinSourceWidth = 12;
static const unsigned int kQualitySliderWidth = 28;
static const unsigned int kDurationWidth = 10;
 
 
// #pragma mark - DirectoryFilter
 
 
class DirectoryFilter : public BRefFilter {
public:
	DirectoryFilter() {};
	virtual bool Filter(const entry_ref* ref,
		BNode* node, struct stat_beos* st, const char* filetype)
	{
		// ToDo: Fix this properly in Tracker
		// If you create a folder, then rename it, node is NULL.
		// The BeBook says: "Note that the function is never sent an
		// abstract entry, so the node, st, and filetype arguments will
		// always be valid."
		return node == NULL ? false : node->IsDirectory();
	}
};
 
 
// #pragma mark - FileFormatMenuItem
 
 
class FileFormatMenuItem : public BMenuItem {
public:
	FileFormatMenuItem(media_file_format* format);
	virtual ~FileFormatMenuItem();
 
	media_file_format fFileFormat;
};
 
 
FileFormatMenuItem::FileFormatMenuItem(media_file_format* format)
	:
	BMenuItem(format->pretty_name, new BMessage(FORMAT_SELECT_MESSAGE))
{
	memcpy(&fFileFormat, format, sizeof(fFileFormat));
}
 
 
FileFormatMenuItem::~FileFormatMenuItem()
{
}
 
 
// #pragma mark - CodecMenuItem
 
 
class CodecMenuItem : public BMenuItem {
public:
	CodecMenuItem(media_codec_info* ci, uint32 message_type);
	virtual ~CodecMenuItem();
 
	media_codec_info fCodecInfo;
};
 
 
CodecMenuItem::CodecMenuItem(media_codec_info* ci, uint32 message_type)
	:
	BMenuItem(ci->pretty_name, new BMessage(message_type))
{
	memcpy(&fCodecInfo, ci, sizeof(fCodecInfo));
}
 
 
CodecMenuItem::~CodecMenuItem()
{
}
 
 
// #pragma mark - OutputBox
 
 
class OutputBox : public BBox {
public:
	OutputBox(border_style border, BView* child);
	virtual void FrameResized(float width, float height)
	{
		MediaConverterWindow* window
			= dynamic_cast<MediaConverterWindow*>(Window());
		if (window != NULL)
			window->TruncateOutputFolderPath();
		BBox::FrameResized(width, height);
	}
};
 
 
OutputBox::OutputBox(border_style border, BView* child)
	:
	BBox(border, child)
{
}
 
 
// #pragma mark - MediaConverterWindow
 
 
MediaConverterWindow::MediaConverterWindow(BRect frame)
	:
	BWindow(frame, B_TRANSLATE_SYSTEM_NAME("MediaConverter"),
		B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL, B_NOT_ZOOMABLE
		| B_NOT_V_RESIZABLE | B_ASYNCHRONOUS_CONTROLS
		| B_AUTO_UPDATE_SIZE_LIMITS),
	fVideoQuality(75),
	fAudioQuality(75),
	fSaveFilePanel(NULL),
	fOpenFilePanel(NULL),
	fOutputDirSpecified(false),
	fEnabled(true),
	fConverting(false),
	fCancelling(false)
{
	BPath outputDir;
	if (find_directory(B_USER_DIRECTORY, &outputDir) != B_OK)
		outputDir.SetTo("/boot/home");
	fOutputDir.SetTo(outputDir.Path());
 
	fMenuBar = new BMenuBar("menubar");
	_CreateMenu();
 
	float padding = be_control_look->DefaultItemSpacing();
 
	fListView = new MediaFileListView();
	fListView->SetExplicitMinSize(BSize(padding * kMinSourceWidth, B_SIZE_UNSET));
	BScrollView* scroller = new BScrollView(NULL, fListView, 0, false, true);
 
	// file list view box
	fSourcesBox = new BBox(B_FANCY_BORDER, scroller);
	fSourcesBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0));
		// fSourcesBox's layout adjusted in _UpdateLabels
 
	// info box
	fInfoView = new MediaFileInfoView();
	fInfoView->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
		B_ALIGN_VERTICAL_UNSET));
	fInfoBox = new BBox(B_FANCY_BORDER, fInfoView);
 
	// output menu fields
	fFormatMenu = new BMenuField(NULL, B_TRANSLATE("File format:"),
		new BPopUpMenu(""));
	fAudioMenu = new BMenuField(NULL, B_TRANSLATE("Audio encoding:"),
		new BPopUpMenu(""));
	fVideoMenu = new BMenuField(NULL, B_TRANSLATE("Video encoding:"),
		new BPopUpMenu(""));
 
	// output folder
	fDestButton = new BButton(B_TRANSLATE("Output folder"),
		new BMessage(OUTPUT_FOLDER_MESSAGE));
	BAlignment labelAlignment(be_control_look->DefaultLabelAlignment());
	fOutputFolder = new BStringView(NULL, outputDir.Path());
	fOutputFolder->SetExplicitAlignment(labelAlignment);
 
	// start/end duration
	fStartDurationTC = new BTextControl(NULL, "0", NULL);
	BLayoutItem* startDuration = fStartDurationTC->CreateTextViewLayoutItem();
	startDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET));
	startDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
		B_ALIGN_VERTICAL_CENTER));
	fEndDurationTC = new BTextControl(NULL, "0", NULL);
	BLayoutItem* endDuration = fEndDurationTC->CreateTextViewLayoutItem();
	endDuration->SetExplicitSize(BSize(padding * kDurationWidth, B_SIZE_UNSET));
	endDuration->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
		B_ALIGN_VERTICAL_CENTER));
 
	// video quality
	fVideoQualitySlider = new BSlider("VSlider", "" ,
		new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL);
	fVideoQualitySlider->SetModificationMessage(
		new BMessage(VIDEO_QUALITY_CHANGED_MESSAGE));
	fVideoQualitySlider->SetValue(fVideoQuality);
	fVideoQualitySlider->SetEnabled(false);
	fVideoQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth,
		B_SIZE_UNSET));
 
	// audio quality
	fAudioQualitySlider = new BSlider("ASlider", "" ,
		new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE), 1, 100, B_HORIZONTAL);
	fAudioQualitySlider->SetModificationMessage(
		new BMessage(AUDIO_QUALITY_CHANGED_MESSAGE));
	fAudioQualitySlider->SetValue(fAudioQuality);
	fAudioQualitySlider->SetEnabled(false);
	fAudioQualitySlider->SetExplicitSize(BSize(padding * kQualitySliderWidth,
		B_SIZE_UNSET));
 
	// output format box
	BView* outputGrid = BLayoutBuilder::Grid<>()
		.Add(fFormatMenu->CreateLabelLayoutItem(), 0, 0)
		.Add(fFormatMenu->CreateMenuBarLayoutItem(), 1, 0)
		.Add(fAudioMenu->CreateLabelLayoutItem(), 0, 1)
		.Add(fAudioMenu->CreateMenuBarLayoutItem(), 1, 1)
		.Add(fVideoMenu->CreateLabelLayoutItem(), 0, 2)
		.Add(fVideoMenu->CreateMenuBarLayoutItem(), 1, 2)
		.Add(fDestButton, 0, 3)
		.Add(fOutputFolder, 1, 3)
		.Add(fStartDurationTC->CreateLabelLayoutItem(), 0, 4)
		.Add(startDuration, 1, 4)
		.Add(fEndDurationTC->CreateLabelLayoutItem(), 0, 5)
		.Add(endDuration, 1, 5)
		.Add(fVideoQualitySlider, 0, 6, 2, 1)
		.Add(fAudioQualitySlider, 0, 7, 2, 1)
		.View();
	outputGrid->SetExplicitAlignment(BAlignment(B_ALIGN_USE_FULL_WIDTH,
		B_ALIGN_USE_FULL_HEIGHT));
	fOutputBox = new OutputBox(B_FANCY_BORDER, outputGrid);
	fOutputBox->SetLayout(new BGroupLayout(B_HORIZONTAL, 0));
		// fOutputBox's layout adjusted in _UpdateLabels
 
	// buttons
	fPreviewButton = new BButton(B_TRANSLATE("Preview"),
		new BMessage(PREVIEW_MESSAGE));
	fPreviewButton->SetEnabled(false);
 
	fConvertButton = new BButton(B_TRANSLATE("Convert"),
		new BMessage(CONVERT_BUTTON_MESSAGE));
 
	// Status views
	fStatus = new BStringView(NULL, NULL);
	fStatus->SetExplicitAlignment(labelAlignment);
	fFileStatus = new BStringView(NULL, NULL);
	fFileStatus->SetExplicitAlignment(labelAlignment);
 
	SetStatusMessage("");
	_UpdateLabels();
 
	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
		.SetInsets(0, 0, 0, 0)
		.Add(fMenuBar)
		.AddSplit(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
			.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
				B_USE_WINDOW_SPACING, 0)
			.Add(fSourcesBox)
			.AddGroup(B_VERTICAL, B_USE_ITEM_SPACING)
				.Add(fInfoBox)
				.Add(fOutputBox)
			.End()
		.End()
		.AddGrid(B_USE_ITEM_SPACING)
			.SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING,
				B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING)
			.Add(fStatus, 0, 0)
			.Add(fFileStatus, 0, 1)
			.Add(BSpaceLayoutItem::CreateGlue(), 1, 0)
			.Add(fPreviewButton, 2, 0)
			.Add(fConvertButton, 3, 0)
		.End();
}
 
 
MediaConverterWindow::~MediaConverterWindow()
{
	delete fSaveFilePanel;
	delete fOpenFilePanel;
}
 
 
// #pragma mark -
 
 
void
MediaConverterWindow::MessageReceived(BMessage* message)
{
	entry_ref inRef;
 
	char buffer[40];
	BEntry inEntry;
 
	switch (message->what) {
		#if B_BEOS_VERSION <= B_BEOS_VERSION_6
		case B_LANGUAGE_CHANGED:
			LanguageChanged();
			break;
		#endif
 
		case INIT_FORMAT_MENUS:
			BuildFormatMenu();
			if (CountSourceFiles() == 0)
				SetEnabled(false, false);
			break;
 
		case B_SIMPLE_DATA:
			if (message->WasDropped()) {
				DetachCurrentMessage();
				message->what = B_REFS_RECEIVED;
				BMessenger(be_app).SendMessage(message);
				delete message;
			}
			break;
 
		case FORMAT_SELECT_MESSAGE:
			BuildAudioVideoMenus();
			break;
		case AUDIO_CODEC_SELECT_MESSAGE:
			break;
		case VIDEO_CODEC_SELECT_MESSAGE:
			break;
 
		case CONVERT_BUTTON_MESSAGE:
			if (!fConverting) {
				fConvertButton->SetLabel(B_TRANSLATE("Cancel"));
				fConverting = true;
				SetStatusMessage(B_TRANSLATE("Convert"));
				SetEnabled(false, true);
				BMessenger(be_app).SendMessage(START_CONVERSION_MESSAGE);
			} else if (!fCancelling) {
				fCancelling = true;
				SetStatusMessage(B_TRANSLATE("Cancelling" B_UTF8_ELLIPSIS));
				BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE);
			}
			break;
 
		case CONVERSION_DONE_MESSAGE:
		{
			SetStatusMessage(fCancelling ? B_TRANSLATE("Conversion cancelled")
				: B_TRANSLATE("Conversion completed"));
			fConverting = false;
			fCancelling = false;
			bool enable = CountSourceFiles() > 0;
			SetEnabled(enable, enable);
			fConvertButton->SetLabel(B_TRANSLATE("Convert"));
			break;
		}
 
		case OUTPUT_FOLDER_MESSAGE:
			// Execute Save Panel
			if (fSaveFilePanel == NULL) {
				BButton* selectThisDir;
 
				BMessage folderSelect(FOLDER_SELECT_MESSAGE);
				fSaveFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL,
					B_DIRECTORY_NODE, true, &folderSelect, NULL, false, true);
				fSaveFilePanel->SetButtonLabel(B_DEFAULT_BUTTON,
					B_TRANSLATE("Select"));
				fSaveFilePanel->SetTarget(this);
 
				fSaveFilePanel->Window()->Lock();
				fSaveFilePanel->Window()->SetTitle(
					B_TRANSLATE("MediaConverter+:SaveDirectory"));
				BRect buttonRect
					= fSaveFilePanel->Window()->ChildAt(0)->FindView(
						"cancel button")->Frame();
				buttonRect.right  = buttonRect.left - 20;
				buttonRect.left = buttonRect.right - 130;
				selectThisDir = new BButton(buttonRect, NULL,
					B_TRANSLATE("Select this folder"),
					new BMessage(SELECT_THIS_DIR_MESSAGE),
					B_FOLLOW_BOTTOM | B_FOLLOW_RIGHT);
				selectThisDir->SetTarget(this);
				fSaveFilePanel->Window()->ChildAt(0)->AddChild(selectThisDir);
				fSaveFilePanel->Window()->Unlock();
 
				fSaveFilePanel->SetRefFilter(new DirectoryFilter);
			}
			fSaveFilePanel->Show();
			break;
 
		case FOLDER_SELECT_MESSAGE:
			// "SELECT" Button at Save Panel Pushed
			fSaveFilePanel->GetNextSelectedRef(&inRef);
			inEntry.SetTo(&inRef, true);
			_SetOutputFolder(inEntry);
			fOutputDirSpecified = true;
			fSaveFilePanel->Rewind();
			break;
 
		case SELECT_THIS_DIR_MESSAGE:
			// "THIS DIR" Button at Save Panel Pushed
			fSaveFilePanel->GetPanelDirectory(&inRef);
			fSaveFilePanel->Hide();
			inEntry.SetTo(&inRef, true);
			_SetOutputFolder(inEntry);
			fOutputDirSpecified = true;
			break;
 
		case OPEN_FILE_MESSAGE:
			// Execute Open Panel
			if (!fOpenFilePanel) {
				fOpenFilePanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL,
					B_FILE_NODE, true, NULL, NULL, false, true);
				fOpenFilePanel->SetTarget(this);
			}
			fOpenFilePanel->Show();
			break;
 
		case B_REFS_RECEIVED:
			// Media Files Seleced by Open Panel
			DetachCurrentMessage();
			message->what = B_REFS_RECEIVED;
			BMessenger(be_app).SendMessage(message);
			// fall through
 
		case B_CANCEL:
			break;
 
		case QUIT_MESSAGE:
			MediaConverterWindow::QuitRequested();
			break;
 
		case PREVIEW_MESSAGE:
		{
			// Build the command line to launch the preview application.
			// TODO: Launch the default app instead of hardcoded MediaPlayer!
			int32 srcIndex = fListView->CurrentSelection();
			BMediaFile* inFile = NULL;
			status_t status = GetSourceFileAt(srcIndex, &inFile, &inRef);
 
			const char* argv[3];
			BString startPosString;
			BPath path;
 
			if (status == B_OK) {
				argv[0] = "-pos";
					// NOTE: -pos argument is currently not supported by Haiku
					// MediaPlayer.
				startPosString << fStartDurationTC->Text();
				startPosString << "000";
				argv[1] = startPosString.String();
 
				status = inEntry.SetTo(&inRef);
			}
 
			if (status == B_OK) {
				status = inEntry.GetPath(&path);
				if (status == B_OK)
					argv[2] = path.Path();
			}
 
			if (status == B_OK) {
				status = be_roster->Launch(
					"application/x-vnd.Haiku-MediaPlayer",
					3, (char**)argv, NULL);
			}
 
			if (status != B_OK && status != B_ALREADY_RUNNING) {
				BString errorString(B_TRANSLATE("Error launching: %strError%"));
				errorString.ReplaceFirst("%strError%", strerror(status));
				BAlert* alert = new BAlert(B_TRANSLATE("Error"),
					errorString.String(), B_TRANSLATE("OK"));
				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
				alert->Go();
			}
			break;
		}
 
		case VIDEO_QUALITY_CHANGED_MESSAGE:
		{
			int32 value;
			message->FindInt32("be:value", &value);
			snprintf(buffer, sizeof(buffer),
				B_TRANSLATE("Video quality: %3d%%"), (int8)value);
			fVideoQualitySlider->SetLabel(buffer);
			fVideoQuality = value;
			break;
		}
 
		case AUDIO_QUALITY_CHANGED_MESSAGE:
		{
			int32 value;
			message->FindInt32("be:value", &value);
			snprintf(buffer, sizeof(buffer),
				B_TRANSLATE("Audio quality: %3d%%"), (int8)value);
			fAudioQualitySlider->SetLabel(buffer);
			fAudioQuality = value;
			break;
		}
 
		default:
			BWindow::MessageReceived(message);
	}
}
 
 
bool
MediaConverterWindow::QuitRequested()
{
	if (!fConverting) {
		BMessenger(be_app).SendMessage(B_QUIT_REQUESTED);
		return true;
	} else if (!fCancelling) {
		fCancelling = true;
		SetStatusMessage(B_TRANSLATE("Cancelling"));
		BMessenger(be_app).SendMessage(CANCEL_CONVERSION_MESSAGE);
	}
 
	return false;
}
 
 
// #pragma mark -
 
 
void
MediaConverterWindow::LanguageChanged()
{
	_DestroyMenu();
	_CreateMenu();
	_UpdateLabels();
	BuildAudioVideoMenus();
	Lock();
	fInfoView->Invalidate();
	Unlock();
}
 
 
void
MediaConverterWindow::BuildAudioVideoMenus()
{
	BMenu* menu = fAudioMenu->Menu();
	BMenuItem* item;
 
	// clear out old audio codec menu items
	while ((item = menu->RemoveItem((int32)0)) != NULL)
		delete item;
 
	bool separator = true;
 
	// get selected file format
	FileFormatMenuItem* ffmi
		= (FileFormatMenuItem*)fFormatMenu->Menu()->FindMarked();
	media_file_format* mf_format = &(ffmi->fFileFormat);
 
	media_format format, outfmt;
	format.Clear();
	media_codec_info codec_info;
	int32 cookie = 0;
	CodecMenuItem* cmi;
 
	// add available audio encoders to menu
	format.type = B_MEDIA_RAW_AUDIO;
	format.u.raw_audio = media_raw_audio_format::wildcard;
	while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info)
			== B_OK) {
		if (separator) {
			menu->AddItem(new BMenuItem(
				B_TRANSLATE_CONTEXT("No audio", "Audio codecs list"),
				new BMessage(AUDIO_CODEC_SELECT_MESSAGE)));
			menu->AddSeparatorItem();
			separator = false;
		}
 
		cmi = new CodecMenuItem(&codec_info, AUDIO_CODEC_SELECT_MESSAGE);
		menu->AddItem(cmi);
		// reset media format struct
/*
		format.type = B_MEDIA_RAW_AUDIO;
		format.u.raw_audio = media_raw_audio_format::wildcard;
*/
	}
 
	// mark first audio encoder
	item = menu->ItemAt(0);
	if (item != NULL) {
		fAudioMenu->SetEnabled(fEnabled);
		fAudioQualitySlider->SetEnabled(fEnabled);
		item->SetMarked(true);
		((BInvoker*)item)->Invoke();
	} else {
		item = new BMenuItem(
			B_TRANSLATE_CONTEXT("None available", "Audio codecs"), NULL);
		menu->AddItem(item);
		item->SetMarked(true);
		fAudioMenu->SetEnabled(false);
		fAudioQualitySlider->SetEnabled(false);
	}
 
	// clear out old video codec menu items
	menu = fVideoMenu->Menu();
	while ((item = menu->RemoveItem((int32)0)) != NULL)
		delete item;
 
	separator = true;
 
	// construct a generic video format.  Some of these parameters
	// seem silly, but are needed for R4.5.x, which is more picky
	// than subsequent BeOS releases will be.
	format.Clear();
	format.type = B_MEDIA_RAW_VIDEO;
	format.u.raw_video.last_active = (uint32)(240 - 1);
	format.u.raw_video.orientation = B_VIDEO_TOP_LEFT_RIGHT;
	format.u.raw_video.display.format = B_RGB32;
	format.u.raw_video.display.line_width = (int32)320;
	format.u.raw_video.display.line_count = (int32)240;
	format.u.raw_video.display.bytes_per_row = 4 * 320;
 
	// add available video encoders to menu
	cookie = 0;
	while (get_next_encoder(&cookie, mf_format, &format, &outfmt, &codec_info)
			== B_OK) {
		if (separator) {
			menu->AddItem(new BMenuItem(
				B_TRANSLATE_CONTEXT("No video", "Video codecs list"),
				new BMessage(VIDEO_CODEC_SELECT_MESSAGE)));
			menu->AddSeparatorItem();
			separator = false;
		}
 
		cmi = new CodecMenuItem(&codec_info, VIDEO_CODEC_SELECT_MESSAGE);
		menu->AddItem(cmi);
	}
 
	// mark first video encoder
	item = menu->ItemAt(0);
	if (item != NULL) {
		fVideoMenu->SetEnabled(fEnabled);
		fVideoQualitySlider->SetEnabled(fEnabled);
		item->SetMarked(true);
		((BInvoker*)item)->Invoke();
	} else {
		item = new BMenuItem(
			B_TRANSLATE_CONTEXT("None available", "Video codecs"), NULL);
		menu->AddItem(item);
		item->SetMarked(true);
		fVideoMenu->SetEnabled(false);
		fVideoQualitySlider->SetEnabled(false);
	}
}
 
void
MediaConverterWindow::GetSelectedFormatInfo(media_file_format** format,
	media_codec_info** audio, media_codec_info** video)
{
	*audio = NULL;
	*video = NULL;
	*format = NULL;
 
	FileFormatMenuItem* formatItem =
		dynamic_cast<FileFormatMenuItem*>(fFormatMenu->Menu()->FindMarked());
	if (formatItem != NULL)
		*format = &(formatItem->fFileFormat);
 
	*audio = *video = NULL;
	CodecMenuItem* codecItem =
		dynamic_cast<CodecMenuItem*>(fAudioMenu->Menu()->FindMarked());
	if (codecItem != NULL)
		*audio =  &(codecItem->fCodecInfo);
 
	codecItem = dynamic_cast<CodecMenuItem*>(fVideoMenu->Menu()->FindMarked());
	if (codecItem != NULL)
		*video =  &(codecItem->fCodecInfo);
}
 
 
void
MediaConverterWindow::BuildFormatMenu()
{
	BMenu* menu = fFormatMenu->Menu();
	BMenuItem* item;
 
	// clear out old format menu items
	while ((item = menu->RemoveItem((int32)0)) != NULL)
		delete item;
 
	// add menu items for each file format
	media_file_format mfi;
	int32 cookie = 0;
	FileFormatMenuItem* ff_item;
	while (get_next_file_format(&cookie, &mfi) == B_OK) {
		if ((mfi.capabilities & media_file_format::B_WRITABLE) == 0)
			continue;
		ff_item = new FileFormatMenuItem(&mfi);
		menu->AddItem(ff_item);
	}
 
	// mark first item
	item = menu->ItemAt(0);
	if (item != NULL) {
		item->SetMarked(true);
		((BInvoker*)item)->Invoke();
	}
}
 
 
void
MediaConverterWindow::SetFileMessage(const char* message)
{
	fFileStatus->SetText(message);
}
 
 
void
MediaConverterWindow::SetStatusMessage(const char* message)
{
	fStatus->SetText(message);
}
 
 
// #pragma mark -
 
 
bool
MediaConverterWindow::AddSourceFile(BMediaFile* file, const entry_ref& ref)
{
	if (!fListView->AddMediaItem(file, ref))
		return false;
 
	if (!fOutputDirSpecified) {
		BEntry entry(&ref);
		entry.GetParent(&entry);
		_SetOutputFolder(entry);
	}
 
	return true;
}
 
 
void
MediaConverterWindow::RemoveSourceFile(int32 index)
{
	delete fListView->RemoveItem(index);
	fStartDurationTC->SetText("0");
	fEndDurationTC->SetText("0");
}
 
 
int32
MediaConverterWindow::CountSourceFiles()
{
	return fListView->CountItems();
}
 
 
status_t
MediaConverterWindow::GetSourceFileAt(int32 index, BMediaFile** _file,
	entry_ref* ref)
{
	MediaFileListItem* item = dynamic_cast<MediaFileListItem*>(
		fListView->ItemAt(index));
	if (item != NULL) {
		*_file = item->fMediaFile;
		*ref = item->fRef;
		return B_OK;
	} else
		return B_ERROR;
}
 
 
void
MediaConverterWindow::SourceFileSelectionChanged()
{
	int32 selected = fListView->CurrentSelection();
	BMediaFile* file = NULL;
	entry_ref ref;
	bool enabled = GetSourceFileAt(selected, &file, &ref) == B_OK;
 
	fPreviewButton->SetEnabled(enabled);
	fVideoQualitySlider->SetEnabled(enabled);
	fAudioQualitySlider->SetEnabled(enabled);
	fStartDurationTC->SetEnabled(enabled);
	fEndDurationTC->SetEnabled(enabled);
 
	BString duration;
	if (enabled) {
		fInfoView->Update(file, &ref);
		// HACK: get the fInfoView to update the duration "synchronously"
		UpdateIfNeeded();
		duration << fInfoView->Duration() / 1000;
	} else
		duration = "0";
 
	// update duration text controls
	fStartDurationTC->SetText("0");
	fEndDurationTC->SetText(duration.String());
}
 
 
// #pragma mark -
 
 
void
MediaConverterWindow::SetEnabled(bool enabled, bool convertEnabled)
{
	fConvertButton->SetEnabled(convertEnabled);
	if (enabled == fEnabled)
		return;
 
	fFormatMenu->SetEnabled(enabled);
	fAudioMenu->SetEnabled(enabled);
	fVideoMenu->SetEnabled(enabled);
	fListView->SetEnabled(enabled);
	fStartDurationTC->SetEnabled(enabled);
	fEndDurationTC->SetEnabled(enabled);
 
	fEnabled = enabled;
}
 
 
bool
MediaConverterWindow::IsEnabled()
{
	return fEnabled;
}
 
 
const char*
MediaConverterWindow::StartDuration() const
{
	return fStartDurationTC->Text();
}
 
 
const char*
MediaConverterWindow::EndDuration() const
{
	return fEndDurationTC->Text();
}
 
 
BDirectory
MediaConverterWindow::OutputDirectory() const
{
	return fOutputDir;
}
 
 
void
MediaConverterWindow::SetAudioQualityLabel(const char* label)
{
	fAudioQualitySlider->SetLabel(label);
}
 
 
void
MediaConverterWindow::SetVideoQualityLabel(const char* label)
{
	fVideoQualitySlider->SetLabel(label);
}
 
 
void
MediaConverterWindow::TruncateOutputFolderPath()
{
	BEntry entry;
	fOutputDir.GetEntry(&entry);
	BPath path;
	entry.GetPath(&path);
	BString pathString(path.Path());
	float maxWidth = fVideoMenu->MenuBar()->Frame().Width();
 
	fOutputFolder->TruncateString(&pathString, B_TRUNCATE_MIDDLE, maxWidth);
	fOutputFolder->SetText(pathString.String());
	if (fOutputFolder->StringWidth(path.Path()) > maxWidth)
		fOutputFolder->SetToolTip(path.Path());
	else
		fOutputFolder->SetToolTip((const char*)NULL);
}
 
 
// #pragma mark -
 
 
void
MediaConverterWindow::_UpdateLabels()
{
	if (fSourcesBox != NULL) {
		fSourcesBox->SetLabel(B_TRANSLATE("Source files"));
		_UpdateBBoxLayoutInsets(fSourcesBox);
	}
 
	if (fInfoBox != NULL)
		fInfoBox->SetLabel(B_TRANSLATE("File details"));
 
	if (fOutputBox != NULL) {
		fOutputBox->SetLabel(B_TRANSLATE("Output format"));
		_UpdateBBoxLayoutInsets(fOutputBox);
	}
 
	if (fConvertButton != NULL)
		fConvertButton->SetLabel(B_TRANSLATE("Convert"));
 
	if (fPreviewButton != NULL)
		fPreviewButton->SetLabel(B_TRANSLATE("Preview"));
 
	if (fDestButton != NULL)
		fDestButton->SetLabel(B_TRANSLATE("Output folder"));
 
	if (fVideoQualitySlider != NULL) {
		char buffer[40];
		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Video quality: %3d%%"),
			(int8)fVideoQuality);
		fVideoQualitySlider->SetLabel(buffer);
		fVideoQualitySlider->SetLimitLabels(B_TRANSLATE("Low"),
			B_TRANSLATE("High"));
	}
 
	if (fAudioQualitySlider != NULL) {
		char buffer[40];
		snprintf(buffer, sizeof(buffer), B_TRANSLATE("Audio quality: %3d%%"),
			(int8)fAudioQuality);
		fAudioQualitySlider->SetLabel(buffer);
		fAudioQualitySlider->SetLimitLabels(B_TRANSLATE("Low"),
			B_TRANSLATE("High"));
	}
 
	if (fStartDurationTC != NULL)
		fStartDurationTC->SetLabel(B_TRANSLATE("Start [ms]: "));
 
	if (fEndDurationTC != NULL)
		fEndDurationTC->SetLabel(B_TRANSLATE("End   [ms]: "));
 
	if (fFormatMenu != NULL)
		fFormatMenu->SetLabel(B_TRANSLATE("File format:"));
 
	if (fAudioMenu != NULL)
		fAudioMenu->SetLabel(B_TRANSLATE("Audio encoding:"));
 
	if (fVideoMenu != NULL)
		fVideoMenu->SetLabel(B_TRANSLATE("Video encoding:"));
 
	SetFileMessage(B_TRANSLATE("Drop media files onto this window"));
}
 
 
void
MediaConverterWindow::_UpdateBBoxLayoutInsets(BBox* box)
{
	BTwoDimensionalLayout* layout
		= dynamic_cast<BTwoDimensionalLayout*>(box->GetLayout());
	if (layout != NULL) {
		float padding = be_control_look->DefaultItemSpacing();
		layout->SetInsets(padding, box->TopBorderOffset() + padding, padding,
			padding);
	}
}
 
 
void
MediaConverterWindow::_DestroyMenu()
{
	BMenu* menu;
 
	while ((menu = fMenuBar->SubmenuAt(0)) != NULL) {
		fMenuBar->RemoveItem(menu);
		delete menu;
	}
}
 
 
void
MediaConverterWindow::_CreateMenu()
{
	BMenu* menu;
	BMenuItem* item;
 
	menu = new BMenu(B_TRANSLATE_CONTEXT("File", "Menu"));
	item = new BMenuItem(B_TRANSLATE_CONTEXT("Open" B_UTF8_ELLIPSIS, "Menu"),
		new BMessage(OPEN_FILE_MESSAGE), 'O');
	menu->AddItem(item);
	menu->AddSeparatorItem();
	item = new BMenuItem(B_TRANSLATE_CONTEXT("Quit", "Menu"),
		new BMessage(QUIT_MESSAGE), 'Q');
	menu->AddItem(item);
 
	fMenuBar->AddItem(menu);
}
 
 
void
MediaConverterWindow::_SetOutputFolder(BEntry entry)
{
	BPath path;
	entry.GetPath(&path);
	if (access(path.Path(), W_OK) != -1) {
		fOutputDir.SetTo(&entry);
	} else {
		BString errorString(B_TRANSLATE("Error writing to location: %strPath%."
			" Defaulting to location: /boot/home"));
		errorString.ReplaceFirst("%strPath%", path.Path());
		BAlert* alert = new BAlert(B_TRANSLATE("Error"),
			errorString.String(), B_TRANSLATE("OK"));
		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
		alert->Go();
		fOutputDir.SetTo("/boot/home");
	}
	TruncateOutputFolderPath();
}

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.

V773 Visibility scope of the 'alert' pointer was exited without releasing the memory. A memory leak is possible.