#include "CodyCam.h"
 
#include <stdio.h>
#include <string.h>
#include <unistd.h>
 
#include <Alert.h>
#include <Button.h>
#include <Catalog.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <MediaDefs.h>
#include <MediaNode.h>
#include <MediaRoster.h>
#include <MediaTheme.h>
#include <Menu.h>
#include <MenuBar.h>
#include <MenuItem.h>
#include <Path.h>
#include <PopUpMenu.h>
#include <scheduler.h>
#include <TabView.h>
#include <TextControl.h>
#include <TimeSource.h>
#include <TranslationUtils.h>
 
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "CodyCam"
 
#define VIDEO_SIZE_X 320
#define VIDEO_SIZE_Y 240
 
#define WINDOW_SIZE_X (VIDEO_SIZE_X + 80)
#define WINDOW_SIZE_Y (VIDEO_SIZE_Y + 230)
 
#define WINDOW_OFFSET_X 28
#define WINDOW_OFFSET_Y 28
 
#define	CALL		printf
#define ERROR		printf
#define FTPINFO		printf
#define	INFO		printf
 
 
// Utility functions
 
namespace {
 
// functions for EnumeratedStringValueSettings
 
const char*
CaptureRateAt(int32 i)
{
	return (i >= 0 && i < kCaptureRatesCount) ? kCaptureRates[i].name : NULL;
}
 
 
const char*
UploadClientAt(int32 i)
{
	return (i >= 0 && i < kUploadClientsCount) ? kUploadClients[i] : NULL;
}
 
 
}; // end anonymous namespace
 
 
 
