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