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