//	#pragma mark -
 
 
CodyCam::CodyCam()
	:
	BApplication("application/x-vnd.Haiku-CodyCam"),
	fMediaRoster(NULL),
	fVideoConsumer(NULL),
	fWindow(NULL),
	fPort(0),
	fVideoControlWindow(NULL)
{
	int32 index = 0;
	kCaptureRates[index++].name = B_TRANSLATE("Every 15 seconds");
	kCaptureRates[index++].name = B_TRANSLATE("Every 30 seconds");
	kCaptureRates[index++].name = B_TRANSLATE("Every minute");
	kCaptureRates[index++].name = B_TRANSLATE("Every 5 minutes");
	kCaptureRates[index++].name = B_TRANSLATE("Every 10 minutes");
	kCaptureRates[index++].name = B_TRANSLATE("Every 15 minutes");
	kCaptureRates[index++].name = B_TRANSLATE("Every 30 minutes");
	kCaptureRates[index++].name = B_TRANSLATE("Every hour");
	kCaptureRates[index++].name = B_TRANSLATE("Every 2 hours");
	kCaptureRates[index++].name = B_TRANSLATE("Every 4 hours");
	kCaptureRates[index++].name = B_TRANSLATE("Every 8 hours");
	kCaptureRates[index++].name = B_TRANSLATE("Every 24 hours");
	kCaptureRates[index++].name = B_TRANSLATE("Never");
 
	index = 0;
	kUploadClients[index++] = B_TRANSLATE("FTP");
	kUploadClients[index++] = B_TRANSLATE("SFTP");
	kUploadClients[index++] = B_TRANSLATE("Local");
 
	BPath homeDir;
	if (find_directory(B_USER_DIRECTORY, &homeDir) != B_OK)
		homeDir.SetTo("/boot/home");
 
	chdir(homeDir.Path());
}
 
 
CodyCam::~CodyCam()
{
	CALL("CodyCam::~CodyCam\n");
 
	// release the video consumer node
	// the consumer node cleans up the window
	if (fVideoConsumer) {
		fVideoConsumer->Release();
		fVideoConsumer = NULL;
	}
 
	CALL("CodyCam::~CodyCam - EXIT\n");
}
 
 
void
CodyCam::ReadyToRun()
{
	fWindow = new VideoWindow(
		(const char*) B_TRANSLATE_SYSTEM_NAME("CodyCam"), B_TITLED_WINDOW,
		B_NOT_ZOOMABLE | B_NOT_V_RESIZABLE
		| B_AUTO_UPDATE_SIZE_LIMITS, &fPort);
 
	if (_SetUpNodes() != B_OK)
		fWindow->ToggleMenuOnOff();
 
	((VideoWindow*)fWindow)->ApplyControls();
}
 
 
bool
CodyCam::QuitRequested()
{
	_TearDownNodes();
	snooze(100000);
 
	return true;
}
 
 
void
CodyCam::MessageReceived(BMessage *message)
{
	switch (message->what) {
		case msg_start:
		{
			BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(
				fTimeSourceNode);
			bigtime_t real = BTimeSource::RealTime();
			bigtime_t perf = timeSource->PerformanceTimeFor(real) + 10000;
			status_t status = fMediaRoster->StartNode(fProducerNode, perf);
			if (status != B_OK)
				ERROR("error starting producer!");
			timeSource->Release();
			break;
		}
 
		case msg_stop:
			fMediaRoster->StopNode(fProducerNode, 0, true);
			break;
 
		case msg_video:
		{
			if (fVideoControlWindow) {
				fVideoControlWindow->Activate();
				break;
			}
			BParameterWeb* web = NULL;
			BView* view = NULL;
			media_node node = fProducerNode;
			status_t err = fMediaRoster->GetParameterWebFor(node, &web);
			if (err >= B_OK && web != NULL) {
				view = BMediaTheme::ViewFor(web);
				fVideoControlWindow = new ControlWindow(view, node);
				fMediaRoster->StartWatching(BMessenger(NULL,
					fVideoControlWindow), node,	B_MEDIA_WEB_CHANGED);
				fVideoControlWindow->Show();
			}
			break;
		}
 
		case msg_control_win:
			// our control window is being asked to go away
			// set our pointer to NULL
			fVideoControlWindow = NULL;
			break;
 
		default:
			BApplication::MessageReceived(message);
			break;
	}
}
 
 
status_t
CodyCam::_SetUpNodes()
{
	status_t status = B_OK;
 
	/* find the media roster */
	fMediaRoster = BMediaRoster::Roster(&status);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot find the media roster"),
			status);
		return status;
	}
 
	/* find the time source */
	status = fMediaRoster->GetTimeSource(&fTimeSourceNode);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot get a time source"), status);
		return status;
	}
 
	/* find a video producer node */
	INFO("CodyCam acquiring VideoInput node\n");
	status = fMediaRoster->GetVideoInput(&fProducerNode);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot find a video source.\n"
			"You need a webcam to use CodyCam."), status);
		return status;
	}
 
	/* create the video consumer node */
	fVideoConsumer = new VideoConsumer("CodyCam",
		((VideoWindow*)fWindow)->VideoView(),
		((VideoWindow*)fWindow)->StatusLine(), NULL, 0);
	if (!fVideoConsumer) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot create a video window"),
			B_ERROR);
		return B_ERROR;
	}
 
	/* register the node */
	status = fMediaRoster->RegisterNode(fVideoConsumer);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot register the video window"),
			status);
		return status;
	}
	fPort = fVideoConsumer->ControlPort();
 
	/* find free producer output */
	int32 cnt = 0;
	status = fMediaRoster->GetFreeOutputsFor(fProducerNode, &fProducerOut, 1,
		&cnt, B_MEDIA_RAW_VIDEO);
	if (status != B_OK || cnt < 1) {
		status = B_RESOURCE_UNAVAILABLE;
		fWindow->ErrorAlert(B_TRANSLATE("Cannot find an available video stream"),
			status);
		return status;
	}
 
	/* find free consumer input */
	cnt = 0;
	status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(),
		&fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO);
	if (status != B_OK || cnt < 1) {
		status = B_RESOURCE_UNAVAILABLE;
		fWindow->ErrorAlert(B_TRANSLATE("Can't find an available connection to "
			"the video window"), status);
		return status;
	}
 
	/* Connect The Nodes!!! */
	media_format format;
	format.type = B_MEDIA_RAW_VIDEO;
	media_raw_video_format vid_format = {0, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT,
		1, 1, {B_RGB32, VIDEO_SIZE_X, VIDEO_SIZE_Y, VIDEO_SIZE_X * 4, 0, 0}};
	format.u.raw_video = vid_format;
 
	/* connect producer to consumer */
	status = fMediaRoster->Connect(fProducerOut.source,
		fConsumerIn.destination, &format, &fProducerOut, &fConsumerIn);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot connect the video source to "
			"the video window"), status);
		return status;
	}
 
 
	/* set time sources */
	status = fMediaRoster->SetTimeSourceFor(fProducerNode.node,
		fTimeSourceNode.node);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the "
			"video source"), status);
		return status;
	}
 
	status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(),
		fTimeSourceNode.node);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot set the time source for the "
			"video window"), status);
		return status;
	}
 
	/* figure out what recording delay to use */
	bigtime_t latency = 0;
	status = fMediaRoster->GetLatencyFor(fProducerNode, &latency);
	status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency);
 
	/* start the nodes */
	bigtime_t initLatency = 0;
	status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency);
	if (status < B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Error getting initial latency for the "
			"capture node"), status);
		return status;
	}
 
	initLatency += estimate_max_scheduling_latency();
 
	BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode);
	bool running = timeSource->IsRunning();
 
	/* workaround for people without sound cards */
	/* because the system time source won't be running */
	bigtime_t real = BTimeSource::RealTime();
	if (!running) {
		status = fMediaRoster->StartTimeSource(fTimeSourceNode, real);
		if (status != B_OK) {
			timeSource->Release();
			fWindow->ErrorAlert(B_TRANSLATE("Cannot start time source!"),
				status);
			return status;
		}
		status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real);
		if (status != B_OK) {
			timeSource->Release();
			fWindow->ErrorAlert(B_TRANSLATE("Cannot seek time source!"),
				status);
			return status;
		}
	}
 
	bigtime_t perf = timeSource->PerformanceTimeFor(real + latency
		+ initLatency);
	timeSource->Release();
 
	/* start the nodes */
	status = fMediaRoster->StartNode(fProducerNode, perf);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video source"),
			status);
		return status;
	}
	status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf);
	if (status != B_OK) {
		fWindow->ErrorAlert(B_TRANSLATE("Cannot start the video window"),
			status);
		return status;
	}
 
	return status;
}
 
 
void
CodyCam::_TearDownNodes()
{
	CALL("CodyCam::_TearDownNodes\n");
	if (!fMediaRoster)
		return;
 
	if (fVideoConsumer) {
		/* stop */
		INFO("stopping nodes!\n");
//		fMediaRoster->StopNode(fProducerNode, 0, true);
		fMediaRoster->StopNode(fVideoConsumer->Node(), 0, true);
 
		/* disconnect */
		fMediaRoster->Disconnect(fProducerOut.node.node, fProducerOut.source,
			fConsumerIn.node.node, fConsumerIn.destination);
 
		if (fProducerNode != media_node::null) {
			INFO("CodyCam releasing fProducerNode\n");
			fMediaRoster->ReleaseNode(fProducerNode);
			fProducerNode = media_node::null;
		}
		fMediaRoster->ReleaseNode(fVideoConsumer->Node());
		fVideoConsumer = NULL;
	}
}
 
 
//	#pragma mark - Video Window Class
 
 
VideoWindow::VideoWindow(const char* title, window_type type,
		uint32 flags, port_id* consumerPort)
	:
	BWindow(BRect(50, 50, 50, 50), title, type, flags),
	fPortPtr(consumerPort),
	fVideoView(NULL)
{
	fFtpInfo.port = 0;
	fFtpInfo.rate = 0x7fffffff;
	fFtpInfo.imageFormat = 0;
	fFtpInfo.translator = 0;
	fFtpInfo.passiveFtp = true;
	fFtpInfo.uploadClient = 0;
	strcpy(fFtpInfo.fileNameText, "filename");
	strcpy(fFtpInfo.serverText, "server");
	strcpy(fFtpInfo.loginText, "login");
	strcpy(fFtpInfo.passwordText, "password");
	strcpy(fFtpInfo.directoryText, "directory");
 
	_SetUpSettings("codycam", "");
 
	BMenuBar* menuBar = new BMenuBar("menu bar");
 
	BMenuItem* menuItem;
	fMenu = new BMenu(B_TRANSLATE("File"));
 
	menuItem = new BMenuItem(B_TRANSLATE("Video settings"),
		new BMessage(msg_video), 'P');
	menuItem->SetTarget(be_app);
	fMenu->AddItem(menuItem);
 
	fMenu->AddSeparatorItem();
 
	menuItem = new BMenuItem(B_TRANSLATE("Start video"),
		new BMessage(msg_start), 'A');
	menuItem->SetTarget(be_app);
	fMenu->AddItem(menuItem);
 
	menuItem = new BMenuItem(B_TRANSLATE("Stop video"),
		new BMessage(msg_stop), 'O');
	menuItem->SetTarget(be_app);
	fMenu->AddItem(menuItem);
 
	fMenu->AddSeparatorItem();
 
	menuItem = new BMenuItem(B_TRANSLATE("Quit"),
		new BMessage(B_QUIT_REQUESTED), 'Q');
	menuItem->SetTarget(be_app);
	fMenu->AddItem(menuItem);
 
	menuBar->AddItem(fMenu);
 
	/* add some controls */
	_BuildCaptureControls();
 
	BBox* box = new BBox("box");
	BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
	box->SetLayout(layout);
	layout->SetInsets(2, 2, 2, 2);
	box->AddChild(fVideoView);
	box->AddChild(fErrorView);
 
	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
		.Add(menuBar)
		.AddGroup(B_VERTICAL)
			.SetInsets(B_USE_WINDOW_SPACING)
			.AddGroup(B_HORIZONTAL)
				.AddGlue()
				.Add(box)
				.AddGlue()
			.End()
			.AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
				.Add(fCaptureSetupBox)
				.Add(fFtpSetupBox)
				.End()
			.Add(fStatusLine)
			.End()
		.AddGlue();
 
	Show();
}
 
 
VideoWindow::~VideoWindow()
{
	_QuitSettings();
}
 
 
bool
VideoWindow::QuitRequested()
{
	be_app->PostMessage(B_QUIT_REQUESTED);
	return false;
}
 
 
void
VideoWindow::MessageReceived(BMessage* message)
{
	BControl* control = NULL;
	message->FindPointer((const char*)"source", (void **)&control);
 
	switch (message->what) {
		case msg_filename:
			if (control != NULL) {
				strlcpy(fFtpInfo.fileNameText,
					((BTextControl*)control)->Text(), 64);
				FTPINFO("file is '%s'\n", fFtpInfo.fileNameText);
			}
			break;
 
		case msg_rate_changed: {
			int32 seconds;
			message->FindInt32("seconds", &seconds);
			if (seconds == 0) {
				FTPINFO("never\n");
				fFtpInfo.rate = (bigtime_t)(B_INFINITE_TIMEOUT);
			} else {
				FTPINFO("%ld seconds\n", (long)seconds);
				fFtpInfo.rate = (bigtime_t)(seconds * 1000000LL);
			}
			break;
		}
 
		case msg_translate:
			message->FindInt32("be:type", (int32*)&(fFtpInfo.imageFormat));
			message->FindInt32("be:translator", &(fFtpInfo.translator));
			break;
 
		case msg_upl_client:
			message->FindInt32("client", &(fFtpInfo.uploadClient));
			FTPINFO("upl client = %" B_PRId32 "\n", fFtpInfo.uploadClient);
			_UploadClientChanged();
			break;
 
		case msg_server:
			if (control != NULL) {
				strlcpy(fFtpInfo.serverText,
					((BTextControl*)control)->Text(), 64);
				FTPINFO("server = '%s'\n", fFtpInfo.serverText);
			}
			break;
 
		case msg_login:
			if (control != NULL) {
				strlcpy(fFtpInfo.loginText,
					((BTextControl*)control)->Text(), 64);
				FTPINFO("login = '%s'\n", fFtpInfo.loginText);
			}
			break;
 
		case msg_password:
			if (control != NULL) {
				strlcpy(fFtpInfo.passwordText,
					((BTextControl*)control)->Text(), 64);
				FTPINFO("password = '%s'\n", fFtpInfo.passwordText);
			}
			break;
 
		case msg_directory:
			if (control != NULL) {
				strlcpy(fFtpInfo.directoryText,
					((BTextControl*)control)->Text(), 64);
				FTPINFO("directory = '%s'\n", fFtpInfo.directoryText);
			}
			break;
 
		case msg_passiveftp:
			if (control != NULL) {
				fFtpInfo.passiveFtp = ((BCheckBox*)control)->Value();
				if (fFtpInfo.passiveFtp)
					FTPINFO("using passive ftp\n");
			}
			break;
 
		default:
			BWindow::MessageReceived(message);
			return;
	}
 
	if (*fPortPtr)
		write_port(*fPortPtr, FTP_INFO, (void*)&fFtpInfo, sizeof(ftp_msg_info));
 
}
 
 
BView*
VideoWindow::VideoView()
{
	return fVideoView;
}
 
 
BStringView*
VideoWindow::StatusLine()
{
	return fStatusLine;
}
 
 
void
VideoWindow::_BuildCaptureControls()
{
	// a view to hold the video image
	fVideoView = new BView("Video preview", B_WILL_DRAW);
	fVideoView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
	fVideoView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
 
	fErrorView = new BTextView("error");
	fErrorView->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
 
	// Capture controls
	BGridLayout *controlsLayout = new BGridLayout(B_USE_DEFAULT_SPACING,
		B_USE_SMALL_SPACING);
	controlsLayout->SetInsets(B_USE_SMALL_SPACING);
 
	BView* controlView = new BView("Controls", B_SUPPORTS_LAYOUT, NULL);
	controlView->SetLayout(controlsLayout);
 
	fCaptureSetupBox = new BBox("Capture Controls", B_WILL_DRAW);
	fCaptureSetupBox->SetLabel(B_TRANSLATE("Capture controls"));
	fCaptureSetupBox->AddChild(controlView);
 
	// file name
	fFileName = new BTextControl("File Name", B_TRANSLATE("File name:"),
		fFilenameSetting->Value(), new BMessage(msg_filename));
	fFileName->SetTarget(BMessenger(NULL, this));
 
	// format menu
	fImageFormatMenu = new BPopUpMenu(B_TRANSLATE("Image Format Menu"));
	BTranslationUtils::AddTranslationItems(fImageFormatMenu, B_TRANSLATOR_BITMAP);
	fImageFormatMenu->SetTargetForItems(this);
 
	if (fImageFormatSettings->Value()
		&& fImageFormatMenu->FindItem(fImageFormatSettings->Value()) != NULL) {
		fImageFormatMenu->FindItem(
			fImageFormatSettings->Value())->SetMarked(true);
	} else if (fImageFormatMenu->FindItem("JPEG image") != NULL)
		fImageFormatMenu->FindItem("JPEG image")->SetMarked(true);
	else
		fImageFormatMenu->ItemAt(0)->SetMarked(true);
 
	fImageFormatSelector = new BMenuField("Format", B_TRANSLATE("Format:"),
		fImageFormatMenu);
 
	// capture rate
	fCaptureRateMenu = new BPopUpMenu(B_TRANSLATE("Capture Rate Menu"));
	for (int32 i = 0; i < kCaptureRatesCount; i++) {
		BMessage* itemMessage = new BMessage(msg_rate_changed);
		itemMessage->AddInt32("seconds", kCaptureRates[i].seconds);
		fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRates[i].name,
			itemMessage));
	}
	fCaptureRateMenu->SetTargetForItems(this);
	fCaptureRateMenu->FindItem(fCaptureRateSetting->Value())->SetMarked(true);
	fCaptureRateSelector = new BMenuField("Rate", B_TRANSLATE("Rate:"),
		fCaptureRateMenu);
 
	BLayoutBuilder::Grid<>(controlsLayout)
		.AddTextControl(fFileName, 0, 0)
		.AddMenuField(fImageFormatSelector, 0, 1)
		.AddMenuField(fCaptureRateSelector, 0, 2)
		.Add(BSpaceLayoutItem::CreateGlue(), 0, 3, 2, 1);
 
	// FTP setup box
	BGridLayout* ftpLayout = new BGridLayout(B_USE_DEFAULT_SPACING,
		B_USE_SMALL_SPACING);
	ftpLayout->SetInsets(B_USE_SMALL_SPACING);
 
	BView* outputView = new BView("Output", B_SUPPORTS_LAYOUT, NULL);
	outputView->SetLayout(ftpLayout);
 
	fFtpSetupBox = new BBox("FTP Setup", B_WILL_DRAW);
	fFtpSetupBox->SetLabel(B_TRANSLATE("Output"));
	fFtpSetupBox->AddChild(outputView);
	float minWidth = be_plain_font->StringWidth(
		"The server label plus ftp.reasonably.com");
	fFtpSetupBox->SetExplicitMinSize(BSize(minWidth, B_SIZE_UNSET));
	fFtpSetupBox->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
 
	fUploadClientMenu = new BPopUpMenu(B_TRANSLATE("Send to" B_UTF8_ELLIPSIS));
	for (int i = 0; i < kUploadClientsCount; i++) {
		BMessage *m = new BMessage(msg_upl_client);
		m->AddInt32("client", i);
		fUploadClientMenu->AddItem(new BMenuItem(kUploadClients[i], m));
	}
 
	fUploadClientMenu->SetTargetForItems(this);
	fUploadClientMenu->FindItem(fUploadClientSetting->Value())->SetMarked(true);
 
	fUploadClientSelector = new BMenuField("UploadClient", NULL,
		fUploadClientMenu);
 
	fUploadClientSelector->SetLabel(B_TRANSLATE("Type:"));
 
	fServerName = new BTextControl("Server", B_TRANSLATE("Server:"),
		fServerSetting->Value(), new BMessage(msg_server));
	fServerName->SetTarget(this);
 
	fLoginId = new BTextControl("Login", B_TRANSLATE("Login:"),
		fLoginSetting->Value(), new BMessage(msg_login));
	fLoginId->SetTarget(this);
 
	fPassword = new BTextControl("Password", B_TRANSLATE("Password:"),
		fPasswordSetting->Value(), new BMessage(msg_password));
	fPassword->SetTarget(this);
	fPassword->TextView()->HideTyping(true);
	// BeOS HideTyping() seems broken, it empties the text
	fPassword->SetText(fPasswordSetting->Value());
 
	fDirectory = new BTextControl("Directory", B_TRANSLATE("Directory:"),
		fDirectorySetting->Value(), new BMessage(msg_directory));
	fDirectory->SetTarget(this);
 
	fPassiveFtp = new BCheckBox("Passive FTP", B_TRANSLATE("Passive FTP"),
		new BMessage(msg_passiveftp));
	fPassiveFtp->SetTarget(this);
	fPassiveFtp->SetValue(fPassiveFtpSetting->Value());
	fPassiveFtp->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
 
	BLayoutBuilder::Grid<>(ftpLayout)
		.AddMenuField(fUploadClientSelector, 0, 0)
		.AddTextControl(fServerName, 0, 1)
		.AddTextControl(fLoginId, 0, 2)
		.AddTextControl(fPassword, 0, 3)
		.AddTextControl(fDirectory, 0, 4)
		.Add(fPassiveFtp, 0, 5, 2, 1);
 
	fStatusLine = new BStringView("Status Line",
		B_TRANSLATE("Waiting" B_UTF8_ELLIPSIS));
	fStatusLine->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
}
 
 
void
VideoWindow::ApplyControls()
{
	if (!Lock())
		return;
 
	// apply controls
	fFileName->Invoke();
	PostMessage(fImageFormatMenu->FindMarked()->Message());
	PostMessage(fCaptureRateMenu->FindMarked()->Message());
	PostMessage(fUploadClientMenu->FindMarked()->Message());
	fServerName->Invoke();
	fLoginId->Invoke();
	fPassword->Invoke();
	fDirectory->Invoke();
	fPassiveFtp->Invoke();
 
	Unlock();
}
 
 
void
VideoWindow::ErrorAlert(const char* message, status_t err)
{
	Lock();
	fErrorView->SetText(message);
	fErrorView->MakeEditable(false);
	fErrorView->MakeSelectable(false);
	fErrorView->SetWordWrap(true);
	fErrorView->SetAlignment(B_ALIGN_CENTER);
	fErrorView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
	fErrorView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
	fErrorView->Show();
	fVideoView->Hide();
	Unlock();
 
	printf("%s\n%s [%" B_PRIx32 "]", message, strerror(err), err);
}
 
 
void
VideoWindow::_SetUpSettings(const char* filename, const char* dirname)
{
	fSettings = new Settings(filename, dirname);
 
	fServerSetting = new StringValueSetting("Server", "ftp.my.server",
		B_TRANSLATE("server address expected"));
 
	fLoginSetting = new StringValueSetting("Login", "loginID",
		B_TRANSLATE("login ID expected"));
 
	fPasswordSetting = new StringValueSetting("Password",
		B_TRANSLATE("password"), B_TRANSLATE("password expected"));
 
	fDirectorySetting = new StringValueSetting("Directory", "web/images",
		B_TRANSLATE("destination directory expected"));
 
	fPassiveFtpSetting = new BooleanValueSetting("PassiveFtp", 1);
 
	fFilenameSetting = new StringValueSetting("StillImageFilename",
		"codycam.jpg", B_TRANSLATE("still image filename expected"));
 
	fImageFormatSettings = new StringValueSetting("ImageFileFormat",
		B_TRANSLATE("JPEG image"), B_TRANSLATE("image file format expected"));
 
	fCaptureRateSetting = new EnumeratedStringValueSetting("CaptureRate",
		kCaptureRates[3].name, &CaptureRateAt,
		B_TRANSLATE("capture rate expected"),
		"unrecognized capture rate specified");
 
	fUploadClientSetting = new EnumeratedStringValueSetting("UploadClient",
		B_TRANSLATE("FTP"), &UploadClientAt,
		B_TRANSLATE("upload client name expected"),
		B_TRANSLATE("unrecognized upload client specified"));
 
	fSettings->Add(fServerSetting);
	fSettings->Add(fLoginSetting);
	fSettings->Add(fPasswordSetting);
	fSettings->Add(fDirectorySetting);
	fSettings->Add(fPassiveFtpSetting);
	fSettings->Add(fFilenameSetting);
	fSettings->Add(fImageFormatSettings);
	fSettings->Add(fCaptureRateSetting);
	fSettings->Add(fUploadClientSetting);
 
	fSettings->TryReadingSettings();
}
 
 
void
VideoWindow::_UploadClientChanged()
{
	bool enableServerControls = fFtpInfo.uploadClient < 2;
	fServerName->SetEnabled(enableServerControls);
	fLoginId->SetEnabled(enableServerControls);
	fPassword->SetEnabled(enableServerControls);
	fDirectory->SetEnabled(enableServerControls);
	fPassiveFtp->SetEnabled(enableServerControls);
}
 
 
void
VideoWindow::_QuitSettings()
{
	fServerSetting->ValueChanged(fServerName->Text());
	fLoginSetting->ValueChanged(fLoginId->Text());
	fPasswordSetting->ValueChanged(fFtpInfo.passwordText);
	fDirectorySetting->ValueChanged(fDirectory->Text());
	fPassiveFtpSetting->ValueChanged(fPassiveFtp->Value());
	fFilenameSetting->ValueChanged(fFileName->Text());
	fImageFormatSettings->ValueChanged(fImageFormatMenu->FindMarked()->Label());
	fCaptureRateSetting->ValueChanged(fCaptureRateMenu->FindMarked()->Label());
	fUploadClientSetting->ValueChanged(fUploadClientMenu->FindMarked()->Label());
 
	fSettings->SaveSettings();
	delete fSettings;
}
 
 
void
VideoWindow::ToggleMenuOnOff()
{
	BMenuItem* item = fMenu->FindItem(msg_video);
	item->SetEnabled(!item->IsEnabled());
 
	item = fMenu->FindItem(msg_start);
	item->SetEnabled(!item->IsEnabled());
 
	item = fMenu->FindItem(msg_stop);
	item->SetEnabled(!item->IsEnabled());
}
 
 
//	#pragma mark -
 
 
ControlWindow::ControlWindow(BView* controls,
	media_node node)
	:
	BWindow(BRect(), B_TRANSLATE("Video settings"), B_TITLED_WINDOW,
		B_ASYNCHRONOUS_CONTROLS)
{
	fView = controls;
	fNode = node;
 
	AddChild(fView);
}
 
 
void
ControlWindow::MessageReceived(BMessage* message)
{
	BParameterWeb* web = NULL;
	status_t err;
 
	switch (message->what) {
		case B_MEDIA_WEB_CHANGED:
		{
			// If this is a tab view, find out which tab
			// is selected
			BTabView* tabView = dynamic_cast<BTabView*>(fView);
			int32 tabNum = -1;
			if (tabView)
				tabNum = tabView->Selection();
 
			RemoveChild(fView);
			delete fView;
 
			err = BMediaRoster::Roster()->GetParameterWebFor(fNode, &web);
 
			if (err >= B_OK && web != NULL) {
				fView = BMediaTheme::ViewFor(web);
				AddChild(fView);
 
				// Another tab view?  Restore previous selection
				if (tabNum > 0) {
					BTabView* newTabView = dynamic_cast<BTabView*>(fView);
					if (newTabView)
						newTabView->Select(tabNum);
				}
			}
			break;
		}
 
		default:
			BWindow::MessageReceived(message);
			break;
	}
}
 
 
bool
ControlWindow::QuitRequested()
{
	be_app->PostMessage(msg_control_win);
	return true;
}
 
 
//	#pragma mark -
 
 
int main() {
	CodyCam app;
	app.Run();
	return 0;
}
 

V542 Consider inspecting an odd type cast: 'bool' to 'char *'.