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