Пример #1
0
void MainHostWindow::filesDropped (const StringArray& files, int x, int y)
{
    if (files.size() == 1 && File (files[0]).hasFileExtension (filenameSuffix))
    {
        GraphDocumentComponent* const graphEditor = getGraphEditor();

        if (graphEditor != 0
             && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
        {
            graphEditor->graph.loadFrom (File (files[0]), true);
        }
    }
    else
    {
        OwnedArray <PluginDescription> typesFound;
        knownPluginList.scanAndAddDragAndDroppedFiles (files, typesFound);

        GraphDocumentComponent* const graphEditor = getGraphEditor();
        if (graphEditor != 0)
            relativePositionToOtherComponent (graphEditor, x, y);

        for (int i = 0; i < jmin (5, typesFound.size()); ++i)
            createPlugin (typesFound.getUnchecked(i), x, y);
    }
}
Пример #2
0
bool MainHostWindow::tryToQuitApplication()
{
    if (getGraphEditor() != 0
        && getGraphEditor()->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
    {
        JUCEApplication::quit();
        return true;
    }

    return false;
}
Пример #3
0
bool MainHostWindow::tryToQuitApplication()
{
    PluginWindow::closeAllCurrentlyOpenWindows();

    if (getGraphEditor() == nullptr
         || getGraphEditor()->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
    {
        JUCEApplication::quit();
        return true;
    }

    return false;
}
Пример #4
0
void MainHostWindow::createPlugin (const PluginDescription* desc, int x, int y)
{
    GraphDocumentComponent* const graphEditor = getGraphEditor();

    if (graphEditor != nullptr)
        graphEditor->createNewPlugin (desc, x, y);
}
Пример #5
0
void MainHostWindow::showAudioSettings()
{
    AudioDeviceSelectorComponent audioSettingsComp (deviceManager,
                                                    0, 256,
                                                    0, 256,
                                                    true, true, true, false);

    audioSettingsComp.setSize (500, 450);

    DialogWindow::showModalDialog (T("Audio Settings"),
                                   &audioSettingsComp,
                                   this,
                                   Colours::azure,
                                   true);

    XmlElement* const audioState = deviceManager.createStateXml();

    ApplicationProperties::getInstance()->getUserSettings()
        ->setValue (T("audioDeviceState"), audioState);

    delete audioState;

    ApplicationProperties::getInstance()->getUserSettings()->saveIfNeeded();

    GraphDocumentComponent* const graphEditor = getGraphEditor();

    if (graphEditor != 0)
        graphEditor->graph.removeIllegalConnections();
}
Пример #6
0
void NodeComponent::mouseUp (const MouseEvent& e)
{
  if (e.mouseWasClicked() && e.getNumberOfClicks() == 2)
  {
    if (const AudioProcessorGraph::Node::Ptr f = audioEngine.getDoc().getNodeForId (nodeID))
    {
      AudioProcessor* const processor = f->getProcessor();
      if(!InternalPluginFormat::isInternalFormat(processor->getName()))
      {
        if (PluginWindow* const w = PluginWindow::getWindowFor (f, PluginWindow::Generic/*Normal*/))
          w->toFront (true);
      }
    }
  }
  else if (! e.mouseWasClicked())
  {
    audioEngine.getDoc().setChangedFlag (true);
    
    if (moving)
    {
      moving = false;
      audioEngine.getDoc().beginTransaction();
      audioEngine.getDoc().perform(new MoveNodeAction(audioEngine, *getGraphEditor(), nodeID, startPos, endPos), "move node");
    }
  }
}
Пример #7
0
void MainHostWindow::showAudioSettings()
{
    AudioDeviceSelectorComponent audioSettingsComp (deviceManager,
                                                    0, 256,
                                                    0, 256,
                                                    true, true, true, false);

    audioSettingsComp.setSize (500, 450);

    DialogWindow::LaunchOptions o;
    o.content.setNonOwned (&audioSettingsComp);
    o.dialogTitle                   = "Audio Settings";
    o.componentToCentreAround       = this;
    o.dialogBackgroundColour        = Colours::azure;
    o.escapeKeyTriggersCloseButton  = true;
    o.useNativeTitleBar             = false;
    o.resizable                     = false;

    o.runModal();

    ScopedPointer<XmlElement> audioState (deviceManager.createStateXml());

    appProperties->getUserSettings()->setValue ("audioDeviceState", audioState);
    appProperties->getUserSettings()->saveIfNeeded();

    GraphDocumentComponent* const graphEditor = getGraphEditor();

    if (graphEditor != nullptr)
        graphEditor->graph.removeIllegalConnections();
}
Пример #8
0
AudioProcessorGraph* FilterIOConfigurationWindow::getGraph() const
{
    if (auto* graphEditor = getGraphEditor())
        if (auto* panel = graphEditor->graph.get())
            return &panel->graph;

    return nullptr;
}
Пример #9
0
void PinComponent::mouseDown (const MouseEvent& e)
{
  getGraphEditor()->beginConnectorDrag (isInput ? 0 : nodeID,
                                       index,
                                       isInput ? nodeID : 0,
                                       index,
                                       e);
}
Пример #10
0
void FilterIOConfigurationWindow::update()
{
    auto nodeID = getNodeID();

    if (auto* graph = getGraph())
        if (nodeID != AudioProcessorGraph::NodeID())
            graph->disconnectNode (nodeID);

    if (auto* graphEditor = getGraphEditor())
        if (auto* panel = graphEditor->graphPanel.get())
            panel->updateComponents();
}
Пример #11
0
bool MainHostWindow::perform (const InvocationInfo& info)
{
    GraphDocumentComponent* const graphEditor = getGraphEditor();

    switch (info.commandID)
    {
    case CommandIDs::open:
        if (graphEditor != 0 && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
            graphEditor->graph.loadFromUserSpecifiedFile (true);

        break;

    case CommandIDs::save:
        if (graphEditor != 0)
            graphEditor->graph.save (true, true);
        break;

    case CommandIDs::saveAs:
        if (graphEditor != 0)
            graphEditor->graph.saveAs (File::nonexistent, true, true, true);
        break;

    case CommandIDs::showPluginListEditor:
        if (PluginListWindow::currentPluginListWindow == 0)
            PluginListWindow::currentPluginListWindow = new PluginListWindow (knownPluginList);

        PluginListWindow::currentPluginListWindow->toFront (true);
        break;

    case CommandIDs::showAudioSettings:
        showAudioSettings();
        break;

    case CommandIDs::aboutBox:
        {
/*            AboutBoxComponent aboutComp;

            DialogWindow::showModalDialog (T("About"),
                                           &aboutComp,
                                           this, Colours::white,
                                           true, false, false);
  */      }

        break;

    default:
        return false;
    }

    return true;
}
Пример #12
0
void ConnectorComponent::mouseDrag (const MouseEvent& e)
{
  if ((! dragging) && ! e.mouseWasClicked())
  {
    dragging = true;
    
    audioEngine.getDoc().removeConnection (sourceNodeId, sourceNodeChannel, destNodeId, destNodeChannel);
    
    double distanceFromStart, distanceFromEnd;
    getDistancesFromEnds (e.x, e.y, distanceFromStart, distanceFromEnd);
    const bool isNearerSource = (distanceFromStart < distanceFromEnd);
    
    getGraphEditor()->beginConnectorDrag (isNearerSource ? 0 : sourceNodeId,
                                         sourceNodeChannel,
                                         isNearerSource ? destNodeId : 0,
                                         destNodeChannel,
                                         e);
  }
  else if (dragging)
  {
    getGraphEditor()->dragConnector (e);
  }
}
Пример #13
0
void ConnectorComponent::getPoints (float& x1, float& y1, float& x2, float& y2) const
{
  x1 = lastInputX;
  y1 = lastInputY;
  x2 = lastOutputX;
  y2 = lastOutputY;
  
  if (GraphEditor* const hostPanel = getGraphEditor())
  {
    if (NodeComponent* srcNodeComp = hostPanel->getComponentForNode (sourceNodeId))
      srcNodeComp->getPinPos (sourceNodeChannel, false, x1, y1);
    
    if (NodeComponent* dstNodeComp = hostPanel->getComponentForNode (destNodeId))
      dstNodeComp->getPinPos (destNodeChannel, true, x2, y2);
  }
}
Пример #14
0
void NodeComponent::mouseDrag (const MouseEvent& e)
{
  if (! e.mods.isPopupMenu())
  {
    Point<int> pos (originalPos + Point<int> (e.getDistanceFromDragStartX(), e.getDistanceFromDragStartY()));
    
    if (getParentComponent() != nullptr)
      pos = getParentComponent()->getLocalPoint (nullptr, pos);
    
    endPos.x = (pos.getX() + getWidth() / 2) / (double) getParentWidth();
    endPos.y = (pos.getY() + getHeight() / 2) / (double) getParentHeight();
    
    audioEngine.getDoc().setNodePosition (nodeID, endPos.x, endPos.y);
    
    getGraphEditor()->updateComponents();
  }
}
Пример #15
0
bool MainHostWindow::perform (const InvocationInfo& info)
{
    GraphDocumentComponent* const graphEditor = getGraphEditor();

    switch (info.commandID)
    {
    case CommandIDs::open:
        if (graphEditor != nullptr && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
            graphEditor->graph.loadFromUserSpecifiedFile (true);

        break;

    case CommandIDs::save:
        if (graphEditor != nullptr)
            graphEditor->graph.save (true, true);
        break;

    case CommandIDs::saveAs:
        if (graphEditor != nullptr)
            graphEditor->graph.saveAs (File::nonexistent, true, true, true);
        break;

    case CommandIDs::showPluginListEditor:
        if (pluginListWindow == nullptr)
            pluginListWindow = new PluginListWindow (*this, formatManager);

        pluginListWindow->toFront (true);
        break;

    case CommandIDs::showAudioSettings:
        showAudioSettings();
        break;

    case CommandIDs::aboutBox:
        // TODO
        break;

    default:
        return false;
    }

    return true;
}
Пример #16
0
void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/)
{
    GraphDocumentComponent* const graphEditor = getGraphEditor();

    if (menuItemID == 250)
    {
        if (graphEditor != 0)
            graphEditor->graph.clear();
    }
    else if (menuItemID >= 100 && menuItemID < 200)
    {
        RecentlyOpenedFilesList recentFiles;
        recentFiles.restoreFromString (ApplicationProperties::getInstance()->getUserSettings()
                                            ->getValue ("recentFilterGraphFiles"));

        if (graphEditor != 0 && graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
            graphEditor->graph.loadFrom (recentFiles.getFile (menuItemID - 100), true);
    }
    else if (menuItemID >= 200 && menuItemID < 210)
    {
        if (menuItemID == 200)
            pluginSortMethod = KnownPluginList::defaultOrder;
        else if (menuItemID == 201)
            pluginSortMethod = KnownPluginList::sortAlphabetically;
        else if (menuItemID == 202)
            pluginSortMethod = KnownPluginList::sortByCategory;
        else if (menuItemID == 203)
            pluginSortMethod = KnownPluginList::sortByManufacturer;
        else if (menuItemID == 204)
            pluginSortMethod = KnownPluginList::sortByFileSystemLocation;

        ApplicationProperties::getInstance()->getUserSettings()
           ->setValue (T("pluginSortMethod"), (int) pluginSortMethod);
    }
    else
    {
        createPlugin (getChosenType (menuItemID),
                      proportionOfWidth (0.3f + Random::getSystemRandom().nextFloat() * 0.6f),
                      proportionOfHeight (0.3f + Random::getSystemRandom().nextFloat() * 0.6f));
    }
}
Пример #17
0
void NodeComponent::paint (Graphics& g)
{
  g.setColour (Colours::lightgrey);
  
  if (highlight)
    g.setColour (Colours::white);

  const int x = 4;
  const int y = pinSize;
  const int w = getWidth() - x * 2;
  const int h = getHeight() - pinSize * 2;
  
  g.fillRect(x, y, w, h);

  g.setColour (Colours::black);
  
  if (getGraphEditor()->getLassoSelection().isSelected(this))
    g.setColour (Colours::black);
  else
    g.setColour (Colours::grey);
  
  g.drawRoundedRectangle(x, y, w, h, 2, 1);
}
Пример #18
0
void MainHostWindow::filesDropped (const StringArray& files, int x, int y)
{
    GraphDocumentComponent* const graphEditor = getGraphEditor();

    if (graphEditor != nullptr)
    {
        if (files.size() == 1 && File (files[0]).hasFileExtension (filenameSuffix))
        {
            if (graphEditor->graph.saveIfNeededAndUserAgrees() == FileBasedDocument::savedOk)
                graphEditor->graph.loadFrom (File (files[0]), true);
        }
        else
        {
            OwnedArray <PluginDescription> typesFound;
            knownPluginList.scanAndAddDragAndDroppedFiles (formatManager, files, typesFound);

            Point<int> pos (graphEditor->getLocalPoint (this, Point<int> (x, y)));

            for (int i = 0; i < jmin (5, typesFound.size()); ++i)
                createPlugin (typesFound.getUnchecked(i), pos.getX(), pos.getY());
        }
    }
}
Пример #19
0
void NodeComponent::mouseDown (const MouseEvent& e)
{
  originalPos = localPointToGlobal (Point<int>());
  
  toFront (true);
  
  if (e.mods.isPopupMenu())
  {
    PopupMenu m;
    m.addItem (1, TRANS("Delete this node"));
    m.addItem (2, TRANS("Disconnect all pins"));
    
    if (AudioProcessorGraph::Node::Ptr f = audioEngine.getDoc().getNodeForId (nodeID))
    {
      AudioProcessor* const processor = f->getProcessor();
      jassert (processor != nullptr);
      
      if(!InternalPluginFormat::isInternalFormat(processor->getName()))
      {
        bool hasParams = (processor->getNumParameters() > 0);
        
        m.addItem (3, TRANS("Add a pMix Preset"), hasParams);
        m.addItem (4, TRANS("Set pMix Colour"), hasParams);
        m.addItem (5, TRANS("Interpolate all Parameters"), hasParams);
        m.addItem (6, TRANS("Clear all Parameters"), hasParams);
        m.addSeparator();
        
        int uiStatus = f->properties["uiStatus"];
        
        PopupMenu ui;
        ui.addItem (7, TRANS("Disabled"), true, uiStatus == kUIStatusNone);
        ui.addItem (8, TRANS("Embedded"), true, uiStatus == kUIStatusEmbed);
//        ui.addItem (9, TRANS("Floating"), true, uiStatus == kUIStatusFloating);

        m.addSubMenu (TRANS("Set UI Mode"), ui);
      }
    }
    
    const int r = m.show();
    
    if (r == 1)
    {
      if (AudioProcessorGraph::Node::Ptr f = audioEngine.getDoc().getNodeForId (nodeID))
      {
        AudioPluginInstance* const instance = dynamic_cast<AudioPluginInstance*>(f->getProcessor());
      
        if (instance)
        {
          removeEditor();
          
          audioEngine.getDoc().beginTransaction();
          audioEngine.getDoc().perform(new RemoveNodeAction(audioEngine, *getGraphEditor(), nodeID), TRANS("remove node"));
          
          getGraphEditor()->clearSelection();
        }
      }
      return;
    }
    else if (r == 2)
    {
      audioEngine.getDoc().disconnectNode (nodeID);
    }
    else if (r == 3)
    {
      Random rand;
      audioEngine.getDoc().addPreset(nodeID, rand.nextFloat(), rand.nextFloat());
    }
    else if (r == 4)
    {
      ColourSelector* colourSelector = new ColourSelector(ColourSelector::showSliders|ColourSelector::showColourAtTop|ColourSelector::showColourspace);
      colourSelector->setName ("background");
      colourSelector->setCurrentColour (audioEngine.getDoc().getNodeColour(nodeID));
      colourSelector->addChangeListener (this);
      colourSelector->setColour (ColourSelector::backgroundColourId, Colours::lightgrey);
      colourSelector->setSize (300, 400);
      
      CallOutBox::launchAsynchronously (colourSelector, getScreenBounds(), nullptr);
    }
    else if (r == 5 || r == 6)
    {
      if (AudioProcessorGraph::Node::Ptr f = audioEngine.getDoc().getNodeForId (nodeID))

      {
        for (int p=0; p < f->getProcessor()->getNumParameters(); p++)
        {
          audioEngine.getDoc().setParameterToInterpolate(nodeID, p, r==5);
        }
        
        repaint();
      }
    }
    else if (r == 7)
    {
      audioEngine.getDoc().setNodeUIStatus(nodeID, kUIStatusNone);
      removeEditor();
      PluginWindow::closeCurrentlyOpenWindowsFor(nodeID);
      update();
    }
    else if (r == 8)
    {
      audioEngine.getDoc().setNodeUIStatus(nodeID, kUIStatusEmbed);
      PluginWindow::closeCurrentlyOpenWindowsFor(nodeID);
      update();
    }
//    else if (r == 9)
//    {
//      if (AudioProcessorGraph::Node::Ptr f = audioEngine.getDoc().getNodeForId (nodeID))
//      {
//        AudioProcessor* const processor = f->getProcessor();
//        jassert (processor != nullptr);
//        
//        String name = processor->getName();
//        
//        if (r > 0)
//        {
//          removeEditor();
//
//          PluginWindow::WindowFormatType type = processor->hasEditor() ? PluginWindow::Normal : PluginWindow::Generic;
//          
//          if (PluginWindow* const w = PluginWindow::getWindowFor (f, type))
//            w->toFront (true);
//          
//          audioEngine.getDoc().setNodeUIStatus(nodeID, kUIStatusFloating);
//          update();
//        };
//      }
//    }
  }
  else
  {
    moving = true;
    getGraphEditor()->getLassoSelection().selectOnly(this);
    audioEngine.getDoc().getNodePosition(nodeID, startPos.x, startPos.y);
  }
}
Пример #20
0
void PinComponent::mouseUp (const MouseEvent& e)
{
  getGraphEditor()->endDraggingConnector (e);
}
Пример #21
0
void PinComponent::mouseDrag (const MouseEvent& e)
{
  getGraphEditor()->dragConnector (e);
}
Пример #22
0
void ConnectorComponent::mouseUp (const MouseEvent& e)
{
  if (dragging)
    getGraphEditor()->endDraggingConnector (e);
}
Пример #23
0
BlockPropertiesPanel::BlockPropertiesPanel(GraphBlock *block, QWidget *parent):
    QWidget(parent),
    _ignoreChanges(true),
    _idLabel(new QLabel(this)),
    _idLineEdit(new QLineEdit(this)),
    _affinityZoneLabel(new QLabel(this)),
    _affinityZoneBox(nullptr),
    _blockErrorLabel(new QLabel(this)),
    _updateTimer(new QTimer(this)),
    _formLayout(nullptr),
    _block(block)
{
    auto blockDesc = block->getBlockDesc();

    //master layout for this widget
    auto layout = new QVBoxLayout(this);

    //create a scroller and a form layout
    auto scroll = new QScrollArea(this);
    scroll->setWidgetResizable(true);
    scroll->setWidget(new QWidget(scroll));
    _formLayout = new QFormLayout(scroll);
    scroll->widget()->setLayout(_formLayout);
    layout->addWidget(scroll);

    //title
    {
        auto label = new QLabel(QString("<h1>%1</h1>").arg(_block->getTitle().toHtmlEscaped()), this);
        label->setAlignment(Qt::AlignCenter);
        _formLayout->addRow(label);
    }

    //errors
    {
        _formLayout->addRow(_blockErrorLabel);
    }

    //id
    {
        _idOriginal = _block->getId();
        _formLayout->addRow(_idLabel, _idLineEdit);
        connect(_idLineEdit, SIGNAL(textEdited(const QString &)), this, SLOT(handleEditWidgetChanged(const QString &)));
        connect(_idLineEdit, SIGNAL(returnPressed(void)), this, SLOT(handleCommitButton(void)));
    }

    //properties
    for (const auto &prop : _block->getProperties())
    {
        _propIdToOriginal[prop.getKey()] = _block->getPropertyValue(prop.getKey());
        auto paramDesc = _block->getParamDesc(prop.getKey());
        assert(paramDesc);

        //create editable widget
        auto editWidget = new BlockPropertyEditWidget(paramDesc, this);
        connect(editWidget, SIGNAL(valueChanged(void)), this, SLOT(handleEditWidgetChanged(void)));
        connect(editWidget, SIGNAL(commitRequested(void)), this, SLOT(handleCommitButton(void)));
        _propIdToEditWidget[prop.getKey()] = editWidget;

        //create labels
        _propIdToFormLabel[prop.getKey()] = new QLabel(this);
        _propIdToErrorLabel[prop.getKey()] = new QLabel(this);
        editWidget->setToolTip(this->getParamDocString(_block->getParamDesc(prop.getKey())));

        //layout stuff
        auto editLayout = new QVBoxLayout();
        editLayout->addWidget(editWidget);
        editLayout->addWidget(_propIdToErrorLabel[prop.getKey()]);
        _formLayout->addRow(_propIdToFormLabel[prop.getKey()], editLayout);
    }

    //affinity zone
    {
        _affinityZoneOriginal = _block->getAffinityZone();
        auto dock = dynamic_cast<AffinityZonesDock *>(getObjectMap()["affinityZonesDock"]);
        assert(dock != nullptr);
        _affinityZoneBox = dock->makeComboBox(this);
        _formLayout->addRow(_affinityZoneLabel, _affinityZoneBox);
        connect(_affinityZoneBox, SIGNAL(activated(const QString &)), this, SLOT(handleEditWidgetChanged(const QString &)));
    }

    //draw the block's preview onto a mini pixmap
    //this is cool, maybe useful, but its big, where can we put it?
    /*
    {
        const auto bounds = _block->getBoundingRect();
        QPixmap pixmap(bounds.size().toSize()+QSize(2,2));
        pixmap.fill(Qt::transparent);
        QPainter painter(&pixmap);
        painter.translate(-bounds.topLeft()+QPoint(1,1));
        painter.setRenderHint(QPainter::Antialiasing);
        painter.setRenderHint(QPainter::HighQualityAntialiasing);
        painter.setRenderHint(QPainter::SmoothPixmapTransform);
        _block->render(painter);
        painter.end();
        auto label = new QLabel(this);
        label->setPixmap(pixmap);
        _formLayout->addRow(label);
        _formLayout->setAlignment(label, Qt::AlignHCenter);
    }
    */

    //block level description
    if (blockDesc->isArray("docs"))
    {
        QString output;
        output += QString("<h1>%1</h1>").arg(QString::fromStdString(blockDesc->get("name").convert<std::string>()));
        output += QString("<p>%1</p>").arg(QString::fromStdString(block->getBlockDescPath()));
        output += "<p>";
        for (const auto &lineObj : *blockDesc->getArray("docs"))
        {
            const auto line = lineObj.extract<std::string>();
            if (line.empty()) output += "<p /><p>";
            else output += QString::fromStdString(line)+"\n";
        }
        output += "</p>";

        //enumerate properties
        if (not _block->getProperties().empty())
        {
            output += QString("<h2>%1</h2>").arg(tr("Properties"));
            for (const auto &prop : _block->getProperties())
            {
                output += this->getParamDocString(_block->getParamDesc(prop.getKey()));
            }
        }

        //enumerate slots
        if (not block->getSlotPorts().empty())
        {
            output += QString("<h2>%1</h2>").arg(tr("Slots"));
            output += "<ul>";
            for (const auto &port : block->getSlotPorts())
            {
                output += QString("<li>%1(...)</li>").arg(port.getName());
            }
            output += "</ul>";
        }

        //enumerate signals
        if (not block->getSignalPorts().empty())
        {
            output += QString("<h2>%1</h2>").arg(tr("Signals"));
            output += "<ul>";
            for (const auto &port : block->getSignalPorts())
            {
                output += QString("<li>%1(...)</li>").arg(port.getName());
            }
            output += "</ul>";
        }

        auto text = new QLabel(output, this);
        text->setStyleSheet("QLabel{background:white;margin:1px;}");
        text->setWordWrap(true);
        _formLayout->addRow(text);
    }

    //buttons
    {
        auto buttonLayout = new QHBoxLayout();
        layout->addLayout(buttonLayout);
        auto commitButton = new QPushButton(makeIconFromTheme("dialog-ok-apply"), tr("Commit"), this);
        connect(commitButton, SIGNAL(pressed(void)), this, SLOT(handleCommitButton(void)));
        buttonLayout->addWidget(commitButton);
        auto cancelButton = new QPushButton(makeIconFromTheme("dialog-cancel"), tr("Cancel"), this);
        connect(cancelButton, SIGNAL(pressed(void)), this, SLOT(handleCancelButton(void)));
        buttonLayout->addWidget(cancelButton);
    }

    //update timer
    _updateTimer->setSingleShot(true);
    _updateTimer->setInterval(UPDATE_TIMER_MS);
    connect(_updateTimer, SIGNAL(timeout(void)), this, SLOT(handleUpdateTimerExpired(void)));

    //connect state change to the graph editor
    auto draw = dynamic_cast<GraphDraw *>(_block->parent());
    auto editor = draw->getGraphEditor();
    connect(this, SIGNAL(stateChanged(const GraphState &)), editor, SLOT(handleStateChange(const GraphState &)));
    connect(_block, SIGNAL(destroyed(QObject*)), this, SLOT(handleBlockDestroyed(QObject*)));

    this->updateAllForms();
    _ignoreChanges = false;
}