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 // MediaNodePanel.cpp 33 // c.lenz 10oct99 34 35 #include "MediaNodePanel.h" 36 // InfoWindow 37 #include "InfoWindowManager.h" 38 // MediaRoutingView 39 #include "MediaRoutingView.h" 40 #include "MediaWire.h" 41 #include "RouteAppNodeManager.h" 42 // NodeManager 43 #include "NodeRef.h" 44 #include "NodeGroup.h" 45 // ParameterWindow 46 #include "ParameterWindow.h" 47 // Support 48 #include "cortex_ui.h" 49 #include "MediaIcon.h" 50 #include "MediaString.h" 51 // RouteApp 52 #include "RouteWindow.h" 53 // TipManager 54 #include "TipManager.h" 55 56 // App Kit 57 #include <Application.h> 58 #include <Roster.h> 59 // Interface Kit 60 #include <MenuItem.h> 61 #include <PopUpMenu.h> 62 // Media Kit 63 #include <MediaDefs.h> 64 #include <MediaRoster.h> 65 66 using namespace std; 67 68 __USE_CORTEX_NAMESPACE 69 70 #include <Debug.h> 71 #define D_METHOD(x) //PRINT (x) 72 #define D_MESSAGE(x) //PRINT (x) 73 #define D_DRAW(x) //PRINT (x) 74 75 // -------------------------------------------------------- // 76 // constants 77 // -------------------------------------------------------- // 78 79 float MediaNodePanel::M_DEFAULT_WIDTH = 90.0; 80 float MediaNodePanel::M_DEFAULT_HEIGHT = 60.0; 81 float MediaNodePanel::M_LABEL_H_MARGIN = 3.0; 82 float MediaNodePanel::M_LABEL_V_MARGIN = 3.0; 83 float MediaNodePanel::M_BODY_H_MARGIN = 5.0; 84 float MediaNodePanel::M_BODY_V_MARGIN = 5.0; 85 86 // [e.moon 7dec99] 87 const BPoint MediaNodePanel::s_invalidPosition(-200.0, -200.0); 88 89 // -------------------------------------------------------- // 90 // *** ctor/dtor 91 // -------------------------------------------------------- // 92 93 MediaNodePanel::MediaNodePanel( 94 BPoint position, 95 NodeRef *nodeRef) 96 : DiagramBox(BRect(position, position + BPoint(M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT))), 97 BHandler(nodeRef->name()), 98 ref(nodeRef), 99 m_bitmap(0), 100 m_icon(0), 101 m_alternatePosition(s_invalidPosition) 102 { 103 D_METHOD(("MediaNodePanel::MediaNodePanel()\n")); 104 ASSERT(ref); 105 } 106 107 MediaNodePanel::~MediaNodePanel() 108 { 109 D_METHOD(("MediaNodePanel::~MediaNodePanel()\n")); 110 if (m_icon) 111 { 112 delete m_icon; 113 } 114 if (m_bitmap) 115 { 116 delete m_bitmap; 117 } 118 } 119 120 // -------------------------------------------------------- // 121 // *** derived from DiagramBox 122 // -------------------------------------------------------- // 123 124 void MediaNodePanel::attachedToDiagram() 125 { 126 D_METHOD(("MediaNodePanel::attachedToDiagram()\n")); 127 128 resizeTo(M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT); 129 _updateIcon(dynamic_cast<MediaRoutingView *>(view())->getLayout()); 130 _prepareLabel(); 131 populateInit(); 132 arrangeIOJacks(); 133 134 view()->Looper()->AddHandler(this); 135 } 136 137 void MediaNodePanel::detachedFromDiagram() 138 { 139 D_METHOD(("MediaNodePanel::detachedFromDiagram()\n")); 140 141 BRect labelRect = m_labelRect.OffsetByCopy(Frame().LeftTop()); 142 if (m_mouseOverLabel && m_labelTruncated) 143 { 144 TipManager *tips = TipManager::Instance(); 145 tips->hideTip(view()->ConvertToScreen(labelRect)); 146 } 147 148 view()->Looper()->RemoveHandler(this); 149 } 150 151 void MediaNodePanel::DrawBox() 152 { 153 D_DRAW(("MediaNodePanel::DrawBox()\n")); 154 if (m_bitmap) 155 { 156 view()->DrawBitmap(m_bitmap, Frame().LeftTop()); 157 } 158 } 159 160 void MediaNodePanel::MouseDown( 161 BPoint point, 162 uint32 buttons, 163 uint32 clicks) 164 { 165 D_METHOD(("MediaNodePanel::MouseDown()\n")); 166 167 _inherited::MouseDown(point, buttons, clicks); 168 169 // +++ REALLY BAD WORKAROUND 170 MediaJack *jack = dynamic_cast<MediaJack *>(_LastItemUnder()); 171 if (jack && jack->Frame().Contains(point)) 172 { 173 return; 174 } 175 176 switch (buttons) { 177 case B_PRIMARY_MOUSE_BUTTON: 178 { 179 if (clicks == 2) { 180 if (ref->kind() & B_CONTROLLABLE) { 181 BMessage message(MediaRoutingView::M_NODE_TWEAK_PARAMETERS); 182 DiagramView* v = view(); 183 BMessenger(v).SendMessage(&message); 184 } 185 } 186 break; 187 } 188 case B_SECONDARY_MOUSE_BUTTON: 189 { 190 if (clicks == 1) { 191 showContextMenu(point); 192 } 193 break; 194 } 195 } 196 } 197 198 void MediaNodePanel::MouseOver( 199 BPoint point, 200 uint32 transit) 201 { 202 D_METHOD(("MediaNodePanel::MouseOver()\n")); 203 _inherited::MouseOver(point, transit); 204 205 switch (transit) 206 { 207 case B_ENTERED_VIEW: 208 { 209 break; 210 } 211 case B_INSIDE_VIEW: 212 { 213 BRect labelRect = m_labelRect.OffsetByCopy(Frame().LeftTop()); 214 if (labelRect.Contains(point)) 215 { 216 if (!m_mouseOverLabel && m_labelTruncated) 217 { 218 TipManager *tips = TipManager::Instance(); 219 tips->showTip(m_fullLabel.String(), view()->ConvertToScreen(labelRect)); 220 m_mouseOverLabel = true; 221 } 222 } 223 else 224 { 225 m_mouseOverLabel = false; 226 } 227 break; 228 } 229 case B_EXITED_VIEW: 230 { 231 m_mouseOverLabel = false; 232 break; 233 } 234 } 235 } 236 237 void MediaNodePanel::MessageDropped( 238 BPoint point, 239 BMessage *message) 240 { 241 D_METHOD(("MediaNodePanel::MessageDropped()\n")); 242 243 // +++ REALLY BAD WORKAROUND 244 MediaJack *jack = dynamic_cast<MediaJack *>(ItemUnder(point)); 245 if (jack) 246 { 247 jack->MessageDropped(point, message); 248 return; 249 } 250 else 251 { 252 be_app->SetCursor(B_HAND_CURSOR); 253 } 254 } 255 256 void MediaNodePanel::selected() 257 { 258 D_METHOD(("MediaNodePanel::selected()\n")); 259 _updateBitmap(); 260 } 261 262 void MediaNodePanel::deselected() 263 { 264 D_METHOD(("MediaNodePanel::deselected()\n")); 265 _updateBitmap(); 266 } 267 268 // ---------------------------------------------------------------- // 269 // *** updating 270 // ---------------------------------------------------------------- // 271 272 void MediaNodePanel::layoutChanged( 273 int32 layout) 274 { 275 D_METHOD(("MediaNodePanel::layoutChanged()\n")); 276 277 BPoint p = Frame().LeftTop(); 278 if (m_alternatePosition == s_invalidPosition) 279 { 280 m_alternatePosition = dynamic_cast<MediaRoutingView *> 281 (view())->findFreePositionFor(this); 282 } 283 moveTo(m_alternatePosition); 284 m_alternatePosition = p; 285 286 resizeTo(M_DEFAULT_WIDTH, M_DEFAULT_HEIGHT); 287 for (uint32 i = 0; i < CountItems(); i++) 288 { 289 MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i)); 290 jack->layoutChanged(layout); 291 } 292 _updateIcon(layout); 293 _prepareLabel(); 294 arrangeIOJacks(); 295 _updateBitmap(); 296 } 297 298 void MediaNodePanel::populateInit() 299 { 300 D_METHOD(("MediaNodePanel::populateInit()\n")); 301 if (ref->kind() & B_BUFFER_CONSUMER) 302 { 303 vector<media_input> freeInputs; 304 ref->getFreeInputs(freeInputs); 305 for (uint32 i = 0; i < freeInputs.size(); i++) 306 { 307 AddItem(new MediaJack(freeInputs[i])); 308 } 309 } 310 if (ref->kind() & B_BUFFER_PRODUCER) 311 { 312 vector<media_output> freeOutputs; 313 ref->getFreeOutputs(freeOutputs); 314 for (uint32 i = 0; i < freeOutputs.size(); i++) 315 { 316 AddItem(new MediaJack(freeOutputs[i])); 317 } 318 } 319 } 320 321 void MediaNodePanel::updateIOJacks() 322 { 323 D_METHOD(("MediaNodePanel::updateIOJacks()\n")); 324 325 // remove all free inputs/outputs, they may be outdated 326 for (uint32 i = 0; i < CountItems(); i++) 327 { 328 MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i)); 329 if (jack && !jack->isConnected()) 330 { 331 RemoveItem(jack); 332 delete jack; 333 i--; // account for reindexing in the BList 334 } 335 } 336 337 // add free inputs 338 if (ref->kind() & B_BUFFER_CONSUMER) 339 { 340 vector<media_input> freeInputs; 341 ref->getFreeInputs(freeInputs); 342 for (uint32 i = 0; i < freeInputs.size(); i++) 343 { 344 MediaJack *jack; 345 AddItem(jack = new MediaJack(freeInputs[i])); 346 } 347 } 348 349 // add free outputs 350 if (ref->kind() & B_BUFFER_PRODUCER) 351 { 352 vector<media_output> freeOutputs; 353 ref->getFreeOutputs(freeOutputs); 354 for (uint32 i = 0; i < freeOutputs.size(); i++) 355 { 356 MediaJack *jack; 357 AddItem(jack = new MediaJack(freeOutputs[i])); 358 } 359 } 360 361 // the supported media types might have changed -> this could 362 // require changing the icon 363 _updateIcon(dynamic_cast<MediaRoutingView *>(view())->getLayout()); 364 } 365 366 void MediaNodePanel::arrangeIOJacks() 367 { 368 D_METHOD(("MediaNodePanel::arrangeIOJacks()\n")); 369 SortItems(DiagramItem::M_ENDPOINT, &compareTypeAndID); 370 371 switch (dynamic_cast<MediaRoutingView *>(view())->getLayout()) 372 { 373 case MediaRoutingView::M_ICON_VIEW: 374 { 375 BRegion updateRegion; 376 float align = 1.0; 377 view()->GetItemAlignment(0, &align); 378 379 // adjust this panel's size 380 int32 numInputs = 0, numOutputs = 0; 381 for (uint32 i = 0; i < CountItems(); i++) 382 { 383 MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i)); 384 if (jack) 385 { 386 if (jack->isInput()) 387 { 388 numInputs++; 389 } 390 if (jack->isOutput()) 391 { 392 numOutputs++; 393 } 394 } 395 } 396 float minHeight = MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP; 397 minHeight *= numInputs > numOutputs ? numInputs : numOutputs; 398 minHeight += m_labelRect.Height(); 399 minHeight += 2 * MediaJack::M_DEFAULT_GAP; 400 minHeight = ((int)minHeight / (int)align) * align + align; 401 if ((Frame().Height() < minHeight) 402 || ((Frame().Height() > minHeight) 403 && (minHeight >= MediaNodePanel::M_DEFAULT_HEIGHT))) 404 { 405 updateRegion.Include(Frame()); 406 resizeTo(Frame().Width(), minHeight); 407 updateRegion.Include(Frame()); 408 _prepareLabel(); 409 } 410 411 // adjust the placement of the jacks 412 BRect r = m_bodyRect; 413 r.bottom -= M_BODY_V_MARGIN; 414 float inputOffset = 0.0, outputOffset = 0.0; 415 float center = Frame().top + r.top + (r.Height() / 2.0); 416 center += MediaJack::M_DEFAULT_GAP - (MediaJack::M_DEFAULT_HEIGHT / 2.0); 417 center = ((int)center / (int)align) * align; 418 if (numInputs) 419 { 420 if (numInputs % 2) // odd number of inputs 421 { 422 inputOffset = center - (numInputs / 2) * (MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP); 423 } 424 else // even number of inputs 425 { 426 inputOffset = center - ((numInputs + 1) / 2) * (MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP); 427 } 428 } 429 if (numOutputs) 430 { 431 if (numOutputs % 2) // odd number of outputs 432 { 433 outputOffset = center - (numOutputs / 2) * (MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP); 434 } 435 else // even number of outputs 436 { 437 outputOffset = center - ((numOutputs + 1) / 2) * (MediaJack::M_DEFAULT_HEIGHT + MediaJack::M_DEFAULT_GAP); 438 } 439 } 440 for (uint32 i = 0; i < CountItems(); i++) 441 { 442 MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i)); 443 if (jack) 444 { 445 if (jack->isInput()) 446 { 447 jack->setPosition(inputOffset, Frame().left, Frame().right, &updateRegion); 448 inputOffset += jack->Frame().Height() + MediaJack::M_DEFAULT_GAP; 449 } 450 if (jack->isOutput()) 451 { 452 jack->setPosition(outputOffset, Frame().left, Frame().right, &updateRegion); 453 outputOffset += jack->Frame().Height() + MediaJack::M_DEFAULT_GAP; 454 } 455 } 456 } 457 for (int32 i = 0; i < updateRegion.CountRects(); i++) 458 { 459 view()->Invalidate(updateRegion.RectAt(i)); 460 } 461 break; 462 } 463 case MediaRoutingView::M_MINI_ICON_VIEW: 464 { 465 BRegion updateRegion; 466 float align = 1.0; 467 view()->GetItemAlignment(&align, 0); 468 469 // adjust this panel's size 470 int32 numInputs = 0, numOutputs = 0; 471 for (uint32 i = 0; i < CountItems(); i++) 472 { 473 MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i)); 474 if (jack) 475 { 476 if (jack->isInput()) 477 { 478 numInputs++; 479 } 480 if (jack->isOutput()) 481 { 482 numOutputs++; 483 } 484 } 485 } 486 float minWidth = MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP; 487 minWidth *= numInputs > numOutputs ? numInputs : numOutputs; 488 minWidth += m_bodyRect.Width(); 489 minWidth += 2 * MediaJack::M_DEFAULT_GAP; 490 minWidth = ((int)minWidth / (int)align) * align + align; 491 if ((Frame().Width() < minWidth) 492 || ((Frame().Width() > minWidth) 493 && (minWidth >= MediaNodePanel::M_DEFAULT_WIDTH))) 494 { 495 updateRegion.Include(Frame()); 496 resizeTo(minWidth, Frame().Height()); 497 updateRegion.Include(Frame()); 498 _prepareLabel(); 499 } 500 // adjust the placement of the jacks 501 float inputOffset = 0.0, outputOffset = 0.0; 502 float center = Frame().left + m_labelRect.left + (m_labelRect.Width() / 2.0); 503 center += MediaJack::M_DEFAULT_GAP - (MediaJack::M_DEFAULT_WIDTH / 2.0); 504 center = ((int)center / (int)align) * align; 505 if (numInputs) 506 { 507 if (numInputs % 2) // odd number of inputs 508 { 509 inputOffset = center - (numInputs / 2) * (MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP); 510 } 511 else // even number of inputs 512 { 513 inputOffset = center - ((numInputs + 1) / 2) * (MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP); 514 } 515 } 516 if (numOutputs) 517 { 518 if (numOutputs % 2) // odd number of outputs 519 { 520 outputOffset = center - (numOutputs / 2) * (MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP); 521 } 522 else // even number of outputs 523 { 524 outputOffset = center - ((numOutputs + 1) / 2) * (MediaJack::M_DEFAULT_WIDTH + MediaJack::M_DEFAULT_GAP); 525 } 526 } 527 for (uint32 i = 0; i < CountItems(); i++) 528 { 529 MediaJack *jack = dynamic_cast<MediaJack *>(ItemAt(i)); 530 if (jack) 531 { 532 if (jack->isInput()) 533 { 534 jack->setPosition(inputOffset, Frame().top, Frame().bottom, &updateRegion); 535 inputOffset += jack->Frame().Width() + MediaJack::M_DEFAULT_GAP; 536 } 537 if (jack->isOutput()) 538 { 539 jack->setPosition(outputOffset, Frame().top, Frame().bottom, &updateRegion); 540 outputOffset += jack->Frame().Width() + MediaJack::M_DEFAULT_GAP; 541 } 542 } 543 } 544 for (int32 i = 0; i < updateRegion.CountRects(); i++) 545 { 546 view()->Invalidate(updateRegion.RectAt(i)); 547 } 548 break; 549 } 550 } 551 _updateBitmap(); 552 } 553 554 void MediaNodePanel::showContextMenu( 555 BPoint point) 556 { 557 D_METHOD(("MediaNodePanel::showContextMenu()\n")); 558 559 BPopUpMenu *menu = new BPopUpMenu("MediaNodePanel PopUp", false, false, B_ITEMS_IN_COLUMN); 560 menu->SetFont(be_plain_font); 561 562 BMenuItem *item; 563 BMessage *message; 564 565 // add the "Tweak Parameters" item 566 message = new BMessage(MediaRoutingView::M_NODE_TWEAK_PARAMETERS); 567 menu->AddItem(item = new BMenuItem("Tweak parameters", message, 'P')); 568 if (!(ref->kind() & B_CONTROLLABLE)) 569 { 570 item->SetEnabled(false); 571 } 572 573 message = new BMessage(InfoWindowManager::M_INFO_WINDOW_REQUESTED); 574 message->AddInt32("nodeID", ref->id()); 575 menu->AddItem(new BMenuItem("Get info", message, 'I')); 576 menu->AddSeparatorItem(); 577 578 menu->AddItem(item = new BMenuItem("Release", new BMessage(MediaRoutingView::M_DELETE_SELECTION), 'T')); 579 if (!ref->isInternal()) 580 { 581 item->SetEnabled(false); 582 } 583 menu->AddSeparatorItem(); 584 585 // add the "Cycle" item 586 message = new BMessage(MediaRoutingView::M_NODE_CHANGE_CYCLING); 587 message->AddBool("cycle", !ref->isCycling()); 588 menu->AddItem(item = new BMenuItem("Cycle", message)); 589 item->SetMarked(ref->isCycling()); 590 if (ref->flags() & NodeRef::NO_SEEK) 591 { 592 item->SetEnabled(false); 593 } 594 595 // add the "Run Mode" sub menu 596 BMenu *subMenu = new BMenu("Run mode"); 597 subMenu->SetFont(be_plain_font); 598 for (uint32 runMode = 1; runMode <= BMediaNode::B_RECORDING; runMode++) 599 { 600 BString itemName = MediaString::getStringFor(static_cast<BMediaNode::run_mode> 601 (runMode)); 602 message = new BMessage(MediaRoutingView::M_NODE_CHANGE_RUN_MODE); 603 message->AddInt32("run_mode", runMode); 604 subMenu->AddItem(item = new BMenuItem(itemName.String(), message)); 605 if (ref->runMode() == runMode) 606 { 607 item->SetMarked(true); 608 } 609 else if ((ref->runMode() == 0) 610 && (ref->group()) && (ref->group()->runMode() == BMediaNode::run_mode(runMode))) 611 { 612 item->SetMarked(true); 613 } 614 } 615 subMenu->AddSeparatorItem(); 616 message = new BMessage(MediaRoutingView::M_NODE_CHANGE_RUN_MODE); 617 message->AddInt32("run_mode", 0); 618 subMenu->AddItem(item = new BMenuItem("(same as group)", message)); 619 if (ref->group() == 0) 620 { 621 item->SetEnabled(false); 622 } 623 else if ((ref->runMode() < 1) && (ref->group()->runMode() > 0)) 624 { 625 item->SetMarked(true); 626 } 627 menu->AddItem(subMenu); 628 subMenu->SetTargetForItems(view()); 629 630 // [c.lenz 24dec99] hide rarely used commands in a 'Advanced' submenu 631 subMenu = new BMenu("Advanced"); 632 subMenu->SetFont(be_plain_font); 633 // [e.moon 5dec99] ad-hoc timesource support 634 if(ref->kind() & B_TIME_SOURCE) { 635 message = new BMessage(MediaRoutingView::M_NODE_START_TIME_SOURCE); 636 message->AddInt32("nodeID", ref->id()); 637 subMenu->AddItem(new BMenuItem( 638 "Start time source", 639 message)); 640 message = new BMessage(MediaRoutingView::M_NODE_START_TIME_SOURCE); 641 message->AddInt32("nodeID", ref->id()); 642 subMenu->AddItem(new BMenuItem( 643 "Stop time source", 644 message)); 645 } 646 // [c.lenz 24dec99] support for BControllable::StartControlPanel() 647 if(ref->kind() & B_CONTROLLABLE) { 648 if (subMenu->CountItems() > 0) 649 subMenu->AddSeparatorItem(); 650 message = new BMessage(MediaRoutingView::M_NODE_START_CONTROL_PANEL); 651 subMenu->AddItem(new BMenuItem("Start Control Panel", message, 652 'P', B_COMMAND_KEY | B_SHIFT_KEY)); 653 } 654 // [em 1feb00] group tweaks 655 if(ref->group()) 656 { 657 message = new BMessage(MediaRoutingView::M_GROUP_SET_LOCKED); 658 message->AddInt32("groupID", ref->group()->id()); 659 bool isLocked = (ref->group()->groupFlags() & NodeGroup::GROUP_LOCKED); 660 message->AddBool("locked", !isLocked); 661 if (subMenu->CountItems() > 0) 662 subMenu->AddSeparatorItem(); 663 subMenu->AddItem( 664 new BMenuItem( 665 isLocked ? "Unlock group" : "Lock group", message)); 666 } 667 668 if (subMenu->CountItems() > 0) 669 { 670 menu->AddItem(subMenu); 671 subMenu->SetTargetForItems(view()); 672 } 673 674 menu->SetTargetForItems(view()); 675 view()->ConvertToScreen(&point); 676 point -= BPoint(1.0, 1.0); 677 menu->Go(point, true, true, true); 678 } 679 680 // ---------------------------------------------------------------- // 681 // BHandler impl 682 // ---------------------------------------------------------------- // 683 684 void MediaNodePanel::MessageReceived( 685 BMessage *message) 686 { 687 D_METHOD(("MediaNodePanel::MessageReceived()\n")); 688 switch (message->what) 689 { 690 case NodeRef::M_INPUTS_CHANGED: 691 { 692 D_MESSAGE(("MediaNodePanel::MessageReceived(NodeRef::M_INPUTS_CHANGED)\n")); 693 _updateIcon(dynamic_cast<MediaRoutingView *>(view())->getLayout()); 694 break; 695 } 696 case NodeRef::M_OUTPUTS_CHANGED: 697 { 698 D_MESSAGE(("MediaNodePanel::MessageReceived(NodeRef::M_OUTPUTS_CHANGED)\n")); 699 _updateIcon(dynamic_cast<MediaRoutingView *>(view())->getLayout()); 700 break; 701 } 702 default: 703 { 704 BHandler::MessageReceived(message); 705 break; 706 } 707 } 708 } 709 710 // -------------------------------------------------------- // 711 // *** IStateArchivable 712 // -------------------------------------------------------- // 713 714 status_t MediaNodePanel::importState( 715 const BMessage* archive) { 716 717 BPoint iconPos(s_invalidPosition); 718 BPoint miniIconPos(s_invalidPosition); 719 720 MediaRoutingView* v = dynamic_cast<MediaRoutingView*>(view()); 721 ASSERT(v); 722 MediaRoutingView::layout_t layoutMode = v->getLayout(); 723 archive->FindPoint("iconPos", &iconPos); 724 archive->FindPoint("miniIconPos", &miniIconPos); 725 726 switch(layoutMode) { 727 case MediaRoutingView::M_ICON_VIEW: 728 if(iconPos != s_invalidPosition) 729 moveTo(iconPos); 730 m_alternatePosition = miniIconPos; 731 break; 732 733 case MediaRoutingView::M_MINI_ICON_VIEW: 734 if(miniIconPos != s_invalidPosition) 735 moveTo(miniIconPos); 736 m_alternatePosition = iconPos; 737 break; 738 } 739 740 return B_OK; 741 } 742 743 status_t MediaNodePanel::exportState( 744 BMessage* archive) const { 745 746 BPoint iconPos, miniIconPos; 747 748 MediaRoutingView* v = dynamic_cast<MediaRoutingView*>(view()); 749 ASSERT(v); 750 MediaRoutingView::layout_t layoutMode = v->getLayout(); 751 switch(layoutMode) { 752 case MediaRoutingView::M_ICON_VIEW: 753 iconPos = Frame().LeftTop(); 754 miniIconPos = m_alternatePosition; 755 break; 756 757 case MediaRoutingView::M_MINI_ICON_VIEW: 758 miniIconPos = Frame().LeftTop(); 759 iconPos = m_alternatePosition; 760 break; 761 } 762 763 if(iconPos != s_invalidPosition) 764 archive->AddPoint("iconPos", iconPos); 765 if(miniIconPos != s_invalidPosition) 766 archive->AddPoint("miniIconPos", miniIconPos); 767 768 // determine if I'm a 'system' node 769 port_info portInfo; 770 app_info appInfo; 771 772 if ((get_port_info(ref->node().port, &portInfo) == B_OK) 773 && (be_roster->GetRunningAppInfo(portInfo.team, &appInfo) == B_OK)) { 774 BEntry appEntry(&appInfo.ref); 775 char appName[B_FILE_NAME_LENGTH]; 776 if( 777 appEntry.InitCheck() == B_OK && 778 appEntry.GetName(appName) == B_OK && 779 (!strcmp(appName, "media_addon_server") || 780 !strcmp(appName, "audio_server"))) { 781 782 archive->AddBool("sysOwned", true); 783 } 784 } 785 786 return B_OK; 787 } 788 789 // ---------------------------------------------------------------- // 790 // *** internal operations 791 // ---------------------------------------------------------------- // 792 793 void MediaNodePanel::_prepareLabel() 794 { 795 // find out if its a file node first 796 if (ref->kind() & B_FILE_INTERFACE) 797 { 798 entry_ref nodeFile; 799 status_t error = BMediaRoster::Roster()->GetRefFor(ref->node(), &nodeFile); 800 if (error) 801 { 802 m_fullLabel = ref->name(); 803 m_fullLabel += " (no file)"; 804 } 805 else 806 { 807 BEntry entry(&nodeFile); 808 char fileName[B_FILE_NAME_LENGTH]; 809 entry.GetName(fileName); 810 m_fullLabel = fileName; 811 } 812 } 813 else 814 { 815 m_fullLabel = ref->name(); 816 } 817 818 int32 layout = dynamic_cast<MediaRoutingView *>(view())->getLayout(); 819 820 // Construct labelRect 821 font_height fh; 822 be_plain_font->GetHeight(&fh); 823 switch (layout) 824 { 825 case MediaRoutingView::M_ICON_VIEW: 826 { 827 m_labelRect = Frame(); 828 m_labelRect.OffsetTo(0.0, 0.0); 829 m_labelRect.bottom = 2 * M_LABEL_V_MARGIN + (fh.ascent + fh.descent + fh.leading) + 1.0; 830 break; 831 } 832 case MediaRoutingView::M_MINI_ICON_VIEW: 833 { 834 m_labelRect = Frame(); 835 m_labelRect.OffsetTo(0.0, 0.0); 836 m_labelRect.left = M_BODY_H_MARGIN + B_MINI_ICON; 837 m_labelRect.top += MediaJack::M_DEFAULT_HEIGHT; 838 m_labelRect.bottom -= MediaJack::M_DEFAULT_HEIGHT; 839 break; 840 } 841 } 842 843 // truncate the label to fit in the panel 844 float maxWidth = m_labelRect.Width() - (2.0 * M_LABEL_H_MARGIN) - 2.0; 845 if (be_plain_font->StringWidth(m_fullLabel.String()) > maxWidth) 846 { 847 char *truncatedLabel[1]; 848 truncatedLabel[0] = new char[B_MEDIA_NAME_LENGTH]; 849 const char *originalLabel[1]; 850 originalLabel[0] = new char[B_MEDIA_NAME_LENGTH]; 851 m_fullLabel.CopyInto(const_cast<char *>(originalLabel[0]), 0, B_MEDIA_NAME_LENGTH); 852 be_plain_font->GetTruncatedStrings(originalLabel, 1, B_TRUNCATE_END, maxWidth, (char **) truncatedLabel); 853 m_label = truncatedLabel[0]; 854 m_labelTruncated = true; 855 delete [] originalLabel[0]; 856 delete [] truncatedLabel[0]; 857 } 858 else 859 { 860 m_label = m_fullLabel; 861 m_labelTruncated = false; 862 } 863 864 // Construct labelOffset 865 float fw = be_plain_font->StringWidth(m_label.String()); 866 m_labelOffset.x = m_labelRect.left + m_labelRect.Width() / 2.0 - fw / 2.0; 867 m_labelOffset.y = m_labelRect.bottom - M_LABEL_V_MARGIN - fh.descent - (fh.leading / 2.0) - 1.0; 868 869 // Construct bodyRect 870 switch (layout) 871 { 872 case MediaRoutingView::M_ICON_VIEW: 873 { 874 m_bodyRect = Frame(); 875 m_bodyRect.OffsetTo(0.0, 0.0); 876 m_bodyRect.top = m_labelRect.bottom; 877 break; 878 } 879 case MediaRoutingView::M_MINI_ICON_VIEW: 880 { 881 m_bodyRect = Frame(); 882 m_bodyRect.OffsetTo(0.0, 0.0); 883 m_bodyRect.right = m_labelRect.left; 884 break; 885 } 886 } 887 } 888 889 void MediaNodePanel::_updateBitmap() 890 { 891 if (m_bitmap) 892 { 893 delete m_bitmap; 894 } 895 BBitmap *tempBitmap = new BBitmap(Frame().OffsetToCopy(0.0, 0.0), B_CMAP8, true); 896 tempBitmap->Lock(); 897 { 898 BView *tempView = new BView(tempBitmap->Bounds(), "", B_FOLLOW_NONE, 0); 899 tempBitmap->AddChild(tempView); 900 tempView->SetOrigin(0.0, 0.0); 901 902 int32 layout = dynamic_cast<MediaRoutingView *>(view())->getLayout(); 903 _drawInto(tempView, tempView->Bounds(), layout); 904 905 tempView->Sync(); 906 tempBitmap->RemoveChild(tempView); 907 delete tempView; 908 } 909 tempBitmap->Unlock(); 910 m_bitmap = new BBitmap(tempBitmap); 911 delete tempBitmap; 912 } 913 914 void MediaNodePanel::_drawInto( 915 BView *target, 916 BRect targetRect, 917 int32 layout) 918 { 919 switch (layout) 920 { 921 case MediaRoutingView::M_ICON_VIEW: 922 { 923 BRect r; 924 BPoint p; 925 926 // Draw borders 927 r = targetRect; 928 target->BeginLineArray(16); 929 target->AddLine(r.LeftTop(), r.RightTop(), M_DARK_GRAY_COLOR); 930 target->AddLine(r.RightTop(), r.RightBottom(), M_DARK_GRAY_COLOR); 931 target->AddLine(r.RightBottom(), r.LeftBottom(), M_DARK_GRAY_COLOR); 932 target->AddLine(r.LeftBottom(), r.LeftTop(), M_DARK_GRAY_COLOR); 933 r.InsetBy(1.0, 1.0); 934 target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR); 935 target->AddLine(r.RightTop(), r.RightBottom(), M_MED_GRAY_COLOR); 936 target->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR); 937 target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR); 938 target->EndLineArray(); 939 940 // Fill background 941 r.InsetBy(1.0, 1.0); 942 target->SetLowColor(M_GRAY_COLOR); 943 target->FillRect(r, B_SOLID_LOW); 944 945 // Draw icon 946 if (m_icon) 947 { 948 p.x = m_bodyRect.left + m_bodyRect.Width() / 2.0 - B_LARGE_ICON / 2.0; 949 p.y = m_labelRect.bottom + m_bodyRect.Height() / 2.0 - B_LARGE_ICON / 2.0; 950 if (isSelected()) 951 { 952 target->SetDrawingMode(B_OP_INVERT); 953 target->DrawBitmapAsync(m_icon, p); 954 target->SetDrawingMode(B_OP_ALPHA); 955 target->SetHighColor(0, 0, 0, 180); 956 target->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); 957 target->DrawBitmapAsync(m_icon, p); 958 target->SetDrawingMode(B_OP_OVER); 959 } 960 else 961 { 962 target->SetDrawingMode(B_OP_OVER); 963 target->DrawBitmapAsync(m_icon, p); 964 } 965 } 966 967 // Draw label 968 if (isSelected()) 969 { 970 r = m_labelRect; 971 r.InsetBy(M_LABEL_H_MARGIN, M_LABEL_V_MARGIN); 972 target->BeginLineArray(4); 973 target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_BLUE_COLOR); 974 target->AddLine(r.RightTop(), r.RightBottom(), M_LIGHT_BLUE_COLOR); 975 target->AddLine(r.RightBottom(), r.LeftBottom(), M_LIGHT_BLUE_COLOR); 976 target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_BLUE_COLOR); 977 target->EndLineArray(); 978 r.InsetBy(1.0, 1.0); 979 target->SetHighColor(M_DARK_BLUE_COLOR); 980 target->FillRect(r, B_SOLID_HIGH); 981 } 982 target->SetDrawingMode(B_OP_OVER); 983 target->SetHighColor(isSelected() ? M_WHITE_COLOR : M_BLACK_COLOR); 984 target->DrawString(m_label.String(), m_labelOffset); 985 break; 986 } 987 case MediaRoutingView::M_MINI_ICON_VIEW: 988 { 989 BRect r; 990 BPoint p; 991 992 // Draw borders 993 r = targetRect; 994 target->BeginLineArray(16); 995 target->AddLine(r.LeftTop(), r.RightTop(), M_DARK_GRAY_COLOR); 996 target->AddLine(r.RightTop(), r.RightBottom(), M_DARK_GRAY_COLOR); 997 target->AddLine(r.RightBottom(), r.LeftBottom(), M_DARK_GRAY_COLOR); 998 target->AddLine(r.LeftBottom(), r.LeftTop(), M_DARK_GRAY_COLOR); 999 r.InsetBy(1.0, 1.0); 1000 target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR); 1001 target->AddLine(r.RightTop(), r.RightBottom(), M_MED_GRAY_COLOR); 1002 target->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR); 1003 target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR); 1004 target->EndLineArray(); 1005 1006 // Fill background 1007 r.InsetBy(1.0, 1.0); 1008 target->SetLowColor(M_GRAY_COLOR); 1009 target->FillRect(r, B_SOLID_LOW); 1010 1011 // Draw icon 1012 if (m_icon) 1013 { 1014 p.x = m_bodyRect.left + M_BODY_H_MARGIN; 1015 p.y = m_bodyRect.top + (m_bodyRect.Height() / 2.0) - (B_MINI_ICON / 2.0); 1016 if (isSelected()) 1017 { 1018 target->SetDrawingMode(B_OP_INVERT); 1019 target->DrawBitmapAsync(m_icon, p); 1020 target->SetDrawingMode(B_OP_ALPHA); 1021 target->SetHighColor(0, 0, 0, 180); 1022 target->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); 1023 target->DrawBitmapAsync(m_icon, p); 1024 target->SetDrawingMode(B_OP_OVER); 1025 } 1026 else 1027 { 1028 target->SetDrawingMode(B_OP_OVER); 1029 target->DrawBitmapAsync(m_icon, p); 1030 } 1031 } 1032 1033 // Draw label 1034 if (isSelected()) 1035 { 1036 r = m_labelRect; 1037 r.InsetBy(M_LABEL_H_MARGIN, M_LABEL_V_MARGIN); 1038 target->BeginLineArray(4); 1039 target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_BLUE_COLOR); 1040 target->AddLine(r.RightTop(), r.RightBottom(), M_LIGHT_BLUE_COLOR); 1041 target->AddLine(r.RightBottom(), r.LeftBottom(), M_LIGHT_BLUE_COLOR); 1042 target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_BLUE_COLOR); 1043 target->EndLineArray(); 1044 r.InsetBy(1.0, 1.0); 1045 target->SetHighColor(M_DARK_BLUE_COLOR); 1046 target->FillRect(r, B_SOLID_HIGH); 1047 } 1048 target->SetDrawingMode(B_OP_OVER); 1049 target->SetHighColor(isSelected() ? M_WHITE_COLOR : M_BLACK_COLOR); 1050 target->DrawString(m_label.String(), m_labelOffset); 1051 break; 1052 } 1053 } 1054 } 1055 1056 void MediaNodePanel::_updateIcon( 1057 int32 layout) 1058 { 1059 D_METHOD(("MediaNodePanel::_updateIcon()\n")); 1060 1061 if (m_icon) 1062 { 1063 delete m_icon; 1064 m_icon = 0; 1065 } 1066 RouteAppNodeManager *manager; 1067 manager = dynamic_cast<MediaRoutingView *>(view())->manager; 1068 switch (layout) 1069 { 1070 case MediaRoutingView::M_ICON_VIEW: 1071 { 1072 const MediaIcon *icon = manager->mediaIconFor(ref->id(), B_LARGE_ICON); 1073 m_icon = new BBitmap(dynamic_cast<const BBitmap *>(icon)); 1074 break; 1075 } 1076 case MediaRoutingView::M_MINI_ICON_VIEW: 1077 { 1078 const MediaIcon *icon = manager->mediaIconFor(ref->id(), B_MINI_ICON); 1079 m_icon = new BBitmap(dynamic_cast<const BBitmap *>(icon)); 1080 break; 1081 } 1082 } 1083 } 1084 1085 // -------------------------------------------------------- // 1086 // *** sorting methods (friend) 1087 // -------------------------------------------------------- // 1088 1089 int __CORTEX_NAMESPACE__ compareID( 1090 const void *lValue, 1091 const void *rValue) 1092 { 1093 int retValue = 0; 1094 const MediaNodePanel *lPanel = *(reinterpret_cast<MediaNodePanel * const*>(reinterpret_cast<void * const*>(lValue))); 1095 const MediaNodePanel *rPanel = *(reinterpret_cast<MediaNodePanel * const*>(reinterpret_cast<void * const*>(rValue))); 1096 if (lPanel && rPanel) 1097 { 1098 if (lPanel->ref->id() < rPanel->ref->id()) 1099 { 1100 retValue = -1; 1101 } 1102 else if (lPanel->ref->id() > rPanel->ref->id()) 1103 { 1104 retValue = 1; 1105 } 1106 } 1107 return retValue; 1108 } 1109 1110 // END -- MediaNodePanel.cpp -- 1111