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