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