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 message->FindInt32("client", &(fFtpInfo.uploadClient)); 602 FTPINFO("upl client = %ld\n", fFtpInfo.uploadClient); 603 } 604 break; 605 606 case msg_server: 607 if (control != NULL) { 608 strncpy(fFtpInfo.serverText, ((BTextControl*)control)->Text(), 64); 609 FTPINFO("server = '%s'\n", fFtpInfo.serverText); 610 } 611 break; 612 613 case msg_login: 614 if (control != NULL) { 615 strncpy(fFtpInfo.loginText, ((BTextControl*)control)->Text(), 64); 616 FTPINFO("login = '%s'\n", fFtpInfo.loginText); 617 } 618 break; 619 620 case msg_password: 621 if (control != NULL) { 622 strncpy(fFtpInfo.passwordText, ((BTextControl*)control)->Text(), 64); 623 FTPINFO("password = '%s'\n", fFtpInfo.passwordText); 624 if (Lock()) { 625 ((BTextControl*)control)->SetText("<HIDDEN>"); 626 Unlock(); 627 } 628 } 629 break; 630 631 case msg_directory: 632 if (control != NULL) { 633 strncpy(fFtpInfo.directoryText, ((BTextControl*)control)->Text(), 64); 634 FTPINFO("directory = '%s'\n", fFtpInfo.directoryText); 635 } 636 break; 637 638 case msg_passiveftp: 639 if (control != NULL) { 640 fFtpInfo.passiveFtp = ((BCheckBox*)control)->Value(); 641 if (fFtpInfo.passiveFtp) 642 FTPINFO("using passive ftp\n"); 643 } 644 break; 645 646 default: 647 BWindow::MessageReceived(message); 648 return; 649 } 650 651 if (*fPortPtr) 652 write_port(*fPortPtr, FTP_INFO, (void*)&fFtpInfo, sizeof(ftp_msg_info)); 653 654 } 655 656 657 BView* 658 VideoWindow::VideoView() 659 { 660 return fVideoView; 661 } 662 663 664 BStringView* 665 VideoWindow::StatusLine() 666 { 667 return fStatusLine; 668 } 669 670 671 void 672 VideoWindow::_BuildCaptureControls(BView* theView) 673 { 674 BRect aFrame, theFrame; 675 676 theFrame = theView->Bounds(); 677 theFrame.top += VIDEO_SIZE_Y + 2 * kYBuffer + 40; 678 theFrame.left += kXBuffer; 679 theFrame.right -= (WINDOW_SIZE_X / 2 + 5); 680 theFrame.bottom -= kXBuffer; 681 682 fCaptureSetupBox = new BBox(theFrame, "Capture Controls", B_FOLLOW_ALL, B_WILL_DRAW); 683 fCaptureSetupBox->SetLabel("Capture Controls"); 684 theView->AddChild(fCaptureSetupBox); 685 686 aFrame = fCaptureSetupBox->Bounds(); 687 aFrame.InsetBy(kXBuffer, kYBuffer); 688 aFrame.top += kYBuffer / 2; 689 aFrame.bottom = aFrame.top + kMenuHeight; 690 691 fFileName = new BTextControl(aFrame, "File Name", "File Name:", 692 fFilenameSetting->Value(), new BMessage(msg_filename)); 693 694 fFileName->SetTarget(BMessenger(NULL, this)); 695 fFileName->SetDivider(fFileName->Divider() - 30); 696 fCaptureSetupBox->AddChild(fFileName); 697 698 aFrame.top = aFrame.bottom + kYBuffer; 699 aFrame.bottom = aFrame.top + kMenuHeight; 700 701 fImageFormatMenu = new BPopUpMenu("Image Format Menu"); 702 AddTranslationItems(fImageFormatMenu, B_TRANSLATOR_BITMAP); 703 fImageFormatMenu->SetTargetForItems(this); 704 705 if (fImageFormatSettings->Value() && fImageFormatMenu->FindItem(fImageFormatSettings->Value()) != NULL) 706 fImageFormatMenu->FindItem(fImageFormatSettings->Value())->SetMarked(true); 707 else if (fImageFormatMenu->FindItem("JPEG Image") != NULL) 708 fImageFormatMenu->FindItem("JPEG Image")->SetMarked(true); 709 else if (fImageFormatMenu->FindItem("JPEG image") != NULL) 710 fImageFormatMenu->FindItem("JPEG image")->SetMarked(true); 711 else 712 fImageFormatMenu->ItemAt(0)->SetMarked(true); 713 714 fImageFormatSelector = new BMenuField(aFrame, "Format", "Format:", fImageFormatMenu); 715 fImageFormatSelector->SetDivider(fImageFormatSelector->Divider() - 30); 716 fCaptureSetupBox->AddChild(fImageFormatSelector); 717 718 aFrame.top = aFrame.bottom + kYBuffer; 719 aFrame.bottom = aFrame.top + kMenuHeight; 720 721 fCaptureRateMenu = new BPopUpMenu("Capture Rate Menu"); 722 fCaptureRateMenu->AddItem(new BMenuItem("Every 15 seconds", new BMessage(msg_rate_15s))); 723 fCaptureRateMenu->AddItem(new BMenuItem("Every 30 seconds", new BMessage(msg_rate_30s))); 724 fCaptureRateMenu->AddItem(new BMenuItem("Every minute", new BMessage(msg_rate_1m))); 725 fCaptureRateMenu->AddItem(new BMenuItem("Every 5 minutes", new BMessage(msg_rate_5m))); 726 fCaptureRateMenu->AddItem(new BMenuItem("Every 10 minutes", new BMessage(msg_rate_10m))); 727 fCaptureRateMenu->AddItem(new BMenuItem("Every 15 minutes", new BMessage(msg_rate_15m))); 728 fCaptureRateMenu->AddItem(new BMenuItem("Every 30 minutes", new BMessage(msg_rate_30m))); 729 fCaptureRateMenu->AddItem(new BMenuItem("Every hour", new BMessage(msg_rate_1h))); 730 fCaptureRateMenu->AddItem(new BMenuItem("Every 2 hours", new BMessage(msg_rate_2h))); 731 fCaptureRateMenu->AddItem(new BMenuItem("Every 4 hours", new BMessage(msg_rate_4h))); 732 fCaptureRateMenu->AddItem(new BMenuItem("Every 8 hours", new BMessage(msg_rate_8h))); 733 fCaptureRateMenu->AddItem(new BMenuItem("Every 24 hours", new BMessage(msg_rate_24h))); 734 fCaptureRateMenu->AddItem(new BMenuItem("Never", new BMessage(msg_rate_never))); 735 fCaptureRateMenu->SetTargetForItems(this); 736 fCaptureRateMenu->FindItem(fCaptureRateSetting->Value())->SetMarked(true); 737 fCaptureRateSelector = new BMenuField(aFrame, "Rate", "Rate:", fCaptureRateMenu); 738 fCaptureRateSelector->SetDivider(fCaptureRateSelector->Divider() - 30); 739 fCaptureSetupBox->AddChild(fCaptureRateSelector); 740 741 aFrame = theView->Bounds(); 742 aFrame.top += VIDEO_SIZE_Y + 2 * kYBuffer + 40; 743 aFrame.left += WINDOW_SIZE_X / 2 + 5; 744 aFrame.right -= kXBuffer; 745 aFrame.bottom -= kYBuffer; 746 747 fFtpSetupBox = new BBox(aFrame, "Ftp Setup", B_FOLLOW_ALL, B_WILL_DRAW); 748 fUploadClientMenu = new BPopUpMenu("Send to" B_UTF8_ELLIPSIS); 749 for (int i = 0; kUploadClient[i]; i++) { 750 BMessage *m = new BMessage(msg_upl_client); 751 m->AddInt32("client", i); 752 fUploadClientMenu->AddItem(new BMenuItem(kUploadClient[i], m)); 753 } 754 fUploadClientMenu->SetTargetForItems(this); 755 fUploadClientMenu->FindItem(fUploadClientSetting->Value())->SetMarked(true); 756 fUploadClientSelector = new BMenuField(aFrame, "UploadClient", "", fUploadClientMenu); 757 fUploadClientSelector->SetDivider(0.0); 758 759 fFtpSetupBox->SetLabel(fUploadClientSelector); 760 theView->AddChild(fFtpSetupBox); 761 762 aFrame = fFtpSetupBox->Bounds(); 763 aFrame.InsetBy(kXBuffer,kYBuffer); 764 aFrame.top += kYBuffer/2; 765 aFrame.bottom = aFrame.top + kMenuHeight; 766 aFrame.right = aFrame.left + 160; 767 768 fServerName = new BTextControl(aFrame, "Server", "Server:", fServerSetting->Value(), 769 new BMessage(msg_server)); 770 fServerName->SetTarget(this); 771 fServerName->SetDivider(fServerName->Divider() - 30); 772 fFtpSetupBox->AddChild(fServerName); 773 774 aFrame.top = aFrame.bottom + kYBuffer; 775 aFrame.bottom = aFrame.top + kMenuHeight; 776 777 fLoginId = new BTextControl(aFrame, "Login", "Login:", fLoginSetting->Value(), 778 new BMessage(msg_login)); 779 fLoginId->SetTarget(this); 780 fLoginId->SetDivider(fLoginId->Divider() - 30); 781 fFtpSetupBox->AddChild(fLoginId); 782 783 aFrame.top = aFrame.bottom + kYBuffer; 784 aFrame.bottom = aFrame.top + kMenuHeight; 785 786 fPassword = new BTextControl(aFrame, "Password", "Password:", 787 fPasswordSetting->Value(), new BMessage(msg_password)); 788 fPassword->SetTarget(this); 789 fPassword->SetDivider(fPassword->Divider() - 30); 790 fFtpSetupBox->AddChild(fPassword); 791 792 aFrame.top = aFrame.bottom + kYBuffer; 793 aFrame.bottom = aFrame.top + kMenuHeight; 794 795 fDirectory = new BTextControl(aFrame, "Directory", "Directory:", 796 fDirectorySetting->Value(), new BMessage(msg_directory)); 797 fDirectory->SetTarget(this); 798 fDirectory->SetDivider(fDirectory->Divider() - 30); 799 fFtpSetupBox->AddChild(fDirectory); 800 801 aFrame.top = aFrame.bottom + kYBuffer; 802 aFrame.bottom = aFrame.top + kMenuHeight; 803 804 fPassiveFtp = new BCheckBox(aFrame, "Passive ftp", "Passive ftp", 805 new BMessage(msg_passiveftp)); 806 fPassiveFtp->SetTarget(this); 807 fPassiveFtp->SetValue(fPassiveFtpSetting->Value()); 808 fFtpSetupBox->AddChild(fPassiveFtp); 809 810 aFrame = theView->Bounds(); 811 aFrame.top += VIDEO_SIZE_Y + 2 * kYBuffer; 812 aFrame.left += kXBuffer; 813 aFrame.right -= kXBuffer; 814 aFrame.bottom = aFrame.top + kMenuHeight + 2 * kYBuffer; 815 816 fStatusBox = new BBox(aFrame, "Status", B_FOLLOW_ALL, B_WILL_DRAW); 817 fStatusBox->SetLabel("Status"); 818 theView->AddChild(fStatusBox); 819 820 aFrame = fStatusBox->Bounds(); 821 aFrame.InsetBy(kXBuffer, kYBuffer); 822 823 fStatusLine = new BStringView(aFrame, "Status Line", "Waiting" B_UTF8_ELLIPSIS); 824 fStatusBox->AddChild(fStatusLine); 825 } 826 827 828 void 829 VideoWindow::ApplyControls() 830 { 831 // apply controls 832 fFileName->Invoke(); 833 PostMessage(fImageFormatMenu->FindMarked()->Message()); 834 PostMessage(fCaptureRateMenu->FindMarked()->Message()); 835 PostMessage(fUploadClientMenu->FindMarked()->Message()); 836 fServerName->Invoke(); 837 fLoginId->Invoke(); 838 fPassword->Invoke(); 839 fDirectory->Invoke(); 840 fPassiveFtp->Invoke(); 841 } 842 843 844 void 845 VideoWindow::_SetUpSettings(const char* filename, const char* dirname) 846 { 847 fSettings = new Settings(filename, dirname); 848 849 fSettings->Add(fServerSetting = new StringValueSetting("Server", "ftp.my.server", 850 "server address expected", "")); 851 fSettings->Add(fLoginSetting = new StringValueSetting("Login", "loginID", 852 "login ID expected", "")); 853 fSettings->Add(fPasswordSetting = new StringValueSetting("Password", "password", 854 "password expected", "")); 855 fSettings->Add(fDirectorySetting = new StringValueSetting("Directory", "web/images", 856 "destination directory expected", "")); 857 fSettings->Add(fPassiveFtpSetting = new BooleanValueSetting("PassiveFtp", 1)); 858 fSettings->Add(fFilenameSetting = new StringValueSetting("StillImageFilename", 859 "codycam.jpg", "still image filename expected", "")); 860 fSettings->Add(fImageFormatSettings = new StringValueSetting("ImageFileFormat", 861 "JPEG Image", "image file format expected", "")); 862 fSettings->Add(fCaptureRateSetting = new EnumeratedStringValueSetting("CaptureRate", 863 "Every 5 minutes", kCaptureRate, "capture rate expected", 864 "unrecognized capture rate specified")); 865 fSettings->Add(fUploadClientSetting = new EnumeratedStringValueSetting("UploadClient", 866 "FTP", kUploadClient, "upload client name expected", 867 "unrecognized upload client specified")); 868 869 fSettings->TryReadingSettings(); 870 } 871 872 873 void 874 VideoWindow::_QuitSettings() 875 { 876 fServerSetting->ValueChanged(fServerName->Text()); 877 fLoginSetting->ValueChanged(fLoginId->Text()); 878 fPasswordSetting->ValueChanged(fFtpInfo.passwordText); 879 fDirectorySetting->ValueChanged(fDirectory->Text()); 880 fPassiveFtpSetting->ValueChanged(fPassiveFtp->Value()); 881 fFilenameSetting->ValueChanged(fFileName->Text()); 882 fImageFormatSettings->ValueChanged(fImageFormatMenu->FindMarked()->Label()); 883 fCaptureRateSetting->ValueChanged(fCaptureRateMenu->FindMarked()->Label()); 884 fUploadClientSetting->ValueChanged(fUploadClientMenu->FindMarked()->Label()); 885 886 fSettings->SaveSettings(); 887 delete fSettings; 888 } 889 890 891 // #pragma mark - 892 893 894 ControlWindow::ControlWindow(const BRect& frame, BView* controls, media_node node) 895 : BWindow(frame, "Video Preferences", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS) 896 { 897 fView = controls; 898 fNode = node; 899 900 AddChild(fView); 901 } 902 903 904 void 905 ControlWindow::MessageReceived(BMessage* message) 906 { 907 BParameterWeb* web = NULL; 908 status_t err; 909 910 switch (message->what) { 911 case B_MEDIA_WEB_CHANGED: 912 { 913 // If this is a tab view, find out which tab 914 // is selected 915 BTabView* tabView = dynamic_cast<BTabView*>(fView); 916 int32 tabNum = -1; 917 if (tabView) 918 tabNum = tabView->Selection(); 919 920 RemoveChild(fView); 921 delete fView; 922 923 err = BMediaRoster::Roster()->GetParameterWebFor(fNode, &web); 924 925 if (err >= B_OK && web != NULL) { 926 fView = BMediaTheme::ViewFor(web); 927 AddChild(fView); 928 929 // Another tab view? Restore previous selection 930 if (tabNum > 0) { 931 BTabView* newTabView = dynamic_cast<BTabView*>(fView); 932 if (newTabView) 933 newTabView->Select(tabNum); 934 } 935 } 936 break; 937 } 938 939 default: 940 BWindow::MessageReceived(message); 941 } 942 } 943 944 945 bool 946 ControlWindow::QuitRequested() 947 { 948 be_app->PostMessage(msg_control_win); 949 return true; 950 } 951 952 953 // #pragma mark - 954 955 956 int main() { 957 CodyCam app; 958 app.Run(); 959 return 0; 960 } 961