root/src/apps/cortex/MediaRoutingView/MediaRoutingView.cpp
/*
 * Copyright (c) 1999-2000, Eric Moon.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions, and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


// MediaRoutingView.cpp

#include "MediaRoutingView.h"

// RouteApp
#include "RouteApp.h"
#include "RouteAppNodeManager.h"
#include "RouteWindow.h"
#include "NodeSetIOContext.h"
// DormantNodeView
#include "DormantNodeView.h"
// InfoWindow
#include "InfoWindowManager.h"
// TransportWindow
#include "TransportWindow.h"
// MediaRoutingView
#include "MediaNodePanel.h"
#include "MediaWire.h"
// NodeManager
#include "NodeGroup.h"
#include "NodeRef.h"
#include "Connection.h"
// ParameterWindow
#include "ParameterWindowManager.h"

// Application Kit
#include <Application.h>
// Interface Kit
#include <Alert.h>
#include <Bitmap.h>
#include <MenuItem.h>
#include <PopUpMenu.h>
#include <Window.h>
// Locale Kit
#undef B_CATALOG
#define B_CATALOG (&sCatalog)
#include <Catalog.h>
// Media Kit
#include <MediaRoster.h>
// Storage Kit
#include <File.h>
#include <NodeInfo.h>
#include <Path.h>
// Translation Kit
#include <BitmapStream.h>
#include <TranslatorRoster.h>

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "MediaRoutingView"

__USE_CORTEX_NAMESPACE

#include <Debug.h>
#define D_METHOD(x) //PRINT (x)
#define D_MESSAGE(x) //PRINT (x)
#define D_MOUSE(x) //PRINT (x)
#define D_KEY(x) //PRINT (x)

static BCatalog sCatalog("x-vnd.Cortex.MediaRoutingView");

// ---------------------------------------------------------------- //
// constants
// ---------------------------------------------------------------- //

float   MediaRoutingView::M_CLEANUP_H_GAP                       = 25.0;
float   MediaRoutingView::M_CLEANUP_V_GAP                       = 10.0;
float   MediaRoutingView::M_CLEANUP_H_MARGIN            = 15.0;
float   MediaRoutingView::M_CLEANUP_V_MARGIN            = 10.0;

// ---------------------------------------------------------------- //
// m_inactiveNodeState content
// ---------------------------------------------------------------- //

struct _inactive_node_state_entry {
        _inactive_node_state_entry(
                const char* _name, int32 _kind, const BMessage& _state) :
                name(_name), kind(_kind), state(_state) {}

        BString name;
        uint32 kind;
        BMessage state;
};

// ---------------------------------------------------------------- //
// ctor/dtor
// ---------------------------------------------------------------- //

MediaRoutingView::MediaRoutingView(
        RouteAppNodeManager *nodeManager,
        BRect frame,
        const char *name,
        uint32 resizeMode)
        : DiagramView(frame, "MediaRoutingView", true, B_FOLLOW_ALL_SIDES),
          manager(nodeManager),
          m_layout(M_ICON_VIEW),
          m_nextGroupNumber(1),
          m_lastDroppedNode(0),
          m_draggedWire(0)
{
        D_METHOD(("MediaRoutingView::MediaRoutingView()\n"));
        ASSERT(manager);

        setBackgroundColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), B_DARKEN_2_TINT));
        SetItemAlignment(5.0, 5.0);
        _initLayout();
}

MediaRoutingView::~MediaRoutingView() {
        D_METHOD(("MediaRoutingView::~MediaRoutingView()\n"));

        _emptyInactiveNodeState();

        // quit ParameterWindowManager if necessary
        ParameterWindowManager::shutDown();

        // quit InfoWindowManager if necessary
        InfoWindowManager::shutDown();
}

// -------------------------------------------------------- //
// *** derived from DiagramView
// -------------------------------------------------------- //

void MediaRoutingView::connectionAborted(
        DiagramEndPoint *fromWhich)
{
        D_METHOD(("MediaRoutingView::connectionAborted()\n"));

        be_app->SetCursor(B_HAND_CURSOR);
}

void MediaRoutingView::connectionEstablished(
        DiagramEndPoint *fromWhich,
        DiagramEndPoint *toWhich)
{
        D_METHOD(("MediaRoutingView::connectionEstablished()\n"));

        be_app->SetCursor(B_HAND_CURSOR);
}

DiagramWire *MediaRoutingView::createWire(
        DiagramEndPoint *fromWhich,
        DiagramEndPoint *toWhich)
{
        D_METHOD(("MediaRoutingView::createWire()\n"));

        MediaJack *outputJack, *inputJack;
        MediaJack *jack = dynamic_cast<MediaJack *>(fromWhich);

        if (jack == NULL)
                return 0;

        if (jack->isOutput())
        {
                outputJack = jack;
                inputJack = dynamic_cast<MediaJack *>(toWhich);
                if (!inputJack || !inputJack->isInput())
                {
                        return 0;
                }
        }
        else
        {
                inputJack = jack;
                outputJack = dynamic_cast<MediaJack *>(toWhich);
                if (!outputJack || !outputJack->isOutput())
                {
                        return 0;
                }
        }
        if (!outputJack->isConnected() && !inputJack->isConnected())
        {
                media_output output;
                media_input input;
                if ((outputJack->getOutput(&output) == B_OK)
                 && (inputJack->getInput(&input) == B_OK))
                {
                        status_t error;
                        Connection connection;
                        error = manager->connect(output, input, &connection);
                        if (error)
                        {
                                showErrorMessage(B_TRANSLATE("Could not connect"), error);
                        }
                }
        }
        return 0;
}

DiagramWire *MediaRoutingView::createWire(
        DiagramEndPoint *fromWhich)
{
        D_METHOD(("MediaRoutingView::createWire(temp)\n"));

        MediaJack *jack = dynamic_cast<MediaJack *>(fromWhich);
        if (jack)
        {
                if (jack->isOutput()) // this is the start point
                {
                        return new MediaWire(jack, true);
                }
                else
                {
                        return new MediaWire(jack, false);
                }
        }
        return 0;
}


void
MediaRoutingView::BackgroundMouseDown(BPoint point, uint32 buttons,
        uint32 clicks)
{
        D_MOUSE(("MediaRoutingView::BackgroundMouseDown()\n"));

        if ((buttons == B_SECONDARY_MOUSE_BUTTON)
         || (modifiers() & B_CONTROL_KEY)) {
                EndRectTracking();
                showContextMenu(point);
        }
}


void
MediaRoutingView::MessageDropped(BPoint point, BMessage *message)
{
        D_METHOD(("MediaRoutingView::MessageDropped()\n"));

        switch (message->what) {
                case DormantNodeView::M_INSTANTIATE_NODE:
                {
                        D_MESSAGE(("MediaRoutingView::MessageDropped(DormantNodeView::M_INSTANTIATE_NODE)\n"));
                        type_code type;
                        int32 count;
                        if (message->GetInfo("which", &type, &count) == B_OK) {
                                for (int32 n = 0; n < count; n++) {
                                        dormant_node_info info;
                                        const void *data;
                                        ssize_t dataSize;
                                        if (message->FindData("which", B_RAW_TYPE, &data, &dataSize) == B_OK) {
                                                memcpy(reinterpret_cast<void *>(&info), data, dataSize);
                                                NodeRef* droppedNode;
                                                status_t error;
                                                error = manager->instantiate(info, &droppedNode);
                                                if (!error) {
                                                        m_lastDroppedNode = droppedNode->id();
                                                        BPoint dropPoint, dropOffset;
                                                        dropPoint = message->DropPoint(&dropOffset);
                                                        m_lastDropPoint = Align(ConvertFromScreen(dropPoint - dropOffset));
                                                } else {
                                                        BString s = B_TRANSLATE(
                                                                "Could not instantiate '%infoname%'");
                                                        s.ReplaceFirst("%infoname%", info.name);
                                                        showErrorMessage(s, error);
                                                }
                                        }
                                }
                        }
                        break;
                }

                case B_SIMPLE_DATA:
                {
                        D_MESSAGE(("MediaRoutingView::MessageDropped(B_SIMPLE_DATA)\n"));
                        entry_ref fileRef;
                        if (message->FindRef("refs", &fileRef) == B_OK)
                                _checkDroppedFile(&fileRef, ConvertFromScreen(message->DropPoint()));
                        break;
                }

                case B_PASTE:
                {
                        D_MESSAGE(("MediaRoutingView::MessageDropped(B_PASTE)\n"));
                        ssize_t size;
                        const rgb_color *color; // [e.moon 21nov99] fixed const error
                        if (message->FindData("RGBColor", B_RGB_COLOR_TYPE,
                                reinterpret_cast<const void **>(&color), &size) == B_OK)
                                _changeBackground(*color);
                        break;
                }

                default:
                        DiagramView::MessageDropped(point, message);
        }
}


void
MediaRoutingView::SelectionChanged()
{
        D_METHOD(("MediaRoutingView::selectionChanged()\n"));
        _broadcastSelection();
}

// ---------------------------------------------------------------- //
// *** BView impl.
// ---------------------------------------------------------------- //

void MediaRoutingView::AttachedToWindow()
{
        D_METHOD(("MediaRoutingView::AttachedToWindow()\n"));
        _inherited::AttachedToWindow();

        // attach to manager
        ASSERT(manager);
        add_observer(this, manager);

        // add the context-menu shortcuts to the window
        _addShortcuts();

        // populate with existing nodes & connections
        _initContent();

        // [e.moon 29nov99] moved from AllAttached()
        cleanUp();
}

void MediaRoutingView::AllAttached()
{
        D_METHOD(("MediaRoutingView::AllAttached()\n"));
        _inherited::AllAttached();

        _adjustScrollBars();

        // grab keyboard events
        MakeFocus();
}

void MediaRoutingView::DetachedFromWindow()
{
        D_METHOD(("MediaRoutingView::DetachedFromWindow()\n"));
        _inherited::DetachedFromWindow();

        // detach from manager
        if (manager)
        {
                Autolock lock(manager);
                void *cookie = 0;
                NodeRef *ref;
                while (manager->getNextRef(&ref, &cookie) == B_OK)
                {
                        remove_observer(this, ref);
                }
                remove_observer(this, manager);
                const_cast<RouteAppNodeManager *&>(manager) = 0;
        }
}

void MediaRoutingView::KeyDown(
        const char *bytes,
        int32 numBytes)
{
        D_METHOD(("MediaRoutingView::KeyDown()\n"));

        switch (bytes[0])
        {
                case B_ENTER:
                {
                        D_KEY(("MediaRoutingView::KeyDown(B_ENTER)\n"));
                        _openParameterWindowsForSelection();
                        break;
                }
                case B_DELETE:
                {
                        D_KEY(("MediaRoutingView::KeyDown(B_DELETE)\n"));
                        _deleteSelection();
                        break;
                }
                case B_SPACE:
                {
                        // [e.moon 1dec99]
                        BWindow* w = Window();
                        ASSERT(w);
                        BMessenger(w).SendMessage(RouteWindow::M_TOGGLE_GROUP_ROLLING);
                        break;
                }
                default:
                {
                        DiagramView::KeyDown(bytes, numBytes);
                        break;
                }
        }
}

void MediaRoutingView::Pulse()
{
        // do the animation
}

// ---------------------------------------------------------------- //
// BHandler impl
// ---------------------------------------------------------------- //

void MediaRoutingView::MessageReceived(
        BMessage* message)
{
        D_METHOD(("MediaRoutingView::MessageReceived()\n"));

        switch (message->what)
        {
                case B_MEDIA_NODE_CREATED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_NODE_CREATED)\n"));
                        type_code type;
                        int32 count;
                        if (message->GetInfo("media_node_id", &type, &count) == B_OK)
                        {
                                for(int32 n = 0; n < count; n++)
                                {
                                        int32 id;
                                        if (message->FindInt32("media_node_id", n, &id) == B_OK)
                                        {
                                                // [e.moon 8dec99] allow for existing panel
                                                MediaNodePanel* panel;
                                                if(_findPanelFor(id, &panel) < B_OK)
                                                        _addPanelFor(id, BPoint(5.0, 5.0));
                                        }
                                }
                        }
                        break;
                }
                case B_MEDIA_NODE_DELETED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_NODE_DELETED)\n"));
                        type_code type;
                        int32 count;
                        if (message->GetInfo("media_node_id", &type, &count) == B_OK)
                        {
                                for (int32 n = 0; n < count; n++)
                                {
                                        int32 id;
                                        if (message->FindInt32("media_node_id", n, &id) == B_OK)
                                        {
                                                _removePanelFor(id);
                                        }
                                }
                        }
                        break;
                }
                case B_MEDIA_CONNECTION_MADE:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_CONNECTION_MADE)\n"));
                        type_code type;
                        int32 count;
                        if (message->GetInfo("output", &type, &count) == B_OK)
                        {
                                for (int32 n = 0; n < count; n++)
                                {
                                        media_output output;
                                        const void *data;
                                        ssize_t dataSize;
                                        if (message->FindData("output", B_RAW_TYPE, n, &data, &dataSize) == B_OK)
                                        {
                                                output = *reinterpret_cast<const media_output *>(data);
                                                Connection connection;
                                                if (manager->findConnection(output.node.node, output.source, &connection) == B_OK)
                                                {
                                                        _addWireFor(connection);
                                                }
                                        }
                                }
                        }
                        break;
                }
                case B_MEDIA_CONNECTION_BROKEN:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_CONNECTION_BROKEN)\n"));
                        type_code type;
                        int32 count;
                        if (message->GetInfo("__connection_id", &type, &count) == B_OK)
                        {
                                for (int32 n = 0; n < count; n++)
                                {
                                        int32 id;
                                        if (message->FindInt32("__connection_id", n, &id) == B_OK)
                                        {
                                                _removeWireFor(id);
                                        }
                                }
                        }
                        break;
                }
                case B_MEDIA_FORMAT_CHANGED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(B_MEDIA_FORMAT_CHANGED)\n"));

                        media_node_id nodeID;
                        if(message->FindInt32("__source_node_id", &nodeID) < B_OK)
                                break;

                        uint32 connectionID;
                        if(message->FindInt32("__connection_id", (int32*)&connectionID) < B_OK)
                                break;

                        media_source* source;
                        ssize_t dataSize;
                        if(message->FindData("be:source", B_RAW_TYPE, (const void**)&source, &dataSize) < B_OK)
                                break;

                        MediaWire* wire;
                        if(_findWireFor(connectionID, &wire) == B_OK) {
                                // copy new connection data
                                manager->findConnection(nodeID, *source, &wire->connection);
                        }
                        break;
                }
                case M_CLEANUP_REQUESTED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_M_CLEANUP_REQUESTED)\n"));
                        cleanUp();
                        break;
                }
                case M_SELECT_ALL:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_SELECT_ALL)\n"));
                        SelectAll(DiagramItem::M_BOX);
                        break;
                }
                case M_DELETE_SELECTION:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_DELETE_SELECTION)\n"));
                        _deleteSelection();
                        break;
                }
                case M_NODE_CHANGE_CYCLING:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_NODE_CYCLING_CHANGED)\n"));
                        bool cycle;
                        if (message->FindBool("cycle", &cycle) == B_OK)
                        {
                                _changeCyclingForSelection(cycle);
                        }
                        break;
                }
                case M_NODE_CHANGE_RUN_MODE:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_NODE_RUNMODE_CHANGED)\n"));
                        int32 mode;
                        if (message->FindInt32("run_mode", &mode) == B_OK)
                        {
                                _changeRunModeForSelection(static_cast<uint32>(mode));
                        }
                        break;
                }
                case M_LAYOUT_CHANGED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_LAYOUT_CHANGED)\n"));
                        layout_t layout;
                        if (message->FindInt32("layout", (int32*)&layout) == B_OK)
                        {
                                if (layout != m_layout)
                                {
                                        layoutChanged(layout);
                                        updateDataRect();
                                        Invalidate();
                                }
                        }
                        break;
                }
                case M_NODE_START_TIME_SOURCE:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_NODE_START_TIME_SOURCE)\n"));
                        int32 id;
                        if(message->FindInt32("nodeID", &id) < B_OK)
                                break;
                        NodeRef* ref;
                        if(manager->getNodeRef(id, &ref) < B_OK)
                                break;

                        bigtime_t when = system_time();
                        status_t err = manager->roster->StartTimeSource(ref->node(), when);
                        if(err < B_OK) {
                                PRINT((
                                        "! StartTimeSource(%" B_PRId32 "): '%s'\n",
                                        ref->id(), strerror(err)));
                        }
                        break;
                }
                case M_NODE_STOP_TIME_SOURCE:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_NODE_STOP_TIME_SOURCE)\n"));
                        int32 id;
                        if(message->FindInt32("nodeID", &id) < B_OK)
                                break;
                        NodeRef* ref;
                        if(manager->getNodeRef(id, &ref) < B_OK)
                                break;

                        bigtime_t when = system_time();
                        status_t err = manager->roster->StopTimeSource(ref->node(), when);
                        if(err < B_OK) {
                                PRINT((
                                        "! StopTimeSource(%" B_PRId32 "): '%s'\n",
                                        ref->id(), strerror(err)));
                        }
                        break;
                }
                case M_NODE_TWEAK_PARAMETERS: {
                        D_MESSAGE((" -> M_NODE_TWEAK_PARAMETERS\n"));
                        _openParameterWindowsForSelection();
                        break;
                }
                case M_NODE_START_CONTROL_PANEL: {
                        D_MESSAGE((" -> M_NODE_START_CONTROL_PANEL\n"));
                        _startControlPanelsForSelection();
                        break;
                }
                case M_GROUP_SET_LOCKED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(M_GROUP_SET_LOCKED)\n"));
                        int32 groupID;
                        if(message->FindInt32("groupID", &groupID) < B_OK)
                                break;
                        bool locked;
                        if(message->FindBool("locked", &locked) < B_OK)
                                break;
                        NodeGroup* group;
                        if(manager->findGroup(groupID, &group) < B_OK)
                                break;
                        uint32 f = locked ?
                                group->groupFlags() | NodeGroup::GROUP_LOCKED :
                                group->groupFlags() & ~NodeGroup::GROUP_LOCKED;
                        group->setGroupFlags(f);
                        break;
                }
                case M_BROADCAST_SELECTION: {
                        D_MESSAGE((" -> M_BROADCAST_SELECTION\n"));
                        _broadcastSelection();
                        break;
                }
                case InfoWindowManager::M_INFO_WINDOW_REQUESTED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(InfoView::M_INFO_WINDOW_REQUESTED)\n"));
                        type_code type;
                        int32 count;
                        if (message->GetInfo("input", &type, &count) == B_OK)
                        {
                                for (int32 i = 0; i < count; i++)
                                {
                                        media_input input;
                                        const void *data;
                                        ssize_t dataSize;
                                        if (message->FindData("input", B_RAW_TYPE, i, &data, &dataSize) == B_OK)
                                        {
                                                input = *reinterpret_cast<const media_input *>(data);
                                                InfoWindowManager *manager = InfoWindowManager::Instance();
                                                if (manager && manager->Lock()) {
                                                        manager->openWindowFor(input);
                                                        manager->Unlock();
                                                }
                                        }
                                }
                        }
                        else if (message->GetInfo("output", &type, &count) == B_OK)
                        {
                                for (int32 i = 0; i < count; i++)
                                {
                                        media_output output;
                                        const void *data;
                                        ssize_t dataSize;
                                        if (message->FindData("output", B_RAW_TYPE, i, &data, &dataSize) == B_OK)
                                        {
                                                output = *reinterpret_cast<const media_output *>(data);
                                                InfoWindowManager *manager = InfoWindowManager::Instance();
                                                if (manager && manager->Lock()) {
                                                        manager->openWindowFor(output);
                                                        manager->Unlock();
                                                }
                                        }
                                }
                        }
                        else
                        {
                                _openInfoWindowsForSelection();
                        }
                        break;
                }
                case NodeManager::M_RELEASED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(NodeManager::M_RELEASED)\n"));
                        remove_observer(this, manager);
                        const_cast<RouteAppNodeManager*&>(manager) = 0;
                        // +++++ disable view!
                        break;
                }
                case NodeRef::M_RELEASED:
                {
                        D_MESSAGE(("MediaRoutingView::MessageReceived(NodeRef::M_RELEASED)\n"));
                        // only relevant on shutdown; do nothing
                        break;
                }
                default:
                {
                        DiagramView::MessageReceived(message);
                }
        }
}

// ---------------------------------------------------------------- //
// *** operations (public)
// ---------------------------------------------------------------- //

BPoint MediaRoutingView::findFreePositionFor(
        const MediaNodePanel* panel) const
{
        D_METHOD(("MediaRoutingView::_findFreeSpotFor()\n"));

        BPoint p(M_CLEANUP_H_MARGIN, M_CLEANUP_V_MARGIN);
        if (panel)
        {
                switch (m_layout)
                {
                        case M_ICON_VIEW:
                        {
                                // find the target column by node_kind
                                p.x += M_CLEANUP_H_GAP + MediaNodePanel::M_DEFAULT_WIDTH;
                                if (panel->ref->kind() & B_BUFFER_PRODUCER)
                                {
                                        p.x -= M_CLEANUP_H_GAP + MediaNodePanel::M_DEFAULT_WIDTH;
                                }
                                if (panel->ref->kind() & B_BUFFER_CONSUMER)
                                {
                                        p.x += M_CLEANUP_H_GAP + MediaNodePanel::M_DEFAULT_WIDTH;
                                }
                                // find the bottom item in the column
                                float bottom = 0.0;
                                for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
                                {
                                        BRect r = ItemAt(i, DiagramItem::M_BOX)->Frame();
                                        if ((r.left >= p.x)
                                         && (r.left <= p.x + MediaNodePanel::M_DEFAULT_WIDTH))
                                        {
                                                bottom = (r.bottom > bottom) ? r.bottom : bottom;
                                        }
                                }
                                if (bottom >= p.y)
                                {
                                        p.y = bottom + M_CLEANUP_V_GAP;
                                }
                                break;
                        }
                        case M_MINI_ICON_VIEW:
                        {
                                // find the target row by node_kind
                                p.y += M_CLEANUP_V_GAP + MediaNodePanel::M_DEFAULT_HEIGHT;
                                if (panel->ref->kind() & B_BUFFER_PRODUCER)
                                {
                                        p.y -= M_CLEANUP_V_GAP + MediaNodePanel::M_DEFAULT_HEIGHT;
                                }
                                if (panel->ref->kind() & B_BUFFER_CONSUMER)
                                {
                                        p.y += M_CLEANUP_V_GAP + MediaNodePanel::M_DEFAULT_HEIGHT;
                                }
                                // find the right-most item in the row
                                float right = 0.0;
                                for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
                                {
                                        BRect r = ItemAt(i, DiagramItem::M_BOX)->Frame();
                                        if ((r.top >= p.y)
                                         && (r.top <= p.y + MediaNodePanel::M_DEFAULT_HEIGHT))
                                        {
                                                right = (r.right > right) ? r.right : right;
                                        }
                                }
                                if (right >= p.x)
                                {
                                        p.x = right + M_CLEANUP_H_GAP;
                                }
                                break;
                        }
                }
        }
        return p;
}

// ---------------------------------------------------------------- //
// *** operations (protected)
// ---------------------------------------------------------------- //

void MediaRoutingView::layoutChanged(
        layout_t layout)
{
        D_METHOD(("MediaRoutingView::layoutChanged()\n"));

        switch (layout)
        {
                case M_ICON_VIEW:
                {
                        float temp;

                        // swap the cleanup defaults
                        temp = M_CLEANUP_H_GAP;
                        M_CLEANUP_H_GAP = M_CLEANUP_V_GAP;
                        M_CLEANUP_V_GAP = temp;
                        temp = M_CLEANUP_H_MARGIN;
                        M_CLEANUP_H_MARGIN = M_CLEANUP_V_MARGIN;
                        M_CLEANUP_V_MARGIN = temp;

                        // swap the default dimensions for MediaJacks
                        temp = MediaJack::M_DEFAULT_WIDTH;
                        MediaJack::M_DEFAULT_WIDTH = MediaJack::M_DEFAULT_HEIGHT;
                        MediaJack::M_DEFAULT_HEIGHT = temp;

                        // Add space for the 3-letter i/o-abbreviation
                        BFont font(be_plain_font);
                        font.SetSize(font.Size() - 2.0);
                        for (int i = 0; i < MediaJack::M_MAX_ABBR_LENGTH; i++)
                        {
                                MediaJack::M_DEFAULT_WIDTH += font.StringWidth("M");
                        }
                        MediaJack::M_DEFAULT_WIDTH += 2.0; // add some padding

                        // Adjust the default size for MediaNodePanels
                        float labelWidth, bodyWidth;
                        float labelHeight, bodyHeight;
                        font_height fh;
                        be_plain_font->GetHeight(&fh);
                        labelWidth = 4 * MediaNodePanel::M_LABEL_H_MARGIN
                                                 + be_plain_font->StringWidth(" Be Audio Mixer ");
                        bodyWidth = 2 * MediaNodePanel::M_BODY_H_MARGIN + B_LARGE_ICON
                                                + 2 * MediaJack::M_DEFAULT_WIDTH;
                        labelHeight = 2 * MediaNodePanel::M_LABEL_V_MARGIN
                                                  + fh.ascent + fh.descent + fh.leading + 1.0;
                        bodyHeight = 2 * MediaNodePanel::M_BODY_V_MARGIN + B_LARGE_ICON;
                        MediaNodePanel::M_DEFAULT_WIDTH = labelWidth > bodyWidth ? labelWidth : bodyWidth;
                        MediaNodePanel::M_DEFAULT_HEIGHT = labelHeight + bodyHeight;
                        Align(&MediaNodePanel::M_DEFAULT_WIDTH, &MediaNodePanel::M_DEFAULT_HEIGHT);
                        break;
                }
                case M_MINI_ICON_VIEW:
                {
                        float temp;

                        // Swap the cleanup defaults
                        temp = M_CLEANUP_H_GAP;
                        M_CLEANUP_H_GAP = M_CLEANUP_V_GAP;
                        M_CLEANUP_V_GAP = temp;
                        temp = M_CLEANUP_H_MARGIN;
                        M_CLEANUP_H_MARGIN = M_CLEANUP_V_MARGIN;
                        M_CLEANUP_V_MARGIN = temp;

                        // Subtract space for the 3-letter i/o-abbreviation
                        BFont font(be_plain_font);
                        font.SetSize(font.Size() - 2.0);
                        for (int i = 0; i < MediaJack::M_MAX_ABBR_LENGTH; i++)
                        {
                                MediaJack::M_DEFAULT_WIDTH -= font.StringWidth("M");
                        }
                        MediaJack::M_DEFAULT_WIDTH -= 2.0; // substract the padding

                        // Swap the default dimensions for MediaJacks
                        temp = MediaJack::M_DEFAULT_WIDTH;
                        MediaJack::M_DEFAULT_WIDTH = MediaJack::M_DEFAULT_HEIGHT;
                        MediaJack::M_DEFAULT_HEIGHT = temp;

                        // Adjust the default size for MediaNodePanels
                        float labelWidth, bodyWidth;
                        float labelHeight, bodyHeight;
                        font_height fh;
                        be_plain_font->GetHeight(&fh);
                        labelWidth = 4 * MediaNodePanel::M_LABEL_H_MARGIN
                                                 + be_plain_font->StringWidth(" Be Audio Mixer ");
                        bodyWidth = 2 * MediaNodePanel::M_BODY_H_MARGIN + B_MINI_ICON;
                        labelHeight = 3 * MediaNodePanel::M_LABEL_V_MARGIN
                                                  + fh.ascent + fh.descent + fh.leading
                                                  + 2 * MediaJack::M_DEFAULT_HEIGHT;
                        bodyHeight = 2 * MediaNodePanel::M_BODY_V_MARGIN + B_MINI_ICON;
                        MediaNodePanel::M_DEFAULT_WIDTH = labelWidth + bodyWidth;
                        MediaNodePanel::M_DEFAULT_HEIGHT = labelHeight > bodyHeight ? labelHeight : bodyHeight;
                        Align(&MediaNodePanel::M_DEFAULT_WIDTH, &MediaNodePanel::M_DEFAULT_HEIGHT);
                        break;
                }
        }
        m_layout = layout;
        for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
        {
                MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(ItemAt(i, DiagramItem::M_BOX));
                if (panel)
                {
                        panel->layoutChanged(layout);
                }
        }

        _adjustScrollBars();
}

void MediaRoutingView::cleanUp()
{
        D_METHOD(("MediaRoutingView::cleanUp()\n"));

        SortItems(DiagramItem::M_BOX, compareID);

        // move all the panels offscreen
        for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
        {
                ItemAt(i, DiagramItem::M_BOX)->moveTo(BPoint(-200.0, -200.0));
        }

        // move all panels to their 'ideal' position
        for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
        {
                MediaNodePanel *panel;
                panel = dynamic_cast<MediaNodePanel *>(ItemAt(i, DiagramItem::M_BOX));
                BPoint p = findFreePositionFor(panel);
                panel->moveTo(p);
        }

        SortItems(DiagramItem::M_BOX, compareSelectionTime);
        Invalidate();
        updateDataRect();
}

void MediaRoutingView::showContextMenu(
        BPoint point)
{
        D_METHOD(("MediaRoutingView::showContextMenu()\n"));

        BPopUpMenu *menu = new BPopUpMenu("MediaRoutingView PopUp", false, false, B_ITEMS_IN_COLUMN);
        menu->SetFont(be_plain_font);

        // add layout options
        BMenuItem *item;
        BMessage *message = new BMessage(M_LAYOUT_CHANGED);
        message->AddInt32("layout", M_ICON_VIEW);
        menu->AddItem(item = new BMenuItem(B_TRANSLATE("Icon view"), message));
        if (m_layout == M_ICON_VIEW)
        {
                item->SetMarked(true);
        }
        message = new BMessage(M_LAYOUT_CHANGED);
        message->AddInt32("layout", M_MINI_ICON_VIEW);
        menu->AddItem(item = new BMenuItem(B_TRANSLATE("Mini icon view"), message));
        if (m_layout == M_MINI_ICON_VIEW)
        {
                item->SetMarked(true);
        }
        menu->AddSeparatorItem();

        // add 'CleanUp' command
        menu->AddItem(new BMenuItem(B_TRANSLATE("Clean up"),
                new BMessage(M_CLEANUP_REQUESTED), 'K'));

        // add 'Select All' command
        menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
                new BMessage(M_SELECT_ALL), 'A'));

        menu->SetTargetForItems(this);
        ConvertToScreen(&point);
        point -= BPoint(1.0, 1.0);
        menu->Go(point, true, true, true);
}

void MediaRoutingView::showErrorMessage(
        BString text,
        status_t error)
{
        D_METHOD(("MediaRoutingView::showErrorMessage()\n"));

        if (error) {
                text << " (" << strerror(error) << ")";
        }

        BMessage message(M_SHOW_ERROR_MESSAGE);
        message.AddString("text", text.String());
        if (error) {
                message.AddBool("error", true);
        }
        BMessenger messenger(0, Window());
        if (!messenger.IsValid()
         || (messenger.SendMessage(&message) != B_OK)) {
                BAlert *alert = new BAlert(B_TRANSLATE("Error"), text.String(),
                        B_TRANSLATE("OK"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
                alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
                alert->Go();
        }
}

// -------------------------------------------------------- //
// *** IStateArchivable
// -------------------------------------------------------- //

status_t MediaRoutingView::importState(
        const BMessage*                                         archive) {

        status_t err;

        _emptyInactiveNodeState();

        layout_t layout;
        err = archive->FindInt32("layout", (int32*)&layout);
        if(err == B_OK && layout != m_layout) {
                layoutChanged(layout);
        }

        const char* path;
        err = archive->FindString("bgBitmap", &path);
        if(err == B_OK) {
                BEntry entry(path);
                entry_ref ref;
                err = entry.GetRef(&ref);
                if(err == B_OK)
                        _changeBackground(&ref);
        }
        else {
                rgb_color color;
                color.alpha = 255;
                if(
                        archive->FindInt8("bgRed", (int8*)&color.red) == B_OK &&
                        archive->FindInt8("bgGreen", (int8*)&color.green) == B_OK &&
                        archive->FindInt8("bgBlue", (int8*)&color.blue) == B_OK)
                                _changeBackground(color);
        }

        for(int32 n = 0; ; ++n) {

                // find panel state info; stop when exhausted
                BMessage m;
                err = archive->FindMessage("panel", n, &m);
                if(err < B_OK)
                        break;

                const char* nodeName;
                err = archive->FindString("nodeName", n, &nodeName);
                if(err < B_OK)
                        break;

                uint32 nodeKind;
                err = archive->FindInt32("nodeKind", n, (int32*)&nodeKind);
                if(err < B_OK)
                        break;

                // look up matching panel +++++ SLOW +++++
                uint32 panelIndex;
                uint32 items = CountItems(DiagramItem::M_BOX);
                for(
                        panelIndex = 0;
                        panelIndex < items;
                        ++panelIndex) {

                        MediaNodePanel* panel = dynamic_cast<MediaNodePanel*>(
                                ItemAt(panelIndex, DiagramItem::M_BOX));

                        if(panel &&
                                !strcmp(panel->ref->name(), nodeName) &&
                                panel->ref->kind() == nodeKind) {

                                // found match; hand message to panel
                                panel->importState(&m);
                                break;
                        }
                }
                if(panelIndex == items) {
                        // no panel found
                        // if a "system node" hang onto (and re-export) state info
                        bool sysOwned;
                        if(m.FindBool("sysOwned", &sysOwned) == B_OK && sysOwned) {
                                m_inactiveNodeState.AddItem(
                                        new _inactive_node_state_entry(
                                                nodeName, nodeKind, m));
                        }
                }
        }

        updateDataRect();

        return B_OK;
}

// +++++ export state info for currently inactive system nodes +++++
status_t MediaRoutingView::exportState(
        BMessage*                                                                       archive) const {

        // store layout mode
        archive->AddInt32("layout", m_layout);

        // store background settings
        if(m_backgroundBitmapEntry.InitCheck() == B_OK) {
                BPath path;
                m_backgroundBitmapEntry.GetPath(&path);
                archive->AddString("bgBitmap", path.Path());
        } else {
                rgb_color c = backgroundColor();
                archive->AddInt8("bgRed", c.red);
                archive->AddInt8("bgGreen", c.green);
                archive->AddInt8("bgBlue", c.blue);
        }

        // store panel positions w/ node names & signatures
        for(uint32 n = 0; n < CountItems(DiagramItem::M_BOX); ++n) {
                MediaNodePanel* panel = dynamic_cast<MediaNodePanel*>(
                        ItemAt(n, DiagramItem::M_BOX));
                if(!panel)
                        continue;

                if(panel->ref->isInternal())
                        // skip internal nodes
                        continue;

                BMessage m;
                panel->exportState(&m);
                archive->AddString("nodeName", panel->ref->name());
                archive->AddInt32("nodeKind", panel->ref->kind());
                archive->AddMessage("panel", &m);
        }

        // copy inactive node state info
        for(int32 n = 0; n < m_inactiveNodeState.CountItems(); ++n) {
                _inactive_node_state_entry* e = reinterpret_cast<_inactive_node_state_entry*>(
                        m_inactiveNodeState.ItemAt(n));

                archive->AddString("nodeName", e->name.String());
                archive->AddInt32("nodeKind", e->kind);
                archive->AddMessage("panel", &e->state);
        }

        return B_OK;
}

// [e.moon 8dec99] subset support

status_t MediaRoutingView::importStateFor(
        const NodeSetIOContext*         context,
        const BMessage*                                         archive) {

        status_t err;

        for(int32 archiveIndex = 0;; ++archiveIndex) {

                // fetch archived key & panel data
                const char* key;
                err = archive->FindString("nodeKey", archiveIndex, &key);
                if(err < B_OK)
                        break;

                BMessage m;
                err = archive->FindMessage("panel", archiveIndex, &m);
                if(err < B_OK) {
                        PRINT((
                                "!!! MediaRoutingView::importStateFor(): missing panel %"
                                        B_PRId32 "\n", archiveIndex));
                        continue;
                }

                // find corresponding node
                media_node_id id;
                err = context->getNodeFor(key, &id);
                if(err < B_OK) {
                        PRINT((
                                "!!! MediaRoutingView::importStateFor(): missing node '%s'\n",
                                key));
                        continue;
                }

                // look for panel, create it if necessary
                MediaNodePanel* panel;
                err = _findPanelFor(id, &panel);
                if(err < B_OK) {
                        // create it
                        err = _addPanelFor(
                                id,
                                BPoint(5.0, 5.0));
                        if(err < B_OK) {
                                PRINT((
                                        "!!! MediaRoutingView::importStateFor(): _addPanelFor():\n"
                                        "    %s\n", strerror(err)));
                                continue;
                        }

                        err = _findPanelFor(id, &panel);
                        if(err < B_OK) {
                                PRINT((
                                        "!!! MediaRoutingView::importStateFor(): _findPanelFor():\n"
                                        "    %s\n", strerror(err)));
                                continue;
                        }
                }

                // pass state data along
                panel->importState(&m);

                // select the panel
                SelectItem(panel, false);
        }

        return B_OK;
}

status_t MediaRoutingView::exportStateFor(
        const NodeSetIOContext*         context,
        BMessage*                                                                       archive) const {

        status_t err;

        for(uint32 n = 0; n < context->countNodes(); ++n) {
                MediaNodePanel* panel;
                err = _findPanelFor(
                        context->nodeAt(n),
                        &panel);
                if(err < B_OK) {
                        PRINT((
                                "!!! MediaRoutingView::exportStateFor():\n"
                                "    no panel for node %" B_PRId32 "\n",
                                context->nodeAt(n)));
                        return B_BAD_VALUE;
                }

                const char* key = context->keyAt(n);

                archive->AddString("nodeKey", key);
                BMessage m;
                panel->exportState(&m);
                archive->AddMessage("panel", &m);
        }

        return B_OK;
}

// -------------------------------------------------------- //
// *** children management
// -------------------------------------------------------- //

status_t MediaRoutingView::_addPanelFor(
        media_node_id id,
        BPoint atPoint)
{
        D_METHOD(("MediaRoutingView::_addPanelFor()\n"));

        manager->lock();
        NodeRef *ref;
        status_t error = manager->getNodeRef(id, &ref);
        manager->unlock();
        if (!error)
        {
                add_observer(this, ref);
                MediaNodePanel *panel = 0;
                if (id == m_lastDroppedNode) // this was instantiated thru drag & drop
                {
                        AddItem(panel = new MediaNodePanel(m_lastDropPoint, ref));
                        SelectItem(panel, true);
                        m_lastDroppedNode = 0;
                }
                else // this was an externally created node, must find a nice position first
                {
                        panel = new MediaNodePanel(BPoint(0.0, 0.0), ref);
                        AddItem(panel);
                        BMessage state;
                        if(_fetchInactiveNodeState(panel, &state) == B_OK)
                                panel->importState(&state);
                        else {
                                BPoint p = findFreePositionFor(panel);
                                panel->moveTo(p);
                        }
                        Invalidate(panel->Frame());
                }
        }
        updateDataRect();
        return error;
}

status_t MediaRoutingView::_findPanelFor(
        media_node_id id,
        MediaNodePanel **outPanel) const
{
        D_METHOD(("MediaRoutingView::_findPanelFor()\n"));

        for (uint32 i = 0; i < CountItems(DiagramItem::M_BOX); i++)
        {
                MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(ItemAt(i, DiagramItem::M_BOX));
                if (panel)
                {
                        if (panel->ref->id() == id)
                        {
                                *outPanel = panel;
                                return B_OK;
                        }
                }
        }
        return B_ERROR;
}

status_t MediaRoutingView::_removePanelFor(
        media_node_id id)
{
        D_METHOD(("MediaRoutingView::_removePanelFor()\n"));

        MediaNodePanel *panel;
        if (_findPanelFor(id, &panel) == B_OK)
        {
                if (RemoveItem(panel))
                {
                        remove_observer(this, panel->ref);
                        Invalidate(panel->Frame());
                        delete panel;
                        return B_OK;
                }
        }
        return B_ERROR;
}

status_t MediaRoutingView::_addWireFor(
        Connection& connection)
{
        D_METHOD(("MediaRoutingView::_addWireFor()\n"));

        MediaNodePanel *source, *destination;
        if ((_findPanelFor(connection.sourceNode(), &source) == B_OK)
         && (_findPanelFor(connection.destinationNode(), &destination) == B_OK))
        {
                status_t error;

                media_output output;
                error = connection.getOutput(&output);
                if (error)
                {
                        return error;
                }
                MediaJack *outputJack = new MediaJack(output);
                source->AddItem(outputJack);

                media_input input;
                error = connection.getInput(&input);
                if (error)
                {
                        return error;
                }
                MediaJack *inputJack =  new MediaJack(input);
                destination->AddItem(inputJack);

                MediaWire *wire = new MediaWire(connection, outputJack, inputJack);
                AddItem(wire);
                source->updateIOJacks();
                source->arrangeIOJacks();
                destination->updateIOJacks();
                destination->arrangeIOJacks();
                updateDataRect();

                // [e.moon 21nov99] group creation/merging now performed by
                // RouteAppNodeManager

                Invalidate(source->Frame());
                Invalidate(destination->Frame());
                Invalidate(wire->Frame());
                return B_OK;
        }
        else
        {
                return B_ERROR;
        }
}

status_t MediaRoutingView::_findWireFor(
        uint32 connectionID,
        MediaWire **outWire) const
{
        D_METHOD(("MediaRoutingView::_findWireFor()\n"));

        for (uint32 i = 0; i < CountItems(DiagramItem::M_WIRE); i++)
        {
                MediaWire *wire = dynamic_cast<MediaWire *>(ItemAt(i, DiagramItem::M_WIRE));
                if (wire && wire->connection.id() == connectionID)
                {
                        *outWire = wire;
                        return B_OK;
                }
        }
        return B_ERROR;
}

status_t MediaRoutingView::_removeWireFor(
        uint32 connectionID)
{
        D_METHOD(("MediaRoutingView::_removeWireFor()\n"));

        MediaWire *wire;
        if (_findWireFor(connectionID, &wire) == B_OK)
        {
                MediaNodePanel *source, *destination;
                _findPanelFor(wire->connection.sourceNode(), &source);
                _findPanelFor(wire->connection.destinationNode(), &destination);
                RemoveItem(wire);
                Invalidate(wire->Frame());
                delete wire;
                if (source)
                {
                        source->updateIOJacks();
                        source->arrangeIOJacks();
                        Invalidate(source->Frame());
                }
                if (destination)
                {
                        destination->updateIOJacks();
                        destination->arrangeIOJacks();
                        Invalidate(destination->Frame());
                }

                // [e.moon 21nov99] group split/remove now performed by
                // RouteAppNodeManager

                updateDataRect();
                return B_OK;
        }
        return B_ERROR;
}

// -------------------------------------------------------- //
// *** internal methods
// -------------------------------------------------------- //

void MediaRoutingView::_addShortcuts()
{
        Window()->AddShortcut('A', B_COMMAND_KEY,
                                                  new BMessage(M_SELECT_ALL), this);
        Window()->AddShortcut('K', B_COMMAND_KEY,
                                                  new BMessage(M_CLEANUP_REQUESTED), this);
        Window()->AddShortcut('T', B_COMMAND_KEY,
                                                  new BMessage(M_DELETE_SELECTION), this);
        Window()->AddShortcut('P', B_COMMAND_KEY,
                                                  new BMessage(M_NODE_TWEAK_PARAMETERS), this);
        Window()->AddShortcut('P', B_COMMAND_KEY | B_SHIFT_KEY,
                                                  new BMessage(M_NODE_START_CONTROL_PANEL), this);
        Window()->AddShortcut('I', B_COMMAND_KEY,
                                                  new BMessage(InfoWindowManager::M_INFO_WINDOW_REQUESTED), this);
}

void MediaRoutingView::_initLayout()
{
        D_METHOD(("MediaRoutingView::_initLayout()\n"));

        BString measure;
        measure << " " << B_TRANSLATE("Be Audio Mixer") << " ";

        switch (m_layout)
        {
                case M_ICON_VIEW:
                {
                        // Adjust the jack width for displaying the abbreviated
                        // input/output name
                        BFont font(be_plain_font);
                        font.SetSize(font.Size() - 2.0);
                        for (int i = 0; i < MediaJack::M_MAX_ABBR_LENGTH; i++)
                        {
                                MediaJack::M_DEFAULT_WIDTH += font.StringWidth("M");
                        }
                        MediaJack::M_DEFAULT_WIDTH += 2.0; // add some padding

                        // Adjust the default size for MediaNodePanels to fit the
                        // size of be_plain_font
                        float labelWidth, bodyWidth;
                        float labelHeight, bodyHeight;
                        font_height fh;
                        be_plain_font->GetHeight(&fh);
                        labelWidth = 4 * MediaNodePanel::M_LABEL_H_MARGIN
                                                 + be_plain_font->StringWidth(measure.String());
                        bodyWidth = 2 * MediaNodePanel::M_BODY_H_MARGIN + B_LARGE_ICON
                                                + 2 * MediaJack::M_DEFAULT_WIDTH;
                        labelHeight = 2 * MediaNodePanel::M_LABEL_V_MARGIN
                                                  + fh.ascent + fh.descent + fh.leading + 1.0;
                        bodyHeight = 2 * MediaNodePanel::M_BODY_V_MARGIN + B_LARGE_ICON;
                        MediaNodePanel::M_DEFAULT_WIDTH = labelWidth > bodyWidth ? labelWidth : bodyWidth;
                        MediaNodePanel::M_DEFAULT_HEIGHT = labelHeight + bodyHeight;
                        Align(&MediaNodePanel::M_DEFAULT_WIDTH, &MediaNodePanel::M_DEFAULT_HEIGHT);

                        // Adjust the cleanup settings
                        M_CLEANUP_H_GAP += MediaNodePanel::M_DEFAULT_WIDTH;
                        break;
                }
                case M_MINI_ICON_VIEW:
                {
                        // Adjust the default size for MediaNodePanels to fit the
                        // size of be_plain_font
                        float labelWidth, bodyWidth;
                        float labelHeight, bodyHeight;
                        font_height fh;
                        be_plain_font->GetHeight(&fh);
                        labelWidth = 4 * MediaNodePanel::M_LABEL_H_MARGIN
                                                 + be_plain_font->StringWidth(measure.String());
                        bodyWidth = 2 * MediaNodePanel::M_BODY_H_MARGIN + B_MINI_ICON;
                        labelHeight = 3 * MediaNodePanel::M_LABEL_V_MARGIN
                                                  + fh.ascent + fh.descent + fh.leading
                                                  + 2 * MediaJack::M_DEFAULT_HEIGHT;
                        bodyHeight = 2 * MediaNodePanel::M_BODY_V_MARGIN + B_MINI_ICON;
                        MediaNodePanel::M_DEFAULT_WIDTH = labelWidth + bodyWidth;
                        MediaNodePanel::M_DEFAULT_HEIGHT = labelHeight > bodyHeight ? labelHeight : bodyHeight;
                        Align(&MediaNodePanel::M_DEFAULT_WIDTH, &MediaNodePanel::M_DEFAULT_HEIGHT);

                        // Adjust the cleanup settings
                        M_CLEANUP_V_GAP += MediaNodePanel::M_DEFAULT_HEIGHT;
                        break;
                }
        }
}

void MediaRoutingView::_initContent()
{
        D_METHOD(("MediaRoutingView::_initContent()\n"));

        Autolock lock(manager);

        void *cookie = 0;
        NodeRef *ref;
        while (manager->getNextRef(&ref, &cookie) == B_OK)
        {
                // add self as observer
                add_observer(this, ref);
                // create & place node view (+++++ defer until observer status confirmed!)
                _addPanelFor(ref->id(), BPoint(M_CLEANUP_H_MARGIN, M_CLEANUP_V_MARGIN));
        }
        cookie = 0;
        Connection connection;
        while (manager->getNextConnection(&connection, &cookie) == B_OK)
        {
                _addWireFor(connection);
        }

        // create default groups
        NodeGroup* group;
        NodeRef* videoIn = manager->videoInputNode();
        if (videoIn)
        {
                group = manager->createGroup(B_TRANSLATE("Video input"));
                group->setRunMode(BMediaNode::B_RECORDING);
                group->addNode(videoIn);
        }
        NodeRef* audioIn = manager->audioInputNode();
        if (audioIn)
        {
                group = manager->createGroup(B_TRANSLATE("Audio input"));
                group->setRunMode(BMediaNode::B_RECORDING);
                group->addNode(audioIn);
        }
        NodeRef* videoOut = manager->videoOutputNode();
        if (videoOut)
        {
                group = manager->createGroup(B_TRANSLATE("Video output"));
                group->addNode(videoOut);
        }
}

void MediaRoutingView::_changeCyclingForSelection(
        bool cycle)
{
        D_METHOD(("MediaRoutingView::_changeCyclingForSelection()\n"));

        if (SelectedType() == DiagramItem::M_BOX)
        {
                manager->lock();
                for (uint32 i = 0; i < CountSelectedItems(); i++)
                {
                        MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
                        if (panel && (panel->ref->isCycling() != cycle))
                        {
                                panel->ref->setCycling(cycle);
                        }
                }
                manager->unlock();
        }
}

void MediaRoutingView::_changeRunModeForSelection(
        uint32 mode)
{
        D_METHOD(("MediaRoutingView::_changeRunModeForSelection()\n"));

        if (SelectedType() == DiagramItem::M_BOX)
        {
                manager->lock();
                for (uint32 i = 0; i < CountSelectedItems(); i++)
                {
                        MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
                        if (panel && (panel->ref->runMode() != mode))
                        {
                                panel->ref->setRunMode(mode);
                        }
                }
                manager->unlock();
        }
}

void MediaRoutingView::_openInfoWindowsForSelection() {
        D_METHOD(("MediaRoutingView::_openInfoWindowsForSelection()\n"));

        InfoWindowManager *manager = InfoWindowManager::Instance();
        if (!manager) {
                return;
        }

        if (SelectedType() == DiagramItem::M_BOX) {
                for (uint32 i = 0; i < CountSelectedItems(); i++) {
                        MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
                        if (panel && manager->Lock()) {
                                manager->openWindowFor(panel->ref);
                                manager->Unlock();
                        }
                }
        }
        else if (SelectedType() == DiagramItem::M_WIRE) {
                for (uint32 i = 0; i < CountSelectedItems(); i++) {
                        MediaWire *wire = dynamic_cast<MediaWire *>(SelectedItemAt(i));
                        if (wire && manager->Lock()) {
                                manager->openWindowFor(wire->connection);
                                manager->Unlock();
                        }
                }
        }
}

void MediaRoutingView::_openParameterWindowsForSelection() {
        D_METHOD(("MediaRoutingView::_openParameterWindowsForSelection()\n"));

        if (SelectedType() != DiagramItem::M_BOX) {
                // can only open parameter window for nodes
                return;
        }

        for (uint32 i = 0; i < CountSelectedItems(); i++) {
                MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
                if (panel && (panel->ref->kind() & B_CONTROLLABLE)) {
                        ParameterWindowManager *paramMgr= ParameterWindowManager::Instance();
                        if (paramMgr && paramMgr->Lock()) {
                                paramMgr->openWindowFor(panel->ref);
                                paramMgr->Unlock();
                        }
                }
        }
}

void MediaRoutingView::_startControlPanelsForSelection() {
        D_METHOD(("MediaRoutingView::_startControlPanelsForSelection()\n"));

        if (SelectedType() != DiagramItem::M_BOX) {
                // can only start control panel for nodes
                return;
        }

        for (uint32 i = 0; i < CountSelectedItems(); i++) {
                MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
                if (panel && (panel->ref->kind() & B_CONTROLLABLE)) {
                        ParameterWindowManager *paramMgr= ParameterWindowManager::Instance();
                        if (paramMgr && paramMgr->Lock()) {
                                paramMgr->startControlPanelFor(panel->ref);
                                paramMgr->Unlock();
                        }
                }
        }
}

void MediaRoutingView::_deleteSelection()
{
        D_METHOD(("MediaRoutingView::_deleteSelection()\n"));
        if (SelectedType() == DiagramItem::M_BOX)
        {
                for (uint32 i = 0; i < CountSelectedItems(); i++)
                {
                        MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
                        if (panel && panel->ref->isInternal())
                        {
                                status_t error = panel->ref->releaseNode();
                                if (error)
                                {
                                        BString s = B_TRANSLATE("Could not release '%refname%'");
                                        s.ReplaceFirst("%rename%", panel->ref->name());
                                        showErrorMessage(s, error);
                                }
                        }
                }
        }
        else if (SelectedType() == DiagramItem::M_WIRE)
        {
                for (uint32 i = 0; i < CountSelectedItems(); i++)
                {
                        MediaWire *wire = dynamic_cast<MediaWire *>(SelectedItemAt(i));
                        if (wire && !(wire->connection.flags() & Connection::LOCKED))
                        {
                                status_t error = manager->disconnect(wire->connection);
                                if (error)
                                {
                                        showErrorMessage(
                                                B_TRANSLATE("Could not disconnect"), error);
                                }
                        }
                }
        }
        // make sure none of the deleted items is still displaying its mouse cursor !
        be_app->SetCursor(B_HAND_CURSOR);
}

void MediaRoutingView::_checkDroppedFile(
        entry_ref *ref,
        BPoint dropPoint)
{
        D_METHOD(("MediaRoutingView::_checkDroppedFile()\n"));

        // [cell 26apr00] traverse links
        BEntry entry(ref, true);
        entry.GetRef(ref);

        BNode node(ref);
        if (node.InitCheck() == B_OK)
        {
                BNodeInfo nodeInfo(&node);
                if (nodeInfo.InitCheck() == B_OK)
                {
                        char mimeString[B_MIME_TYPE_LENGTH];
                        if (nodeInfo.GetType(mimeString) == B_OK)
                        {
                                BMimeType mimeType(mimeString);
                                BMimeType superType;

                                // [e.moon 22dec99] handle dropped node-set files
                                if(mimeType == RouteApp::s_nodeSetType) {
                                        BMessage m(B_REFS_RECEIVED);
                                        m.AddRef("refs", ref);
                                        be_app_messenger.SendMessage(&m);
                                }
                                else if (mimeType.GetSupertype(&superType) == B_OK)
                                {
                                        if (superType == "image")
                                        {
                                                _changeBackground(ref);
                                        }
                                        else if ((superType == "audio") || (superType == "video"))
                                        {
                                                NodeRef* droppedNode;
                                                status_t error;
                                                error = manager->instantiate(*ref, B_BUFFER_PRODUCER, &droppedNode);
                                                if (!error)
                                                {
                                                        media_output encVideoOutput;
                                                        if (droppedNode->findFreeOutput(&encVideoOutput, B_MEDIA_ENCODED_VIDEO) == B_OK)
                                                        {
                                                                droppedNode->setFlags(droppedNode->flags() | NodeRef::NO_POSITION_REPORTING);
                                                        }
                                                        m_lastDroppedNode = droppedNode->id();
                                                        m_lastDropPoint = Align(dropPoint);
                                                }
                                                else
                                                {
                                                        char fileName[B_FILE_NAME_LENGTH];
                                                        BEntry entry(ref);
                                                        entry.GetName(fileName);
                                                        BString s = B_TRANSLATE(
                                                                "Could not load '%filename%'");
                                                        s.ReplaceFirst("%filename%", fileName);
                                                        showErrorMessage(s, error);
                                                }
                                        }
                                }
                        }
                }
        }
}

void MediaRoutingView::_changeBackground(
        entry_ref *ref)
{
        D_METHOD(("MediaRoutingView::_changeBackground()\n"));

        status_t error;
        BBitmap *background = 0;
        BFile file(ref, B_READ_ONLY);
        error = file.InitCheck();
        if (!error)
        {
                BTranslatorRoster *roster = BTranslatorRoster::Default();
                BBitmapStream stream;
                error = roster->Translate(&file, NULL, NULL, &stream, B_TRANSLATOR_BITMAP);
                if (!error)
                {
                        stream.DetachBitmap(&background);
                        setBackgroundBitmap(background);
                        Invalidate();

                        // [e.moon 1dec99] persistence, yay
                        m_backgroundBitmapEntry.SetTo(ref);
                }
        }
        delete background;
}

void MediaRoutingView::_changeBackground(
        rgb_color color)
{
        D_METHOD(("MediaRoutingView::_changeBackground()\n"));
        setBackgroundColor(color);
        Invalidate();

        // [e.moon 1dec99] persistence, yay
        m_backgroundBitmapEntry.Unset();
}

void
MediaRoutingView::_adjustScrollBars()
{
        D_METHOD(("MediaRoutingView::_adjustScrollBars()\n"));

        BScrollBar *scrollBar;

        // adjust horizontal scroll bar
        scrollBar = ScrollBar(B_HORIZONTAL);
        if (scrollBar) {
                float bigStep = floor(MediaNodePanel::M_DEFAULT_WIDTH + M_CLEANUP_H_GAP);
                scrollBar->SetSteps(floor(bigStep / 10.0), bigStep);
        }

        // adjust vertical scroll bar
        scrollBar = ScrollBar(B_VERTICAL);
        if (scrollBar) {
                float bigStep = floor(MediaNodePanel::M_DEFAULT_HEIGHT + M_CLEANUP_V_GAP);
                scrollBar->SetSteps(floor(bigStep / 10.0), bigStep);
        }
}

void
MediaRoutingView::_broadcastSelection() const
{
        int32 selectedGroup = 0;

        if (SelectedType() == DiagramItem::M_BOX) {
                // iterate thru the list of selected node panels and make the
                // first group we find the selected group
                for (uint32 i = 0; i < CountSelectedItems(); i++) {
                        MediaNodePanel *panel = dynamic_cast<MediaNodePanel *>(SelectedItemAt(i));
                        if (panel && panel->ref->group()) {
                                selectedGroup = panel->ref->group()->id();
                                BMessenger messenger(Window());
                                BMessage groupMsg(M_GROUP_SELECTED);
                                groupMsg.AddInt32("groupID", selectedGroup);
                                messenger.SendMessage(&groupMsg);
                                return;
                        }
                }
        }

        // currently no group is selected
        BMessenger messenger(Window());
        BMessage groupMsg(M_GROUP_SELECTED);
        groupMsg.AddInt32("groupID", selectedGroup);
        messenger.SendMessage(&groupMsg);
}

status_t
MediaRoutingView::_fetchInactiveNodeState(MediaNodePanel *forPanel, BMessage *outMessage)
{
        // copy inactive node state info
        int32 c = m_inactiveNodeState.CountItems();
        for(int32 n = 0; n < c; n++) {
                _inactive_node_state_entry* e = reinterpret_cast<_inactive_node_state_entry*>(
                        m_inactiveNodeState.ItemAt(n));
                ASSERT(e);
                if(e->name != forPanel->ref->name())
                        continue;

                if(e->kind != forPanel->ref->kind())
                        continue;

                // found match; extract message & remove entry
                *outMessage = e->state;
                m_inactiveNodeState.RemoveItem(n);
                return B_OK;
        }

        return B_BAD_VALUE;
}

void
MediaRoutingView::_emptyInactiveNodeState()
{
        int32 c = m_inactiveNodeState.CountItems();
        for(int32 n = 0; n < c; n++) {
                _inactive_node_state_entry* e = reinterpret_cast<_inactive_node_state_entry*>(
                        m_inactiveNodeState.ItemAt(n));
                ASSERT(e);
                delete e;
        }
        m_inactiveNodeState.MakeEmpty();
}


// END -- MediaRoutingView.cpp --