/* ============================================================================== This file is part of the JUCETICE project - Copyright 2009 by Lucio Asnaghi. JUCETICE is based around the JUCE library - "Jules' Utility Class Extensions" Copyright 2007 by Julian Storer. ------------------------------------------------------------------------------ JUCE and JUCETICE can be redistributed and/or modified 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. JUCE and JUCETICE are distributed in the hope that they 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 JUCE and JUCETICE; if not, visit www.gnu.org/licenses or write to Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ============================================================================== */ BEGIN_JUCE_NAMESPACE #include "jucetice_GraphLinkComponent.h" //============================================================================== GraphNodeComponent::GraphNodeComponent () : listener (0), userData (0), uniqueID (0), nodeColour (Colours::grey), leftToRight (true), hasBeenLocked (false), originalX (0), originalY (0), lastX (0), lastY (0)/*, dropShadower (0.5f, 0, 1, 2.0f)*/ { setBufferedToImage (false); setOpaque (true); toFront (true); constrainer.setMinimumOnscreenAmounts (9999,9999,9999,9999); //dropShadower.setOwner (this); } GraphNodeComponent::~GraphNodeComponent() { } //============================================================================== void GraphNodeComponent::moved() { updateConnectors(); // update last position lastX = getX (); lastY = getY (); } void GraphNodeComponent::resized() { updateConnectors(); } void GraphNodeComponent::mouseDoubleClick (const MouseEvent& e) { // notify listener if (listener) listener->nodeDoubleClicked (this, e); } void GraphNodeComponent::mouseDown (const MouseEvent& e) { if (listener) listener->nodeSelected (this); if (e.mods.isRightButtonDown()) { if (listener) listener->nodePopupMenuSelected (this); } else if (e.mods.isLeftButtonDown()) { if (! hasBeenLocked) { originalX = getX(); originalY = getY(); } } } void GraphNodeComponent::mouseDrag (const MouseEvent& e) { if (! hasBeenLocked && e.mods.isLeftButtonDown()) { int x = originalX + e.getDistanceFromDragStartX(); int y = originalY + e.getDistanceFromDragStartY(); int w = getWidth(); int h = getHeight(); const Component* const parentComp = getParentComponent(); Rectangle limits; limits.setSize (parentComp->getWidth(), parentComp->getHeight()); Rectangle bounds (x, y, w, h); constrainer.checkBounds (bounds, getBounds(), limits, false, false, false, false); if (listener) listener->nodeMoved (this, x - lastX, y - lastY); constrainer.applyBoundsToComponent (*this, bounds); } } //============================================================================== void GraphNodeComponent::paint (Graphics& g) { if (listener) listener->nodePaint (this, g); } //============================================================================== void GraphNodeComponent::addInputConnector (const int connectorType) { GraphConnectorComponentInput* connector = new GraphConnectorComponentInput (this, connectorType); connector->setConnectorID (graphInputs.size()); getParentComponent()->addAndMakeVisible (connector); graphInputs.add (connector); updateConnectors(); } void GraphNodeComponent::addOutputConnector (const int connectorType) { GraphConnectorComponentOutput* connector = new GraphConnectorComponentOutput (this, connectorType); connector->setConnectorID (graphOutputs.size()); getParentComponent()->addAndMakeVisible (connector); graphOutputs.add (connector); updateConnectors(); } //============================================================================== GraphConnectorComponentInput* GraphNodeComponent::getInputConnector (const int connectorID) { for (int i = 0; i < graphInputs.size(); i++) { GraphConnectorComponentInput* input = graphInputs.getUnchecked (i); if (input && (input->getConnectorID() == connectorID)) { return input; } } return 0; } GraphConnectorComponentOutput* GraphNodeComponent::getOutputConnector (const int connectorID) { for (int i = 0; i < graphOutputs.size(); i++) { GraphConnectorComponentOutput* output = graphOutputs.getUnchecked (i); if (output && (output->getConnectorID() == connectorID)) { return output; } } return 0; } //============================================================================== int GraphNodeComponent::getFirstInputOfType (const int connectorType) { for (int i = 0; i < graphInputs.size(); i++) { GraphConnectorComponentInput* input = graphInputs.getUnchecked (i); if (input && (input->getType() == connectorType)) { return i; } } return -1; } int GraphNodeComponent::getFirstOutputOfType (const int connectorType) { for (int i = 0; i < graphOutputs.size(); i++) { GraphConnectorComponentOutput* output = graphOutputs.getUnchecked (i); if (output && (output->getType() == connectorType)) { return i; } } return -1; } //============================================================================== int GraphNodeComponent::getInputLinksCount () const { int linksCount = 0; Array links; for (int i = 0; i < graphInputs.size(); i++) { graphInputs.getUnchecked (i)->getLinkedConnectors (links); linksCount += links.size (); } return linksCount; } int GraphNodeComponent::getOutputLinksCount () const { int linksCount = 0; Array links; for (int i = 0; i < graphOutputs.size(); i++) { graphOutputs.getUnchecked (i)->getLinkedConnectors (links); linksCount += links.size (); } return linksCount; } //============================================================================== void GraphNodeComponent::deleteConnectors (const bool freeConnectors) { breakAllLinks (false); if (freeConnectors) { for (int i = 0; i < graphInputs.size(); i++) delete graphInputs.getUnchecked (i); } if (freeConnectors) { for (int i = 0; i < graphOutputs.size(); i++) delete graphOutputs.getUnchecked (i); } graphInputs.clear (); graphOutputs.clear (); } //============================================================================== void GraphNodeComponent::breakAllLinks (const bool notifyListeners) { for (int i = 0; i < graphInputs.size(); i++) graphInputs.getUnchecked (i)->destroyAllLinks (notifyListeners); for (int i = 0; i < graphOutputs.size(); i++) graphOutputs.getUnchecked (i)->destroyAllLinks (notifyListeners); if (notifyListeners) notifyGraphChanged (); } void GraphNodeComponent::breakInputLinks (const bool notifyListeners) { for (int i = 0; i < graphInputs.size(); i++) graphInputs.getUnchecked (i)->destroyAllLinks (notifyListeners); if (notifyListeners) notifyGraphChanged (); } void GraphNodeComponent::breakOutputLinks (const bool notifyListeners) { for (int i = 0; i < graphOutputs.size(); i++) graphOutputs.getUnchecked (i)->destroyAllLinks (notifyListeners); if (notifyListeners) notifyGraphChanged (); } //============================================================================== void GraphNodeComponent::setNodeListener (GraphNodeListener* const listener_) { listener = listener_; } void GraphNodeComponent::notifyConnectorPopupMenuSelected (GraphConnectorComponent* changedConnector) { if (listener) listener->connectorPopupMenuSelected (changedConnector); } void GraphNodeComponent::notifyLinkPopupMenuSelected (GraphLinkComponent* changedLink) { if (listener) listener->linkPopupMenuSelected (changedLink); } void GraphNodeComponent::notifyLinkConnected (GraphLinkComponent* changedLink) { if (listener) listener->linkConnected (changedLink); } void GraphNodeComponent::notifyLinkDisconnected (GraphLinkComponent* changedLink) { if (listener) listener->linkDisconnected (changedLink); } void GraphNodeComponent::notifyGraphChanged () { if (listener) listener->graphChanged (); } Colour GraphNodeComponent::getConnectorColour (GraphConnectorComponent* connector, const bool isSelected) { if (listener) return listener->getConnectorColour (connector, isSelected); return Colours::black; } //============================================================================== bool GraphNodeComponent::connectToNode (const int sourcePort, GraphNodeComponent* destinationNode, const int destinationPort, const bool notifyListeners) { GraphConnectorComponentOutput* out = getOutputConnector (sourcePort); GraphConnectorComponentInput* in = destinationNode->getInputConnector (destinationPort); if (out != 0 && in != 0) { GraphLinkComponent* newLink = new GraphLinkComponent (leftToRight); newLink->from = out; if (in->canConnectFrom (newLink)) { int startX = out->getBounds ().getCentreX (); int startY = out->getBounds ().getCentreY (); int endX = in->getBounds ().getCentreX (); int endY = in->getBounds ().getCentreY (); newLink->setStartPoint (startX, startY); newLink->setEndPoint (endX, endY); if (in->connectFrom (newLink)) { out->addLink (newLink); if (notifyListeners) notifyGraphChanged (); getParentComponent()->addAndMakeVisible (newLink); newLink->toBack (); newLink->repaint(); return true; } } deleteAndZero (newLink); } return false; } //============================================================================== bool GraphNodeComponent::isAfterNode (GraphNodeComponent* nodeBefore) { bool findConnector = false; for (int j = getInputConnectorCount (); --j >= 0;) { GraphConnectorComponent* connector = getInputConnector (j); if (connector) { Array linked; connector->getLinkedConnectors (linked); for (int c = linked.size (); --c >= 0;) { GraphConnectorComponent* other = linked.getUnchecked (c); if (other) { if (other->getParentGraphComponent() == nodeBefore) { return true; } else { findConnector = other->getParentGraphComponent()->isAfterNode (nodeBefore); } } if (findConnector) return true; } } } return findConnector; } bool GraphNodeComponent::isBeforeNode (GraphNodeComponent* nodeAfter) { bool findConnector = false; for (int j = getOutputConnectorCount (); --j >= 0;) { GraphConnectorComponent* connector = getOutputConnector (j); if (connector) { Array linked; connector->getLinkedConnectors (linked); for (int c = linked.size (); --c >= 0;) { GraphConnectorComponent* other = linked.getUnchecked (c); if (other) { if (other->getParentGraphComponent() == nodeAfter) { return true; } else { findConnector = other->getParentGraphComponent()->isBeforeNode (nodeAfter); } } if (findConnector) return true; } } } return findConnector; } //============================================================================== void GraphNodeComponent::updateConnectors() { float x_pos, y_pos, y_pos_step; float height, width; if (leftToRight) { height = (float) getHeight(); width = (float) getWidth(); } else { width = (float) getHeight(); height = (float) getWidth(); } if (graphInputs.size() > 0) { y_pos_step = height / (float) graphInputs.size(); x_pos = 0.0; y_pos = (height / 2) - (((float)y_pos_step * ((float) graphInputs.size() - 1.0f)) / 2.0f); for (int i = 0; i < graphInputs.size(); i++) { if (leftToRight) graphInputs[i]->setRelativePosition ((int) (x_pos - graphInputs[i]->getWidth()), (int) (y_pos - graphInputs[i]->getWidth() / 2)); else graphInputs[i]->setRelativePosition ((int) (y_pos - graphInputs[i]->getWidth() / 2), (int) (x_pos - graphInputs[i]->getWidth())); y_pos += y_pos_step; } } if (graphOutputs.size() > 0) { y_pos_step = height / (float) graphOutputs.size(); x_pos = width; y_pos = (height / 2) - (((float)y_pos_step * ((float) graphOutputs.size() - 1.0f)) / 2.0f); for(int i = 0; i < graphOutputs.size(); i++) { if (leftToRight) graphOutputs[i]->setRelativePosition ((int) (x_pos), (int) (y_pos - graphOutputs[i]->getWidth() / 2)); else graphOutputs[i]->setRelativePosition ((int) (y_pos - graphOutputs[i]->getWidth() / 2), (int) (x_pos)); y_pos += y_pos_step; } } } END_JUCE_NAMESPACE