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