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