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