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