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