Ejemplo n.º 1
0
MultipleKnobEditsUndoCommand::MultipleKnobEditsUndoCommand(const KnobGuiPtr& knob,
                                                           ValueChangedReasonEnum reason,
                                                           bool createNew,
                                                           bool setKeyFrame,
                                                           const Variant & value,
                                                           int dimension,
                                                           double time)
    : QUndoCommand()
    , knobs()
    , createNew(createNew)
    , firstRedoCalled(false)
    , _reason(reason)
{
    assert(knob);
    std::list<ValueToSet>& vlist = knobs[knob];
    ValueToSet v;
    v.newValue = value;
    v.dimension = dimension;
    assert(dimension != -1);
    v.time = time;
    v.setKeyFrame = setKeyFrame;
    v.setValueRetCode = -1;
    vlist.push_back(v);

    KnobHolder* holder = knob->getKnob()->getHolder();
    EffectInstance* effect = dynamic_cast<EffectInstance*>(holder);
    QString holderName;
    if (effect) {
        holderName = QString::fromUtf8( effect->getNode()->getLabel().c_str() );
    }

    setText( tr("Multiple edits for %1").arg(holderName) );
}
Ejemplo n.º 2
0
LinkToKnobDialog::LinkToKnobDialog(const KnobGuiPtr& from,
                                   QWidget* parent)
    : QDialog(parent)
    , _imp( new LinkToKnobDialogPrivate(from) )
{
    _imp->mainLayout = new QVBoxLayout(this);

    _imp->firstLine = new QWidget(this);
    _imp->firstLineLayout = new QHBoxLayout(_imp->firstLine);

    _imp->mainLayout->addWidget(_imp->firstLine);

    _imp->buttons = new QDialogButtonBox(QDialogButtonBox::StandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel),
                                         Qt::Horizontal, this);
    QObject::connect( _imp->buttons, SIGNAL(accepted()), this, SLOT(accept()) );
    QObject::connect( _imp->buttons, SIGNAL(rejected()), this, SLOT(reject()) );
    _imp->mainLayout->addWidget(_imp->buttons);

    _imp->selectNodeLabel = new Label(tr("Parent:"), _imp->firstLine);
    _imp->firstLineLayout->addWidget(_imp->selectNodeLabel);


    EffectInstance* isEffect = dynamic_cast<EffectInstance*>( from->getKnob()->getHolder() );
    assert(isEffect);
    if (!isEffect) {
        throw std::logic_error("");
    }
    boost::shared_ptr<NodeCollection> group = isEffect->getNode()->getGroup();
    group->getActiveNodes(&_imp->allNodes);
    NodeGroup* isGroup = dynamic_cast<NodeGroup*>( group.get() );
    if (isGroup) {
        _imp->allNodes.push_back( isGroup->getNode() );
    }
    QStringList nodeNames;
    for (NodesList::iterator it = _imp->allNodes.begin(); it != _imp->allNodes.end(); ++it) {
        QString name = QString::fromUtf8( (*it)->getLabel().c_str() );
        nodeNames.push_back(name);
        //_imp->nodeSelectionCombo->addItem(name);
    }
    nodeNames.sort();
    _imp->nodeSelectionCombo = new CompleterLineEdit(nodeNames, nodeNames, false, this);
    _imp->nodeSelectionCombo->setToolTip( GuiUtils::convertFromPlainText(tr("Input the name of a node in the current project."), Qt::WhiteSpaceNormal) );
    _imp->firstLineLayout->addWidget(_imp->nodeSelectionCombo);


    _imp->nodeSelectionCombo->setFocus(Qt::PopupFocusReason);
    QTimer::singleShot( 25, _imp->nodeSelectionCombo, SLOT(showCompleter()) );

    _imp->knobSelectionCombo = new ComboBox(_imp->firstLine);
    _imp->firstLineLayout->addWidget(_imp->knobSelectionCombo);

    QObject::connect( _imp->nodeSelectionCombo, SIGNAL(itemCompletionChosen()), this, SLOT(onNodeComboEditingFinished()) );

    _imp->firstLineLayout->addStretch();
}
Ejemplo n.º 3
0
void
KnobFile::reloadFile()
{
    assert( getHolder() );
    EffectInstance* effect = dynamic_cast<EffectInstance*>( getHolder() );
    if (effect) {
        effect->purgeCaches();
        effect->clearPersistentMessage(false);
    }
    evaluateValueChange(0, getCurrentTime(), ViewIdx(0), eValueChangedReasonNatronInternalEdited);
}
Ejemplo n.º 4
0
void
NodeGraph::removeNode(const NodeGuiPtr & node)
{
 
    NodeGroup* isGrp = node->getNode()->isEffectGroup();
    const KnobsVec & knobs = node->getNode()->getKnobs();

    
    for (U32 i = 0; i < knobs.size(); ++i) {
        KnobI::ListenerDimsMap listeners;
        knobs[i]->getListeners(listeners);
        ///For all listeners make sure they belong to a node
        bool foundEffect = false;
        for (KnobI::ListenerDimsMap::iterator it2 = listeners.begin(); it2 != listeners.end(); ++it2) {
            KnobPtr listener = it2->first.lock();
            if (!listener) {
                continue;
            }
            EffectInstance* isEffect = dynamic_cast<EffectInstance*>(listener->getHolder());
            if (!isEffect) {
                continue;
            }
            if (isGrp && isEffect->getNode()->getGroup().get() == isGrp) {
                continue;
            }
            
            if ( isEffect && ( isEffect != node->getNode()->getEffectInstance().get() ) ) {
                foundEffect = true;
                break;
            }
        }
        if (foundEffect) {
            StandardButtonEnum reply = Dialogs::questionDialog( tr("Delete").toStdString(), tr("This node has one or several "
                                                                                                  "parameters from which other parameters "
                                                                                                  "of the project rely on through expressions "
                                                                                                  "or links. Deleting this node will "
                                                                                                  "remove these expressions  "
                                                                                                  "and undoing the action will not recover "
                                                                                                  "them. Do you wish to continue ?")
                                                                   .toStdString(), false );
            if (reply == eStandardButtonNo) {
                return;
            }
            break;
        }
    }

    node->setUserSelected(false);
    NodesGuiList nodesToRemove;
    nodesToRemove.push_back(node);
    pushUndoCommand( new RemoveMultipleNodesCommand(this,nodesToRemove) );
}
Ejemplo n.º 5
0
quint32*
EffectUser::applyFilters( const Workflow::Frame* frame,
                             qint64 currentFrame, double time )
{
    QReadLocker     lock( m_effectsLock );

    if ( m_filters.size() == 0 )
        return NULL;
    EffectsEngine::EffectList::const_iterator     it = m_filters.constBegin();
    EffectsEngine::EffectList::const_iterator     ite = m_filters.constEnd();

    quint32         *buff1 = NULL;
    quint32         *buff2 = NULL;
    const quint32   *input = frame->buffer();
    bool            firstBuff = true;

    while ( it != ite )
    {
        if ( (*it)->begin() < currentFrame &&
             ( (*it)->end() < 0 || (*it)->end() > currentFrame ) )
        {
            quint32     **buff;
            if ( firstBuff == true )
                buff = &buff1;
            else
                buff = &buff2;
            if ( *buff == NULL )
                *buff = new quint32[frame->nbPixels()];
            EffectInstance      *effect = (*it)->effectInstance();
            effect->process( time, input, *buff );
            input = *buff;
            firstBuff = !firstBuff;
        }
        ++it;
    }
    if ( buff1 != NULL || buff2 != NULL )
    {
        if ( firstBuff == true )
        {
            delete[] buff1;
            return buff2;
        }
        else
        {
            delete[] buff2;
            return buff1;
        }
    }
    return NULL;
}
Ejemplo n.º 6
0
void
KnobGui::onCreateAliasOnGroupActionTriggered()
{
    KnobHolder* holder = getKnob()->getHolder();
    EffectInstance* isEffect = dynamic_cast<EffectInstance*>(holder);

    assert(isEffect);
    if (!isEffect) {
        throw std::logic_error("");
    }
    boost::shared_ptr<NodeCollection> collec = isEffect->getNode()->getGroup();
    NodeGroup* isCollecGroup = dynamic_cast<NodeGroup*>( collec.get() );
    assert(isCollecGroup);
    if (isCollecGroup) {
        createDuplicateOnNode(isCollecGroup, true, boost::shared_ptr<KnobPage>(), boost::shared_ptr<KnobGroup>(), -1);
    }
}
Ejemplo n.º 7
0
void
QtWriter::getFrameRange(double *first,
                        double *last)
{
    int index = _frameRangeChoosal->getValue();

    if (index == 0) {
        EffectInstance* inp = getInput(0);
        if (inp) {
            inp->getFrameRange_public(inp->getRenderHash(), first, last);
        } else {
            *first = 0;
            *last = 0;
        }
    } else if (index == 1) {
        getApp()->getFrameRange(first, last);
    } else {
        *first = _firstFrameKnob->getValue();
        *last = _lastFrameKnob->getValue();
    }
}
Ejemplo n.º 8
0
void
QtWriter::getFrameRange(SequenceTime *first,
                        SequenceTime *last)
{
    int index = _frameRangeChoosal->getValue();

    if (index == 0) {
        EffectInstance* inp = getInput(0);
        if (inp) {
            inp->getFrameRange_public(inp->getRenderHash(),first, last);
        } else {
            *first = 0;
            *last = 0;
        }
    } else if (index == 1) {
        *first = getApp()->getTimeLine()->leftBound();
        *last = getApp()->getTimeLine()->rightBound();
    } else {
        *first = _firstFrameKnob->getValue();
        *last = _lastFrameKnob->getValue();
    }
}
Ejemplo n.º 9
0
void
KnobGui::linkTo(int dimension)
{
    KnobPtr thisKnob = getKnob();

    assert(thisKnob);
    EffectInstance* isEffect = dynamic_cast<EffectInstance*>( thisKnob->getHolder() );
    if (!isEffect) {
        return;
    }


    for (int i = 0; i < thisKnob->getDimension(); ++i) {
        if ( (i == dimension) || (dimension == -1) ) {
            std::string expr = thisKnob->getExpression(dimension);
            if ( !expr.empty() ) {
                Dialogs::errorDialog( tr("Param Link").toStdString(), tr("This parameter already has an expression set, edit or clear it.").toStdString() );

                return;
            }
        }
    }

    LinkToKnobDialog dialog( shared_from_this(), _imp->copyRightClickMenu->parentWidget() );

    if ( dialog.exec() ) {
        KnobPtr otherKnob = dialog.getSelectedKnobs();
        if (otherKnob) {
            if ( !thisKnob->isTypeCompatible(otherKnob) ) {
                Dialogs::errorDialog( tr("Param Link").toStdString(), tr("Types are incompatible!").toStdString() );

                return;
            }


            for (int i = 0; i < thisKnob->getDimension(); ++i) {
                std::pair<int, KnobPtr > existingLink = thisKnob->getMaster(i);
                if (existingLink.second) {
                    Dialogs::errorDialog( tr("Param Link").toStdString(),
                                          tr("Cannot link %1 because the knob is already linked to %2.")
                                          .arg( QString::fromUtf8( thisKnob->getLabel().c_str() ) )
                                          .arg( QString::fromUtf8( existingLink.second->getLabel().c_str() ) )
                                          .toStdString() );

                    return;
                }
            }

            EffectInstance* otherEffect = dynamic_cast<EffectInstance*>( otherKnob->getHolder() );
            if (!otherEffect) {
                return;
            }

            std::stringstream expr;
            boost::shared_ptr<NodeCollection> thisCollection = isEffect->getNode()->getGroup();
            NodeGroup* otherIsGroup = dynamic_cast<NodeGroup*>(otherEffect);
            if ( otherIsGroup == thisCollection.get() ) {
                expr << "thisGroup"; // make expression generic if possible
            } else {
                expr << otherEffect->getNode()->getFullyQualifiedName();
            }
            expr << "." << otherKnob->getName() << ".get()";
            if (otherKnob->getDimension() > 1) {
                expr << "[dimension]";
            }

            thisKnob->beginChanges();
            for (int i = 0; i < thisKnob->getDimension(); ++i) {
                if ( (i == dimension) || (dimension == -1) ) {
                    thisKnob->setExpression(i, expr.str(), false, false);
                }
            }
            thisKnob->endChanges();


            thisKnob->getHolder()->getApp()->triggerAutoSave();
        }
    }
} // KnobGui::linkTo
Ejemplo n.º 10
0
void
RestoreDefaultsCommand::redo()
{
    std::list<SequenceTime> times;
    KnobPtr first = _knobs.front().lock();
    AppInstance* app = 0;
    KnobHolder* holder = first->getHolder();
    EffectInstance* isEffect = dynamic_cast<EffectInstance*>(holder);

    if (holder) {
        app = holder->getApp();
        holder->beginChanges();
    }


    /*
       First reset all knobs values, this will not call instanceChanged action
     */
    for (std::list<KnobWPtr >::iterator it = _knobs.begin(); it != _knobs.end(); ++it) {
        KnobPtr itKnob = it->lock();
        if (!itKnob) {
            continue;
        }
        if ( itKnob->getHolder() && itKnob->getHolder()->getApp() ) {
            int dim = itKnob->getDimension();
            for (int i = 0; i < dim; ++i) {
                if ( (i == _targetDim) || (_targetDim == -1) ) {
                    boost::shared_ptr<Curve> c = itKnob->getCurve(ViewIdx(0), i);
                    if (c) {
                        KeyFrameSet kfs = c->getKeyFrames_mt_safe();
                        for (KeyFrameSet::iterator it = kfs.begin(); it != kfs.end(); ++it) {
                            times.push_back( std::floor(it->getTime() + 0.5) );
                        }
                    }
                }
            }
        }

        if ( itKnob->getHolder() ) {
            itKnob->getHolder()->beginChanges();
        }
        itKnob->blockValueChanges();

        for (int d = 0; d < itKnob->getDimension(); ++d) {
            itKnob->resetToDefaultValue(d);
        }

        itKnob->unblockValueChanges();

        if ( itKnob->getHolder() ) {
            itKnob->getHolder()->endChanges(true);
        }
    }

    /*
       Block value changes and call instanceChange on all knobs  afterwards to put back the plug-in
       in a correct state
     */
    double time = 0;
    if (app) {
        time = app->getTimeLine()->currentFrame();
    }
    for (std::list<KnobWPtr >::iterator it = _knobs.begin(); it != _knobs.end(); ++it) {
        KnobPtr itKnob = it->lock();
        if (!itKnob) {
            continue;
        }
        if ( itKnob->getHolder() ) {
            itKnob->getHolder()->onKnobValueChanged_public(itKnob.get(), eValueChangedReasonRestoreDefault, time, ViewIdx(0), true);
        }
    }

    if (app) {
        app->removeMultipleKeyframeIndicator(times, true);
    }


    if ( holder && holder->getApp() ) {
        holder->endChanges();
    }

    if (_isNodeReset && isEffect) {
        isEffect->purgeCaches();
    }


    if ( first->getHolder() ) {
        first->getHolder()->incrHashAndEvaluate(true, true);
        if ( first->getHolder()->getApp() ) {
            first->getHolder()->getApp()->redrawAllViewers();
        }
    }
    setText( tr("Restore default value(s)") );
} // RestoreDefaultsCommand::redo
void
ProjectGuiSerialization::initialize(const ProjectGui* projectGui)
{
    NodesList activeNodes;

    projectGui->getInternalProject()->getActiveNodesExpandGroups(&activeNodes);

    _serializedNodes.clear();
    for (NodesList::iterator it = activeNodes.begin(); it != activeNodes.end(); ++it) {
        boost::shared_ptr<NodeGuiI> nodegui_i = (*it)->getNodeGui();
        if (!nodegui_i) {
            continue;
        }
        NodeGuiPtr nodegui = boost::dynamic_pointer_cast<NodeGui>(nodegui_i);

        if ( nodegui->isVisible() ) {
            boost::shared_ptr<NodeCollection> isInCollection = (*it)->getGroup();
            NodeGroup* isCollectionAGroup = dynamic_cast<NodeGroup*>( isInCollection.get() );
            if (!isCollectionAGroup) {
                ///Nodes within a group will be serialized recursively in the node group serialization
                NodeGuiSerialization state;
                nodegui->serialize(&state);
                _serializedNodes.push_back(state);
            }
            ViewerInstance* viewer = (*it)->isEffectViewer();
            if (viewer) {
                ViewerTab* tab = projectGui->getGui()->getViewerTabForInstance(viewer);
                assert(tab);
                ViewerData viewerData;
                double zoompar;
                tab->getViewer()->getProjection(&viewerData.zoomLeft, &viewerData.zoomBottom, &viewerData.zoomFactor, &zoompar);
                viewerData.userRoI = tab->getViewer()->getUserRegionOfInterest();
                viewerData.userRoIenabled = tab->getViewer()->isUserRegionOfInterestEnabled();
                viewerData.isClippedToProject = tab->isClippedToProject();
                viewerData.autoContrastEnabled = tab->isAutoContrastEnabled();
                viewerData.gain = tab->getGain();
                viewerData.gamma = tab->getGamma();
                viewerData.colorSpace = tab->getColorSpace();
                viewerData.channels = tab->getChannelsString();
                viewerData.renderScaleActivated = tab->getRenderScaleActivated();
                viewerData.mipMapLevel = tab->getMipMapLevel();
                viewerData.zoomOrPanSinceLastFit = tab->getZoomOrPannedSinceLastFit();
                viewerData.wipeCompositingOp = (int)tab->getCompositingOperator();
                viewerData.leftToolbarVisible = tab->isLeftToolbarVisible();
                viewerData.rightToolbarVisible = tab->isRightToolbarVisible();
                viewerData.topToolbarVisible = tab->isTopToolbarVisible();
                viewerData.infobarVisible = tab->isInfobarVisible();
                viewerData.playerVisible = tab->isPlayerVisible();
                viewerData.timelineVisible = tab->isTimelineVisible();
                viewerData.checkerboardEnabled = tab->isCheckerboardEnabled();
                viewerData.fps = tab->getDesiredFps();
                viewerData.fpsLocked = tab->isFPSLocked();
                viewerData.isPauseEnabled[0] = tab->isViewerPaused(0);
                viewerData.isPauseEnabled[1] = tab->isViewerPaused(1);
                viewerData.layerName = tab->getCurrentLayerName().toStdString();
                viewerData.alphaLayerName = tab->getCurrentAlphaLayerName().toStdString();
                tab->getTimelineBounds(&viewerData.leftBound, &viewerData.rightBound);
                tab->getActiveInputs(&viewerData.aChoice, &viewerData.bChoice);
                viewerData.version = VIEWER_DATA_SERIALIZATION_VERSION;
                _viewersData.insert( std::make_pair(viewer->getNode()->getScriptName_mt_safe(), viewerData) );
            }
        }
    }

    ///Init windows
    _layoutSerialization.initialize( projectGui->getGui() );

    ///save histograms
    std::list<Histogram*> histograms = projectGui->getGui()->getHistograms_mt_safe();
    for (std::list<Histogram*>::const_iterator it = histograms.begin(); it != histograms.end(); ++it) {
        _histograms.push_back( (*it)->objectName().toStdString() );
    }

    ///save opened panels by order

    std::list<DockablePanel*> panels = projectGui->getGui()->getVisiblePanels_mt_safe();
    for (std::list<DockablePanel*>::iterator it = panels.begin(); it != panels.end(); ++it) {
        if ( (*it)->isVisible() ) {
            KnobHolder* holder = (*it)->getHolder();
            assert(holder);

            EffectInstance* isEffect = dynamic_cast<EffectInstance*>(holder);
            Project* isProject = dynamic_cast<Project*>(holder);

            if (isProject) {
                _openedPanelsOrdered.push_back(kNatronProjectSettingsPanelSerializationName);
            } else if (isEffect) {
                _openedPanelsOrdered.push_back( isEffect->getNode()->getFullyQualifiedName() );
            }
        }
    }

    _scriptEditorInput = projectGui->getGui()->getScriptEditor()->getAutoSavedScript().toStdString();

    std::map<NATRON_PYTHON_NAMESPACE::PyPanel*, std::string> pythonPanels = projectGui->getGui()->getPythonPanels();
    for (std::map<NATRON_PYTHON_NAMESPACE::PyPanel*, std::string>::iterator it = pythonPanels.begin(); it != pythonPanels.end(); ++it) {
        boost::shared_ptr<PythonPanelSerialization> s(new PythonPanelSerialization);
        s->initialize(it->first, it->second);
        _pythonPanels.push_back(s);
    }
} // initialize
Ejemplo n.º 12
0
void
KnobGuiButton::createWidget(QHBoxLayout* layout)
{
    boost::shared_ptr<KnobButton> knob = _knob.lock();
    QString label = QString::fromUtf8( knob->getLabel().c_str() );
    QString onIconFilePath = QString::fromUtf8( knob->getIconLabel(true).c_str() );
    QString offIconFilePath = QString::fromUtf8( knob->getIconLabel(false).c_str() );


    if ( !onIconFilePath.isEmpty() && !QFile::exists(onIconFilePath) ) {
        EffectInstance* isEffect = dynamic_cast<EffectInstance*>( knob->getHolder() );
        if (isEffect) {
            //Prepend the resources path
            QString resourcesPath = QString::fromUtf8( isEffect->getNode()->getPluginResourcesPath().c_str() );
            if ( !resourcesPath.endsWith( QLatin1Char('/') ) ) {
                resourcesPath += QLatin1Char('/');
            }
            onIconFilePath.prepend(resourcesPath);
        }
    }
    if ( !offIconFilePath.isEmpty() && !QFile::exists(offIconFilePath) ) {
        EffectInstance* isEffect = dynamic_cast<EffectInstance*>( knob->getHolder() );
        if (isEffect) {
            //Prepend the resources path
            QString resourcesPath = QString::fromUtf8( isEffect->getNode()->getPluginResourcesPath().c_str() );
            if ( !resourcesPath.endsWith( QLatin1Char('/') ) ) {
                resourcesPath += QLatin1Char('/');
            }
            offIconFilePath.prepend(resourcesPath);
        }
    }

    bool checkable = knob->getIsCheckable();
    QIcon icon;
    QPixmap pixChecked, pixUnchecked;
    if ( !offIconFilePath.isEmpty() ) {
        if ( pixUnchecked.load(offIconFilePath) ) {
            icon.addPixmap(pixUnchecked, QIcon::Normal, QIcon::Off);
        }
    }
    if ( !onIconFilePath.isEmpty() ) {
        if ( pixChecked.load(onIconFilePath) ) {
            icon.addPixmap(pixChecked, QIcon::Normal, QIcon::On);
        }
    }

    if ( !icon.isNull() ) {
        _button = new Button( icon, QString(), layout->parentWidget() );
        _button->setFixedSize(NATRON_MEDIUM_BUTTON_SIZE, NATRON_MEDIUM_BUTTON_SIZE);
        _button->setIconSize( QSize(NATRON_MEDIUM_BUTTON_ICON_SIZE, NATRON_MEDIUM_BUTTON_ICON_SIZE) );
    } else {
        _button = new Button( label, layout->parentWidget() );
    }
    if (checkable) {
        _button->setCheckable(true);
        bool checked = knob->getValue();
        _button->setChecked(checked);
        _button->setDown(checked);
    }
    QObject::connect( _button, SIGNAL(clicked(bool)), this, SLOT(emitValueChanged(bool)) );
    if ( hasToolTip() ) {
        _button->setToolTip( toolTip() );
    }
    layout->addWidget(_button);
} // KnobGuiButton::createWidget
Ejemplo n.º 13
0
void
NodeGraph::deleteSelection()
{
    if ( !_imp->_selection.empty()) {
        NodesGuiList nodesToRemove = _imp->_selection;

        
        ///For all backdrops also move all the nodes contained within it
        for (NodesGuiList::iterator it = nodesToRemove.begin(); it != nodesToRemove.end(); ++it) {
            NodesGuiList nodesWithinBD = getNodesWithinBackdrop(*it);
            for (NodesGuiList::iterator it2 = nodesWithinBD.begin(); it2 != nodesWithinBD.end(); ++it2) {
                NodesGuiList::iterator found = std::find(nodesToRemove.begin(),nodesToRemove.end(),*it2);
                if ( found == nodesToRemove.end()) {
                    nodesToRemove.push_back(*it2);
                }
            }
        }


        for (NodesGuiList::iterator it = nodesToRemove.begin(); it != nodesToRemove.end(); ++it) {
            
            const KnobsVec & knobs = (*it)->getNode()->getKnobs();
            bool mustBreak = false;
            
            NodeGroup* isGrp = (*it)->getNode()->isEffectGroup();
            
            for (U32 i = 0; i < knobs.size(); ++i) {
                KnobI::ListenerDimsMap listeners;
                knobs[i]->getListeners(listeners);

                ///For all listeners make sure they belong to a node
                bool foundEffect = false;
                for (KnobI::ListenerDimsMap::iterator it2 = listeners.begin(); it2 != listeners.end(); ++it2) {
                    KnobPtr listener = it2->first.lock();
                    if (!listener) {
                        continue;
                    }
                    EffectInstance* isEffect = dynamic_cast<EffectInstance*>(listener->getHolder() );
                    
                    if (!isEffect) {
                        continue;
                    }
                    if (isGrp && isEffect->getNode()->getGroup().get() == isGrp) {
                        continue;
                    }
                    
                    if (!isEffect->getNode()->getGroup()) {
                        continue;
                    }
                    
                    if ( isEffect && ( isEffect != (*it)->getNode()->getEffectInstance().get() ) ) {
                        foundEffect = true;
                        break;
                    }
                }
                if (foundEffect) {
                    StandardButtonEnum reply = Dialogs::questionDialog( tr("Delete").toStdString(),
                                                                           tr("This node has one or several "
                                                                              "parameters from which other parameters "
                                                                              "of the project rely on through expressions "
                                                                              "or links. Deleting this node will "
                                                                              "may break these expressions."
                                                                              "\nContinue anyway ?")
                                                                           .toStdString(), false );
                    if (reply == eStandardButtonNo) {
                        return;
                    }
                    mustBreak = true;
                    break;
                }
            }
            if (mustBreak) {
                break;
            }
        }


        for (NodesGuiList::iterator it = nodesToRemove.begin(); it != nodesToRemove.end(); ++it) {
            (*it)->setUserSelected(false);
        }


        pushUndoCommand( new RemoveMultipleNodesCommand(this,nodesToRemove) );
        _imp->_selection.clear();
    }
} // deleteSelection