/*
 * SceneGraphView.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include "stdafx.h"

#include "SceneGraphView.h"
#include "Scene.h"
#include "Node.h"
#include "Proto.h"
#include "Path.h"
#include "FieldValue.h"
#include "Field.h"
#include "EventIn.h"
#include "EventOut.h"
#include "SFNode.h"
#include "MFNode.h"
#include "MoveCommand.h"
#include "RouteCommand.h"
#include "UnRouteCommand.h"
#include "CommandList.h"
#include "resource.h"
#include "Util.h"

#include "IconSize.h"

#define SOCKET_WIDTH 6
#define SOCKET_HEIGHT 6
#define SOCKET_SPACING 2

#define BORDER_WIDTH 2 
//#define ICON_SIZE_X 16
//#define ICON_SIZE_Y 15
#define NODE_WIDTH 190

#define ADD_WIDTH 8
#define ADD_HEIGHT 4

#ifdef _WIN32
// near windows canvas limit 
# define MAX_X 8000
# define MAX_Y 8000
#else
// near motif canvas limit
# define MAX_X 32000
# define MAX_Y 32000
#endif

#define MINIMAL_SIZE (2 * BORDER_WIDTH + 1 + ICON_SIZE_X)   

// position from left of node to icon
#define ICON_OFFSET (BORDER_WIDTH + SOCKET_WIDTH + ICON_SIZE_X)   

// position from left of node to text
#define TEXT_OFFSET (ICON_OFFSET + ICON_SIZE_X + 4)   

#define LINE_STUB (SOCKET_WIDTH + BORDER_WIDTH + 2)

#define SHADOW_OFFSET 1

#define AUTOSCROLL_MARGIN 10
#define AUTOSCROLL_AMOUNT 20

static int currentX, currentY;

// maximum size for drawing to bitmap
#define MAX_BACK_WIDTH 500
#define MAX_BACK_HEIGHT 500

#define MoveTo(dc, x, y) { currentX = x; currentY = y; }
#define LineTo(dc, x, y) { swDrawLine(dc, currentX, currentY, x, y); \
                           currentX = x; currentY = y; }
#define InvalidateTo(wnd, x, y) { int minX = MIN(currentX, x); \
                                  int minY = MIN(currentY, y); \
                                  int maxX = MAX(currentX, x); \
                                  int maxY = MAX(currentY, y); \
swInvalidateRect(wnd, minX, minY, maxX - minX + 2, maxY - minY + 2); \
                   currentX = x; currentY = y; }

static void
expose(void *data, int x, int y, int width, int height)
{
    ((SceneGraphView *) data)->OnDraw(x, y, width, height);
}

static void
keyCB(void *data, int key, int value, int x, int y, int modifiers)
{
    if (key == SW_MOUSE1) {
        if (value) {
            ((SceneView *) data)->OnLButtonDown(x, y, modifiers);
        } else {
            ((SceneView *) data)->OnLButtonUp(x, y, modifiers);
        }
    } else if (value) {
        ((SceneView *) data)->OnKeyDown(key, x, y, modifiers);
    } else {
        ((SceneView *) data)->OnKeyUp(key, x, y, modifiers);
    }
}

static void
mouseCB(void *data, int x, int y, int modifiers)
{
    ((SceneView *) data)->OnMouseMove(x, y, modifiers);
}

static void
mouseEnterCB(void *data, int value)
{
    if (value) {
        ((SceneView *) data)->OnMouseEnter();
    } else {
        ((SceneView *) data)->OnMouseLeave();
    }
}

SceneGraphView::SceneGraphView(Scene *scene, SWND parent)
  : SceneView(scene, parent)
{
    int width, height;
    swGetSize(parent, &width, &height);

    _lastXPosition = 0;
    _lastYPosition = 0;
    _maxYPosition = 0;
    _mode = NONE;
    _dstNode = NULL;
    _dstSocket = -1;
    _dstSide = 0;
    _srcNode = NULL;
    _srcSocket = -1;
    _srcSide = 0;
    _scroller = swCreateScrolledWindow(0, 0, width, height, parent);
    _window = swCreateCanvas("", 0, 0, width, height, _scroller);
    swScrolledWindowSetChild(_scroller, _window);
    swSetExposeCallback(_window, expose);
    swSetKeyCallback(_window, keyCB);
    swSetMouseCallback(_window, mouseCB);
    swSetEnterCallback(_window, mouseEnterCB);
    swSetClientData(_window, this);
    _face = swGetWindowColor(_window, SW_COLOR_FACE);
    _highlight = swGetWindowColor(_window, SW_COLOR_TSHADOW);
    _shadow = swGetWindowColor(_window, SW_COLOR_BSHADOW);
    int mapFrom[4] = { 0x808000, 0x000000, 0xC0C0C0, 0x808080 };
    int mapTo[4] = { _face, _shadow, _highlight, _face };
    int idb_node_icons = IDB_NODE_ICONS;
    if (TheApp->getBlackAndWhiteIcons())
        idb_node_icons = IDB_NODE_ICONS_BW;
    _nodeBitmap = swLoadMappedBitmap(_window, idb_node_icons, mapFrom, mapTo, 1);
    _socketBitmap = swLoadMappedBitmap(_window, IDB_EMPTY_SOCKET, mapFrom, mapTo, 4); 
    _socketBitmapRecommended = swLoadMappedBitmap(_window, IDB_RECOMMENDED_SOCKET, mapFrom, mapTo, 4); 
    BuildSocketBitmaps();
    _autoScrolling = false;
    _zoom = 1.0;
    _floodedWarning = false;

    _XNodesOnlyEventOuts = ADD_WIDTH;
    _YNodesOnlyEventOuts = ADD_HEIGHT;

    _XNodesBothEvents = NODE_WIDTH + 2 * ADD_WIDTH;  
    _YNodesBothEvents = ADD_HEIGHT;

    _XNodesOnlyEventIns = 2 * NODE_WIDTH + 3 * ADD_WIDTH;  
    _YNodesOnlyEventIns = ADD_HEIGHT;
}

SceneGraphView::~SceneGraphView()
{
    for (int i = 0; i < 20; i++) {
        swDestroyBitmap(_socketBitmaps[i]);
    }
    swDestroyBitmap(_nodeBitmap);
    swDestroyBitmap(_socketBitmap);
    swDestroyWindow(_window);
    swDestroyWindow(_scroller);
}

void SceneGraphView::Initialize()
{
    const NodeList *nodes = _scene->getNodes();

    _XNodesOnlyEventOuts = ADD_WIDTH;
    _YNodesOnlyEventOuts = ADD_HEIGHT;

    _XNodesBothEvents = NODE_WIDTH + 2 * ADD_WIDTH;  
    _YNodesBothEvents = ADD_HEIGHT;

    _XNodesOnlyEventIns = 2 * NODE_WIDTH + 3 * ADD_WIDTH;  
    _YNodesOnlyEventIns = ADD_HEIGHT;

    _lastXPosition = _lastYPosition = 0;

    int i;
    for (i = 0; i < nodes->size(); i++) {
        Node *node = nodes->get(i);
        if (node->isInScene(_scene) && node != _scene->getRoot())
            setZoomGraphPosition(node, 0, 0);
            accountGraphSize(node);
    }
    if (TheApp->GetShowRoutesAtBegin()) {
        for (i = 0; i < nodes->size(); i++) {
            Node *node = nodes->get(i);
            if (node->isInScene(_scene) && node != _scene->getRoot()) {
                int width, height;
                getZoomGraphSize(width, height, node);
                if (node->hasOutputs() && !node->hasInputs()) {
                    setZoomGraphPosition(node, 
                                         _XNodesOnlyEventOuts, 
                                         _YNodesOnlyEventOuts);
                    _YNodesOnlyEventOuts += height + ADD_HEIGHT;
                    _XNodesOnlyEventOuts += TheApp->GetRoutedNodeIndent();
                    _XNodesBothEvents += TheApp->GetRoutedNodeIndent();
                    _XNodesOnlyEventIns += TheApp->GetRoutedNodeIndent();
                }
            }
        }
        for (i = 0; i < nodes->size(); i++) {
            Node *node = nodes->get(i);
            if (node->isInScene(_scene) && node != _scene->getRoot()) {
                int width, height;
                getZoomGraphSize(width, height, node);
                if (node->hasOutputs() && node->hasInputs()) {
                    setZoomGraphPosition(node, 
                                         _XNodesBothEvents, _YNodesBothEvents);
                    _YNodesBothEvents += height + ADD_HEIGHT;
                    _XNodesBothEvents += TheApp->GetRoutedNodeIndent();
                    _XNodesOnlyEventIns += TheApp->GetRoutedNodeIndent();
                }
            }
        }
        for (i = 0; i < nodes->size(); i++) {
            Node *node = nodes->get(i);
            if (node->isInScene(_scene) && node != _scene->getRoot()) {
                int width, height;
                getZoomGraphSize(width, height, node);
                if (node->hasInputs() && !node->hasOutputs()) {
                    setZoomGraphPosition(node, 
                                         _XNodesOnlyEventIns,
                                         _YNodesOnlyEventIns);
                    _YNodesOnlyEventIns += height + ADD_HEIGHT;
                }
            }
        }
        _lastYPosition = getFirstYPosition();
    }

    for (i = 0; i < nodes->size(); i++) {
        Node *node = nodes->get(i);
        if (node->isInScene(_scene) && node != _scene->getRoot()) {
            Position(node);
        }
    }
    if (!TheApp->GetRouteViewShowAll())
        _maxYPosition = GetYEndRouteBlock();
    swInvalidateWindow(_window);
    SetScrollSizes(_lastXPosition, _maxYPosition);
    swSetScrollPosition(_scroller, 0, 0);
}

void SceneGraphView::moveRoutesToTop(Node *moveNode)
{
    int i;

    int eventIns = moveNode->getProto()->getNumEventIns();
    int eventOuts = moveNode->getProto()->getNumEventOuts();

    NodeList movingNodes;
  
    // collect nodes of eventOuts 
    for (i = 0; i < eventOuts; i++) {
        for (SocketList::Iterator *j = moveNode->getOutput(i).first(); 
             j != NULL; j = j->next()) {
            Node *outputNode = j->item().getNode();
            bool isInList= false;
            for (int n = 0; n < movingNodes.size(); n++)
                if (outputNode == movingNodes[n]) {
                    isInList = true;
                    break;
                }    
            if (!isInList)
                movingNodes.append(outputNode);
        }
    }
    movingNodes.append(moveNode);
    // collect nodes of eventIns 
    for (i = 0; i < eventIns; i++) {
        for (SocketList::Iterator *j = moveNode->getInput(i).first(); j != NULL;
             j = j->next()) {
            Node *inputNode = j->item().getNode();
            bool isInList= false;
            for (int n = 0; n < movingNodes.size(); n++)
                if (inputNode == movingNodes[n]) {
                    isInList = true;
                    break;
                }    
            if (!isInList)
                movingNodes.append(inputNode);
        }
    }

    for (i = 0; i < movingNodes.size(); i++) {
        int x;
        int y;
        getZoomGraphPosition(x, y, movingNodes[i]);
        moveToTop(movingNodes[i], x);
    }

    SetScrollSizes(_lastXPosition, _maxYPosition);
    swSetScrollPosition(_scroller, 0, 0);
    swInvalidateWindow(_window);
}

void SceneGraphView::moveToTop(Node *moveNode, Node* ignoreNode)
{
    moveToTop(moveNode, ADD_WIDTH, ignoreNode);

    SetScrollSizes(_lastXPosition, _maxYPosition);
    swSetScrollPosition(_scroller, 0, 0);
    swInvalidateWindow(_window);
}

void SceneGraphView::moveToTop(Node *moveNode, int x, Node* ignoreNode)
{
    const NodeList *nodes = _scene->getNodes();

    if (moveNode->isInScene(_scene) && moveNode != _scene->getRoot()) {
        setZoomGraphPosition(moveNode, x, ADD_HEIGHT);
        accountGraphSize(moveNode);
    } else
        return;

    int moveWidth, moveHeight;
    getZoomGraphSize(moveWidth, moveHeight, moveNode);

    _YNodesOnlyEventIns += moveHeight;

    for (int i = 0; i < nodes->size(); i++) {
        Node *node = nodes->get(i);
        if (node->isInScene(_scene) && (node != _scene->getRoot()) &&
            (node != moveNode)) {
            int width, height;
            getZoomGraphPosition(width, height, node);
            setZoomGraphPosition(node, width, height + moveHeight + ADD_HEIGHT);
            Position(node);

            getZoomGraphPosition(width, height, node);
            if (node != ignoreNode) {
                bool hasInputs = node->hasInputs();
                bool hasOutputs = node->hasOutputs();
                if (hasOutputs && !hasInputs)
                    if (_YNodesOnlyEventOuts < height)
                        _YNodesOnlyEventOuts = height;
                if (hasOutputs && hasInputs)
                    if (_YNodesBothEvents < height)
                        _YNodesBothEvents = height;
                if (!hasOutputs && hasInputs)
                    if (_YNodesOnlyEventIns < height)
                        _YNodesOnlyEventIns = height;
            }
            if ((GetYEndRouteBlock() + moveHeight) > _maxYPosition)
                _maxYPosition = GetYEndRouteBlock() + moveHeight;
        }
    }
    if (!TheApp->GetRouteViewShowAll())
        _maxYPosition = MAX(_maxYPosition, getFirstYPosition());
}

void SceneGraphView::OnDraw(int x, int y, int width, int height)
{
    if (!swIsVisible(_wnd))
        return;

    SDC dc, frontDC;

    const Path *sel = _scene->getSelection();
    Node *current = sel ? sel->getNode() : NULL;

    if (width == 0 || height == 0) {
        swGetSize(_window, &width, &height);
    }

    frontDC = swCreateDC(_window);
    dc = swCreateBitmapDC(frontDC, x + width, y + height);

    if (dc == NULL) {
        swDestroyDC(frontDC);
        return;
    }

    swSetFGColor(dc, _shadow);
    swFillRect(dc, x, y, width, height);

    int i;

    const NodeList *nodes = _scene->getNodes();

    for (i = 0; i < nodes->size(); i++) {
        Node *node = nodes->get(i);
        if (node->isInScene(_scene)) {
            int nx, ny, nw, nh;
            getZoomGraphPosition(nx, ny, node);
            getZoomGraphSize(nw, nh, node);
            if (node != current
                && node != _scene->getRoot()
                && nx + nw >= x && nx <= x + width
                && ny + nh >= y && ny <= y + height) {
                if (nx > MAX_X) {
                    warnFlooded();
                } else                   
                    if (TheApp->GetRouteViewShowAll() || 
                        (ny <= GetYEndRouteBlock()))
                    DrawNode(dc, node, nx, ny);
            }
        }
    }

    if (current) {
        int nx, ny, nw, nh;
        getZoomGraphPosition(nx, ny, current);
        getZoomGraphSize(nw, nh, current);
        if (current->isInScene(_scene)
            && current != _scene->getRoot()
            && nx + nw >= x && nx <= x + width
            && ny + nh >= y && ny <= y + height) {
            if (TheApp->GetRouteViewShowAll() || (ny <= GetYEndRouteBlock()))
                DrawNode(dc, current, nx, ny);
        }
    }

    for (i = 0; i < nodes->size(); i++) {
        Node *node = nodes->get(i);

        if (node->isInScene(_scene)) {
            DrawRoutes(dc, node);
        }
    }
    
    int cindex;

    if (_mode == ROUTING) {
        Proto *proto = _srcNode->getProto();
        if (_srcSide == 1) {
            if ((_srcNode->getType() == VRML_SCRIPT) &&
                (proto->getNumEventOuts() == _srcSocket)) {
                cindex = LAST_TYPE + 1;
                if ((_dstNode != NULL) && (_srcNode != _dstNode)) {
                    Proto *dstProto = _dstNode->getProto();
                    if ((_dstSocket > 0) 
                        && (_dstSocket < dstProto->getNumEventIns()))
                        cindex = dstProto->getEventIn(_dstSocket)->getType();
                }
            } else
                cindex = proto->getEventOut(_srcSocket)->getType();
        } else {
            if ((_srcNode->getType() == VRML_SCRIPT) &&
                (proto->getNumEventIns() == _srcSocket)) {
                cindex = LAST_TYPE + 1;
                if ((_dstNode != NULL) && (_srcNode != _dstNode)) {
                    Proto *dstProto = _dstNode->getProto();
                    if ((_dstSocket > 0) 
                        && (_dstSocket < dstProto->getNumEventOuts()))
                        cindex = dstProto->getEventOut(_dstSocket)->getType();
                }
            } else
                cindex = proto->getEventIn(_srcSocket)->getType();
        }

        DrawRoute(dc, 
            GetSocketPosition(_srcNode, _srcSocket, _srcSide),
             _point1, _typeColors[cindex] );
    } else if (_mode == CUTTING) {
        swSetFGColor(dc, 0x0000ff);
        swDrawLine(dc, _point1.x, _point1.y, _point2.x, _point2.y);
    }

    swCopyRect(dc, frontDC, x, y, x, y, width, height);
    swDestroyDC(frontDC);
    swDestroyDC(dc);
}

void SceneGraphView::OnSize(int width, int height)
{
    SetSize(width, height);
    SetScrollSizes(_lastXPosition, _maxYPosition);
}

void SceneGraphView::OnMouseLeave()
{
#ifdef HAVE_OLD_MOTIF
//
// don't ask me, why some versions of lesstif/openmotif see
// highlighting of a node socket in the route view as a leaving of window...
//
    if (_dstNode)
    {
        InvalidateNode(_dstNode);
        _dstNode = NULL;
        _dstSocket = -1;
    }
#endif
}

void SceneGraphView::YPosition(int width, int height)
{
    if ((_lastYPosition + height + ADD_HEIGHT) < MAX_Y) {
        setLastYPosition(_lastYPosition + height + ADD_HEIGHT);
        if (_maxYPosition < MAX_Y)
           _maxYPosition = _lastYPosition;
    } else {
        setLastYPosition(height + ADD_HEIGHT + getFirstYPosition());
        setLastXPosition(_lastXPosition + width + ADD_WIDTH); 
        _maxYPosition = MAX_Y;
    }
}

void SceneGraphView::accountGraphSize(Node *node)
{
    int height = 0;
    int eventIns = node->getProto()->getNumEventIns();
    int eventOuts = node->getProto()->getNumEventOuts();
    int sockets = MAX(eventIns, eventOuts);
    if (node->getType() == VRML_SCRIPT)
         sockets++;
    height = sockets * SOCKET_HEIGHT + 
             (sockets + 1) * SOCKET_SPACING + BORDER_WIDTH * 2;
    height = MAX(height, ICON_SIZE_Y + BORDER_WIDTH * 6);

    setGraphSize(node, NODE_WIDTH, height);
}

void SceneGraphView::accountGraphPosition(Node *node)
{
    int width, height;
    getZoomGraphSize(width, height, node);
    width = NODE_WIDTH + LINE_STUB;
    setLastXPosition(MAX(_lastXPosition, ADD_WIDTH + width));
    YPosition(width, height);
    setZoomGraphPosition(node, _lastXPosition - width , 
                               _lastYPosition - height);
}

void SceneGraphView::Position(Node *node)
{
    int width, height;
    getZoomGraphPosition(width, height, node);
    if (height == 0) {
        accountGraphSize(node);
        accountGraphPosition(node);
    }
}

void SceneGraphView::DrawNode(SDC dc, Node *node, int xPos, int yPos)
{
    int eventIns = node->getProto()->getNumEventIns();
    int eventOuts = node->getProto()->getNumEventOuts();
    int width, height;

    getZoomGraphSize(width, height, node);

    int i;
    int y;

    SBITMAP sbitmap;

    for (i = 0; i < BORDER_WIDTH; i++) {
        swDraw3DRect(dc, _window, xPos + i, yPos + i,
                     width - i * 2, height - i * 2);
    }

    const Path *sel = _scene->getSelection();
    Node *current = sel ? sel->getNode() : NULL;
    if (current == node)
        swSetFGColor(dc, 0xFF0000);
    else
        swSetFGColor(dc, _face);
    int rectY = yPos + BORDER_WIDTH; 
    int rectHeight = height - BORDER_WIDTH * 2;
    swFillRect(dc, xPos + BORDER_WIDTH, rectY,
               width - BORDER_WIDTH * 2, rectHeight);

    if (height > ICON_SIZE_Y) {
        swSetFGColor(dc, _face);
        swFillRect(dc, xPos + ICON_OFFSET - BORDER_WIDTH, 
                   yPos + height / 2 - 8 - BORDER_WIDTH,
                   ICON_SIZE_X + BORDER_WIDTH * 2, 
                   ICON_SIZE_Y + BORDER_WIDTH * 2);
        swDrawBitmap(dc, _nodeBitmap, node->getType() * ICON_SIZE_X, 0,
                     xPos + ICON_OFFSET, yPos + height / 2 - 8, 
                     ICON_SIZE_X, ICON_SIZE_Y);
    }

    if (_zoom != 1.0)
        return;

    if (current == node) {
        swSetFGColor(dc, _face);
        int rectWidth = SOCKET_WIDTH + 2 * SOCKET_SPACING;
        swFillRect(dc, xPos + BORDER_WIDTH, rectY, rectWidth, rectHeight);
        swFillRect(dc, xPos + width - rectWidth - BORDER_WIDTH, rectY,
                   rectWidth, rectHeight);
    }
    bool x3d = _scene->isX3d();
    MyString name;
    name = node->getName();
    if (name[0] == 0)
       name = node->getProto()->getName(x3d);
    if (current == node)
        swSetFGColor(dc, _face);
    else
        swSetFGColor(dc, 0x000000);
    swDrawText(dc, xPos + TEXT_OFFSET, yPos + height / 2 + 6, name);

    y = yPos + BORDER_WIDTH + SOCKET_SPACING;
    for (i = 0; i < eventIns; i++) {
        EventIn *eventIn = node->getProto()->getEventIn(i);
        if (_scene->isInvalidElement(eventIn)) {
            y += SOCKET_HEIGHT + SOCKET_SPACING;
            continue;
        }
        SBITMAP selectedSocketBitmap = _socketBitmaps[eventIn->getType()];
        const char *name = eventIn->getName(node->getScene()->isX3d());
        bool drawName = false;
        if (node == _dstNode && _dstSide == 0 && _dstSocket == i && 
            (_mode != ROUTING || _srcSide == 1 && 
             Scene::validRoute(_srcNode, _srcSocket, _dstNode, _dstSocket))) {
            sbitmap = selectedSocketBitmap;
            drawName = true;
        } else if (_mode == ROUTING && node == _srcNode && _srcSide == 0 
                   && _srcSocket == i) {
            sbitmap = selectedSocketBitmap;
            drawName = true;
        } else if (node->getInput(i).size() > 0) {
            sbitmap = selectedSocketBitmap;
        } else if (eventIn->getFlags() & EIF_RECOMMENDED) {
            sbitmap = _socketBitmapRecommended;
        } else {
            sbitmap = _socketBitmap;
        }
        if (drawName)
            DrawSocketName(dc, xPos + BORDER_WIDTH + SOCKET_WIDTH 
                               + SOCKET_SPACING * 2, y + SOCKET_HEIGHT / 2, 
                           name, false);
        swDrawBitmap(dc, sbitmap, 0, 0, 
                xPos + BORDER_WIDTH + SOCKET_SPACING, y,
                SOCKET_WIDTH, SOCKET_HEIGHT);
        y += SOCKET_HEIGHT + SOCKET_SPACING;
    }
    if (node->getType() == VRML_SCRIPT) {
        sbitmap = _socketBitmap;
        bool drawName = false;
        if (_mode == ROUTING && node == _srcNode && _srcSide == 0 
            && _srcSocket == i) {
            if (_dstSocket >= 0) {
                Proto *proto = _dstNode->getProto();
                if (_dstSocket < proto->getNumEventOuts()) {
                    int type = proto->getEventOut(_dstSocket)->getType();
                    sbitmap = _socketBitmaps[type];
                }
            }
            drawName = true;
        } else if (node == _dstNode && _dstSide == 0 && _dstSocket == i && 
             (_mode != ROUTING || _srcSide == 1 && 
             Scene::validRoute(_srcNode, _srcSocket, _dstNode, _dstSocket))) {
            drawName = true; 
            if ((_mode == ROUTING) && (_srcSocket >= 0)) {
                Proto *proto = _srcNode->getProto();
                if (_srcSocket < proto->getNumEventOuts()) {
                    int type = proto->getEventOut(_srcSocket)->getType();
                    sbitmap = _socketBitmaps[type];
                }
            }
        }
        if (drawName)
            DrawSocketName(dc, xPos + BORDER_WIDTH + SOCKET_WIDTH 
                               + SOCKET_SPACING * 2, y + SOCKET_HEIGHT / 2, 
                           "(connect anything)", false);
        swDrawBitmap(dc, sbitmap, 0, 0,
                         xPos + BORDER_WIDTH + SOCKET_SPACING, y,
                         SOCKET_WIDTH, SOCKET_HEIGHT);
    }
    y = yPos + BORDER_WIDTH + SOCKET_SPACING;
    for (i = 0; i < eventOuts; i++) {
        EventOut *eventOut = node->getProto()->getEventOut(i);
        if (_scene->isInvalidElement(eventOut)) {
            y += SOCKET_HEIGHT + SOCKET_SPACING;
            continue;
        }
        SBITMAP selectedSocketBitmap = _socketBitmaps[eventOut->getType()];
        const char *name = eventOut->getName(x3d);
        bool drawName = false;
        if (node == _dstNode && _dstSide == 1 && _dstSocket == i && 
            (_mode != ROUTING || _srcSide == 0 && 
             Scene::validRoute(_dstNode, _dstSocket, _srcNode, _srcSocket))) {
            sbitmap = selectedSocketBitmap;
            drawName = true;
        } else if (_mode == ROUTING && node == _srcNode && _srcSide == 1 && 
                   _srcSocket == i) {
            sbitmap = selectedSocketBitmap;
            drawName = true;
        } else if (node->getOutput(i).size() > 0)  { 
            sbitmap = selectedSocketBitmap;
        } else if (eventOut->getFlags() & EOF_RECOMMENDED) {
            sbitmap = _socketBitmapRecommended;
        } else {
            sbitmap = _socketBitmap;
        }
        if (drawName)
            DrawSocketName(dc, xPos + width - BORDER_WIDTH - SOCKET_SPACING*2 
                               - SOCKET_WIDTH, y + SOCKET_HEIGHT / 2, 
                           name, true);
        swDrawBitmap(dc, sbitmap, 0, 0,
                xPos + width - BORDER_WIDTH - SOCKET_SPACING - SOCKET_WIDTH, y,
                SOCKET_WIDTH, SOCKET_HEIGHT);
        y += SOCKET_HEIGHT + SOCKET_SPACING;
    }
    if (node->getType() == VRML_SCRIPT) {
        sbitmap = _socketBitmap;
        bool drawName = false;
        if (_mode == ROUTING && node == _srcNode && _srcSide == 1 && 
            _srcSocket == i) {
            if (_dstSocket >= 0) {
                Proto *proto = _dstNode->getProto();
                if (_dstSocket < proto->getNumEventIns()) {
                    int type = proto->getEventIn(_dstSocket)->getType();
                    sbitmap = _socketBitmaps[type];
                }
            }
            drawName = true;
        } else if (node == _dstNode && _dstSide == 1 && _dstSocket == i
                   && (_mode != ROUTING || _srcSide == 0)) {
            drawName = true; 
            if ((_mode == ROUTING) && (_srcSocket >= 0)) {
                Proto *proto = _srcNode->getProto();
                if (_srcSocket < proto->getNumEventIns()) {
                    int type = proto->getEventIn(_srcSocket)->getType();
                    sbitmap = _socketBitmaps[type];
                }
            }
        }    
        if (drawName)
            DrawSocketName(dc, 
                xPos + width - BORDER_WIDTH - SOCKET_SPACING*2 - SOCKET_WIDTH,
                y + SOCKET_HEIGHT / 2, "(connect anything)", true);
        swDrawBitmap(dc, sbitmap, 0, 0, 
                xPos + width - BORDER_WIDTH - SOCKET_SPACING - SOCKET_WIDTH, y,
                SOCKET_WIDTH, SOCKET_HEIGHT);
    }
}

void SceneGraphView::DrawRoute(SDC dc, Point start, Point end, int color)
{
    int midx = (start.x + end.x) / 2;
    int midy = (start.y + end.y) / 2;

    if (end.x - start.x < LINE_STUB * 2) {
        swSetFGColor(dc, 0x000000);
        MoveTo(dc, start.x + SHADOW_OFFSET, start.y + SHADOW_OFFSET);
        LineTo(dc, start.x + LINE_STUB + SHADOW_OFFSET, start.y + SHADOW_OFFSET);
        LineTo(dc, start.x + LINE_STUB + SHADOW_OFFSET, midy + SHADOW_OFFSET);
        LineTo(dc, end.x - LINE_STUB + SHADOW_OFFSET, midy + SHADOW_OFFSET);
        LineTo(dc, end.x - LINE_STUB + SHADOW_OFFSET, end.y + SHADOW_OFFSET);
        LineTo(dc, end.x + SHADOW_OFFSET, end.y + SHADOW_OFFSET);
        swSetFGColor(dc, color);
        MoveTo(dc, start.x, start.y);
        LineTo(dc, start.x + LINE_STUB, start.y);
        LineTo(dc, start.x + LINE_STUB, midy);
        LineTo(dc, end.x - LINE_STUB, midy);
        LineTo(dc, end.x - LINE_STUB, end.y);
        LineTo(dc, end.x, end.y);
    } else {
        swSetFGColor(dc, 0x000000);
        MoveTo(dc, start.x + SHADOW_OFFSET, start.y + SHADOW_OFFSET);
        LineTo(dc, midx + SHADOW_OFFSET, start.y + SHADOW_OFFSET);
        LineTo(dc, midx + SHADOW_OFFSET, end.y + SHADOW_OFFSET);
        LineTo(dc, end.x + SHADOW_OFFSET, end.y + SHADOW_OFFSET);
        swSetFGColor(dc, color);
        MoveTo(dc, start.x, start.y);
        LineTo(dc, midx, start.y);
        LineTo(dc, midx, end.y);
        LineTo(dc, end.x, end.y);
    }
}

bool SceneGraphView::IntersectRoute(Point start, Point end, 
                                    Point rStart, Point rEnd)
{
    int midx = (rStart.x + rEnd.x) / 2;
    int midy = (rStart.y + rEnd.y) / 2;
    bool rc;

    if (rEnd.x - rStart.x < LINE_STUB * 2) {
        rc = Util::IntersectLines(start.x, start.y, end.x, end.y,
                                  rStart.x, rStart.y,
                                  rStart.x + LINE_STUB, rStart.y)
          || Util::IntersectLines(start.x, start.y, end.x, end.y,
                                  rStart.x + LINE_STUB, rStart.y,
                                  rStart.x + LINE_STUB, midy)
          || Util::IntersectLines(start.x, start.y, end.x, end.y,
                                  rStart.x + LINE_STUB, midy,
                                  rEnd.x - LINE_STUB, midy)
          || Util::IntersectLines(start.x, start.y, end.x, end.y,
                                  rEnd.x - LINE_STUB, midy,
                                  rEnd.x - LINE_STUB, rEnd.y)
          || Util::IntersectLines(start.x, start.y, end.x, end.y,
                                  rEnd.x - LINE_STUB, rEnd.y,
                                  rEnd.x, rEnd.y);
    } else {
        rc = Util::IntersectLines(start.x, start.y, end.x, end.y,
                                  rStart.x, rStart.y,
                                  midx, rStart.y)
          || Util::IntersectLines(start.x, start.y, end.x, end.y,
                                  midx, rStart.y,
                                  midx, rEnd.y)
          || Util::IntersectLines(start.x, start.y, end.x, end.y,
                                  midx, rEnd.y,
                                  rEnd.x, rEnd.y);
    }
    return rc;
}

void SceneGraphView::InvalidateRoute(Point start, Point end)
{
    int midx = (start.x + end.x) / 2;
    int midy = (start.y + end.y) / 2;

    swInvalidateRect(_window, start.x - 3, start.y - 2, 6, 6);
    swInvalidateRect(_window, end.x, end.y - 2, 6, 6);
    if (end.x - start.x < LINE_STUB * 2) {
        MoveTo(_window, start.x, start.y);
        InvalidateTo(_window, start.x + LINE_STUB, start.y);
        InvalidateTo(_window, start.x + LINE_STUB, midy);
        InvalidateTo(_window, end.x - LINE_STUB, midy);
        InvalidateTo(_window, end.x - LINE_STUB, end.y);
        InvalidateTo(_window, end.x, end.y);
    } else {
        MoveTo(_window, start.x, start.y);
        InvalidateTo(_window, midx, start.y);
        InvalidateTo(_window, midx, end.y);
        InvalidateTo(_window, end.x, end.y);
    }
}

void SceneGraphView::DrawRoutes(SDC dc, Node *node)
{
    int eventOuts = node->getProto()->getNumEventOuts();

    for (int i = 0; i < eventOuts; i++) {
        for (SocketList::Iterator *j = node->getOutput(i).first(); j != NULL;
             j = j->next()) {
            RouteSocket s = j->item();
            int c = _typeColors[node->getProto()->getEventOut(i)->getType()];
            Point dest = GetSocketPosition(s.getNode(), s.getField(), 0);
            DrawRoute(dc, GetSocketPosition(node, i, 1), 
                      Point(dest.x - SOCKET_WIDTH / 2, dest.y), c);
        }
    }

}

void SceneGraphView::DrawSocketName(SDC dc, int x, int y, const char *name,
                                    bool rightAlign)
{
    const int margin = 3;
    int width = swGetStringWidth(swGetDefaultFont(), name) + margin * 2;
    int descent = swGetFontDescent(swGetDefaultFont());
    int ascent = swGetFontAscent(swGetDefaultFont());
    int height = ascent + descent + margin * 2;
    if (rightAlign) x -= width;
    y -= height / 2;

    swSetFGColor(dc, _face);
    swFillRect(dc, x, y, width, height);
    swDraw3DRect(dc, _window, x, y, width, height);
    swSetFGColor(dc, 0x000000);
    swDrawText(dc, x + margin, y + ascent + margin, name);
}

void SceneGraphView::InvalidateNode(Node *node)
{
    int i;

    if (!node) return;

    int eventIns = node->getProto()->getNumEventIns();
    int eventOuts = node->getProto()->getNumEventOuts();
    int x, y, width, height;

    getZoomGraphPosition(x, y, node);
    getZoomGraphSize(width, height, node);
    swInvalidateRect(_window, x, y, width + 1, height + 1);
    for (i = 0; i < eventOuts; i++) {
        for (SocketList::Iterator *j = node->getOutput(i).first(); j != NULL;
             j = j->next()) {
            RouteSocket s = j->item();
            InvalidateRoute(GetSocketPosition(node, i, 1), 
                            GetSocketPosition(s.getNode(), s.getField(), 0) - 
                            Point(SOCKET_WIDTH / 2, 0));
        }
    }
    for (i = 0; i < eventIns; i++) {
        for (SocketList::Iterator *j = node->getInput(i).first(); j != NULL;
             j = j->next()) {
            RouteSocket s = j->item();
            InvalidateRoute(GetSocketPosition(s.getNode(), s.getField(), 1),
                            GetSocketPosition(node, i, 0) - 
                            Point(SOCKET_WIDTH / 2, 0));
        }
    }
}

void SceneGraphView::InvalidateNodeRec(Node *node)
{
    Position(node);
    InvalidateNode(node);
    for (int i = 0; i < node->getProto()->getNumFields(); i++) {
        if (node->getField(i) != NULL) {
            if (node->getField(i)->getType() == SFNODE) {
                Node *child = ((SFNode *) node->getField(i))->getValue();
                if (child != NULL) InvalidateNodeRec(child);
            } else if (node->getField(i)->getType() == MFNODE) {
                NodeList *value = ((MFNode *) node->getField(i))->getValues();
                for (int j = 0; j < value->size(); j++) {
                    InvalidateNodeRec(value->get(j));
                }
            }
        }
    }
}

Node *SceneGraphView::HitTest(int x, int y) const
{
    const NodeList *nodes = _scene->getNodes();

    for (int i = 0; i < nodes->size(); i++) {
        Node *node = nodes->get(i);
        if (node->isInScene(_scene) && (node != _scene->getRoot())) {
            int nx, ny, width, height;
            getZoomGraphPosition(nx, ny, node);
            getZoomGraphSize(width, height, node);
            if (x >= nx && y >= ny && x < nx + width && y < ny + height)
                return node;
        }
    }
    return NULL;
}

int SceneGraphView::SocketHitTest(int x, int y, Node *node, int *side) const
{
    int nx, ny, width, height;

    if (_zoom != 1.0)
        return -1;
    getZoomGraphPosition(nx, ny, node);
    getZoomGraphSize(width, height, node);
    int px = x - nx;
    int py = y - ny;
    int sock = (py - BORDER_WIDTH - SOCKET_SPACING) 
               / (SOCKET_HEIGHT + SOCKET_SPACING);
    int socketsIn = node->getProto()->getNumEventIns();
    int socketsOut = node->getProto()->getNumEventOuts();
    if (node->getType() == VRML_SCRIPT) {
        socketsIn++;
        socketsOut++;
    }
    if (px >= BORDER_WIDTH + SOCKET_SPACING
        && px < BORDER_WIDTH + SOCKET_SPACING + SOCKET_WIDTH 
        && sock < socketsIn) {
        *side = 0;  // left side, inputs
        return sock;
    } else if (px >= width - BORDER_WIDTH - SOCKET_WIDTH - SOCKET_SPACING
               && px < width - BORDER_WIDTH - SOCKET_SPACING
               && sock < socketsOut) {
        *side = 1;  // right side, outputs
        return sock;
    } else {
        return -1;
    }
}

void SceneGraphView::OnLButtonDown(int x, int y, int modifiers) 
{
    Node *node = HitTest(x, y);
    const Path *sel = _scene->getSelection();
    Node *current = sel ? sel->getNode() : NULL;

    swSetFocus(_wnd);
    if (node) {
        if (current != node) {
            _scene->setSelection(node);
            _scene->UpdateViews(this, UPDATE_SELECTION);
        }
        if (_dstSocket != -1) {
            _mode = ROUTING;
            _srcNode = node;
            _srcSocket = _dstSocket;
            _srcSide = _dstSide;
            _point1 = _point2 = Point(x, y);
        } else {
            _mode = TRACKING;
            _dstNode = node;
            int nx, ny;
            getZoomGraphPosition(nx, ny, node);
            _point2 = Point(x - nx, y - ny);
        }
    } else {
        _mode = CUTTING;
        _point1 = _point2 = Point(x, y);
    }
    swSetCapture(_window);
}

void SceneGraphView::OnLButtonUp(int x, int y, int modifiers) 
{
    swReleaseCapture(_window);
    if (_autoScrolling) {
        swKillTimer(_timer);
        _autoScrolling = false;
    }
    if (_mode == TRACKING) {
        _dstNode = NULL;
    } else if (_mode == ROUTING) {
        if (_dstNode != NULL && _dstSocket != -1 && _dstSide != _srcSide) {
            // got a route, try to assign it
            Node *src, *dst;
            int srcSocket, dstSocket;

            if (_srcSide == 0) {
                // routing backwards
                src = _dstNode;
                dst = _srcNode;
                srcSocket = _dstSocket;
                dstSocket = _srcSocket;
            } else {
                src = _srcNode;
                dst = _dstNode;
                srcSocket = _srcSocket;
                dstSocket = _dstSocket;
            }
            if (Scene::validRoute(src, srcSocket, dst, dstSocket)) {
                _scene->execute(
                    new RouteCommand(src, srcSocket, dst, dstSocket));
            }   
        }
        InvalidateRoute(GetSocketPosition(_srcNode, _srcSocket, _srcSide),
                        _point1);
        InvalidateNode(_srcNode);
        InvalidateNode(_dstNode);
    } else if (_mode == CUTTING) {
        CutRoutes(_point1, _point2);
        int top = MIN(_point1.y, _point2.y);
        int bottom = MAX(_point1.y, _point2.y);
        int left = MIN(_point1.x, _point2.x);
        int right = MAX(_point1.x, _point2.x);
        swInvalidateRect(_window, left, top, right - left + 1, bottom - top + 1);
    }
    _mode = NONE;
}

void SceneGraphView::CutRoutes(const Point &start, const Point &end)
{
    CommandList *list = NULL;
    const NodeList *nodes = _scene->getNodes();

    for (int i = 0; i < nodes->size(); i++) {
        Node *node = nodes->get(i);

        if (node->isInScene(_scene)) {
            int eventOuts = node->getProto()->getNumEventOuts();
            for (int j = 0; j < eventOuts; j++) {
                for (SocketList::Iterator *k = node->getOutput(j).first(); 
                     k != NULL; k = k->next()) {
                    RouteSocket  s = k->item();
                    if (IntersectRoute(start, end, 
                                       GetSocketPosition(node, j, 1), 
                                       GetSocketPosition(s.getNode(), 
                                                         s.getField(), 0) - 
                                       Point(SOCKET_WIDTH / 2, 0))) {
                        if (!list) list = new CommandList();
                        list->append(new UnRouteCommand(node, j, 
                                                        s.getNode(), 
                                                        s.getField()));
                    }
                }
            }
        }
    }
    if (list) _scene->execute(list);
}

void SceneGraphView::OnMouseMove(int x, int y, int modifiers) 
{
    if (_mode != NONE) {
        CheckAutoScroll(x, y);
        setLastXPosition(MAX(_lastXPosition, x));
        setLastYPosition(MAX(_lastYPosition, y));
        if (TheApp->GetRouteViewShowAll())
            _maxYPosition = MAX(_maxYPosition, _lastYPosition);
        else
            _maxYPosition = MAX(getFirstYPosition(), y);
        SetScrollSizes(_lastXPosition, _maxYPosition);
    }
    DoMouseMove(x, y, modifiers);
}

void SceneGraphView::DoMouseMove(int px, int py, int modifiers)
{
    if ((_mode == TRACKING) && (_dstNode!=NULL)) {
        int oldX, oldY, width, height;
        getZoomGraphPosition(oldX, oldY, _dstNode);
        getZoomGraphSize(width, height, _dstNode);
        InvalidateNode(_dstNode);
        int newX = MAX(px - _point2.x, 0);
        int newY = MAX(py - _point2.y, 0);
        if (newX != oldX || newY != oldY) {
            InvalidateNode(_dstNode);
            setZoomGraphPosition(_dstNode, newX, newY);
            InvalidateNode(_dstNode);
            setLastXPosition(MAX(_lastXPosition, newX + width + LINE_STUB));
            setLastYPosition(MAX(_lastYPosition, newY + height));
            if (TheApp->GetRouteViewShowAll())
                _maxYPosition = MAX(_maxYPosition, _lastYPosition);
            else
                _maxYPosition = MAX(getFirstYPosition(), newY + height);
            if (_maxYPosition > GetYEndRouteBlock())
                _YNodesOnlyEventOuts = _maxYPosition;
            int sx, sy;
            swGetScrollPosition(_scroller, &sx, &sy);
            SetScrollSizes(_lastXPosition, _maxYPosition);
            swSetScrollPosition(_scroller, sx, sy);
        }
    } else if (_mode == ROUTING) {
        InvalidateRoute(GetSocketPosition(_srcNode, _srcSocket, _srcSide),
                        _point1);
        _point1 = Point(px, py);
        InvalidateRoute(GetSocketPosition(_srcNode, _srcSocket, _srcSide),
                        _point1);

        HighlightSocket(_point1);
    } else if (_mode == CUTTING) {
        int top, left, right, bottom;
        top = MIN(_point1.y, _point2.y);
        bottom = MAX(_point1.y, _point2.y);
        left = MIN(_point1.x, _point2.x);
        right = MAX(_point1.x, _point2.x);
        swInvalidateRect(_window, left, top, right - left + 1, bottom - top + 1);
        _point2 = Point(px, py);
        top = MIN(_point1.y, _point2.y);
        bottom = MAX(_point1.y, _point2.y);
        left = MIN(_point1.x, _point2.x);
        right = MAX(_point1.x, _point2.x);
        swInvalidateRect(_window, left, top, right - left + 1, bottom - top + 1);
    } else {
        HighlightSocket(Point(px, py));
    }
}

static int
timerCB(void *data)
{
    return ((SceneGraphView *) data)->OnTimer();
}

void SceneGraphView::CheckAutoScroll(int px, int py)
{
    bool autoScroll = true;
    int x, y, width, height, sx, sy;
    int dx = 0, dy = 0;

    swGetPosition(_scroller, &x, &y);
    swGetScrollViewportSize(_scroller, &width, &height);
    swGetScrollPosition(_scroller, &sx, &sy);
    px -= sx;
    py -= sy;
    _autoScrollPX = px;
    _autoScrollPY = py;

    if (px < AUTOSCROLL_MARGIN) {
        dx = -AUTOSCROLL_AMOUNT;
    } else if (px > x + width - AUTOSCROLL_MARGIN) {
        dx = AUTOSCROLL_AMOUNT;
    } else if (py < AUTOSCROLL_MARGIN) {
        dy = -AUTOSCROLL_AMOUNT;
    } else if (py > y + height - AUTOSCROLL_MARGIN) {
        dy = AUTOSCROLL_AMOUNT;
    } else {
        autoScroll = false;
    }

    if (autoScroll && !_autoScrolling) {   // start scrolling
        _timer = swSetTimer(_window, 70, timerCB, this);
        _autoScrollDX = dx;
        _autoScrollDY = dy;
        _autoScrolling = true;
    }
    if (!autoScroll && _autoScrolling) {   // stop scrolling
        swKillTimer(_timer);
        _autoScrolling = false;
    }
}

void SceneGraphView::HighlightSocket(Point point)
{
    // check if we're over a socket
    Node *node = HitTest(point.x, point.y);
    int socket = -1;
    int side = -1;

    if (node) {
        socket = SocketHitTest(point.x, point.y, node, &side);
    }

    if (_dstNode && node != _dstNode) {
        InvalidateNode(_dstNode);
    } else if (node && (socket != _dstSocket || side != _dstSide)) {
        InvalidateNode(node);
    }
    _dstNode = node;
    _dstSocket = socket;
    _dstSide = side;
}

void SceneGraphView::OnUpdate(SceneView *sender, int type, Hint *hint) 
{
    NodeUpdate *nodeUpdate;
    RouteUpdate *routeUpdate;

    switch (type) {
      CASE_UPDATE(UPDATE_ALL)
        Initialize();
        break;
      CASE_UPDATE(UPDATE_SELECTION)
      CASE_UPDATE(UPDATE_SELECTION_NAME)
        if (swIsVisible(_wnd)) {
            InvalidateNode(_scene->getSelection()->getNode());
            swInvalidateWindow(_window);
        }
        break;
      CASE_UPDATE(UPDATE_CHANGE_INTERFACE_NODE)
        nodeUpdate = (NodeUpdate *) hint;
        // when the number of events change, size can change
        accountGraphSize(nodeUpdate->node);
      CASE_UPDATE(UPDATE_ADD_NODE)
      CASE_UPDATE(UPDATE_ADD_NODE_SCENE_GRAPH_VIEW)
      CASE_UPDATE(UPDATE_REMOVE_NODE)
        nodeUpdate = (NodeUpdate *) hint;
        accountGraphSize(nodeUpdate->node);
        InvalidateNodeRec(nodeUpdate->node);
        if (!TheApp->GetRouteViewShowAll())
            _maxYPosition = getFirstYPosition();
        SetScrollSizes(_lastXPosition, _maxYPosition);
        break;
      CASE_UPDATE(UPDATE_ADD_ROUTE)
        routeUpdate = (RouteUpdate *) hint;
        if (!TheApp->GetRouteViewShowAll()) {
            int width, height;
            getZoomGraphPosition(width, height, routeUpdate->src);
            if (height > GetYEndRouteBlock())
                moveToTop(routeUpdate->src, routeUpdate->dest);
            getZoomGraphPosition(width, height, routeUpdate->dest);
            if (height > GetYEndRouteBlock())
                moveToTop(routeUpdate->dest, routeUpdate->src);
        }
      CASE_UPDATE(UPDATE_DELETE_ROUTE)
        routeUpdate = (RouteUpdate *) hint;
        InvalidateRoute(GetSocketPosition(routeUpdate->src,
                                          routeUpdate->eventOut, 1), 
                        GetSocketPosition(routeUpdate->dest,
                                          routeUpdate->eventIn, 0)
                          - Point(SOCKET_WIDTH / 2, 0));
        break;
      CASE_UPDATE(UPDATE_REDRAW)
        swInvalidateWindow(_window);
        break;
    }
}

Point SceneGraphView::GetSocketPosition(Node *node, int socket, int side) const
{
    int x, y, width, height;
    
    getZoomGraphPosition(x, y, node);
    getZoomGraphSize(width, height, node);
    y += (BORDER_WIDTH + ((SOCKET_SPACING + SOCKET_HEIGHT) * 
                          (2 * socket + 1)) / 2) * _zoom;
    if (side == 0) { // left
        x += (BORDER_WIDTH + SOCKET_SPACING + SOCKET_WIDTH / 2) * _zoom;
    } else {
        x += width - (BORDER_WIDTH + SOCKET_SPACING  + SOCKET_WIDTH / 2) * 
                     _zoom;
    }
    return Point(x, y);
}

SBITMAP
SceneGraphView::LoadSocketBitmap(int id, int color)
{
    int mapFrom[4] = { 0x808000, 0x000000, 0xC0C0C0, 0x808080 };
    int mapTo[4] = { _face,
      SW_RGB(SW_RED(color) / 3, SW_GREEN(color) / 3, SW_BLUE(color) / 3),
      SW_RGB(MIN(SW_RED(color) + 128, 255),
             MIN(SW_GREEN(color) + 128, 255),
             MIN(SW_BLUE(color) + 128, 255)),
             color
    };

    return swLoadMappedBitmap(_window, id, mapFrom, mapTo, 4);
}

void
SceneGraphView::BuildSocketBitmaps()
{
    int i;
    for (i = 0; i < LAST_TYPE + 2 ; i++) {
        _typeColors[i] = 0xFFFFFF;
    }

    int numColors = 0;

    _typeColors[SFBOOL]      = 0x7F7F7F;
    numColors++;
    _typeColors[MFBOOL]      = 0x1F1F1F;
    numColors++;

    _typeColors[SFCOLOR]     = 0x2F4FCF;
    numColors++;
    _typeColors[MFCOLOR]     = 0x3F3F7F;
    numColors++;

    _typeColors[SFCOLORRGBA] = 0x1FCFCF;
    numColors++;
    _typeColors[MFCOLORRGBA] = 0x3F7F7F;
    numColors++;

    _typeColors[SFDOUBLE]    = 0xCFFFFF;
    numColors++;
    _typeColors[MFDOUBLE]    = 0x5F7F7F;
    numColors++;

    _typeColors[SFFLOAT]     = 0xCFCFFF;
    numColors++;
    _typeColors[MFFLOAT]     = 0x5F5F7F;
    numColors++;

    _typeColors[SFIMAGE]     = 0xCF3FCF;
    numColors++;
    
    _typeColors[SFINT32]     = 0x00FFFF;
    numColors++;
    _typeColors[MFINT32]     = 0x007F7F; 
    numColors++;

    _typeColors[SFNODE]      = 0xFF1F7F;
    numColors++;
    _typeColors[MFNODE]      = 0xCF005F;
    numColors++;

    _typeColors[SFROTATION]  = 0xCF00CF;
    numColors++;
    _typeColors[MFROTATION]  = 0x7F007F;
    numColors++;

    _typeColors[SFSTRING]    = 0xCFCF00;
    numColors++;
    _typeColors[MFSTRING]    = 0x7F7F00;
    numColors++;

    _typeColors[SFTIME]      = 0xCF5F00;
    numColors++;
    _typeColors[MFTIME]      = 0x7F5F00;
    numColors++;

    _typeColors[SFVEC2F]     = 0x1F7F7F;
    numColors++;
    _typeColors[MFVEC2F]     = 0x005F5F;
    numColors++;

    _typeColors[SFVEC3F]     = 0xFF0000;
    numColors++;
    _typeColors[MFVEC3F]     = 0x7F4F4F;
    numColors++;

    _typeColors[SFVEC3D]     = 0xFF3737;
    numColors++;
    _typeColors[MFVEC3D]     = 0x7F7777;
    numColors++;

    _typeColors[SFVEC4F]     = 0xFF1717;
    numColors++;
    _typeColors[SFVEC4F]     = 0xFF5757;
    numColors++;

    _typeColors[SFMATRIX3F]  = 0xCFFFCF;
    numColors++;
    _typeColors[SFMATRIX4F]  = 0xCF77CF;
    numColors++;

    _typeColors[MFMATRIX3F]  = 0x1FFF1F;
    numColors++;
    _typeColors[MFMATRIX4F]  = 0x1F001F;
    numColors++;

    _typeColors[LAST_TYPE + 1] = 0xFFFFFF;
    numColors++;

    // warn, if a new added type got no color
    if (numColors != (LAST_TYPE + 2))
        _scene->errorf("Warning: not enough colors (%d of %d) in RouteView\n",
                       numColors, (LAST_TYPE + 2));

    for (i = 0; i < LAST_TYPE + 2 ; i++) {
        _socketBitmaps[i] = LoadSocketBitmap(IDB_FULL_SOCKET, _typeColors[i]);
    }
}

int SceneGraphView::OnTimer() 
{
    int sx, sy;
    int w, h, vw, vh;

    swGetScrollPosition(_scroller, &sx, &sy);
    swGetSize(_window, &w, &h);
    swGetScrollViewportSize(_scroller, &vw, &vh);
    sx += _autoScrollDX;
    if (sx >= w - vw) sx = w - vw;
    if (sx < 0) sx = 0;
    sy += _autoScrollDY;
    if (sy >= h - vh) sy = h - vh;
    if (sy < 0) sy = 0;
    if (sy > MAX_Y) sy = MAX_Y;
    if (sy > _maxYPosition) sy = _maxYPosition;
    swSetScrollPosition(_scroller, sx, sy);
    DoMouseMove(_autoScrollPX + sx, _autoScrollPY + sy, 0);
    return TRUE;  // continue timer
}

void
SceneGraphView::getZoomGraphPosition(int &width, int &height, Node *node) const
{
    float w, h;
    node->getGraphPosition(&w, &h);
    width = (int) w * _zoom; 
    height = (int) h * _zoom; 
}

void
SceneGraphView::setZoomGraphPosition(Node *node, int x, int y)
{
    node->setGraphPosition(x / _zoom, y / _zoom);
}

void
SceneGraphView::getZoomGraphSize(int &width, int &height, Node *node) const
{
    node->getGraphSize(&width, &height);
    width *= _zoom; 
    if (width == 0)
        width = 1;
    height *= _zoom; 
    if (height == 0)
        height = 1;
}

void
SceneGraphView::setGraphSize(Node *node, int width, int height)
{
    node->setGraphSize(width, height);
}

void
SceneGraphView::zoomIn(void)
{ 
    if (_zoom >= 1.0) { 
        _zoom = 1.0;
        return;
    }
    setLastXPosition(_lastXPosition * 2);
    _maxYPosition *= 2;
    _zoom *= 2.0; 
    int sx, sy;
    swGetScrollPosition(_scroller, &sx, &sy);
    SetScrollSizes(_lastXPosition, _maxYPosition);
    swSetScrollPosition(_scroller, sx * 2, sy * 2);
    swInvalidateWindow(_window);
}

void
SceneGraphView::zoomOut(void)
{ 
    setLastXPosition(_lastXPosition / 2);
    _maxYPosition /= 2;
    _zoom /= 2.0; 
    int sx, sy;
    swGetScrollPosition(_scroller, &sx, &sy);
    SetScrollSizes(_lastXPosition, _maxYPosition);
    swSetScrollPosition(_scroller, sx / 2, sy / 2);
    swInvalidateWindow(_window);
}

void
SceneGraphView::unZoom(void)
{ 
    int lastPosition = _lastXPosition / _zoom;
    setLastXPosition(lastPosition);
    _maxYPosition /= _zoom;
    _zoom = 1.0; 
    jumpToSelection();
    SetScrollSizes(_lastXPosition, _maxYPosition);
    swInvalidateWindow(_window);
}

void
SceneGraphView::jumpToSelection(void)
{
    const Path *sel = _scene->getSelection();
    Node *current = sel ? sel->getNode() : NULL;
    if (current == NULL) 
        return;
    if (current == _scene->getRoot())
        return;
    int sx, sy;
    getZoomGraphPosition(sx, sy, current);
    int width, height;
    swGetScrollViewportSize(_scroller, &width, &height);
    sx = sx - width / 2;
    if (sx < 0)
        sx = 0;
    sy = sy - height  / 2;
    if (sy < 0)
        sy = 0;
    swSetScrollPosition(_scroller, sx, sy);
}

void
SceneGraphView::warnFlooded(void)
{
    if (!_floodedWarning) {
        _floodedWarning = true;
        TheApp->PrintMessageWindowsId(IDS_ROUTE_VIEW_FLOODED);
    }
}


void
SceneGraphView::setLastXPosition(int x)
{
    if (x < MAX_X)
        _lastXPosition = x;
}

void
SceneGraphView::setLastYPosition(int y)
{
    _lastYPosition = y;
}

int
SceneGraphView::getFirstYPosition(void)
{
    int ret = MAX(_YNodesOnlyEventOuts, _YNodesBothEvents);
    return MAX(ret, _YNodesOnlyEventIns);
}

void 
SceneGraphView::SetScrollSizes(int width, int height) 
{
    int x = MAX(width, _XNodesOnlyEventIns + NODE_WIDTH);
    swSetScrollSizes(_scroller, x, height);
}
        
void 
SceneGraphView::SetSize(int width, int height)
{
    swSetSize(_scroller, width, height);
}
           
int
SceneGraphView::GetYEndRouteBlock(void)
{
    int ret = 0;
    if (_YNodesOnlyEventOuts > ret)
        ret = _YNodesOnlyEventOuts;
    if (_YNodesBothEvents > ret)
        ret = _YNodesBothEvents;
    if (_YNodesOnlyEventIns > ret)
        ret = _YNodesOnlyEventIns;
    return ret;
}


