1 // TransportView.cpp 2 3 #include "TransportView.h" 4 5 #include "RouteApp.h" 6 #include "RouteWindow.h" 7 8 #include "RouteAppNodeManager.h" 9 #include "NodeGroup.h" 10 11 #include "NumericValControl.h" 12 #include "TextControlFloater.h" 13 14 #include <Button.h> 15 #include <Debug.h> 16 #include <Font.h> 17 #include <Invoker.h> 18 #include <StringView.h> 19 #include <MediaRoster.h> 20 #include <MenuField.h> 21 #include <MenuItem.h> 22 #include <PopUpMenu.h> 23 #include <String.h> 24 #include <TextControl.h> 25 26 #include <algorithm> 27 #include <functional> 28 29 using namespace std; 30 31 __USE_CORTEX_NAMESPACE 32 33 // -------------------------------------------------------- // 34 // _GroupInfoView 35 // -------------------------------------------------------- // 36 37 __BEGIN_CORTEX_NAMESPACE 38 class _GroupInfoView : 39 public BView { 40 typedef BView _inherited; 41 42 public: // ctor/dtor 43 _GroupInfoView( 44 BRect frame, 45 TransportView* parent, 46 const char* name, 47 uint32 resizeMode =B_FOLLOW_LEFT|B_FOLLOW_TOP, 48 uint32 flags =B_WILL_DRAW|B_FRAME_EVENTS) : 49 50 BView(frame, name, resizeMode, flags), 51 m_parent(parent), 52 m_plainFont(be_plain_font), 53 m_boldFont(be_bold_font) { 54 55 _initViews(); 56 _initColors(); 57 _updateLayout(); 58 } 59 60 public: // BView 61 virtual void FrameResized( 62 float width, 63 float height) { 64 65 _inherited::FrameResized(width, height); 66 _updateLayout(); 67 Invalidate(); 68 } 69 70 virtual void GetPreferredSize( 71 float* width, 72 float* height) { 73 font_height fh; 74 m_plainFont.GetHeight(&fh); 75 76 *width = 0.0; 77 *height = (fh.ascent+fh.descent+fh.leading) * 2; 78 *height += 4.0; //+++++ 79 } 80 81 82 virtual void Draw( 83 BRect updateRect) { 84 85 NodeGroup* g = m_parent->m_group; 86 BRect b = Bounds(); 87 88 // border 89 rgb_color hi = tint_color(ViewColor(), B_LIGHTEN_2_TINT); 90 rgb_color lo = tint_color(ViewColor(), B_DARKEN_2_TINT); 91 SetHighColor(lo); 92 StrokeLine( 93 b.LeftTop(), b.RightTop()); 94 StrokeLine( 95 b.LeftTop(), b.LeftBottom()); 96 97 SetHighColor(hi); 98 StrokeLine( 99 b.LeftBottom(), b.RightBottom()); 100 StrokeLine( 101 b.RightTop(), b.RightBottom()); 102 103 SetHighColor(255,255,255,255); 104 105 // background +++++ 106 107 // name 108 BString name = g ? g->name() : "(no group)"; 109 // +++++ constrain width 110 SetFont(&m_boldFont); 111 DrawString(name.String(), m_namePosition); 112 113 SetFont(&m_plainFont); 114 115 // node count 116 BString nodeCount; 117 if(g) 118 nodeCount << g->countNodes(); 119 else 120 nodeCount << '0'; 121 nodeCount << ((nodeCount == "1") ? " node." : " nodes."); 122 // +++++ constrain width 123 DrawString(nodeCount.String(), m_nodeCountPosition); 124 125 // status 126 BString status = "No errors."; 127 // +++++ constrain width 128 DrawString(status.String(), m_statusPosition); 129 } 130 131 virtual void MouseDown( 132 BPoint point) { 133 134 NodeGroup* g = m_parent->m_group; 135 if(!g) 136 return; 137 138 font_height fh; 139 m_boldFont.GetHeight(&fh); 140 141 BRect nameBounds( 142 m_namePosition.x, 143 m_namePosition.y - fh.ascent, 144 m_namePosition.x + m_maxNameWidth, 145 m_namePosition.y + (fh.ascent+fh.leading-4.0)); 146 if(nameBounds.Contains(point)) { 147 ConvertToScreen(&nameBounds); 148 nameBounds.OffsetBy(-7.0, -3.0); 149 new TextControlFloater( 150 nameBounds, 151 B_ALIGN_LEFT, 152 &m_boldFont, 153 g->name(), 154 m_parent, 155 new BMessage(TransportView::M_SET_NAME)); 156 } 157 } 158 159 public: // implementation 160 void _initViews() { 161 // +++++ 162 } 163 164 void _initColors() { 165 // +++++ these colors need to be centrally defined 166 SetViewColor(16, 64, 96, 255); 167 SetLowColor(16, 64, 96, 255); 168 SetHighColor(255,255,255,255); 169 } 170 171 void _updateLayout() { 172 float _edge_pad_x = 3.0; 173 float _edge_pad_y = 1.0; 174 175 BRect b = Bounds(); 176 font_height fh; 177 m_plainFont.GetHeight(&fh); 178 179 float realWidth = b.Width() - (_edge_pad_x * 2); 180 181 m_maxNameWidth = realWidth * 0.7; 182 m_maxNodeCountWidth = realWidth - m_maxNameWidth; 183 m_namePosition.x = _edge_pad_x; 184 m_namePosition.y = _edge_pad_x + fh.ascent - 2.0; 185 m_nodeCountPosition = m_namePosition; 186 m_nodeCountPosition.x = m_maxNameWidth; 187 188 m_maxStatusWidth = realWidth; 189 m_statusPosition.x = _edge_pad_x; 190 m_statusPosition.y = b.Height() - (fh.descent + fh.leading + _edge_pad_y); 191 } 192 193 private: 194 TransportView* m_parent; 195 196 BFont m_plainFont; 197 BFont m_boldFont; 198 199 BPoint m_namePosition; 200 float m_maxNameWidth; 201 202 BPoint m_nodeCountPosition; 203 float m_maxNodeCountWidth; 204 205 BPoint m_statusPosition; 206 float m_maxStatusWidth; 207 }; 208 __END_CORTEX_NAMESPACE 209 210 // -------------------------------------------------------- // 211 // *** ctors 212 // -------------------------------------------------------- // 213 214 TransportView::TransportView( 215 NodeManager* manager, 216 const char* name) : 217 218 BView( 219 BRect(), 220 name, 221 B_FOLLOW_ALL_SIDES, 222 B_WILL_DRAW|B_FRAME_EVENTS), 223 m_manager(manager), 224 m_group(0) { 225 226 // initialize 227 _initLayout(); 228 _constructControls(); 229 // _updateLayout(); deferred until AttachedToWindow(): 24aug99 230 _disableControls(); 231 232 SetViewColor( 233 tint_color( 234 ui_color(B_PANEL_BACKGROUND_COLOR), 235 B_LIGHTEN_1_TINT)); 236 } 237 238 // -------------------------------------------------------- // 239 // *** BView 240 // -------------------------------------------------------- // 241 242 void TransportView::AttachedToWindow() { 243 _inherited::AttachedToWindow(); 244 245 // finish layout 246 _updateLayout(); 247 248 // watch the node manager (for time-source create/delete notification) 249 RouteApp* app = dynamic_cast<RouteApp*>(be_app); 250 ASSERT(app); 251 add_observer(this, app->manager); 252 } 253 254 void TransportView::AllAttached() { 255 _inherited::AllAttached(); 256 257 // set message targets for view-configuation controls 258 for(target_set::iterator it = m_localTargets.begin(); 259 it != m_localTargets.end(); ++it) { 260 ASSERT(*it); 261 (*it)->SetTarget(this); 262 } 263 } 264 265 void TransportView::DetachedFromWindow() { 266 _inherited::DetachedFromWindow(); 267 268 RouteApp* app = dynamic_cast<RouteApp*>(be_app); 269 ASSERT(app); 270 remove_observer(this, app->manager); 271 } 272 273 void TransportView::FrameResized( 274 float width, 275 float height) { 276 277 _inherited::FrameResized(width, height); 278 // _updateLayout(); 279 } 280 281 void TransportView::KeyDown( 282 const char* bytes, 283 int32 count) { 284 285 switch(bytes[0]) { 286 case B_SPACE: { 287 RouteApp* app = dynamic_cast<RouteApp*>(be_app); 288 ASSERT(app); 289 BMessenger(app->routeWindow).SendMessage( 290 RouteWindow::M_TOGGLE_GROUP_ROLLING); 291 break; 292 } 293 294 default: 295 _inherited::KeyDown(bytes, count); 296 } 297 } 298 299 300 void TransportView::MouseDown( 301 BPoint where) { 302 303 MakeFocus(true); 304 } 305 306 307 // -------------------------------------------------------- // 308 // *** BHandler 309 // -------------------------------------------------------- // 310 311 void TransportView::MessageReceived( 312 BMessage* message) { 313 status_t err; 314 uint32 groupID; 315 316 // PRINT(( 317 // "TransportView::MessageReceived()\n")); 318 // message->PrintToStream(); 319 320 switch(message->what) { 321 322 case NodeGroup::M_RELEASED: 323 { 324 err = message->FindInt32("groupID", (int32*)&groupID); 325 if(err < B_OK) { 326 PRINT(( 327 "* TransportView::MessageReceived(NodeGroup::M_RELEASED)\n" 328 " no groupID!\n")); 329 } 330 if(!m_group || groupID != m_group->id()) { 331 PRINT(( 332 "* TransportView::MessageReceived(NodeGroup::M_RELEASED)\n" 333 " mismatched groupID.\n")); 334 break; 335 } 336 337 _releaseGroup(); 338 // 339 // BMessage m(M_REMOVE_OBSERVER); 340 // m.AddMessenger("observer", BMessenger(this)); 341 // message->SendReply(&m); 342 } 343 break; 344 345 case NodeGroup::M_OBSERVER_ADDED: 346 err = message->FindInt32("groupID", (int32*)&groupID); 347 if(err < B_OK) { 348 PRINT(( 349 "* TransportView::MessageReceived(NodeGroup::M_OBSERVER_ADDED)\n" 350 " no groupID!\n")); 351 break; 352 } 353 if(!m_group || groupID != m_group->id()) { 354 PRINT(( 355 "* TransportView::MessageReceived(NodeGroup::M_OBSERVER_ADDED)\n" 356 " mismatched groupID; ignoring.\n")); 357 break; 358 } 359 360 _enableControls(); 361 break; 362 363 case NodeGroup::M_TRANSPORT_STATE_CHANGED: 364 uint32 groupID; 365 err = message->FindInt32("groupID", (int32*)&groupID); 366 if(err < B_OK) { 367 PRINT(( 368 "* TransportView::MessageReceived(NodeGroup::M_TRANSPORT_STATE_CHANGED)\n" 369 " no groupID!\n")); 370 break; 371 } 372 if(!m_group || groupID != m_group->id()) { 373 PRINT(( 374 "* TransportView::MessageReceived(NodeGroup::M_TRANSPORT_STATE_CHANGED)\n" 375 " mismatched groupID; ignoring.\n")); 376 break; 377 } 378 379 _updateTransportButtons(); 380 break; 381 382 case NodeGroup::M_TIME_SOURCE_CHANGED: 383 //_updateTimeSource(); +++++ check group? 384 break; 385 386 case NodeGroup::M_RUN_MODE_CHANGED: 387 //_updateRunMode(); +++++ check group? 388 break; 389 390 // * CONTROL PROCESSING 391 392 case NodeGroup::M_SET_START_POSITION: { 393 394 if(!m_group) 395 break; 396 397 bigtime_t position = _scalePosition( 398 m_regionStartView->value()); 399 message->AddInt64("position", position); 400 BMessenger(m_group).SendMessage(message); 401 402 // update start-button duty/label [e.moon 11oct99] 403 if(m_group->transportState() == NodeGroup::TRANSPORT_STOPPED) 404 _updateTransportButtons(); 405 406 break; 407 } 408 409 case NodeGroup::M_SET_END_POSITION: { 410 411 if(!m_group) 412 break; 413 414 bigtime_t position = _scalePosition( 415 m_regionEndView->value()); 416 message->AddInt64("position", position); 417 BMessenger(m_group).SendMessage(message); 418 419 // update start-button duty/label [e.moon 11oct99] 420 if(m_group->transportState() == NodeGroup::TRANSPORT_STOPPED) 421 _updateTransportButtons(); 422 423 break; 424 } 425 426 case M_SET_NAME: 427 { 428 const char* name; 429 err = message->FindString("_value", &name); 430 if(err < B_OK) { 431 PRINT(( 432 "! TransportView::MessageReceived(M_SET_NAME): no _value!\n")); 433 break; 434 } 435 if(m_group) { 436 m_group->setName(name); 437 m_infoView->Invalidate(); 438 } 439 } 440 break; 441 442 case RouteAppNodeManager::M_TIME_SOURCE_CREATED: 443 _timeSourceCreated(message); 444 break; 445 446 case RouteAppNodeManager::M_TIME_SOURCE_DELETED: 447 _timeSourceDeleted(message); 448 break; 449 450 default: 451 _inherited::MessageReceived(message); 452 break; 453 } 454 } 455 456 // -------------------------------------------------------- // 457 // *** BHandler impl. 458 // -------------------------------------------------------- // 459 460 void TransportView::_handleSelectGroup( 461 BMessage* message) { 462 463 uint32 groupID; 464 status_t err = message->FindInt32("groupID", (int32*)&groupID); 465 if(err < B_OK) { 466 PRINT(( 467 "* TransportView::_handleSelectGroup(): no groupID\n")); 468 return; 469 } 470 471 if(m_group && groupID != m_group->id()) 472 _releaseGroup(); 473 474 _selectGroup(groupID); 475 476 Invalidate(); 477 } 478 479 // -------------------------------------------------------- // 480 // *** internal operations 481 // -------------------------------------------------------- // 482 483 // select the given group; initialize controls 484 // (if 0, gray out all controls) 485 void TransportView::_selectGroup( 486 uint32 groupID) { 487 status_t err; 488 489 if(m_group) 490 _releaseGroup(); 491 492 if(!groupID) { 493 _disableControls(); 494 return; 495 } 496 497 err = m_manager->findGroup(groupID, &m_group); 498 if(err < B_OK) { 499 PRINT(( 500 "* TransportView::_selectGroup(%ld): findGroup() failed:\n" 501 " %s\n", 502 groupID, 503 strerror(err))); 504 return; 505 } 506 507 _observeGroup(); 508 } 509 510 void TransportView::_observeGroup() { 511 ASSERT(m_group); 512 513 add_observer(this, m_group); 514 } 515 516 void TransportView::_releaseGroup() { 517 ASSERT(m_group); 518 519 remove_observer(this, m_group); 520 m_group = 0; 521 } 522 // -------------------------------------------------------- // 523 // *** CONTROLS 524 // -------------------------------------------------------- // 525 526 const char _run_mode_strings[][20] = { 527 "Offline", 528 "Decrease Precision", 529 "Increase Latency", 530 "Drop Data", 531 "Recording" 532 }; 533 const int _run_modes = 5; 534 535 //const char _time_source_strings[][20] = { 536 // "DAC Time Source", 537 // "System Clock" 538 //}; 539 //const int _time_sources = 2; 540 541 const char* _region_start_label = "From:"; 542 const char* _region_end_label = "To:"; 543 544 void TransportView::_constructControls() { 545 546 BMessage* m; 547 548 // * create and populate, but don't position, the views: 549 550 // // create and populate, but don't position, the views: 551 // m_nameView = new BStringView( 552 // BRect(), 553 // "nameView", 554 // "Group Name", 555 // B_FOLLOW_NONE); 556 // AddChild(m_nameView); 557 558 m_infoView = new _GroupInfoView( 559 BRect(), 560 this, 561 "infoView"); 562 AddChild(m_infoView); 563 564 m_runModeView = new BMenuField( 565 BRect(), 566 "runModeView", 567 "Run Mode:", 568 new BPopUpMenu("runModeMenu")); 569 _populateRunModeMenu( 570 m_runModeView->Menu()); 571 AddChild(m_runModeView); 572 573 m_timeSourceView = new BMenuField( 574 BRect(), 575 "timeSourceView", 576 "Time Source:", 577 new BPopUpMenu("timeSourceMenu")); 578 _populateTimeSourceMenu( 579 m_timeSourceView->Menu()); 580 AddChild(m_timeSourceView); 581 582 583 m_fromLabel = new BStringView(BRect(), 0, "Roll from"); 584 AddChild(m_fromLabel); 585 586 587 m = new BMessage(NodeGroup::M_SET_START_POSITION); 588 m_regionStartView = new NumericValControl( 589 BRect(), 590 "regionStartView", 591 m, 592 2, 4, // * DIGITS 593 false, ValControl::ALIGN_FLUSH_LEFT); 594 595 // redirect view back to self. this gives me the chance to 596 // augment the message with the position before sending 597 _addLocalTarget(m_regionStartView); 598 AddChild(m_regionStartView); 599 600 m_toLabel = new BStringView(BRect(), 0, "to"); 601 AddChild(m_toLabel); 602 603 m = new BMessage(NodeGroup::M_SET_END_POSITION); 604 m_regionEndView = new NumericValControl( 605 BRect(), 606 "regionEndView", 607 m, 608 2, 4, // * DIGITS 609 false, ValControl::ALIGN_FLUSH_LEFT); 610 611 // redirect view back to self. this gives me the chance to 612 // augment the message with the position before sending 613 _addLocalTarget(m_regionEndView); 614 AddChild(m_regionEndView); 615 616 // m = new BMessage(NodeGroup::M_SET_START_POSITION); 617 // m_regionStartView = new BTextControl( 618 // BRect(), 619 // "regionStartView", 620 // _region_start_label, 621 // "0", 622 // m); 623 // 624 // _addGroupTarget(m_regionStartView); 625 //// m_regionStartView->TextView()->SetAlignment(B_ALIGN_RIGHT); 626 // 627 // AddChild(m_regionStartView); 628 // 629 // m = new BMessage(NodeGroup::M_SET_END_POSITION); 630 // m_regionEndView = new BTextControl( 631 // BRect(), 632 // "regionEndView", 633 // _region_end_label, 634 // "0", 635 // m); 636 // 637 // _addGroupTarget(m_regionEndView); 638 //// m_regionEndView->TextView()->SetAlignment(B_ALIGN_RIGHT); 639 // AddChild(m_regionEndView); 640 641 m = new BMessage(NodeGroup::M_START); 642 m_startButton = new BButton( 643 BRect(), 644 "startButton", 645 "Start", 646 m); 647 _addGroupTarget(m_startButton); 648 AddChild(m_startButton); 649 650 m = new BMessage(NodeGroup::M_STOP); 651 m_stopButton = new BButton( 652 BRect(), 653 "stopButton", 654 "Stop", 655 m); 656 _addGroupTarget(m_stopButton); 657 AddChild(m_stopButton); 658 659 m = new BMessage(NodeGroup::M_PREROLL); 660 m_prerollButton = new BButton( 661 BRect(), 662 "prerollButton", 663 "Preroll", 664 m); 665 _addGroupTarget(m_prerollButton); 666 AddChild(m_prerollButton); 667 } 668 669 // convert a position control's value to bigtime_t 670 // [e.moon 11oct99] 671 bigtime_t TransportView::_scalePosition( 672 double value) { 673 674 return bigtime_t(value * 1000000.0); 675 } 676 677 void TransportView::_populateRunModeMenu( 678 BMenu* menu) { 679 680 BMessage* m; 681 for(int n = 0; n < _run_modes; ++n) { 682 m = new BMessage(NodeGroup::M_SET_RUN_MODE); 683 m->AddInt32("runMode", n+1); 684 685 BMenuItem* i = new BMenuItem( 686 _run_mode_strings[n], m); 687 menu->AddItem(i); 688 _addGroupTarget(i); 689 } 690 } 691 692 void TransportView::_populateTimeSourceMenu( 693 BMenu* menu) { 694 695 // find the standard time sources 696 status_t err; 697 media_node dacTimeSource, systemTimeSource; 698 BMessage* m; 699 BMenuItem* i; 700 err = m_manager->roster->GetTimeSource(&dacTimeSource); 701 if(err == B_OK) { 702 m = new BMessage(NodeGroup::M_SET_TIME_SOURCE); 703 m->AddData( 704 "timeSourceNode", 705 B_RAW_TYPE, 706 &dacTimeSource, 707 sizeof(media_node)); 708 i = new BMenuItem( 709 "DAC Time Source", 710 m); 711 menu->AddItem(i); 712 _addGroupTarget(i); 713 } 714 715 err = m_manager->roster->GetSystemTimeSource(&systemTimeSource); 716 if(err == B_OK) { 717 m = new BMessage(NodeGroup::M_SET_TIME_SOURCE); 718 m->AddData( 719 "timeSourceNode", 720 B_RAW_TYPE, 721 &systemTimeSource, 722 sizeof(media_node)); 723 i = new BMenuItem( 724 "System Clock", 725 m); 726 menu->AddItem(i); 727 _addGroupTarget(i); 728 } 729 730 // find other time source nodes 731 732 Autolock _l(m_manager); 733 void* cookie = 0; 734 NodeRef* r; 735 char nameBuffer[B_MEDIA_NAME_LENGTH+16]; 736 bool needSeparator = (menu->CountItems() > 0); 737 while(m_manager->getNextRef(&r, &cookie) == B_OK) { 738 if(!(r->kind() & B_TIME_SOURCE)) 739 continue; 740 if(r->id() == dacTimeSource.node) 741 continue; 742 if(r->id() == systemTimeSource.node) 743 continue; 744 745 if(needSeparator) { 746 needSeparator = false; 747 menu->AddItem(new BSeparatorItem()); 748 } 749 750 m = new BMessage(NodeGroup::M_SET_TIME_SOURCE); 751 m->AddData( 752 "timeSourceNode", 753 B_RAW_TYPE, 754 &r->node(), 755 sizeof(media_node)); 756 sprintf(nameBuffer, "%s: %ld", 757 r->name(), 758 r->id()); 759 i = new BMenuItem( 760 nameBuffer, 761 m); 762 menu->AddItem(i); 763 _addGroupTarget(i); 764 } 765 766 // BMessage* m; 767 // for(int n = 0; n < _time_sources; ++n) { 768 // m = new BMessage(NodeGroup::M_SET_TIME_SOURCE); 769 // m->AddData( 770 // "timeSourceNode", 771 // B_RAW_TYPE, 772 // &m_timeSourcePresets[n], 773 // sizeof(media_node)); 774 // // +++++ copy media_node of associated time source! 775 //// m->AddInt32("timeSource", n); 776 // 777 // BMenuItem* i = new BMenuItem( 778 // _time_source_strings[n], m); 779 // menu->AddItem(i); 780 // _addGroupTarget(i); 781 // } 782 } 783 784 // add the given invoker to be retargeted to this 785 // view (used for controls whose messages need a bit more 786 // processing before being forwarded to the NodeManager.) 787 788 void TransportView::_addLocalTarget( 789 BInvoker* invoker) { 790 791 m_localTargets.push_back(invoker); 792 if(Window()) 793 invoker->SetTarget(this); 794 } 795 796 void TransportView::_addGroupTarget( 797 BInvoker* invoker) { 798 799 m_groupTargets.push_back(invoker); 800 if(m_group) 801 invoker->SetTarget(m_group); 802 } 803 804 void TransportView::_refreshTransportSettings() { 805 if(m_group) 806 _updateTransportButtons(); 807 } 808 809 810 void TransportView::_disableControls() { 811 812 // PRINT(( 813 // "*** _disableControls()\n")); 814 815 BWindow* w = Window(); 816 if(w) 817 w->BeginViewTransaction(); 818 819 // m_nameView->SetText("(no group)"); 820 m_infoView->Invalidate(); 821 822 m_runModeView->SetEnabled(false); 823 m_runModeView->Menu()->Superitem()->SetLabel("(none)"); 824 825 m_timeSourceView->SetEnabled(false); 826 m_timeSourceView->Menu()->Superitem()->SetLabel("(none)"); 827 828 m_regionStartView->SetEnabled(false); 829 m_regionStartView->setValue(0); 830 831 m_regionEndView->SetEnabled(false); 832 m_regionEndView->setValue(0); 833 834 m_startButton->SetEnabled(false); 835 m_stopButton->SetEnabled(false); 836 m_prerollButton->SetEnabled(false); 837 838 if(w) 839 w->EndViewTransaction(); 840 841 842 // PRINT(( 843 // "*** DONE: _disableControls()\n")); 844 } 845 846 void TransportView::_enableControls() { 847 848 // PRINT(( 849 // "*** _enableControls()\n")); 850 // 851 ASSERT(m_group); 852 Autolock _l(m_group); // +++++ why? 853 BWindow* w = Window(); 854 if(w) { 855 w->BeginViewTransaction(); 856 857 // clear focused view 858 // 19sep99: TransportWindow is currently a B_AVOID_FOCUS window; the 859 // only way views get focused is if they ask to be (which ValControl 860 // currently does.) 861 BView* focused = w->CurrentFocus(); 862 if(focused) 863 focused->MakeFocus(false); 864 } 865 866 // BString nameViewText = m_group->name(); 867 // nameViewText << ": " << m_group->countNodes() << " nodes."; 868 // m_nameView->SetText(nameViewText.String()); 869 870 m_infoView->Invalidate(); 871 872 m_runModeView->SetEnabled(true); 873 _updateRunMode(); 874 875 m_timeSourceView->SetEnabled(true); 876 _updateTimeSource(); 877 878 _updateTransportButtons(); 879 880 881 // +++++ ick. hard-coded scaling factors make me queasy 882 883 m_regionStartView->SetEnabled(true); 884 m_regionStartView->setValue( 885 ((double)m_group->startPosition()) / 1000000.0); 886 887 m_regionEndView->SetEnabled(true); 888 m_regionEndView->setValue( 889 ((double)m_group->endPosition()) / 1000000.0); 890 891 // target controls on group 892 for(target_set::iterator it = m_groupTargets.begin(); 893 it != m_groupTargets.end(); ++it) { 894 ASSERT(*it); 895 (*it)->SetTarget(m_group); 896 } 897 898 if(w) { 899 w->EndViewTransaction(); 900 } 901 902 // PRINT(( 903 // "*** DONE: _enableControls()\n")); 904 } 905 906 void TransportView::_updateTransportButtons() { 907 908 ASSERT(m_group); 909 910 switch(m_group->transportState()) { 911 case NodeGroup::TRANSPORT_INVALID: 912 case NodeGroup::TRANSPORT_STARTING: 913 case NodeGroup::TRANSPORT_STOPPING: 914 m_startButton->SetEnabled(false); 915 m_stopButton->SetEnabled(false); 916 m_prerollButton->SetEnabled(false); 917 break; 918 919 case NodeGroup::TRANSPORT_STOPPED: 920 m_startButton->SetEnabled(true); 921 m_stopButton->SetEnabled(false); 922 m_prerollButton->SetEnabled(true); 923 break; 924 925 case NodeGroup::TRANSPORT_RUNNING: 926 case NodeGroup::TRANSPORT_ROLLING: 927 m_startButton->SetEnabled(false); 928 m_stopButton->SetEnabled(true); 929 m_prerollButton->SetEnabled(false); 930 break; 931 } 932 933 // based on group settings, set the start button to do either 934 // a simple start or a roll (atomic start/stop.) [e.moon 11oct99] 935 ASSERT(m_startButton->Message()); 936 937 // get current region settings 938 bigtime_t startPosition = _scalePosition(m_regionStartView->value()); 939 bigtime_t endPosition = _scalePosition(m_regionEndView->value()); 940 941 // get current run-mode setting 942 uint32 runMode = 0; 943 BMenuItem* i = m_runModeView->Menu()->FindMarked(); 944 ASSERT(i); 945 runMode = m_runModeView->Menu()->IndexOf(i) + 1; 946 947 if( 948 endPosition > startPosition && 949 (runMode == BMediaNode::B_OFFLINE || 950 !m_group->canCycle())) { 951 952 m_startButton->SetLabel("Roll"); 953 m_startButton->Message()->what = NodeGroup::M_ROLL; 954 955 } else { 956 m_startButton->SetLabel("Start"); 957 m_startButton->Message()->what = NodeGroup::M_START; 958 } 959 } 960 961 void TransportView::_updateTimeSource() { 962 ASSERT(m_group); 963 964 media_node tsNode; 965 status_t err = m_group->getTimeSource(&tsNode); 966 if(err < B_OK) { 967 PRINT(( 968 "! TransportView::_updateTimeSource(): m_group->getTimeSource():\n" 969 " %s\n", 970 strerror(err))); 971 return; 972 } 973 974 BMenu* menu = m_timeSourceView->Menu(); 975 ASSERT(menu); 976 if(tsNode == media_node::null) { 977 menu->Superitem()->SetLabel("(none)"); 978 return; 979 } 980 981 // find menu item 982 int32 n; 983 for(n = menu->CountItems()-1; n >= 0; --n) { 984 const void* data; 985 media_node itemNode; 986 BMessage* message = menu->ItemAt(n)->Message(); 987 if(!message) 988 continue; 989 990 ssize_t size = sizeof(media_node); 991 err = message->FindData( 992 "timeSourceNode", 993 B_RAW_TYPE, 994 &data, 995 &size); 996 if(err < B_OK) 997 continue; 998 999 memcpy(&itemNode, data, sizeof(media_node)); 1000 if(itemNode != tsNode) 1001 continue; 1002 1003 // found it 1004 PRINT(( 1005 "### _updateTimeSource: %s\n", 1006 menu->ItemAt(n)->Label())); 1007 menu->ItemAt(n)->SetMarked(true); 1008 break; 1009 } 1010 // ASSERT(m_timeSourcePresets); 1011 // int n; 1012 // for(n = 0; n < _time_sources; ++n) { 1013 // if(m_timeSourcePresets[n] == tsNode) { 1014 // BMenuItem* i = m_timeSourceView->Menu()->ItemAt(n); 1015 // ASSERT(i); 1016 // i->SetMarked(true); 1017 // break; 1018 // } 1019 // } 1020 if(n < 0) 1021 menu->Superitem()->SetLabel("(???)"); 1022 1023 } 1024 void TransportView::_updateRunMode() { 1025 ASSERT(m_group); 1026 1027 BMenu* menu = m_runModeView->Menu(); 1028 uint32 runMode = m_group->runMode() - 1; // real run mode starts at 1 1029 ASSERT(menu->CountItems() > runMode); 1030 menu->ItemAt(runMode)->SetMarked(true); 1031 } 1032 1033 //void TransportView::_initTimeSources() { 1034 // 1035 // status_t err; 1036 // media_node node; 1037 // err = m_manager->roster->GetTimeSource(&node); 1038 // if(err == B_OK) { 1039 // m_timeSources.push_back(node.node); 1040 // } 1041 // 1042 // err = m_manager->roster->GetSystemTimeSource(&m_timeSourcePresets[1]); 1043 // if(err < B_OK) { 1044 // PRINT(( 1045 // "* TransportView::_initTimeSources():\n" 1046 // " GetSystemTimeSource() failed: %s\n", strerror(err))); 1047 // } 1048 //} 1049 1050 // [e.moon 2dec99] 1051 void TransportView::_timeSourceCreated( 1052 BMessage* message) { 1053 1054 status_t err; 1055 media_node_id id; 1056 err = message->FindInt32("nodeID", &id); 1057 if(err < B_OK) 1058 return; 1059 1060 // PRINT(("### _timeSourceCreated(): %ld\n", id)); 1061 NodeRef* ref; 1062 err = m_manager->getNodeRef(id, &ref); 1063 if(err < B_OK) { 1064 PRINT(( 1065 "!!! TransportView::_timeSourceCreated(): node %ld doesn't exist\n", 1066 id)); 1067 return; 1068 } 1069 1070 char nameBuffer[B_MEDIA_NAME_LENGTH+16]; 1071 BMessage* m = new BMessage(NodeGroup::M_SET_TIME_SOURCE); 1072 m->AddData( 1073 "timeSourceNode", 1074 B_RAW_TYPE, 1075 &ref->node(), 1076 sizeof(media_node)); 1077 sprintf(nameBuffer, "%s: %ld", 1078 ref->name(), 1079 ref->id()); 1080 BMenuItem* i = new BMenuItem( 1081 nameBuffer, 1082 m); 1083 BMenu* menu = m_timeSourceView->Menu(); 1084 menu->AddItem(i); 1085 _addGroupTarget(i); 1086 } 1087 1088 // +++++ 1089 void TransportView::_timeSourceDeleted( 1090 BMessage* message) { 1091 1092 status_t err; 1093 media_node_id id; 1094 err = message->FindInt32("nodeID", &id); 1095 if(err < B_OK) 1096 return; 1097 1098 // PRINT(("### _timeSourceDeleted(): %ld\n", id)); 1099 1100 BMenu* menu = m_timeSourceView->Menu(); 1101 ASSERT(menu); 1102 1103 // find menu item 1104 int32 n; 1105 for(n = menu->CountItems()-1; n >= 0; --n) { 1106 const void* data; 1107 media_node itemNode; 1108 BMessage* message = menu->ItemAt(n)->Message(); 1109 if(!message) 1110 continue; 1111 1112 ssize_t size = sizeof(media_node); 1113 err = message->FindData( 1114 "timeSourceNode", 1115 B_RAW_TYPE, 1116 &data, 1117 &size); 1118 if(err < B_OK) 1119 continue; 1120 1121 memcpy(&itemNode, data, sizeof(media_node)); 1122 if(itemNode.node != id) 1123 continue; 1124 1125 // found it; remove the item 1126 menu->RemoveItem(n); 1127 break; 1128 } 1129 } 1130 1131 // -------------------------------------------------------- // 1132 // *** LAYOUT *** 1133 // -------------------------------------------------------- // 1134 1135 const float _edge_pad_x = 3.0; 1136 const float _edge_pad_y = 3.0; 1137 1138 const float _column_pad_x = 4.0; 1139 1140 const float _field_pad_x = 20.0; 1141 1142 const float _text_view_pad_x = 10.0; 1143 1144 const float _control_pad_x = 5.0; 1145 const float _control_pad_y = 10.0; 1146 const float _menu_field_pad_x = 30.0; 1147 1148 const float _label_pad_x = 8.0; 1149 1150 const float _transport_pad_y = 4.0; 1151 const float _transport_button_width = 60.0; 1152 const float _transport_button_height = 22.0; 1153 const float _transport_button_pad_x = 4.0; 1154 1155 const float _lock_button_width = 42.0; 1156 const float _lock_button_height = 16.0; 1157 1158 class TransportView::_layout_state { 1159 public: 1160 _layout_state() {} 1161 1162 // +++++ 1163 }; 1164 1165 inline float _menu_width( 1166 const BMenu* menu, 1167 const BFont* font) { 1168 1169 float max = 0.0; 1170 1171 int total = menu->CountItems(); 1172 for(int n = 0; n < total; ++n) { 1173 float w = font->StringWidth( 1174 menu->ItemAt(n)->Label()); 1175 if(w > max) 1176 max = w; 1177 } 1178 1179 return max; 1180 } 1181 1182 // -------------------------------------------------------- // 1183 1184 void TransportView::_initLayout() { 1185 m_layout = new _layout_state(); 1186 } 1187 1188 1189 void TransportView::_updateLayout() // +++++ 1190 { 1191 BWindow* window = Window(); 1192 if(window) 1193 window->BeginViewTransaction(); 1194 1195 // calculate the ideal size of the view 1196 // * max label width 1197 float maxLabelWidth = 0.0; 1198 float w; 1199 1200 maxLabelWidth = be_bold_font->StringWidth( 1201 m_runModeView->Label()); 1202 1203 w = be_bold_font->StringWidth( 1204 m_timeSourceView->Label()); 1205 if(w > maxLabelWidth) 1206 maxLabelWidth = w; 1207 1208 // w = be_bold_font->StringWidth( 1209 // m_regionStartView->Label()); 1210 // if(w > maxLabelWidth) 1211 // maxLabelWidth = w; 1212 // 1213 // w = be_bold_font->StringWidth( 1214 // m_regionEndView->Label()); 1215 // if(w > maxLabelWidth) 1216 // maxLabelWidth = w; 1217 1218 // * max field width 1219 float maxFieldWidth = 0.0; 1220 maxFieldWidth = _menu_width( 1221 m_runModeView->Menu(), be_plain_font); 1222 1223 w = _menu_width( 1224 m_timeSourceView->Menu(), be_plain_font); 1225 if(w > maxFieldWidth) 1226 maxFieldWidth = w; 1227 1228 // * column width 1229 float columnWidth = 1230 maxLabelWidth + 1231 maxFieldWidth + _label_pad_x + _field_pad_x; 1232 1233 // figure columns 1234 float column1_x = _edge_pad_x; 1235 float column2_x = column1_x + columnWidth + _column_pad_x; 1236 1237 // * sum to figure view width 1238 float viewWidth = 1239 column2_x + columnWidth + _edge_pad_x; 1240 1241 // make room for buttons 1242 float buttonSpan = 1243 (_transport_button_width*3) + 1244 (_transport_button_pad_x*2); 1245 if(columnWidth < buttonSpan) 1246 viewWidth += (buttonSpan - columnWidth); 1247 1248 // float insideWidth = viewWidth - (_edge_pad_x*2); 1249 1250 // * figure view height a row at a time 1251 font_height fh; 1252 be_plain_font->GetHeight(&fh); 1253 float lineHeight = fh.ascent + fh.descent + fh.leading; 1254 1255 float prefInfoWidth, prefInfoHeight; 1256 m_infoView->GetPreferredSize(&prefInfoWidth, &prefInfoHeight); 1257 float row_1_height = max(prefInfoHeight, _transport_button_height); 1258 1259 float row1_y = _edge_pad_y; 1260 float row2_y = row1_y + row_1_height + _transport_pad_y; 1261 float row3_y = row2_y + lineHeight + _control_pad_y; 1262 // float row4_y = row3_y + lineHeight + _control_pad_y + _transport_pad_y; 1263 // float row5_y = row4_y + lineHeight + _control_pad_y; 1264 float viewHeight = row3_y + lineHeight + _control_pad_y + _edge_pad_y; 1265 1266 // Place controls 1267 m_infoView->MoveTo( 1268 column1_x+1.0, row1_y+1.0); 1269 m_infoView->ResizeTo( 1270 columnWidth, prefInfoHeight); 1271 1272 BRect br( 1273 column2_x, row1_y, 1274 column2_x+_transport_button_width, 1275 row1_y+_transport_button_height); 1276 if(prefInfoHeight > _transport_button_height) 1277 br.OffsetBy(0.0, (prefInfoHeight - _transport_button_height)/2); 1278 1279 m_startButton->MoveTo(br.LeftTop()); 1280 m_startButton->ResizeTo(br.Width(), br.Height()); 1281 br.OffsetBy(_transport_button_width + _transport_button_pad_x, 0.0); 1282 1283 m_stopButton->MoveTo(br.LeftTop()); 1284 m_stopButton->ResizeTo(br.Width(), br.Height()); 1285 br.OffsetBy(_transport_button_width + _transport_button_pad_x, 0.0); 1286 1287 m_prerollButton->MoveTo(br.LeftTop()); 1288 m_prerollButton->ResizeTo(br.Width(), br.Height()); 1289 1290 m_runModeView->MoveTo( 1291 column2_x, row2_y); 1292 m_runModeView->ResizeTo( 1293 columnWidth, lineHeight); 1294 m_runModeView->SetDivider( 1295 maxLabelWidth+_label_pad_x); 1296 m_runModeView->SetAlignment( 1297 B_ALIGN_LEFT); 1298 1299 m_timeSourceView->MoveTo( 1300 column2_x, row3_y); 1301 m_timeSourceView->ResizeTo( 1302 columnWidth, lineHeight); 1303 m_timeSourceView->SetDivider( 1304 maxLabelWidth+_label_pad_x); 1305 m_timeSourceView->SetAlignment( 1306 B_ALIGN_LEFT); 1307 1308 // float regionControlWidth = columnWidth; 1309 // float regionControlHeight = lineHeight + 4.0; 1310 1311 // m_regionStartView->TextView()->SetResizingMode( 1312 // B_FOLLOW_LEFT_RIGHT|B_FOLLOW_TOP); 1313 1314 // "FROM" 1315 1316 BPoint rtLeftTop(column1_x, row2_y + 5.0); 1317 BPoint rtRightBottom; 1318 1319 m_fromLabel->MoveTo(rtLeftTop); 1320 m_fromLabel->ResizeToPreferred(); 1321 rtRightBottom = rtLeftTop + BPoint( 1322 m_fromLabel->Bounds().Width(), 1323 m_fromLabel->Bounds().Height()); 1324 1325 1326 // (region-start) 1327 1328 rtLeftTop.x = rtRightBottom.x+4; 1329 1330 m_regionStartView->MoveTo(rtLeftTop + BPoint(0.0, 2.0)); 1331 m_regionStartView->ResizeToPreferred(); 1332 rtRightBottom = rtLeftTop + BPoint( 1333 m_regionStartView->Bounds().Width(), 1334 m_regionStartView->Bounds().Height()); 1335 1336 // m_regionStartView->SetDivider( 1337 // maxLabelWidth); 1338 // m_regionStartView->TextView()->ResizeTo( 1339 // regionControlWidth-(maxLabelWidth+_text_view_pad_x), 1340 // regionControlHeight-4.0); 1341 1342 // "TO" 1343 1344 rtLeftTop.x = rtRightBottom.x + 6; 1345 1346 m_toLabel->MoveTo(rtLeftTop); 1347 m_toLabel->ResizeToPreferred(); 1348 rtRightBottom = rtLeftTop + BPoint( 1349 m_toLabel->Bounds().Width(), 1350 m_toLabel->Bounds().Height()); 1351 1352 // (region-end) 1353 1354 rtLeftTop.x = rtRightBottom.x + 4; 1355 1356 m_regionEndView->MoveTo(rtLeftTop + BPoint(0.0, 2.0)); 1357 m_regionEndView->ResizeToPreferred(); 1358 // m_regionEndView->SetDivider( 1359 // maxLabelWidth); 1360 // m_regionEndView->TextView()->ResizeTo( 1361 // regionControlWidth-(maxLabelWidth+_text_view_pad_x), 1362 // regionControlHeight-4.0); 1363 1364 1365 BRect b = Bounds(); 1366 float targetWidth = (b.Width() < viewWidth) ? 1367 viewWidth : 1368 b.Width(); 1369 float targetHeight = (b.Height() < viewHeight) ? 1370 viewHeight : 1371 b.Height(); 1372 1373 // Resize view to fit contents 1374 ResizeTo(targetWidth, targetHeight); 1375 1376 if(window) { 1377 window->ResizeTo(targetWidth, targetHeight); 1378 } 1379 1380 // // +++++ testing NumericValControl [23aug99] 1381 // float valWidth, valHeight; 1382 // m_valView->GetPreferredSize(&valWidth, &valHeight); 1383 // PRINT(( 1384 // "\n\nm_valView preferred size: %.1f x %.1f\n\n", 1385 // valWidth, valHeight)); 1386 // 1387 if(window) 1388 window->EndViewTransaction(); 1389 } 1390 1391 // -------------------------------------------------------- // 1392 // *** dtor 1393 // -------------------------------------------------------- // 1394 1395 TransportView::~TransportView() { 1396 if(m_group) 1397 _releaseGroup(); 1398 if(m_layout) 1399 delete m_layout; 1400 } 1401 1402 1403 // END -- TransportView.cpp -- 1404