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