void GraphComponent::nodeSelected (GraphNodeComponent* node)
{
    DBG ("GraphComponent::nodeSelected");

    jassert (node != 0)

    switch (selectedNodes.getNumSelected ())
    {
    case 0: // no items selected
        {
            BasePlugin* plugin = (BasePlugin*) node->getUserData ();
            if (plugin)
                plugin->setValue (PROP_GRAPHSELECTED, 1);

            selectedNodes.addToSelection (node);

            node->repaint ();
        }
        break;
    default: // one or more items selected
        {
            if (! selectedNodes.isSelected (node))
            {
                for (int i = 0; i < selectedNodes.getNumSelected (); i++)
                {
                    GraphNodeComponent* selected = selectedNodes.getSelectedItem (i);

                    BasePlugin* plugin = (BasePlugin*) selected->getUserData ();
                    if (plugin)
                        plugin->setValue (PROP_GRAPHSELECTED, 0);

                    selected->repaint ();
                }

                selectedNodes.deselectAll ();

                BasePlugin* plugin = (BasePlugin*) node->getUserData ();
                if (plugin)
                    plugin->setValue (PROP_GRAPHSELECTED, 1);

                selectedNodes.addToSelection (node);

                node->repaint ();
            }
        }
        break;
    }
}
bool GraphComponent::loadAndAppendPlugin (const File& file, 
										  int mouseX,
										  int mouseY)
{
    DBG ("GraphComponent::loadAndAppendPlugin");

    jassert (host != 0);

    BasePlugin* plugin = host->loadPlugin (file);
    if (plugin)
    {
        Config::getInstance ()->addRecentPlugin (file);

		plugin->setValue(PROP_GRAPHXPOS, mouseX);
		plugin->setValue(PROP_GRAPHYPOS, mouseY);

        host->addPlugin (plugin);

        createPluginNode (plugin);

        return true;
    }

    return false;
}
void GraphComponent::mouseUp (const MouseEvent& e)
{
    if (e.mods.isLeftButtonDown())
    {
        lassoComponent->endLasso ();
    }
    else if (e.mods.isMiddleButtonDown ())
    {
    }
    else if (e.mods.isRightButtonDown ())
    {
        // try to open internal plugins
        BasePlugin* newPlugin = PluginLoader::handlePopupMenu (false, 0, owner->getFilter());

        // now open plugin
        if (host && newPlugin)
        {
            host->openPlugin (newPlugin);
            host->addPlugin (newPlugin);

            newPlugin->setValue (PROP_GRAPHXPOS, e.x);
            newPlugin->setValue (PROP_GRAPHYPOS, e.y);

            createPluginNode (newPlugin);
        }
    }
    
    lassoComponent->endLasso ();
    setMouseCursor (MouseCursor::NormalCursor);
}
void GraphComponent::nodeMoved (GraphNodeComponent* node, const int deltaX, const int deltaY)
{
//    DBG ("GraphComponent::nodeMoved " + String (deltaX) + " " + String (deltaY));

    jassert (node != 0);

    for (int i = 0; i < selectedNodes.getNumSelected (); i++)
    {
        GraphNodeComponent* selected = selectedNodes.getSelectedItem (i);
        if (selected
            && selected != node
            && ! selected->isLocked ())
        {
            BasePlugin* plugin = (BasePlugin*) selected->getUserData ();
            if (plugin)
            {
                int x = selected->getX() + deltaX;
                int y = selected->getY() + deltaY;

                x = jmax (0, jmin (getWidth () - selected->getWidth(), x));
                y = jmax (0, jmin (getHeight () - selected->getHeight(), y));
            
                plugin->setValue (PROP_GRAPHXPOS, x);
                plugin->setValue (PROP_GRAPHYPOS, y);
                
                selected->setTopLeftPosition (x, y);
            }
        }
    }

    BasePlugin* plugin = (BasePlugin*) node->getUserData ();
    if (plugin)
    {
        plugin->setValue (PROP_GRAPHXPOS, node->getX());
        plugin->setValue (PROP_GRAPHYPOS, node->getY());
    }

    Viewport* parent = findParentComponentOfClass ((Viewport*) 0);
    if (parent)
    {
        parent->notifyComponentChanged ();
    }
}
//==============================================================================
void GraphComponent::changeListenerCallback (void* source)
{
    ColourSelector* cs = (ColourSelector*) source;

    if (currentClickedNode)
    {
        Colour currentColour = cs->getCurrentColour();   
        
        BasePlugin* plugin = (BasePlugin*) currentClickedNode->getUserData ();
        if (plugin)
            plugin->setValue (PROP_GRAPHCOLOUR, currentColour.toString ());
        
        currentClickedNode->setNodeColour (currentColour);
        currentClickedNode->repaint ();

        Viewport* parent = findParentComponentOfClass ((Viewport*) 0);
        if (parent)
        {
            parent->notifyComponentChanged ();
        }
    }
}
void GraphComponent::nodePopupMenuSelected (GraphNodeComponent* node)
{
    DBG ("GraphComponent::nodePopupMenuSelected");

    jassert (node != 0);

    currentClickedNode = node;

    bool addFirstSeparator = false;
    PopupMenu menu, subMenu;

    BasePlugin* plugin = (BasePlugin*) node->getUserData ();

    if (plugin->hasEditor ()
        || plugin->wantsEditor ()
        || plugin->isEditorInternal ())
    {
        menu.addItem (1, "Open editor");
        addFirstSeparator = true;
    }

    if (node != inputs && node != outputs)
    {
        menu.addItem (2, "Remove " + plugin->getName());
        addFirstSeparator = true;
    }

    if (addFirstSeparator)
        menu.addSeparator ();

    menu.addItem (3, "Lock", true, node->isLocked ());

    // node colour..
    ColourSelector colourSelector;
    colourSelector.setName (T("background"));
    colourSelector.setCurrentColour (node->getNodeColour ());
    colourSelector.addChangeListener (this);

    subMenu.addCustomItem (1234, &colourSelector, 300, 300, false);
    menu.addSubMenu (T("Colour"), subMenu);
    menu.addSeparator ();

    menu.addItem (4, "Mute", true, plugin->isMuted ());
    menu.addItem (5, "Bypass", true, plugin->isBypass ());
    menu.addItem (6, "Solo", false, false);
    menu.addSeparator ();

    int inputLinks = node->getInputLinksCount ();
    int outputLinks = node->getOutputLinksCount ();

    if (inputLinks > 0 && outputLinks > 0)
        menu.addItem (7, "Disconnect all");
    if (inputLinks > 0)
        menu.addItem (8, "Disconnect inputs");
    if (outputLinks > 0)
        menu.addItem (9, "Disconnect outputs");

    const int result = menu.show();
    switch (result)
    {
    case 1: // Open editor
        {
            owner->openPluginEditorWindow (plugin);
            break;
        }

    case 2: // Close
        {
            if (owner->isPluginEditorWindowOpen (plugin))
                owner->closePluginEditorWindow (plugin);

            if (plugin)
            {
                host->closePlugin (plugin);

                selectedNodes.deselect (node);
                deletePluginNode (node);

                graphChanged ();
            }

            break;
        }
    case 3: // Lock / unlock
        {
            bool lockedState = ! node->isLocked ();

            if (plugin)
                plugin->setValue (PROP_GRAPHLOCKED, lockedState);

            node->setLocked (lockedState);
            node->repaint ();
        }
        break;
    case 4: // Mute
        {
            if (plugin)
                plugin->setMuted (! plugin->isMuted());
        }
        break;
    case 5: // Bypass
        {
            if (plugin)
                plugin->setBypass (! plugin->isBypass());
        }
        break;
    case 6: // Solo (TODO)
        break;
    case 7: // Disconnect all
        node->breakAllLinks();
        break;
    case 8: // Disconnect inputs
        node->breakInputLinks();
        break;
    case 9: // Disconnect outputs
        node->breakOutputLinks();
        break;
    default:
        break;
    }
    
    currentClickedNode = 0;
}
void GraphComponent::nodePopupMenuSelected (GraphNodeComponent* node)
{
    DBG ("GraphComponent::nodePopupMenuSelected");

    jassert (node != 0);

    currentClickedNode = node;

    bool addFirstSeparator = false;
    PopupMenu menu, subMenu, synthMidiChanMenu, midiChanMenu;

    BasePlugin* plugin = (BasePlugin*) node->getUserData ();

    if (plugin->hasEditor ()
        || plugin->wantsEditor ()
        || plugin->isEditorInternal ())
    {
        menu.addItem (1, "Open editor");
        menu.addItem (2, "Prefer MIDI binding editor", true, plugin->getBoolValue(PROP_WINDOWPREFERGENERIC, false));
        addFirstSeparator = true;
    }

    if (node != inputs && node != outputs)
    {
        menu.addItem (3, "Remove " + plugin->getName() + String(" \"") + plugin->getInstanceName() + String("\""));
        addFirstSeparator = true;
    }

    if (addFirstSeparator)
        menu.addSeparator ();

    menu.addItem (4, "Lock", true, node->isLocked ());

    // node colour..
    ColourSelector colourSelector;
    colourSelector.setName (T("background"));
    colourSelector.setCurrentColour (node->getNodeColour ());
    colourSelector.addChangeListener (this);

    subMenu.addCustomItem (1234, &colourSelector, 300, 300, false);
    menu.addSubMenu (T("Colour"), subMenu);
    menu.addSeparator ();

    menu.addItem (5, "Mute", true, plugin->isMuted ());
    menu.addItem (6, "Bypass", true, plugin->isBypass ());
    menu.addItem (7, "Solo", false, false);
    menu.addSeparator ();

   synthMidiChanMenu.addItem(2020, "Omni", true, !plugin->getSynthInputChannelFilter() || plugin->getSynthInputChannel() == -1);
   midiChanMenu.addItem(1020, "Omni", true, !plugin->getMidiOutputChannelFilter() || plugin->getMidiOutputChannel() == -1);
   for (int i=1; i<17; i++)
   {
      synthMidiChanMenu.addItem(2000+i, String("Ch ") + String(i), true, plugin->getSynthInputChannelFilter() && plugin->getSynthInputChannel() == i);
      midiChanMenu.addItem(1000+i, String("Ch ") + String(i), true, plugin->getMidiOutputChannelFilter() && plugin->getMidiOutputChannel() == i);
   }
   menu.addSubMenu (T("Filter synth input MIDI channel"), synthMidiChanMenu);
   menu.addSubMenu (T("Filter output MIDI channel"), midiChanMenu);
   menu.addSeparator ();

    int inputLinks = node->getInputLinksCount ();
    int outputLinks = node->getOutputLinksCount ();

    if (inputLinks > 0 && outputLinks > 0)
        menu.addItem (8, "Disconnect all");
    if (inputLinks > 0)
        menu.addItem (9, "Disconnect inputs");
    if (outputLinks > 0)
        menu.addItem (10, "Disconnect outputs");

   menu.addItem (11, String("Rename \"") + plugin->getInstanceName() + String("\""));
   menu.addItem (12, "Render stem", true, plugin->getBoolValue(PROP_RENDERSTEM, false)); // only allow changing this if stems aren't rendering right now (i.e. files aren't opened)
   
    const int result = menu.show();
    switch (result)
    {
    case 1: // Open editor
        {
            owner->openPluginEditorWindow (plugin);
            break;
        }
    case 2: // set preferred editor
      plugin->setValue(PROP_WINDOWPREFERGENERIC, !plugin->getBoolValue(PROP_WINDOWPREFERGENERIC, false));
      break;
    case 12: // set stem render option
      plugin->setValue(PROP_RENDERSTEM, !plugin->getBoolValue(PROP_RENDERSTEM, false));
      break;
    case 3: // Close
        {
            if (owner->isPluginEditorWindowOpen (plugin))
                owner->closePluginEditorWindow (plugin);

            if (plugin)
            {
                host->closePlugin (plugin);

                selectedNodes.deselect (node);
                deletePluginNode (node);

                graphChanged ();
            }

            break;
        }
    case 4: // Lock / unlock
        {
            bool lockedState = ! node->isLocked ();

            if (plugin)
                plugin->setValue (PROP_GRAPHLOCKED, lockedState);

            node->setLocked (lockedState);
            node->repaint ();
        }
        break;
    case 5: // Mute
        {
            if (plugin)
                plugin->setMuted (! plugin->isMuted());
        }
        break;
    case 6: // Bypass
        {
            if (plugin)
                plugin->setBypass (! plugin->isBypass());
        }
        break;
    case 7: // Solo (TODO)
        break;
    case 8: // Disconnect all
        node->breakAllLinks();
        break;
    case 9: // Disconnect inputs
        node->breakInputLinks();
        break;
    case 10: // Disconnect outputs
        node->breakOutputLinks();
        break;
    case 11: // Rename instance
    {
      TextEditor instanceName(String("renamePluginInstance"));
      instanceName.setSize(400, 30);
      instanceName.setText(plugin->getInstanceName());
      DialogWindow::showModalDialog(String("Rename " + plugin->getInstanceName()), &instanceName, node, Colours::yellow, true);
      String newName = instanceName.getText();
      if (newName.isNotEmpty())
         setNodeDisplayName(plugin, node, newName);
    }
      break;
   case 1020:
      plugin->clearMidiOutputFilter();
      break;
   case 2020:
      plugin->clearSynthInputFilter();
      break;
    default:
      if (result >= 1001 && result <= 1016)
         plugin->setMidiOutputChannelFilter(result - 1000);
      if (result >= 2001 && result <= 2016)
         plugin->setSynthInputChannelFilter(result - 2000);
      break;
    }
    
    currentClickedNode = 0;
}