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