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