xref: /haiku/src/apps/codycam/CodyCam.cpp (revision a7dde370f552f5376edbf25046ec9cf2ba8bbd1a)
1 #include "CodyCam.h"
2 
3 #include <stdio.h>
4 #include <string.h>
5 #include <unistd.h>
6 
7 #include <Alert.h>
8 #include <Button.h>
9 #include <LayoutBuilder.h>
10 #include <MediaDefs.h>
11 #include <MediaNode.h>
12 #include <MediaRoster.h>
13 #include <MediaTheme.h>
14 #include <Menu.h>
15 #include <MenuBar.h>
16 #include <MenuItem.h>
17 #include <PopUpMenu.h>
18 #include <scheduler.h>
19 #include <TabView.h>
20 #include <TextControl.h>
21 #include <TimeSource.h>
22 #include <TranslationKit.h>
23 
24 
25 #undef B_TRANSLATE_CONTEXT
26 #define B_TRANSLATE_CONTEXT "CodyCam"
27 
28 #define VIDEO_SIZE_X 320
29 #define VIDEO_SIZE_Y 240
30 
31 #define WINDOW_SIZE_X (VIDEO_SIZE_X + 80)
32 #define WINDOW_SIZE_Y (VIDEO_SIZE_Y + 230)
33 
34 #define WINDOW_OFFSET_X 28
35 #define WINDOW_OFFSET_Y 28
36 
37 const int32 kBtnHeight = 20;
38 const int32 kBtnWidth = 60;
39 const int32 kBtnBuffer = 25;
40 const int32 kXBuffer = 10;
41 const int32 kYBuffer = 10;
42 const int32 kMenuHeight = 15;
43 const int32 kButtonHeight = 15;
44 const int32 kSliderViewRectHeight = 40;
45 
46 #define	CALL		printf
47 #define ERROR		printf
48 #define FTPINFO		printf
49 #define	INFO		printf
50 
51 
52 // Utility functions
53 
54 namespace {
55 
56 void
57 ErrorAlert(const char* message, status_t err, BWindow *window = NULL)
58 {
59 	BAlert *alert = new BAlert("", message, B_TRANSLATE("OK"));
60 	if (window != NULL)
61 		alert->CenterIn(window->Frame());
62 	alert->Go();
63 
64 	printf("%s\n%s [%lx]", message, strerror(err), err);
65 //	be_app->PostMessage(B_QUIT_REQUESTED);
66 }
67 
68 
69 status_t
70 AddTranslationItems(BMenu* intoMenu, uint32 fromType)
71 {
72 
73 	BTranslatorRoster* use;
74 	char* translatorTypeName;
75 	const char* translatorIdName;
76 
77 	use = BTranslatorRoster::Default();
78 	translatorIdName = "be:translator";
79 	translatorTypeName = (char *)"be:type";
80 	translator_id* ids = NULL;
81 	int32 count = 0;
82 
83 	status_t err = use->GetAllTranslators(&ids, &count);
84 	if (err < B_OK)
85 		return err;
86 
87 	for (int tix = 0; tix < count; tix++) {
88 		const translation_format* formats = NULL;
89 		int32 num_formats = 0;
90 		bool ok = false;
91 		err = use->GetInputFormats(ids[tix], &formats, &num_formats);
92 		if (err == B_OK)
93 			for (int iix = 0; iix < num_formats; iix++) {
94 				if (formats[iix].type == fromType) {
95 					ok = true;
96 					break;
97 				}
98 			}
99 
100 		if (!ok)
101 			continue;
102 
103 		err = use->GetOutputFormats(ids[tix], &formats, &num_formats);
104 		if (err == B_OK)
105 			for (int oix = 0; oix < num_formats; oix++) {
106  				if (formats[oix].type != fromType) {
107 					BMessage* itemmsg;
108 					itemmsg = new BMessage(msg_translate);
109 					itemmsg->AddInt32(translatorIdName, ids[tix]);
110 					itemmsg->AddInt32(translatorTypeName, formats[oix].type);
111 					intoMenu->AddItem(new BMenuItem(formats[oix].name, itemmsg));
112 				}
113 			}
114 	}
115 	delete[] ids;
116 	return B_OK;
117 }
118 
119 
120 // functions for EnumeratedStringValueSettings
121 
122 const char* CaptureRateAt(int32 i)
123 {
124 	return (i >= 0 && i < kCaptureRatesCount) ? kCaptureRates[i].name : NULL;
125 }
126 
127 
128 const char* UploadClientAt(int32 i)
129 {
130 	return (i >= 0 && i < kUploadClientsCount) ? kUploadClients[i] : NULL;
131 }
132 
133 
134 }; // end anonymous namespace
135 
136 
137 
138 //	#pragma mark -
139 
140 
141 CodyCam::CodyCam()
142 	:
143 	BApplication("application/x-vnd.Haiku.CodyCam"),
144 	fMediaRoster(NULL),
145 	fVideoConsumer(NULL),
146 	fWindow(NULL),
147 	fPort(0),
148 	fVideoControlWindow(NULL)
149 {
150 	int32 index = 0;
151 	kCaptureRates[index++].name = B_TRANSLATE("Every 15 seconds");
152 	kCaptureRates[index++].name = B_TRANSLATE("Every 30 seconds");
153 	kCaptureRates[index++].name = B_TRANSLATE("Every minute");
154 	kCaptureRates[index++].name = B_TRANSLATE("Every 5 minutes");
155 	kCaptureRates[index++].name = B_TRANSLATE("Every 10 minutes");
156 	kCaptureRates[index++].name = B_TRANSLATE("Every 15 minutes");
157 	kCaptureRates[index++].name = B_TRANSLATE("Every 30 minutes");
158 	kCaptureRates[index++].name = B_TRANSLATE("Every hour");
159 	kCaptureRates[index++].name = B_TRANSLATE("Every 2 hours");
160 	kCaptureRates[index++].name = B_TRANSLATE("Every 4 hours");
161 	kCaptureRates[index++].name = B_TRANSLATE("Every 8 hours");
162 	kCaptureRates[index++].name = B_TRANSLATE("Every 24 hours");
163 	kCaptureRates[index++].name = B_TRANSLATE("Never");
164 
165 	index = 0;
166 	kUploadClients[index++] = B_TRANSLATE("FTP");
167 	kUploadClients[index++] = B_TRANSLATE("SFTP");
168 	kUploadClients[index++] = B_TRANSLATE("Local");
169 
170 	chdir("/boot/home");
171 }
172 
173 
174 CodyCam::~CodyCam()
175 {
176 	CALL("CodyCam::~CodyCam\n");
177 
178 	// release the video consumer node
179 	// the consumer node cleans up the window
180 	if (fVideoConsumer) {
181 		fVideoConsumer->Release();
182 		fVideoConsumer = NULL;
183 	}
184 
185 	CALL("CodyCam::~CodyCam - EXIT\n");
186 }
187 
188 
189 void
190 CodyCam::ReadyToRun()
191 {
192 	fWindow = new VideoWindow(BRect(28, 28, 28, 28),
193 		(const char*) B_TRANSLATE("CodyCam"), B_TITLED_WINDOW,
194 		B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS, &fPort);
195 
196 	_SetUpNodes();
197 
198 	((VideoWindow*)fWindow)->ApplyControls();
199 }
200 
201 
202 
203 bool
204 CodyCam::QuitRequested()
205 {
206 	_TearDownNodes();
207 	snooze(100000);
208 
209 	return true;
210 }
211 
212 
213 void
214 CodyCam::MessageReceived(BMessage *message)
215 {
216 	switch (message->what) {
217 		case msg_start:
218 		{
219 			BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(
220 				fTimeSourceNode);
221 			bigtime_t real = BTimeSource::RealTime();
222 			bigtime_t perf = timeSource->PerformanceTimeFor(real) + 10000;
223 			status_t status = fMediaRoster->StartNode(fProducerNode, perf);
224 			if (status != B_OK)
225 				ERROR("error starting producer!");
226 			timeSource->Release();
227 			break;
228 		}
229 
230 		case msg_stop:
231 			fMediaRoster->StopNode(fProducerNode, 0, true);
232 			break;
233 
234 		case msg_video:
235 		{
236 			if (fVideoControlWindow) {
237 				fVideoControlWindow->Activate();
238 				break;
239 			}
240 			BParameterWeb* web = NULL;
241 			BView* view = NULL;
242 			media_node node = fProducerNode;
243 			status_t err = fMediaRoster->GetParameterWebFor(node, &web);
244 			if (err >= B_OK && web != NULL) {
245 				view = BMediaTheme::ViewFor(web);
246 				fVideoControlWindow = new ControlWindow(
247 					BRect(2 * WINDOW_OFFSET_X + WINDOW_SIZE_X, WINDOW_OFFSET_Y,
248 					2 * WINDOW_OFFSET_X + WINDOW_SIZE_X + view->Bounds().right,
249 					WINDOW_OFFSET_Y + view->Bounds().bottom), view, node);
250 				fMediaRoster->StartWatching(BMessenger(NULL,
251 					fVideoControlWindow), node,	B_MEDIA_WEB_CHANGED);
252 				fVideoControlWindow->Show();
253 			}
254 			break;
255 		}
256 
257 		case msg_about:
258 			(new BAlert(B_TRANSLATE("About CodyCam"), B_TRANSLATE("CodyCam\n\n"
259 				"The Original BeOS webcam"), B_TRANSLATE("Close")))->Go();
260 			break;
261 
262 		case msg_control_win:
263 			// our control window is being asked to go away
264 			// set our pointer to NULL
265 			fVideoControlWindow = NULL;
266 			break;
267 
268 		default:
269 			BApplication::MessageReceived(message);
270 			break;
271 	}
272 }
273 
274 
275 status_t
276 CodyCam::_SetUpNodes()
277 {
278 	status_t status = B_OK;
279 
280 	/* find the media roster */
281 	fMediaRoster = BMediaRoster::Roster(&status);
282 	if (status != B_OK) {
283 		ErrorAlert(B_TRANSLATE("Cannot find the media roster"), status,
284 			fWindow);
285 		return status;
286 	}
287 
288 	/* find the time source */
289 	status = fMediaRoster->GetTimeSource(&fTimeSourceNode);
290 	if (status != B_OK) {
291 		ErrorAlert(B_TRANSLATE("Cannot get a time source"), status, fWindow);
292 		return status;
293 	}
294 
295 	/* find a video producer node */
296 	INFO("CodyCam acquiring VideoInput node\n");
297 	status = fMediaRoster->GetVideoInput(&fProducerNode);
298 	if (status != B_OK) {
299 		ErrorAlert(B_TRANSLATE("Cannot find a video source. You need a webcam "
300 			"to use CodyCam."), status, fWindow);
301 		return status;
302 	}
303 
304 	/* create the video consumer node */
305 	fVideoConsumer = new VideoConsumer("CodyCam",
306 		((VideoWindow*)fWindow)->VideoView(),
307 		((VideoWindow*)fWindow)->StatusLine(), NULL, 0);
308 	if (!fVideoConsumer) {
309 		ErrorAlert(B_TRANSLATE("Cannot create a video window"), B_ERROR,
310 			fWindow);
311 		return B_ERROR;
312 	}
313 
314 	/* register the node */
315 	status = fMediaRoster->RegisterNode(fVideoConsumer);
316 	if (status != B_OK) {
317 		ErrorAlert(B_TRANSLATE("Cannot register the video window"), status,
318 			fWindow);
319 		return status;
320 	}
321 	fPort = fVideoConsumer->ControlPort();
322 
323 	/* find free producer output */
324 	int32 cnt = 0;
325 	status = fMediaRoster->GetFreeOutputsFor(fProducerNode, &fProducerOut, 1,
326 		&cnt, B_MEDIA_RAW_VIDEO);
327 	if (status != B_OK || cnt < 1) {
328 		status = B_RESOURCE_UNAVAILABLE;
329 		ErrorAlert(B_TRANSLATE("Cannot find an available video stream"),
330 			status,	fWindow);
331 		return status;
332 	}
333 
334 	/* find free consumer input */
335 	cnt = 0;
336 	status = fMediaRoster->GetFreeInputsFor(fVideoConsumer->Node(),
337 		&fConsumerIn, 1, &cnt, B_MEDIA_RAW_VIDEO);
338 	if (status != B_OK || cnt < 1) {
339 		status = B_RESOURCE_UNAVAILABLE;
340 		ErrorAlert(B_TRANSLATE("Can't find an available connection to the "
341 			"video window"), status, fWindow);
342 		return status;
343 	}
344 
345 	/* Connect The Nodes!!! */
346 	media_format format;
347 	format.type = B_MEDIA_RAW_VIDEO;
348 	media_raw_video_format vid_format = {0, 1, 0, 239, B_VIDEO_TOP_LEFT_RIGHT,
349 		1, 1, {B_RGB32, VIDEO_SIZE_X, VIDEO_SIZE_Y, VIDEO_SIZE_X * 4, 0, 0}};
350 	format.u.raw_video = vid_format;
351 
352 	/* connect producer to consumer */
353 	status = fMediaRoster->Connect(fProducerOut.source,
354 		fConsumerIn.destination, &format, &fProducerOut, &fConsumerIn);
355 	if (status != B_OK) {
356 		ErrorAlert(B_TRANSLATE("Cannot connect the video source to the video "
357 			"window"), status);
358 		return status;
359 	}
360 
361 
362 	/* set time sources */
363 	status = fMediaRoster->SetTimeSourceFor(fProducerNode.node,
364 		fTimeSourceNode.node);
365 	if (status != B_OK) {
366 		ErrorAlert(B_TRANSLATE("Cannot set the time source for the video "
367 			"source"), status);
368 		return status;
369 	}
370 
371 	status = fMediaRoster->SetTimeSourceFor(fVideoConsumer->ID(),
372 		fTimeSourceNode.node);
373 	if (status != B_OK) {
374 		ErrorAlert(B_TRANSLATE("Cannot set the time source for the video "
375 			"window"), status);
376 		return status;
377 	}
378 
379 	/* figure out what recording delay to use */
380 	bigtime_t latency = 0;
381 	status = fMediaRoster->GetLatencyFor(fProducerNode, &latency);
382 	status = fMediaRoster->SetProducerRunModeDelay(fProducerNode, latency);
383 
384 	/* start the nodes */
385 	bigtime_t initLatency = 0;
386 	status = fMediaRoster->GetInitialLatencyFor(fProducerNode, &initLatency);
387 	if (status < B_OK) {
388 		ErrorAlert(B_TRANSLATE("Error getting initial latency for the capture "
389 			"node"), status);
390 		return status;
391 	}
392 
393 	initLatency += estimate_max_scheduling_latency();
394 
395 	BTimeSource* timeSource = fMediaRoster->MakeTimeSourceFor(fProducerNode);
396 	bool running = timeSource->IsRunning();
397 
398 	/* workaround for people without sound cards */
399 	/* because the system time source won't be running */
400 	bigtime_t real = BTimeSource::RealTime();
401 	if (!running) {
402 		status = fMediaRoster->StartTimeSource(fTimeSourceNode, real);
403 		if (status != B_OK) {
404 			timeSource->Release();
405 			ErrorAlert(B_TRANSLATE("Cannot start time source!"), status);
406 			return status;
407 		}
408 		status = fMediaRoster->SeekTimeSource(fTimeSourceNode, 0, real);
409 		if (status != B_OK) {
410 			timeSource->Release();
411 			ErrorAlert(B_TRANSLATE("Cannot seek time source!"), status);
412 			return status;
413 		}
414 	}
415 
416 	bigtime_t perf = timeSource->PerformanceTimeFor(real + latency
417 		+ initLatency);
418 	timeSource->Release();
419 
420 	/* start the nodes */
421 	status = fMediaRoster->StartNode(fProducerNode, perf);
422 	if (status != B_OK) {
423 		ErrorAlert(B_TRANSLATE("Cannot start the video source"), status);
424 		return status;
425 	}
426 	status = fMediaRoster->StartNode(fVideoConsumer->Node(), perf);
427 	if (status != B_OK) {
428 		ErrorAlert(B_TRANSLATE("Cannot start the video window"), status);
429 		return status;
430 	}
431 
432 	return status;
433 }
434 
435 
436 void
437 CodyCam::_TearDownNodes()
438 {
439 	CALL("CodyCam::_TearDownNodes\n");
440 	if (!fMediaRoster)
441 		return;
442 
443 	if (fVideoConsumer) {
444 		/* stop */
445 		INFO("stopping nodes!\n");
446 //		fMediaRoster->StopNode(fProducerNode, 0, true);
447 		fMediaRoster->StopNode(fVideoConsumer->Node(), 0, true);
448 
449 		/* disconnect */
450 		fMediaRoster->Disconnect(fProducerOut.node.node, fProducerOut.source,
451 			fConsumerIn.node.node, fConsumerIn.destination);
452 
453 		if (fProducerNode != media_node::null) {
454 			INFO("CodyCam releasing fProducerNode\n");
455 			fMediaRoster->ReleaseNode(fProducerNode);
456 			fProducerNode = media_node::null;
457 		}
458 		fMediaRoster->ReleaseNode(fVideoConsumer->Node());
459 		fVideoConsumer = NULL;
460 	}
461 }
462 
463 
464 //	#pragma mark - Video Window Class
465 
466 
467 VideoWindow::VideoWindow(BRect frame, const char* title, window_type type,
468 		uint32 flags, port_id* consumerPort)
469 	:
470 	BWindow(frame, title, type, flags),
471 	fPortPtr(consumerPort),
472 	fVideoView(NULL)
473 {
474 	fFtpInfo.port = 0;
475 	fFtpInfo.rate = 0x7fffffff;
476 	fFtpInfo.imageFormat = 0;
477 	fFtpInfo.translator = 0;
478 	fFtpInfo.passiveFtp = true;
479 	fFtpInfo.uploadClient = 0;
480 	strcpy(fFtpInfo.fileNameText, "filename");
481 	strcpy(fFtpInfo.serverText, "server");
482 	strcpy(fFtpInfo.loginText, "login");
483 	strcpy(fFtpInfo.passwordText, "password");
484 	strcpy(fFtpInfo.directoryText, "directory");
485 
486 	_SetUpSettings("codycam", "");
487 
488 	BMenuBar* menuBar = new BMenuBar(BRect(0, 0, 0, 0), "menu bar");
489 
490 	BMenuItem* menuItem;
491 	BMenu* menu = new BMenu(B_TRANSLATE("File"));
492 
493 	menuItem = new BMenuItem(B_TRANSLATE("Video settings"),
494 		new BMessage(msg_video), 'P');
495 	menuItem->SetTarget(be_app);
496 	menu->AddItem(menuItem);
497 
498 	menu->AddSeparatorItem();
499 
500 	menuItem = new BMenuItem(B_TRANSLATE("Start video"),
501 		new BMessage(msg_start), 'A');
502 	menuItem->SetTarget(be_app);
503 	menu->AddItem(menuItem);
504 
505 	menuItem = new BMenuItem(B_TRANSLATE("Stop video"),
506 		new BMessage(msg_stop), 'O');
507 	menuItem->SetTarget(be_app);
508 	menu->AddItem(menuItem);
509 
510 	menu->AddSeparatorItem();
511 
512 	menuItem = new BMenuItem(B_TRANSLATE("About Codycam" B_UTF8_ELLIPSIS),
513 		new BMessage(msg_about), 'B');
514 	menuItem->SetTarget(be_app);
515 	menu->AddItem(menuItem);
516 
517 	menu->AddSeparatorItem();
518 
519 	menuItem = new BMenuItem(B_TRANSLATE("Quit"),
520 		new BMessage(B_QUIT_REQUESTED), 'Q');
521 	menuItem->SetTarget(be_app);
522 	menu->AddItem(menuItem);
523 
524 	menuBar->AddItem(menu);
525 
526 	/* add some controls */
527 	_BuildCaptureControls();
528 
529 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
530 		.SetInsets(0, 0, 0, 0)
531 		.Add(menuBar)
532 		.AddGroup(B_VERTICAL, kYBuffer)
533 			.SetInsets(kXBuffer, kYBuffer, kXBuffer, kYBuffer)
534 			.Add(fVideoView)
535 			.AddGroup(B_HORIZONTAL, kXBuffer)
536 				.SetInsets(0, 0, 0, 0)
537 				.Add(fCaptureSetupBox)
538 				.Add(fFtpSetupBox)
539 				.End()
540 			.Add(fStatusLine);
541 
542 	Show();
543 }
544 
545 
546 
547 VideoWindow::~VideoWindow()
548 {
549 	_QuitSettings();
550 }
551 
552 
553 
554 bool
555 VideoWindow::QuitRequested()
556 {
557 	be_app->PostMessage(B_QUIT_REQUESTED);
558 	return false;
559 }
560 
561 
562 void
563 VideoWindow::MessageReceived(BMessage* message)
564 {
565 	BControl* control = NULL;
566 	message->FindPointer((const char*)"source", (void **)&control);
567 
568 	switch (message->what) {
569 		case msg_filename:
570 			if (control != NULL) {
571 				strncpy(fFtpInfo.fileNameText,
572 					((BTextControl*)control)->Text(), 63);
573 				FTPINFO("file is '%s'\n", fFtpInfo.fileNameText);
574 			}
575 			break;
576 
577 		case msg_rate_changed: {
578 			int32 seconds;
579 			message->FindInt32("seconds", &seconds);
580 			if (seconds == 0) {
581 				FTPINFO("never\n");
582 				fFtpInfo.rate = (bigtime_t)(B_INFINITE_TIMEOUT);
583 			} else {
584 				FTPINFO("%ld seconds\n", (long)seconds);
585 				fFtpInfo.rate = (bigtime_t)(seconds * 1000000LL);
586 			}
587 			break;
588 		}
589 
590 		case msg_translate:
591 			message->FindInt32("be:type", (int32*)&(fFtpInfo.imageFormat));
592 			message->FindInt32("be:translator", &(fFtpInfo.translator));
593 			break;
594 
595 		case msg_upl_client:
596 			message->FindInt32("client", &(fFtpInfo.uploadClient));
597 			FTPINFO("upl client = %ld\n", fFtpInfo.uploadClient);
598 			_UploadClientChanged();
599 			break;
600 
601 		case msg_server:
602 			if (control != NULL) {
603 				strncpy(fFtpInfo.serverText,
604 					((BTextControl*)control)->Text(), 64);
605 				FTPINFO("server = '%s'\n", fFtpInfo.serverText);
606 			}
607 			break;
608 
609 		case msg_login:
610 			if (control != NULL) {
611 				strncpy(fFtpInfo.loginText,
612 					((BTextControl*)control)->Text(), 64);
613 				FTPINFO("login = '%s'\n", fFtpInfo.loginText);
614 			}
615 			break;
616 
617 		case msg_password:
618 			if (control != NULL) {
619 				strncpy(fFtpInfo.passwordText,
620 					((BTextControl*)control)->Text(), 64);
621 				FTPINFO("password = '%s'\n", fFtpInfo.passwordText);
622 			}
623 			break;
624 
625 		case msg_directory:
626 			if (control != NULL) {
627 				strncpy(fFtpInfo.directoryText,
628 					((BTextControl*)control)->Text(), 64);
629 				FTPINFO("directory = '%s'\n", fFtpInfo.directoryText);
630 			}
631 			break;
632 
633 		case msg_passiveftp:
634 			if (control != NULL) {
635 				fFtpInfo.passiveFtp = ((BCheckBox*)control)->Value();
636 				if (fFtpInfo.passiveFtp)
637 					FTPINFO("using passive ftp\n");
638 			}
639 			break;
640 
641 		default:
642 			BWindow::MessageReceived(message);
643 			return;
644 	}
645 
646 	if (*fPortPtr)
647 		write_port(*fPortPtr, FTP_INFO, (void*)&fFtpInfo, sizeof(ftp_msg_info));
648 
649 }
650 
651 
652 BView*
653 VideoWindow::VideoView()
654 {
655 	return fVideoView;
656 }
657 
658 
659 BStringView*
660 VideoWindow::StatusLine()
661 {
662 	return fStatusLine;
663 }
664 
665 
666 void
667 VideoWindow::_BuildCaptureControls()
668 {
669 	// a view to hold the video image
670 	fVideoView = new BView("Video View", B_WILL_DRAW);
671 	fVideoView->SetExplicitMinSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
672 	fVideoView->SetExplicitMaxSize(BSize(VIDEO_SIZE_X, VIDEO_SIZE_Y));
673 
674 	// Capture controls
675 	fCaptureSetupBox = new BBox("Capture Controls", B_WILL_DRAW);
676 	fCaptureSetupBox->SetLabel(B_TRANSLATE("Capture controls"));
677 
678 	BGridLayout *controlsLayout = new BGridLayout(kXBuffer, 0);
679 	controlsLayout->SetInsets(10, 15, 5, 5);
680 	fCaptureSetupBox->SetLayout(controlsLayout);
681 
682 	// file name
683 	fFileName = new BTextControl("File Name", B_TRANSLATE("File name:"),
684 		fFilenameSetting->Value(), new BMessage(msg_filename));
685 	fFileName->SetTarget(BMessenger(NULL, this));
686 
687 	// format menu
688 	fImageFormatMenu = new BPopUpMenu(B_TRANSLATE("Image Format Menu"));
689 	AddTranslationItems(fImageFormatMenu, B_TRANSLATOR_BITMAP);
690 	fImageFormatMenu->SetTargetForItems(this);
691 
692 	if (fImageFormatSettings->Value()
693 		&& fImageFormatMenu->FindItem(fImageFormatSettings->Value()) != NULL) {
694 		fImageFormatMenu->FindItem(
695 			fImageFormatSettings->Value())->SetMarked(true);
696 	} else if (fImageFormatMenu->FindItem("JPEG image") != NULL)
697 		fImageFormatMenu->FindItem("JPEG image")->SetMarked(true);
698 	else
699 		fImageFormatMenu->ItemAt(0)->SetMarked(true);
700 
701 	fImageFormatSelector = new BMenuField("Format", B_TRANSLATE("Format:"),
702 		fImageFormatMenu, NULL);
703 
704 	// capture rate
705 	fCaptureRateMenu = new BPopUpMenu(B_TRANSLATE("Capture Rate Menu"));
706 	for (int32 i = 0; i < kCaptureRatesCount; i++) {
707 		BMessage* itemMessage = new BMessage(msg_rate_changed);
708 		itemMessage->AddInt32("seconds", kCaptureRates[i].seconds);
709 		fCaptureRateMenu->AddItem(new BMenuItem(kCaptureRates[i].name,
710 			itemMessage));
711 	}
712 	fCaptureRateMenu->SetTargetForItems(this);
713 	fCaptureRateMenu->FindItem(fCaptureRateSetting->Value())->SetMarked(true);
714 	fCaptureRateSelector = new BMenuField("Rate", B_TRANSLATE("Rate:"),
715 		fCaptureRateMenu, NULL);
716 
717 	BLayoutBuilder::Grid<>(controlsLayout)
718 		.AddTextControl(fFileName, 0, 0)
719 		.AddMenuField(fImageFormatSelector, 0, 1)
720 		.AddMenuField(fCaptureRateSelector, 0, 2)
721 		.Add(BSpaceLayoutItem::CreateGlue(), 0, 3, 2, 1);
722 
723 	// FTP setup box
724 	fFtpSetupBox = new BBox("FTP Setup", B_WILL_DRAW);
725 
726 	fUploadClientMenu = new BPopUpMenu(B_TRANSLATE("Send to" B_UTF8_ELLIPSIS));
727 	for (int i = 0; i < kUploadClientsCount; i++) {
728 		BMessage *m = new BMessage(msg_upl_client);
729 		m->AddInt32("client", i);
730 		fUploadClientMenu->AddItem(new BMenuItem(kUploadClients[i], m));
731 	}
732 	fUploadClientMenu->SetTargetForItems(this);
733 	fUploadClientMenu->FindItem(fUploadClientSetting->Value())->SetMarked(true);
734 	fUploadClientSelector = new BMenuField("UploadClient", NULL,
735 		fUploadClientMenu, NULL);
736 
737 	fFtpSetupBox->SetLabel(B_TRANSLATE("Output"));
738 	// this doesn't work with the layout manager
739 	// fFtpSetupBox->SetLabel(fUploadClientSelector);
740 	fUploadClientSelector->SetLabel(B_TRANSLATE("Type:"));
741 
742 	BGridLayout *ftpLayout = new BGridLayout(kXBuffer, 0);
743 	ftpLayout->SetInsets(10, 15, 5, 5);
744 	fFtpSetupBox->SetLayout(ftpLayout);
745 
746 	fServerName = new BTextControl("Server", B_TRANSLATE("Server:"),
747 		fServerSetting->Value(), new BMessage(msg_server));
748 	fServerName->SetTarget(this);
749 
750 	fLoginId = new BTextControl("Login", B_TRANSLATE("Login:"),
751 		fLoginSetting->Value(), new BMessage(msg_login));
752 	fLoginId->SetTarget(this);
753 
754 	fPassword = new BTextControl("Password", B_TRANSLATE("Password:"),
755 		fPasswordSetting->Value(), new BMessage(msg_password));
756 	fPassword->SetTarget(this);
757 	fPassword->TextView()->HideTyping(true);
758 	// BeOS HideTyping() seems broken, it empties the text
759 	fPassword->SetText(fPasswordSetting->Value());
760 
761 	fDirectory = new BTextControl("Directory", B_TRANSLATE("Directory:"),
762 		fDirectorySetting->Value(), new BMessage(msg_directory));
763 	fDirectory->SetTarget(this);
764 
765 	fPassiveFtp = new BCheckBox("Passive FTP", B_TRANSLATE("Passive FTP"),
766 		new BMessage(msg_passiveftp));
767 	fPassiveFtp->SetTarget(this);
768 	fPassiveFtp->SetValue(fPassiveFtpSetting->Value());
769 
770 	BLayoutBuilder::Grid<>(ftpLayout)
771 		.AddMenuField(fUploadClientSelector, 0, 0)
772 		.AddTextControl(fServerName, 0, 1)
773 		.AddTextControl(fLoginId, 0, 2)
774 		.AddTextControl(fPassword, 0, 3)
775 		.AddTextControl(fDirectory, 0, 4)
776 		.Add(fPassiveFtp, 0, 5, 2, 1);
777 
778 	fStatusLine = new BStringView("Status Line",
779 		B_TRANSLATE("Waiting" B_UTF8_ELLIPSIS));
780 }
781 
782 
783 void
784 VideoWindow::ApplyControls()
785 {
786 	if (!Lock())
787 		return;
788 
789 	// apply controls
790 	fFileName->Invoke();
791 	PostMessage(fImageFormatMenu->FindMarked()->Message());
792 	PostMessage(fCaptureRateMenu->FindMarked()->Message());
793 	PostMessage(fUploadClientMenu->FindMarked()->Message());
794 	fServerName->Invoke();
795 	fLoginId->Invoke();
796 	fPassword->Invoke();
797 	fDirectory->Invoke();
798 	fPassiveFtp->Invoke();
799 
800 	Unlock();
801 }
802 
803 
804 void
805 VideoWindow::_SetUpSettings(const char* filename, const char* dirname)
806 {
807 	fSettings = new Settings(filename, dirname);
808 
809 	fServerSetting = new StringValueSetting("Server", "ftp.my.server",
810 		B_TRANSLATE("server address expected"));
811 
812 	fLoginSetting = new StringValueSetting("Login", "loginID",
813 		B_TRANSLATE("login ID expected"));
814 
815 	fPasswordSetting = new StringValueSetting("Password",
816 		B_TRANSLATE("password"), B_TRANSLATE("password expected"));
817 
818 	fDirectorySetting = new StringValueSetting("Directory", "web/images",
819 		B_TRANSLATE("destination directory expected"));
820 
821 	fPassiveFtpSetting = new BooleanValueSetting("PassiveFtp", 1);
822 
823 	fFilenameSetting = new StringValueSetting("StillImageFilename",
824 		"codycam.jpg", B_TRANSLATE("still image filename expected"));
825 
826 	fImageFormatSettings = new StringValueSetting("ImageFileFormat",
827 		B_TRANSLATE("JPEG image"), B_TRANSLATE("image file format expected"));
828 
829 	fCaptureRateSetting = new EnumeratedStringValueSetting("CaptureRate",
830 		kCaptureRates[3].name, &CaptureRateAt,
831 		B_TRANSLATE("capture rate expected"),
832 		"unrecognized capture rate specified");
833 
834 	fUploadClientSetting = new EnumeratedStringValueSetting("UploadClient",
835 		B_TRANSLATE("FTP"), &UploadClientAt,
836 		B_TRANSLATE("upload client name expected"),
837 		B_TRANSLATE("unrecognized upload client specified"));
838 
839 	fSettings->Add(fServerSetting);
840 	fSettings->Add(fLoginSetting);
841 	fSettings->Add(fPasswordSetting);
842 	fSettings->Add(fDirectorySetting);
843 	fSettings->Add(fPassiveFtpSetting);
844 	fSettings->Add(fFilenameSetting);
845 	fSettings->Add(fImageFormatSettings);
846 	fSettings->Add(fCaptureRateSetting);
847 	fSettings->Add(fUploadClientSetting);
848 
849 	fSettings->TryReadingSettings();
850 }
851 
852 
853 void
854 VideoWindow::_UploadClientChanged()
855 {
856 	bool enableServerControls = fFtpInfo.uploadClient < 2;
857 	fServerName->SetEnabled(enableServerControls);
858 	fLoginId->SetEnabled(enableServerControls);
859 	fPassword->SetEnabled(enableServerControls);
860 	fDirectory->SetEnabled(enableServerControls);
861 	fPassiveFtp->SetEnabled(enableServerControls);
862 }
863 
864 
865 void
866 VideoWindow::_QuitSettings()
867 {
868 	fServerSetting->ValueChanged(fServerName->Text());
869 	fLoginSetting->ValueChanged(fLoginId->Text());
870 	fPasswordSetting->ValueChanged(fFtpInfo.passwordText);
871 	fDirectorySetting->ValueChanged(fDirectory->Text());
872 	fPassiveFtpSetting->ValueChanged(fPassiveFtp->Value());
873 	fFilenameSetting->ValueChanged(fFileName->Text());
874 	fImageFormatSettings->ValueChanged(fImageFormatMenu->FindMarked()->Label());
875 	fCaptureRateSetting->ValueChanged(fCaptureRateMenu->FindMarked()->Label());
876 	fUploadClientSetting->ValueChanged(fUploadClientMenu->FindMarked()->Label());
877 
878 	fSettings->SaveSettings();
879 	delete fSettings;
880 }
881 
882 
883 //	#pragma mark -
884 
885 
886 ControlWindow::ControlWindow(const BRect& frame, BView* controls,
887 	media_node node)
888 	:
889 	BWindow(frame, B_TRANSLATE("Video settings"), B_TITLED_WINDOW,
890 		B_ASYNCHRONOUS_CONTROLS)
891 {
892 	fView = controls;
893 	fNode = node;
894 
895 	AddChild(fView);
896 }
897 
898 
899 void
900 ControlWindow::MessageReceived(BMessage* message)
901 {
902 	BParameterWeb* web = NULL;
903 	status_t err;
904 
905 	switch (message->what) {
906 		case B_MEDIA_WEB_CHANGED:
907 		{
908 			// If this is a tab view, find out which tab
909 			// is selected
910 			BTabView* tabView = dynamic_cast<BTabView*>(fView);
911 			int32 tabNum = -1;
912 			if (tabView)
913 				tabNum = tabView->Selection();
914 
915 			RemoveChild(fView);
916 			delete fView;
917 
918 			err = BMediaRoster::Roster()->GetParameterWebFor(fNode, &web);
919 
920 			if (err >= B_OK && web != NULL) {
921 				fView = BMediaTheme::ViewFor(web);
922 				AddChild(fView);
923 
924 				// Another tab view?  Restore previous selection
925 				if (tabNum > 0) {
926 					BTabView* newTabView = dynamic_cast<BTabView*>(fView);
927 					if (newTabView)
928 						newTabView->Select(tabNum);
929 				}
930 			}
931 			break;
932 		}
933 
934 		default:
935 			BWindow::MessageReceived(message);
936 	}
937 }
938 
939 
940 bool
941 ControlWindow::QuitRequested()
942 {
943 	be_app->PostMessage(msg_control_win);
944 	return true;
945 }
946 
947 
948 //	#pragma mark -
949 
950 
951 int main() {
952 	CodyCam app;
953 	app.Run();
954 	return 0;
955 }
956 
957