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), 895 B_RGBA32, 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 target->SetDrawingMode(B_OP_OVER); 951 target->DrawBitmapAsync(m_icon, p); 952 } 953 954 // Draw label 955 if (isSelected()) 956 { 957 r = m_labelRect; 958 r.InsetBy(M_LABEL_H_MARGIN, M_LABEL_V_MARGIN); 959 target->BeginLineArray(4); 960 target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_BLUE_COLOR); 961 target->AddLine(r.RightTop(), r.RightBottom(), M_LIGHT_BLUE_COLOR); 962 target->AddLine(r.RightBottom(), r.LeftBottom(), M_LIGHT_BLUE_COLOR); 963 target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_BLUE_COLOR); 964 target->EndLineArray(); 965 r.InsetBy(1.0, 1.0); 966 target->SetHighColor(M_DARK_BLUE_COLOR); 967 target->FillRect(r, B_SOLID_HIGH); 968 } 969 target->SetDrawingMode(B_OP_OVER); 970 target->SetHighColor(isSelected() ? M_WHITE_COLOR : M_BLACK_COLOR); 971 target->DrawString(m_label.String(), m_labelOffset); 972 break; 973 } 974 case MediaRoutingView::M_MINI_ICON_VIEW: 975 { 976 BRect r; 977 BPoint p; 978 979 // Draw borders 980 r = targetRect; 981 target->BeginLineArray(16); 982 target->AddLine(r.LeftTop(), r.RightTop(), M_DARK_GRAY_COLOR); 983 target->AddLine(r.RightTop(), r.RightBottom(), M_DARK_GRAY_COLOR); 984 target->AddLine(r.RightBottom(), r.LeftBottom(), M_DARK_GRAY_COLOR); 985 target->AddLine(r.LeftBottom(), r.LeftTop(), M_DARK_GRAY_COLOR); 986 r.InsetBy(1.0, 1.0); 987 target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_GRAY_COLOR); 988 target->AddLine(r.RightTop(), r.RightBottom(), M_MED_GRAY_COLOR); 989 target->AddLine(r.RightBottom(), r.LeftBottom(), M_MED_GRAY_COLOR); 990 target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_GRAY_COLOR); 991 target->EndLineArray(); 992 993 // Fill background 994 r.InsetBy(1.0, 1.0); 995 target->SetLowColor(M_GRAY_COLOR); 996 target->FillRect(r, B_SOLID_LOW); 997 998 // Draw icon 999 if (m_icon) 1000 { 1001 p.x = m_bodyRect.left + M_BODY_H_MARGIN; 1002 p.y = m_bodyRect.top + (m_bodyRect.Height() / 2.0) - (B_MINI_ICON / 2.0); 1003 target->SetDrawingMode(B_OP_OVER); 1004 target->DrawBitmapAsync(m_icon, p); 1005 } 1006 1007 // Draw label 1008 if (isSelected()) 1009 { 1010 r = m_labelRect; 1011 r.InsetBy(M_LABEL_H_MARGIN, M_LABEL_V_MARGIN); 1012 target->BeginLineArray(4); 1013 target->AddLine(r.LeftTop(), r.RightTop(), M_LIGHT_BLUE_COLOR); 1014 target->AddLine(r.RightTop(), r.RightBottom(), M_LIGHT_BLUE_COLOR); 1015 target->AddLine(r.RightBottom(), r.LeftBottom(), M_LIGHT_BLUE_COLOR); 1016 target->AddLine(r.LeftBottom(), r.LeftTop(), M_LIGHT_BLUE_COLOR); 1017 target->EndLineArray(); 1018 r.InsetBy(1.0, 1.0); 1019 target->SetHighColor(M_DARK_BLUE_COLOR); 1020 target->FillRect(r, B_SOLID_HIGH); 1021 } 1022 target->SetDrawingMode(B_OP_OVER); 1023 target->SetHighColor(isSelected() ? M_WHITE_COLOR : M_BLACK_COLOR); 1024 target->DrawString(m_label.String(), m_labelOffset); 1025 break; 1026 } 1027 } 1028 } 1029 1030 void MediaNodePanel::_updateIcon( 1031 int32 layout) 1032 { 1033 D_METHOD(("MediaNodePanel::_updateIcon()\n")); 1034 1035 if (m_icon) 1036 { 1037 delete m_icon; 1038 m_icon = 0; 1039 } 1040 RouteAppNodeManager *manager; 1041 manager = dynamic_cast<MediaRoutingView *>(view())->manager; 1042 switch (layout) 1043 { 1044 case MediaRoutingView::M_ICON_VIEW: 1045 { 1046 const MediaIcon *icon = manager->mediaIconFor(ref->id(), B_LARGE_ICON); 1047 m_icon = new BBitmap(dynamic_cast<const BBitmap *>(icon)); 1048 break; 1049 } 1050 case MediaRoutingView::M_MINI_ICON_VIEW: 1051 { 1052 const MediaIcon *icon = manager->mediaIconFor(ref->id(), B_MINI_ICON); 1053 m_icon = new BBitmap(dynamic_cast<const BBitmap *>(icon)); 1054 break; 1055 } 1056 } 1057 } 1058 1059 // -------------------------------------------------------- // 1060 // *** sorting methods (friend) 1061 // -------------------------------------------------------- // 1062 1063 int __CORTEX_NAMESPACE__ compareID( 1064 const void *lValue, 1065 const void *rValue) 1066 { 1067 int retValue = 0; 1068 const MediaNodePanel *lPanel = *(reinterpret_cast<MediaNodePanel * const*>(reinterpret_cast<void * const*>(lValue))); 1069 const MediaNodePanel *rPanel = *(reinterpret_cast<MediaNodePanel * const*>(reinterpret_cast<void * const*>(rValue))); 1070 if (lPanel && rPanel) 1071 { 1072 if (lPanel->ref->id() < rPanel->ref->id()) 1073 { 1074 retValue = -1; 1075 } 1076 else if (lPanel->ref->id() > rPanel->ref->id()) 1077 { 1078 retValue = 1; 1079 } 1080 } 1081 return retValue; 1082 } 1083 1084 // END -- MediaNodePanel.cpp -- 1085