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