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