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