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