/* * 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" #define SOCKET_WIDTH 6 #define SOCKET_HEIGHT 6 #define SOCKET_SPACING 2 #define BORDER_WIDTH 2 #define ICON_SIZE 16 #define NODE_WIDTH 190 #define ADD_WIDTH 8 #define ADD_HEIGHT 4 #ifdef WIN32 // near windows canvas limit # define MAX_Y 8000 #else // near motif canvas limit # define MAX_Y 32000 #endif #define MINIMAL_SIZE (BORDER_WIDTH + 1 + ICON_SIZE) // position from left of node to icon #define ICON_OFFSET (BORDER_WIDTH + SOCKET_WIDTH + ICON_SIZE) // position from left of node to text #define TEXT_OFFSET (ICON_OFFSET + ICON_SIZE + 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 }; _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; } 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(); _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); } } for (i = 0; i < nodes->size(); i++) { Node *node = nodes->get(i); if (node->isInScene(_scene) && node != _scene->getRoot()) { Position(node); } } swInvalidateWindow(_window); swSetScrollSizes(_scroller, _lastXPosition, _maxYPosition); } void SceneGraphView::OnDraw(int x, int y, int width, int height) { 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, 0x000000); 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); int nx, ny, nw, nh; getZoomGraphPosition(nx, ny, node); getZoomGraphSize(nw, nh, node); if (node->isInScene(_scene) && node != current && node != _scene->getRoot() && nx + nw >= x && nx <= x + width && ny + nh >= y && ny <= y + height) { 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) { 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() == NODE_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() == NODE_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) { swSetSize(_scroller, width, height); swSetScrollSizes(_scroller, _lastXPosition, _maxYPosition); } void SceneGraphView::OnMouseLeave() { #ifndef HAVE_BUG_IN_LESSTIF // // 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) { _lastYPosition += height + ADD_HEIGHT; if (_maxYPosition < MAX_Y) _maxYPosition = _lastYPosition; } else { _lastYPosition = height + ADD_HEIGHT; _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() == NODE_SCRIPT) sockets++; height = sockets * SOCKET_HEIGHT + (sockets + 1) * SOCKET_SPACING + BORDER_WIDTH * 2; height = MAX(height, ICON_SIZE + BORDER_WIDTH * 2); setGraphSize(node, NODE_WIDTH, height); } void SceneGraphView::accountGraphPosition(Node *node) { int width, height; getZoomGraphSize(width, height, node); width = NODE_WIDTH + LINE_STUB; _lastXPosition = 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) swDrawBitmap(dc, _nodeBitmap, node->getType() * ICON_SIZE, 0, xPos + ICON_OFFSET, yPos + height / 2 - 8, ICON_SIZE, ICON_SIZE); 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); } MyString name; name=node->getName(); if (name[0]==0) name=node->getProto()->getName(); 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); SBITMAP selectedSocketBitmap = _socketBitmaps[eventIn->getType()]; const char *name = eventIn->getName(); 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() == NODE_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); SBITMAP selectedSocketBitmap = _socketBitmaps[eventOut->getType()]; const char *name = eventOut->getName(); 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() == NODE_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()) { Socket s = j->item(); int c = _typeColors[node->getProto()->getEventOut(i)->getType()]; Point dest = GetSocketPosition(s._node, s._index, 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()) { Socket s = j->item(); InvalidateRoute(GetSocketPosition(node, i, 1), GetSocketPosition(s._node, s._index, 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()) { Socket s = j->item(); InvalidateRoute(GetSocketPosition(s._node, s._index, 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); 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() == NODE_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()) { Socket s = k->item(); if (IntersectRoute(start, end, GetSocketPosition(node, j, 1), GetSocketPosition(s._node, s._index, 0) - Point(SOCKET_WIDTH / 2, 0))) { if (!list) list = new CommandList(); list->append(new UnRouteCommand(node, j, s._node, s._index)); } } } } } if (list) _scene->execute(list); } void SceneGraphView::OnMouseMove(int x, int y, int modifiers) { if (_mode != NONE) { CheckAutoScroll(x, y); _lastXPosition = MAX(_lastXPosition, x); _lastYPosition = MAX(_lastYPosition, y); _maxYPosition = MAX(_maxYPosition, _lastYPosition); swSetScrollSizes(_scroller, _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); _lastXPosition = MAX(_lastXPosition, newX + width + LINE_STUB); _lastYPosition = MAX(_lastYPosition, newY + height); _maxYPosition = MAX(_maxYPosition, _lastYPosition); int sx, sy; swGetScrollPosition(_scroller, &sx, &sy); swSetScrollSizes(_scroller, _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_ALL: Initialize(); break; case UPDATE_SELECTION: case UPDATE_SELECTION_NAME: InvalidateNode(_scene->getSelection()->getNode()); swInvalidateWindow(_window); break; case UPDATE_FIELD: // ignore field updates for now break; case UPDATE_CHANGE_INTERFACE_NODE: nodeUpdate = (NodeUpdate *) hint; // when the number of events change, size can change accountGraphSize(nodeUpdate->node); case UPDATE_ADD_NODE: case UPDATE_REMOVE_NODE: nodeUpdate = (NodeUpdate *) hint; InvalidateNodeRec(nodeUpdate->node); swSetScrollSizes(_scroller, _lastXPosition, _maxYPosition); break; case UPDATE_ADD_ROUTE: case 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_MODE: case UPDATE_TIME: 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() { _typeColors[SFBOOL] = 0x7F7F7F; _typeColors[SFCOLOR] = 0x2F4FCF; _typeColors[MFCOLOR] = 0x3F3F7F; _typeColors[SFIMAGE] = 0xCF3FCF; _typeColors[SFINT32] = 0x00FFFF; _typeColors[MFINT32] = 0x007F7F; _typeColors[SFFLOAT] = 0xCFCFFF; _typeColors[MFFLOAT] = 0x5F5F7F; _typeColors[SFNODE] = 0xFF1F7F; _typeColors[MFNODE] = 0xCF005F; _typeColors[SFROTATION] = 0xCF00CF; _typeColors[MFROTATION] = 0x7F007F; _typeColors[SFSTRING] = 0xCFCF00; _typeColors[MFSTRING] = 0x7F7F00; _typeColors[SFTIME] = 0xCF5F00; _typeColors[MFTIME] = 0x7F5F00; _typeColors[SFVEC2F] = 0x1F7F7F; _typeColors[MFVEC2F] = 0x005F5F; _typeColors[SFVEC3F] = 0xFF0000; _typeColors[MFVEC3F] = 0x7F4F4F; _typeColors[LAST_TYPE + 1] = 0xFFFFFF; for (int 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; } _lastXPosition *= 2; _maxYPosition *= 2; _zoom *= 2.0; int sx, sy; swGetScrollPosition(_scroller, &sx, &sy); swSetScrollSizes(_scroller, _lastXPosition, _maxYPosition); swSetScrollPosition(_scroller, sx * 2, sy * 2); swInvalidateWindow(_window); } void SceneGraphView::zoomOut(void) { _lastXPosition /= 2; _maxYPosition /= 2; _zoom /= 2.0; int sx, sy; swGetScrollPosition(_scroller, &sx, &sy); swSetScrollSizes(_scroller, _lastXPosition, _maxYPosition); swSetScrollPosition(_scroller, sx / 2, sy / 2); swInvalidateWindow(_window); } void SceneGraphView::unZoom(void) { _lastXPosition /= _zoom; _maxYPosition /= _zoom; _zoom = 1.0; jumpToSelection(); swSetScrollSizes(_scroller, _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); }