void GuiPrivate::notifyGuiClosing() { ///This is to workaround an issue that when destroying a widget it calls the focusOut() handler hence can ///cause bad pointer dereference to the Gui object since we're destroying it. std::list<TabWidgetI*> tabs = _gui->getApp()->getTabWidgetsSerialization(); for (std::list<TabWidgetI*>::iterator it = tabs.begin(); it != tabs.end(); ++it) { TabWidget* tab = dynamic_cast<TabWidget*>(*it); if (!tab) { continue; } tab->discardGuiPointer(); for (int i = 0; i < tab->count(); ++i) { tab->tabAt(i)->notifyGuiClosingPublic(); } } const NodesGuiList allNodes = _nodeGraphArea->getAllActiveNodes(); // we do not need this list anymore, avoid using it _gui->getApp()->clearSettingsPanels(); for (NodesGuiList::const_iterator it = allNodes.begin(); it != allNodes.end(); ++it) { DockablePanel* panel = (*it)->getSettingPanel(); if (panel) { panel->onGuiClosing(); } } _lastFocusedGraph = 0; }
void NodeGraph::setSelection(const NodesGuiList& nodes) { clearSelection(); for (NodesGuiList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { selectNode(*it, true); } }
void NodeGraph::decloneSelectedNodes() { if ( _imp->_selection.empty() ) { Dialogs::warningDialog( tr("Declone").toStdString(), tr("You must select at least a node to declone first.").toStdString() ); return; } std::map<NodeGuiPtr, NodePtr> nodesToDeclone; for (NodesGuiList::iterator it = _imp->_selection.begin(); it != _imp->_selection.end(); ++it) { BackdropGuiPtr isBd = toBackdropGui(*it); if (isBd) { // Also clone all nodes within the backdrop NodesGuiList nodesWithinBD = getNodesWithinBackdrop(*it); for (NodesGuiList::iterator it2 = nodesWithinBD.begin(); it2 != nodesWithinBD.end(); ++it2) { std::map<NodeGuiPtr, NodePtr>::iterator found = nodesToDeclone.find(*it2); if ( found == nodesToDeclone.end() ) { std::list<NodePtr> linkedNodes; (*it2)->getNode()->getCloneLinkedNodes(&linkedNodes); if (!linkedNodes.empty()) { nodesToDeclone[*it2] = linkedNodes.front(); } } } } std::list<NodePtr> linkedNodes; (*it)->getNode()->getCloneLinkedNodes(&linkedNodes); if (!linkedNodes.empty()) { nodesToDeclone[*it] = linkedNodes.front(); } } pushUndoCommand( new DecloneMultipleNodesCommand(this, nodesToDeclone) ); }
void NodeGraph::refreshNodeLinksNow() { // First clear all previous links for (NodeGraphPrivate::LinkedNodesList::const_iterator it = _imp->linkedNodes.begin(); it != _imp->linkedNodes.end(); ++it) { delete it->arrow; } _imp->linkedNodes.clear(); if (!_imp->_knobLinksVisible) { return; } NodesGuiList nodes; { QMutexLocker k(&_imp->_nodesMutex); nodes = _imp->_nodes; } QColor simpleLinkColor, cloneLinkColor; { double exprColor[3], cloneColor[3]; appPTR->getCurrentSettings()->getExprColor(&exprColor[0], &exprColor[1], &exprColor[2]); appPTR->getCurrentSettings()->getCloneColor(&cloneColor[0], &cloneColor[1], &cloneColor[2]); simpleLinkColor.setRgbF(Image::clamp(exprColor[0], 0., 1.), Image::clamp(exprColor[1], 0., 1.), Image::clamp(exprColor[2], 0., 1.)); cloneLinkColor.setRgbF(Image::clamp(cloneColor[0], 0., 1.), Image::clamp(cloneColor[1], 0., 1.), Image::clamp(cloneColor[2], 0., 1.)); } for (NodesGuiList::const_iterator it = nodes.begin(); it!=nodes.end(); ++it) { NodePtr thisNode = (*it)->getNode(); std::list<std::pair<NodePtr, bool> > linkedNodes; thisNode->getLinkedNodes(&linkedNodes); for (std::list<std::pair<NodePtr, bool> >::const_iterator it2 = linkedNodes.begin(); it2 != linkedNodes.end(); ++it2) { // If the linked node doesn't have a gui or it is not part of this nodegraph don't create a link at all NodeGuiPtr otherNodeGui = toNodeGui(it2->first->getNodeGui()); if (!otherNodeGui || otherNodeGui->getDagGui() != this) { continue; } // Try to find an existing link NodeGraphPrivate::LinkedNodes link; link.nodes[0] = thisNode; link.nodes[1] = it2->first; link.isCloneLink = it2->second; NodeGraphPrivate::LinkedNodesList::iterator foundExistingLink = _imp->linkedNodes.end(); { for (NodeGraphPrivate::LinkedNodesList::iterator it = _imp->linkedNodes.begin(); it != _imp->linkedNodes.end(); ++it) { NodePtr a1 = it->nodes[0].lock(); NodePtr a2 = it->nodes[1].lock(); NodePtr b1 = link.nodes[0].lock(); NodePtr b2 = link.nodes[1].lock(); if ((a1 == b1 || a1 == b2) && (a2 == b1 || a2 == b2)) { foundExistingLink = it; break; } } } if (foundExistingLink == _imp->linkedNodes.end()) { // A link did not exist, create it link.arrow = new LinkArrow( otherNodeGui, *it, (*it)->parentItem() ); link.arrow->setWidth(2); link.arrow->setArrowHeadVisible(false); if (link.isCloneLink) { link.arrow->setColor(cloneLinkColor); } else { link.arrow->setColor(simpleLinkColor); } _imp->linkedNodes.push_back(link); } else { // A link existed, if this is a clone link and it was not before, upgrade it if (!foundExistingLink->isCloneLink && link.isCloneLink) { foundExistingLink->arrow->setColor(cloneLinkColor); } } } (*it)->refreshLinkIndicators(linkedNodes); } } // refreshNodeLinksNow
void Gui::renderSelectedNode() { NodeGraph* graph = getLastSelectedGraph(); if (!graph) { return; } NodesGuiList selectedNodes = graph->getSelectedNodes(); if ( selectedNodes.empty() ) { Dialogs::warningDialog( tr("Render").toStdString(), tr("You must select a node to render first!").toStdString() ); return; } std::list<AppInstance::RenderWork> workList; bool useStats = getApp()->isRenderStatsActionChecked(); for (NodesGuiList::const_iterator it = selectedNodes.begin(); it != selectedNodes.end(); ++it) { NodePtr internalNode = (*it)->getNode(); if (!internalNode) { continue; } EffectInstPtr effect = internalNode->getEffectInstance(); if (!effect) { continue; } if ( effect->isWriter() ) { if ( !effect->areKnobsFrozen() ) { //if ((*it)->getNode()->is) ///if the node is a writer, just use it to render! AppInstance::RenderWork w; w.writer = dynamic_cast<OutputEffectInstance*>( effect.get() ); assert(w.writer); w.firstFrame = INT_MIN; w.lastFrame = INT_MAX; w.frameStep = INT_MIN; w.useRenderStats = useStats; workList.push_back(w); } } else { if (selectedNodes.size() == 1) { ///create a node and connect it to the node and use it to render #ifndef NATRON_ENABLE_IO_META_NODES NodePtr writer = createWriter(); #else NodeGraph* graph = selectedNodes.front()->getDagGui(); NodePtr writer = getApp()->createWriter( "", eCreateNodeReasonInternal, graph->getGroup() ); #endif if (writer) { AppInstance::RenderWork w; w.writer = dynamic_cast<OutputEffectInstance*>( writer->getEffectInstance().get() ); assert(w.writer); w.firstFrame = INT_MIN; w.lastFrame = INT_MAX; w.frameStep = INT_MIN; w.useRenderStats = useStats; workList.push_back(w); } } } } _imp->_appInstance->startWritersRendering(false, workList); } // Gui::renderSelectedNode
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