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