void NodeAnimPrivate::computeReaderRange() { NodeGuiPtr nodeUI = nodeGui.lock(); NodePtr node = nodeUI->getNode(); if (!node) { return; } KnobIntBase *startingTimeKnob = dynamic_cast<KnobIntBase *>( node->getKnobByName(kReaderParamNameStartingTime).get() ); if (!startingTimeKnob) { return; } KnobIntBase *firstFrameKnob = dynamic_cast<KnobIntBase *>( node->getKnobByName(kReaderParamNameFirstFrame).get() ); if (!firstFrameKnob) { return; } KnobIntBase *lastFrameKnob = dynamic_cast<KnobIntBase *>( node->getKnobByName(kReaderParamNameLastFrame).get() ); if (!lastFrameKnob) { return; } int startingTimeValue = startingTimeKnob->getValue(); int firstFrameValue = firstFrameKnob->getValue(); int lastFrameValue = lastFrameKnob->getValue(); frameRange.min = startingTimeValue; frameRange.max = startingTimeValue + (lastFrameValue - firstFrameValue) + 1; } // computeReaderRange
void NodeAnim::initializeKnobsAnim() { // Create dope sheet knobs NodeGuiPtr nodeUI = _imp->nodeGui.lock(); assert(nodeUI); const KnobsVec& knobs = nodeUI->getNode()->getKnobs(); NodeAnimPtr thisShared = shared_from_this(); for (KnobsVec::const_iterator it = knobs.begin(); it != knobs.end(); ++it) { // If the knob is not animted, don't create an item if ( !(*it)->canAnimate() || !(*it)->isAnimationEnabled() ) { continue; } KnobAnimPtr knobAnimObject(KnobAnim::create(getModel(), thisShared, _imp->nameItem, *it)); _imp->knobs.push_back(knobAnimObject); } // for all knobs } // initializeKnobsAnim
NodePtr NodeAnim::getInternalNode() const { NodeGuiPtr node = getNodeGui(); return node ? node->getNode() : NodePtr(); }
void NodeAnimPrivate::refreshParentContainerRange() { NodeGuiPtr nodeUI = nodeGui.lock(); NodePtr node = nodeUI->getNode(); if (!node) { return; } AnimationModulePtr isAnimModule = toAnimationModule(model.lock()); assert(isAnimModule); // If inside a group, refresh the group { NodeGroupPtr parentGroup = toNodeGroup( node->getGroup() ); NodeAnimPtr parentGroupNodeAnim; if (parentGroup) { parentGroupNodeAnim = isAnimModule->findNodeAnim( parentGroup->getNode() ); } if (parentGroupNodeAnim) { parentGroupNodeAnim->refreshFrameRange(); } } // if modified by a time node, refresh its frame range as well { NodeAnimPtr isConnectedToTimeNode = isAnimModule->getNearestTimeNodeFromOutputsInternal(node); if (isConnectedToTimeNode) { isConnectedToTimeNode->refreshFrameRange(); } } } // refreshParentContainerRange
void NodeAnimPrivate::computeTimeOffsetRange() { NodeGuiPtr nodeUI = nodeGui.lock(); NodePtr node = nodeUI->getNode(); if (!node) { return; } // Retrieve nearest reader useful values { AnimationModulePtr isAnimModel = toAnimationModule(model.lock()); NodeAnimPtr nearestReader = isAnimModel->getNearestReaderInternal(node); if (nearestReader) { // Retrieve the time offset values KnobIntBasePtr timeOffsetKnob = toKnobIntBase(node->getKnobByName(kReaderParamNameTimeOffset)); assert(timeOffsetKnob); int timeOffsetValue = timeOffsetKnob->getValue(); frameRange = nearestReader->getFrameRange(); frameRange.min += timeOffsetValue; frameRange.max += timeOffsetValue; } } } // computeTimeOffsetRange
void NodeAnim::refreshVisibility() { AnimationModulePtr animModule = getModel(); QTreeWidgetItem *nodeItem = getTreeItem(); bool showNode = false; int nChildren = nodeItem->childCount(); // Refresh children, which will recursively refresh their children for (int i = 0; i < nChildren; ++i) { QTreeWidgetItem* child = nodeItem->child(i); AnimatedItemTypeEnum type; KnobAnimPtr isKnob; TableItemAnimPtr isTableItem; NodeAnimPtr isNodeItem; ViewSetSpec view; DimSpec dim; bool found = animModule->findItem(child, &type, &isKnob, &isTableItem, &isNodeItem, &view, &dim); if (!found) { continue; } if (isTableItem) { isTableItem->refreshVisibilityConditional(false /*refreshParent*/); } else if (isNodeItem) { isNodeItem->refreshVisibility(); } else if (isKnob) { isKnob->refreshVisibilityConditional(false /*refreshHolder*/); } if (!child->isHidden()) { showNode = true; } } if (!showNode) { // If so far none of the children should be displayed, still check if the node has a range if (isRangeDrawingEnabled()) { showNode = true; } } // If settings panel is not opened and the "Keep in Animation Module" knob is not checked, hide the node. NodeGuiPtr nodeGui = getNodeGui(); bool keepInAnimationModule = nodeGui->getNode()->isKeepInAnimationModuleButtonDown(); if (!keepInAnimationModule && !nodeGui->isSettingsPanelVisible()) { showNode = false; } refreshFrameRange(); nodeItem->setData(0, QT_ROLE_CONTEXT_IS_ANIMATED, showNode); nodeItem->setHidden(!showNode); } // refreshVisibility
bool AnimationModule::isNodeAnimated(const NodeGuiPtr &nodeGui) { NodePtr internalNode = nodeGui->getNode(); if (!internalNode) { return false; } return internalNode->getEffectInstance()->getHasAnimation(); }
void AnimationModule::addNode(const NodeGuiPtr& nodeGui) { // Check if it already exists for (std::list<NodeAnimPtr>::const_iterator it = _imp->nodes.begin(); it != _imp->nodes.end(); ++it) { if ((*it)->getNodeGui() == nodeGui) { (*it)->refreshVisibility(); return; } } // Determinate the node type // It will be useful to identify and sort tree items AnimatedItemTypeEnum nodeType = eAnimatedItemTypeCommon; NodePtr node = nodeGui->getNode(); assert(node && node->getGroup()); if ( !node || !node->getGroup() ) { return; } EffectInstancePtr effectInstance = node->getEffectInstance(); // Don't add an item for this node if it doesn't have any knob that may animate //if ( !getNodeCanAnimate(node) ) { // return; //} std::string pluginID = node->getPluginID(); NodeGroupPtr isGroup = toNodeGroup(effectInstance); if (effectInstance->isReader()) { nodeType = eAnimatedItemTypeReader; } else if (isGroup) { nodeType = eAnimatedItemTypeGroup; } else if (pluginID == PLUGINID_OFX_RETIME) { nodeType = eAnimatedItemTypeRetime; } else if (pluginID == PLUGINID_OFX_TIMEOFFSET) { nodeType = eAnimatedItemTypeTimeOffset; } else if (pluginID == PLUGINID_OFX_FRAMERANGE) { nodeType = eAnimatedItemTypeFrameRange; } // The NodeAnim should not be created if there's no settings panel. assert(nodeGui->getSettingPanel()); NodeAnimPtr anim(NodeAnim::create(toAnimationModule(shared_from_this()), nodeType, nodeGui) ); _imp->nodes.push_back(anim); Q_EMIT nodeAdded(anim); } // AnimationModule::addNode
NodeSettingsPanel::NodeSettingsPanel(const boost::shared_ptr<MultiInstancePanel> & multiPanel, Gui* gui, const NodeGuiPtr &NodeUi, QVBoxLayout* container, QWidget *parent) : DockablePanel(gui, multiPanel.get() != NULL ? dynamic_cast<KnobHolder*>( multiPanel.get() ) : NodeUi->getNode()->getEffectInstance().get(), container, DockablePanel::eHeaderModeFullyFeatured, false, NodeUi->getUndoStack(), QString::fromUtf8( NodeUi->getNode()->getLabel().c_str() ), QString::fromUtf8( NodeUi->getNode()->getPluginDescription().c_str() ), false, QString::fromUtf8("Settings"), parent) , _nodeGUI(NodeUi) , _selected(false) , _settingsButton(0) , _multiPanel(multiPanel) { if (multiPanel) { multiPanel->initializeKnobsPublic(); } QObject::connect( this, SIGNAL(closeChanged(bool)), NodeUi.get(), SLOT(onSettingsPanelClosedChanged(bool)) ); const QSize mediumBSize( TO_DPIX(NATRON_MEDIUM_BUTTON_SIZE), TO_DPIY(NATRON_MEDIUM_BUTTON_SIZE) ); const QSize mediumIconSize( TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), TO_DPIY(NATRON_MEDIUM_BUTTON_ICON_SIZE) ); QPixmap pixSettings; appPTR->getIcon(NATRON_PIXMAP_SETTINGS, TO_DPIX(NATRON_MEDIUM_BUTTON_ICON_SIZE), &pixSettings); _settingsButton = new Button( QIcon(pixSettings), QString(), getHeaderWidget() ); _settingsButton->setFixedSize(mediumBSize); _settingsButton->setIconSize(mediumIconSize); _settingsButton->setToolTip( GuiUtils::convertFromPlainText(tr("Settings and presets."), Qt::WhiteSpaceNormal) ); _settingsButton->setFocusPolicy(Qt::NoFocus); QObject::connect( _settingsButton, SIGNAL(clicked()), this, SLOT(onSettingsButtonClicked()) ); insertHeaderWidget(1, _settingsButton); }
void NodeAnimPrivate::computeFRRange() { NodeGuiPtr nodeUI = nodeGui.lock(); NodePtr node = nodeUI->getNode(); if (!node) { return; } KnobIntBasePtr frameRangeKnob = toKnobIntBase(node->getKnobByName(kFrameRangeParamNameFrameRange)); assert(frameRangeKnob); frameRange.min = frameRangeKnob->getValue(DimIdx(0)); frameRange.max = frameRangeKnob->getValue(DimIdx(1)); }
void NodeAnimPrivate::computeCommonNodeRange() { NodeGuiPtr nodeUI = nodeGui.lock(); NodePtr node = nodeUI->getNode(); if (!node) { return; } int first,last; bool lifetimeEnabled = node->isLifetimeActivated(&first, &last); if (lifetimeEnabled) { frameRange.min = first; frameRange.max = last; } }
void NodeGraph::selectNode(const NodeGuiPtr & n, bool addToSelection) { if ( !n->isVisible() ) { return; } bool alreadyInSelection = std::find(_imp->_selection.begin(),_imp->_selection.end(),n) != _imp->_selection.end(); assert(n); if (addToSelection && !alreadyInSelection) { _imp->_selection.push_back(n); } else if (!addToSelection) { clearSelection(); _imp->_selection.push_back(n); } n->setUserSelected(true); ViewerInstance* isViewer = n->getNode()->isEffectViewer(); if (isViewer) { OpenGLViewerI* viewer = isViewer->getUiContext(); const std::list<ViewerTab*> & viewerTabs = getGui()->getViewersList(); for (std::list<ViewerTab*>::const_iterator it = viewerTabs.begin(); it != viewerTabs.end(); ++it) { if ( (*it)->getViewer() == viewer ) { setLastSelectedViewer( (*it) ); } } } bool magnifiedNodeSelected = false; if (_imp->_magnifiedNode) { magnifiedNodeSelected = std::find(_imp->_selection.begin(),_imp->_selection.end(),_imp->_magnifiedNode) != _imp->_selection.end(); } if (magnifiedNodeSelected && _imp->_magnifOn) { _imp->_magnifOn = false; _imp->_magnifiedNode->setScale_natron(_imp->_nodeSelectedScaleBeforeMagnif); } }
void NodeSettingsPanel::onSettingsButtonClicked() { Menu menu(this); //menu.setFont(QFont(appFont,appFontSize)); NodeGuiPtr node = getNode(); NodePtr master = node->getNode()->getMasterNode(); QAction* importPresets = new QAction(tr("Import presets"), &menu); QObject::connect( importPresets, SIGNAL(triggered()), this, SLOT(onImportPresetsActionTriggered()) ); QAction* exportAsPresets = new QAction(tr("Export as presets"), &menu); QObject::connect( exportAsPresets, SIGNAL(triggered()), this, SLOT(onExportPresetsActionTriggered()) ); menu.addAction(importPresets); menu.addAction(exportAsPresets); menu.addSeparator(); QAction* manageUserParams = new QAction(tr("Manage user parameters..."), &menu); QObject::connect( manageUserParams, SIGNAL(triggered()), this, SLOT(onManageUserParametersActionTriggered()) ); menu.addAction(manageUserParams); menu.addSeparator(); QAction* setKeyOnAll = new QAction(tr("Set key on all parameters"), &menu); QObject::connect( setKeyOnAll, SIGNAL(triggered()), this, SLOT(setKeyOnAllParameters()) ); QAction* removeAnimationOnAll = new QAction(tr("Remove animation on all parameters"), &menu); QObject::connect( removeAnimationOnAll, SIGNAL(triggered()), this, SLOT(removeAnimationOnAllParameters()) ); menu.addAction(setKeyOnAll); menu.addAction(removeAnimationOnAll); if ( master || !node->getDagGui() || !node->getDagGui()->getGui() || node->getDagGui()->getGui()->isGUIFrozen() ) { importPresets->setEnabled(false); exportAsPresets->setEnabled(false); setKeyOnAll->setEnabled(false); removeAnimationOnAll->setEnabled(false); } menu.exec( _settingsButton->mapToGlobal( _settingsButton->pos() ) ); }
void NodeGraph::checkForHints(bool shiftdown, bool controlDown, const NodeGuiPtr& selectedNode, const QRectF& visibleSceneR) { NodePtr internalNode = selectedNode->getNode(); bool doMergeHints = shiftdown && controlDown; bool doConnectionHints = appPTR->getCurrentSettings()->isConnectionHintEnabled(); //Ignore hints for backdrops BackdropGui* isBd = dynamic_cast<BackdropGui*>( selectedNode.get() ); if (isBd) { return; } if (!doMergeHints) { ///for nodes already connected don't show hint if ( ( internalNode->getMaxInputCount() == 0) && internalNode->hasOutputConnected() ) { doConnectionHints = false; } else if ( ( internalNode->getMaxInputCount() > 0) && internalNode->hasAllInputsConnected() && internalNode->hasOutputConnected() ) { doConnectionHints = false; } } if (!doConnectionHints) { return; } QRectF selectedNodeBbox = selectedNode->boundingRectWithEdges(); //selectedNode->mapToParent( selectedNode->boundingRect() ).boundingRect(); double tolerance = 10; selectedNodeBbox.adjust(-tolerance, -tolerance, tolerance, tolerance); NodeGuiPtr nodeToShowMergeRect; NodePtr selectedNodeInternalNode = selectedNode->getNode(); bool selectedNodeIsReader = selectedNodeInternalNode->getEffectInstance()->isReader() || selectedNodeInternalNode->getMaxInputCount() == 0; Edge* edge = 0; std::set<NodeGui*> nodesWithinRect; getNodesWithinViewportRect(visibleWidgetRect(), &nodesWithinRect); { for (std::set<NodeGui*>::iterator it = nodesWithinRect.begin(); it != nodesWithinRect.end(); ++it) { bool isAlreadyAnOutput = false; const NodesWList& outputs = internalNode->getGuiOutputs(); for (NodesWList::const_iterator it2 = outputs.begin(); it2 != outputs.end(); ++it2) { NodePtr node = it2->lock(); if (!node) { continue; } if ( node == (*it)->getNode() ) { isAlreadyAnOutput = true; break; } } if (isAlreadyAnOutput) { continue; } QRectF nodeBbox = (*it)->boundingRectWithEdges(); if ( ( (*it) != selectedNode.get() ) && (*it)->isVisible() && nodeBbox.intersects(visibleSceneR) ) { if (doMergeHints) { //QRectF nodeRect = (*it)->mapToParent((*it)->boundingRect()).boundingRect(); NodePtr internalNode = (*it)->getNode(); if ( !internalNode->isOutputNode() && nodeBbox.intersects(selectedNodeBbox) ) { bool nHasInput = internalNode->hasInputConnected(); int nMaxInput = internalNode->getMaxInputCount(); bool selectedHasInput = selectedNodeInternalNode->hasInputConnected(); int selectedMaxInput = selectedNodeInternalNode->getMaxInputCount(); double nPAR = internalNode->getEffectInstance()->getAspectRatio(-1); double selectedPAR = selectedNodeInternalNode->getEffectInstance()->getAspectRatio(-1); double nFPS = internalNode->getEffectInstance()->getFrameRate(); double selectedFPS = selectedNodeInternalNode->getEffectInstance()->getFrameRate(); bool isValid = true; if ( (selectedPAR != nPAR) || (std::abs(nFPS - selectedFPS) > 0.01) ) { if (nHasInput || selectedHasInput) { isValid = false; } else if ( !nHasInput && (nMaxInput == 0) && !selectedHasInput && (selectedMaxInput == 0) ) { isValid = false; } } if (isValid) { nodeToShowMergeRect = (*it)->shared_from_this(); } } else { (*it)->setMergeHintActive(false); } } else { //!doMergeHints edge = (*it)->hasEdgeNearbyRect(selectedNodeBbox); ///if the edge input is the selected node don't continue if ( edge && ( edge->getSource() == selectedNode) ) { edge = 0; } if ( edge && edge->isOutputEdge() ) { if (selectedNodeIsReader) { continue; } int prefInput = selectedNodeInternalNode->getPreferredInputForConnection(); if (prefInput == -1) { edge = 0; } else { Node::CanConnectInputReturnValue ret = selectedNodeInternalNode->canConnectInput(edge->getSource()->getNode(), prefInput); if (ret != Node::eCanConnectInput_ok) { edge = 0; } } } if ( edge && !edge->isOutputEdge() ) { if ( (*it)->getNode()->getEffectInstance()->isReader() || ( (*it)->getNode()->getMaxInputCount() == 0 ) ) { edge = 0; continue; } if ( (*it)->getNode()->getEffectInstance()->isInputRotoBrush( edge->getInputNumber() ) ) { edge = 0; continue; } //Check that the edge can connect to the selected node { Node::CanConnectInputReturnValue ret = edge->getDest()->getNode()->canConnectInput( selectedNodeInternalNode, edge->getInputNumber() ); if ( (ret == Node::eCanConnectInput_inputAlreadyConnected) && !selectedNodeIsReader ) { ret = Node::eCanConnectInput_ok; } if (ret != Node::eCanConnectInput_ok) { edge = 0; } } //Check that the selected node can connect to the input of the edge if (edge) { NodeGuiPtr edgeHasSource = edge->getSource(); if (edgeHasSource) { int prefInput = selectedNodeInternalNode->getPreferredInputForConnection(); if (prefInput != -1) { Node::CanConnectInputReturnValue ret = selectedNodeInternalNode->canConnectInput(edgeHasSource->getNode(), prefInput); if ( (ret == Node::eCanConnectInput_inputAlreadyConnected) && !selectedNodeIsReader ) { ret = Node::eCanConnectInput_ok; } if (ret != Node::eCanConnectInput_ok) { edge = 0; } } } } } if (edge) { edge->setUseHighlight(true); break; } } // doMergeHints } } } // QMutexLocker l(&_imp->_nodesMutex); if ( _imp->_highLightedEdge && ( _imp->_highLightedEdge != edge) ) { _imp->_highLightedEdge->setUseHighlight(false); _imp->_hintInputEdge->hide(); _imp->_hintOutputEdge->hide(); } _imp->_highLightedEdge = edge; if ( edge && edge->getSource() && edge->getDest() ) { ///setup the hints edge ///find out if the node is already connected to what the edge is connected bool alreadyConnected = false; const std::vector<NodeWPtr > & inpNodes = selectedNode->getNode()->getGuiInputs(); for (std::size_t i = 0; i < inpNodes.size(); ++i) { if ( inpNodes[i].lock() == edge->getSource()->getNode() ) { alreadyConnected = true; break; } } if ( !_imp->_hintInputEdge->isVisible() ) { if (!alreadyConnected) { int prefInput = selectedNode->getNode()->getPreferredInputForConnection(); _imp->_hintInputEdge->setInputNumber(prefInput != -1 ? prefInput : 0); _imp->_hintInputEdge->setSourceAndDestination(edge->getSource(), selectedNode); _imp->_hintInputEdge->setVisible(true); } _imp->_hintOutputEdge->setInputNumber( edge->getInputNumber() ); _imp->_hintOutputEdge->setSourceAndDestination( selectedNode, edge->getDest() ); _imp->_hintOutputEdge->setVisible(true); } else { if (!alreadyConnected) { _imp->_hintInputEdge->initLine(); } _imp->_hintOutputEdge->initLine(); } } else if (edge) { ///setup only 1 of the hints edge if ( _imp->_highLightedEdge && !_imp->_hintInputEdge->isVisible() ) { if ( edge->isOutputEdge() ) { int prefInput = selectedNode->getNode()->getPreferredInputForConnection(); _imp->_hintInputEdge->setInputNumber(prefInput != -1 ? prefInput : 0); _imp->_hintInputEdge->setSourceAndDestination(edge->getSource(), selectedNode); } else { _imp->_hintInputEdge->setInputNumber( edge->getInputNumber() ); _imp->_hintInputEdge->setSourceAndDestination( selectedNode, edge->getDest() ); } _imp->_hintInputEdge->setVisible(true); } else if ( _imp->_highLightedEdge && _imp->_hintInputEdge->isVisible() ) { _imp->_hintInputEdge->initLine(); } } else if (nodeToShowMergeRect) { nodeToShowMergeRect->setMergeHintActive(true); selectedNode->setMergeHintActive(true); _imp->_mergeHintNode = nodeToShowMergeRect; } else { selectedNode->setMergeHintActive(false); _imp->_mergeHintNode.reset(); } } // NodeGraph::checkForHints
void NodeGraph::moveNodesForIdealPosition(const NodeGuiPtr &node, const NodeGuiPtr &selected, bool autoConnect) { BackdropGuiPtr isBd = toBackdropGui(node); if (isBd) { return; } QRectF viewPos = visibleSceneRect(); ///3 possible values: /// 0 = default , i.e: we pop the node in the middle of the graph's current view /// 1 = pop the node above the selected node and move the inputs of the selected node a little /// 2 = pop the node below the selected node and move the outputs of the selected node a little int behavior = 0; if (!selected || !autoConnect) { behavior = 0; } else { ///this function is redundant with Project::autoConnect, depending on the node selected ///and this node we make some assumptions on to where we could put the node. // 1) selected is output // a) created is output --> fail // b) created is input --> connect input // c) created is regular --> connect input // 2) selected is input // a) created is output --> connect output // b) created is input --> fail // c) created is regular --> connect output // 3) selected is regular // a) created is output--> connect output // b) created is input --> connect input // c) created is regular --> connect output ///1) if ( selected->getNode()->isOutputNode() ) { ///case 1-a) just do default we don't know what else to do if ( node->getNode()->isOutputNode() ) { behavior = 0; } else { ///for either cases 1-b) or 1-c) we just connect the created node as input of the selected node. behavior = 1; } } ///2) and 3) are similar except for case b) else { ///case 2 or 3- a): connect the created node as output of the selected node. if ( node->getNode()->isOutputNode() ) { behavior = 2; } ///case b) else if ( node->getNode()->isInputNode() ) { if ( selected->getNode()->getEffectInstance()->isReader() ) { ///case 2-b) just do default we don't know what else to do behavior = 0; } else { ///case 3-b): connect the created node as input of the selected node behavior = 1; } } ///case c) connect created as output of the selected node else { behavior = 2; } } } NodePtr createdNodeInternal = node->getNode(); NodePtr selectedNodeInternal; if (selected) { selectedNodeInternal = selected->getNode(); } ///if behaviour is 1 , just check that we can effectively connect the node to avoid moving them for nothing ///otherwise fallback on behaviour 0 if (behavior == 1) { const std::vector<NodeWPtr > & inputs = selected->getNode()->getGuiInputs(); bool oneInputEmpty = false; for (std::size_t i = 0; i < inputs.size(); ++i) { if ( !inputs[i].lock() ) { oneInputEmpty = true; break; } } if (!oneInputEmpty) { behavior = 0; } } ProjectPtr proj = getGui()->getApp()->getProject(); ///default QPointF position; if (behavior == 0) { position.setX( ( viewPos.bottomRight().x() + viewPos.topLeft().x() ) / 2. ); position.setY( ( viewPos.topLeft().y() + viewPos.bottomRight().y() ) / 2. ); } else if (behavior == 1) { ///pop it above the selected node ///If this is the first connected input, insert it in a "linear" way so the tree remains vertical int nbConnectedInput = 0; const std::vector<Edge*> & selectedNodeInputs = selected->getInputsArrows(); for (std::vector<Edge*>::const_iterator it = selectedNodeInputs.begin(); it != selectedNodeInputs.end(); ++it) { NodeGuiPtr input; if (*it) { input = (*it)->getSource(); } if (input) { ++nbConnectedInput; } } ///connect it to the first input QSize selectedNodeSize = selected->getSize(); QSize createdNodeSize = node->getSize(); if (nbConnectedInput == 0) { QPointF selectedNodeMiddlePos = selected->scenePos() + QPointF(selectedNodeSize.width() / 2, selectedNodeSize.height() / 2); position.setX(selectedNodeMiddlePos.x() - createdNodeSize.width() / 2); position.setY( selectedNodeMiddlePos.y() - selectedNodeSize.height() / 2 - NodeGui::DEFAULT_OFFSET_BETWEEN_NODES - createdNodeSize.height() ); QRectF createdNodeRect( position.x(), position.y(), createdNodeSize.width(), createdNodeSize.height() ); ///now that we have the position of the node, move the inputs of the selected node to make some space for this node for (std::vector<Edge*>::const_iterator it = selectedNodeInputs.begin(); it != selectedNodeInputs.end(); ++it) { if ( (*it)->hasSource() ) { (*it)->getSource()->moveAbovePositionRecursively(createdNodeRect); } } int selectedInput = selectedNodeInternal->getPreferredInputForConnection(); if (selectedInput != -1) { bool ok = proj->connectNodes(selectedInput, createdNodeInternal, selectedNodeInternal, true); Q_UNUSED(ok); } } else { //ViewerInstancePtr isSelectedViewer = selectedNodeInternal->isEffectViewerInstance(); //Don't pop a dot, it will most likely annoy the user, just fallback on behavior 0 /* position.setX( ( viewPos.bottomRight().x() + viewPos.topLeft().x() ) / 2. ); position.setY( ( viewPos.topLeft().y() + viewPos.bottomRight().y() ) / 2. ); } else { */ QRectF selectedBbox = selected->mapToScene( selected->boundingRect() ).boundingRect(); QPointF selectedCenter = selectedBbox.center(); double y = selectedCenter.y() - selectedNodeSize.height() / 2. - NodeGui::DEFAULT_OFFSET_BETWEEN_NODES - createdNodeSize.height(); double x = selectedCenter.x() + nbConnectedInput * 150; position.setX(x - createdNodeSize.width() / 2.); position.setY(y); int index = selectedNodeInternal->getPreferredInputForConnection(); if (index != -1) { bool ok = proj->connectNodes(index, createdNodeInternal, selectedNodeInternal, true); Q_UNUSED(ok); } //} // if (isSelectedViewer) {*/ } // if (nbConnectedInput == 0) { } else { ///pop it below the selected node const NodesWList& outputs = selectedNodeInternal->getGuiOutputs(); if ( !createdNodeInternal->isOutputNode() || outputs.empty() ) { QSize selectedNodeSize = selected->getSize(); QSize createdNodeSize = node->getSize(); QPointF selectedNodeMiddlePos = selected->scenePos() + QPointF(selectedNodeSize.width() / 2, selectedNodeSize.height() / 2); ///actually move the created node where the selected node is position.setX(selectedNodeMiddlePos.x() - createdNodeSize.width() / 2); position.setY(selectedNodeMiddlePos.y() + (selectedNodeSize.height() / 2) + NodeGui::DEFAULT_OFFSET_BETWEEN_NODES); QRectF createdNodeRect( position.x(), position.y(), createdNodeSize.width(), createdNodeSize.height() ); ///and move the selected node below recusively for (NodesWList::const_iterator it = outputs.begin(); it != outputs.end(); ++it) { NodePtr output = it->lock(); if (!output) { continue; } NodeGuiIPtr output_i = output->getNodeGui(); if (!output_i) { continue; } NodeGuiPtr outputGui = toNodeGui( output_i ); assert(outputGui); if (outputGui) { outputGui->moveBelowPositionRecursively(createdNodeRect); } } ///Connect the created node to the selected node ///finally we connect the created node to the selected node int createdInput = createdNodeInternal->getPreferredInputForConnection(); if (createdInput != -1) { ignore_result( createdNodeInternal->connectInput(selectedNodeInternal, createdInput) ); } if ( !createdNodeInternal->isOutputNode() ) { ///we find all the nodes that were previously connected to the selected node, ///and connect them to the created node instead. std::map<NodePtr, int> outputsConnectedToSelectedNode; selectedNodeInternal->getOutputsConnectedToThisNode(&outputsConnectedToSelectedNode); for (std::map<NodePtr, int>::iterator it = outputsConnectedToSelectedNode.begin(); it != outputsConnectedToSelectedNode.end(); ++it) { if ( it->first->getParentMultiInstanceName().empty() && (it->first != createdNodeInternal) ) { /* Internal rotopaint nodes are connecting to the Rotopaint itself... make sure not to connect internal nodes of the tree */ RotoDrawableItemPtr stroke = it->first->getAttachedRotoItem(); if ( stroke && (stroke->getContext()->getNode() == selectedNodeInternal) ) { continue; } ignore_result( it->first->replaceInput(createdNodeInternal, it->second) ); // bool ok = proj->disconnectNodes(selectedNodeInternal.get(), it->first); // if (ok) { // ignore_result(proj->connectNodes(it->second, createdNodeInternal, it->first)); // } //assert(ok); Might not be ok if the disconnectNodes() action above was queued } } } } else { ///the created node is an output node and the selected node already has several outputs, create it aside QSize createdNodeSize = node->getSize(); QRectF selectedBbox = selected->mapToScene( selected->boundingRect() ).boundingRect(); QPointF selectedCenter = selectedBbox.center(); double y = selectedCenter.y() + selectedBbox.height() / 2. + NodeGui::DEFAULT_OFFSET_BETWEEN_NODES; double x = selectedCenter.x() + (int)outputs.size() * 150; position.setX(x - createdNodeSize.width() / 2.); position.setY(y); //Don't pop a dot, it will most likely annoy the user, just fallback on behavior 0 int index = createdNodeInternal->getPreferredInputForConnection(); bool ok = proj->connectNodes(index, selectedNodeInternal, createdNodeInternal, true); Q_UNUSED(ok); } } position = node->mapFromScene(position); position = node->mapToParent(position); node->setPosition( position.x(), position.y() ); } // moveNodesForIdealPosition
PickKnobDialog::PickKnobDialog(DockablePanel* panel, QWidget* parent) : QDialog(parent) , _imp( new PickKnobDialogPrivate(panel) ) { NodeSettingsPanel* nodePanel = dynamic_cast<NodeSettingsPanel*>(panel); assert(nodePanel); if (!nodePanel) { throw std::logic_error("PickKnobDialog::PickKnobDialog()"); } NodeGuiPtr nodeGui = nodePanel->getNodeGui(); NodePtr node = nodeGui->getNode(); NodeGroupPtr isGroup = node->isEffectNodeGroup(); NodeCollectionPtr collec = node->getGroup(); NodeGroupPtr isCollecGroup = toNodeGroup(collec); NodesList collectNodes = collec->getNodes(); for (NodesList::iterator it = collectNodes.begin(); it != collectNodes.end(); ++it) { if ((*it)->isActivated() && (*it)->getNodeGui() && ( (*it)->getKnobs().size() > 0 ) ) { _imp->allNodes.push_back(*it); } } if (isCollecGroup) { _imp->allNodes.push_back( isCollecGroup->getNode() ); } if (isGroup) { NodesList groupnodes = isGroup->getNodes(); for (NodesList::iterator it = groupnodes.begin(); it != groupnodes.end(); ++it) { if ( (*it)->getNodeGui() && (*it)->isActivated() && ( (*it)->getKnobs().size() > 0 ) ) { _imp->allNodes.push_back(*it); } } } 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); } nodeNames.sort(); _imp->mainLayout = new QGridLayout(this); _imp->selectNodeLabel = new Label( tr("Node:") ); _imp->nodeSelectionCombo = new CompleterLineEdit(nodeNames, nodeNames, false, this); _imp->nodeSelectionCombo->setToolTip( NATRON_NAMESPACE::convertFromPlainText(tr("Input the name of a node in the current project."), NATRON_NAMESPACE::WhiteSpaceNormal) ); _imp->nodeSelectionCombo->setFocus(Qt::PopupFocusReason); _imp->knobSelectionCombo = new ComboBox(this); QObject::connect( _imp->knobSelectionCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onKnobComboIndexChanged(int)) ); QString useAliasTt = NATRON_NAMESPACE::convertFromPlainText(tr("If checked, an alias of the selected parameter will be created, coyping entirely its state. " "Only the script-name, label and tooltip will be editable.\n" "For choice parameters this will also " "dynamically refresh the menu entries when the original parameter's menu is changed.\n" "When unchecked, a simple expression will be set linking the two parameters, but things such as dynamic menus " "will be disabled."), NATRON_NAMESPACE::WhiteSpaceNormal); _imp->useAliasLabel = new Label(tr("Make Alias:"), this); _imp->useAliasLabel->setToolTip(useAliasTt); _imp->useAliasCheckBox = new QCheckBox(this); _imp->useAliasCheckBox->setToolTip(useAliasTt); _imp->useAliasCheckBox->setChecked(true); QObject::connect( _imp->nodeSelectionCombo, SIGNAL(itemCompletionChosen()), this, SLOT(onNodeComboEditingFinished()) ); _imp->destPageLabel = new Label(tr("Page:"), this); QString pagett = NATRON_NAMESPACE::convertFromPlainText(tr("Select the page into which the parameter will be created."), NATRON_NAMESPACE::WhiteSpaceNormal); _imp->destPageLabel->setToolTip(pagett); _imp->destPageCombo = new ComboBox(this); _imp->destPageCombo->setToolTip(pagett); QObject::connect( _imp->destPageCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onPageComboIndexChanged(int)) ); const KnobsVec& knobs = node->getKnobs(); for (std::size_t i = 0; i < knobs.size(); ++i) { if ( knobs[i]->isUserKnob() ) { KnobPagePtr isPage = toKnobPage(knobs[i]); if (isPage) { _imp->pages.push_back(isPage); _imp->destPageCombo->addItem( QString::fromUtf8( isPage->getName().c_str() ) ); } else { KnobGroupPtr isGrp = toKnobGroup(knobs[i]); if (isGrp) { _imp->groups.push_back(isGrp); } } } } if (_imp->destPageCombo->count() == 0) { _imp->destPageLabel->hide(); _imp->destPageCombo->hide(); } _imp->groupLabel = new Label(tr("Group:"), this); QString grouptt = NATRON_NAMESPACE::convertFromPlainText(tr("Select the group into which the parameter will be created."), NATRON_NAMESPACE::WhiteSpaceNormal); _imp->groupCombo = new ComboBox(this); _imp->groupLabel->setToolTip(grouptt); _imp->groupCombo->setToolTip(grouptt); onPageComboIndexChanged(0); _imp->buttons = new DialogButtonBox(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->selectNodeLabel, 0, 0, 1, 1); _imp->mainLayout->addWidget(_imp->nodeSelectionCombo, 0, 1, 1, 1); _imp->mainLayout->addWidget(_imp->knobSelectionCombo, 0, 2, 1, 1); _imp->mainLayout->addWidget(_imp->useAliasLabel, 1, 0, 1, 1); _imp->mainLayout->addWidget(_imp->useAliasCheckBox, 1, 1, 1, 1); _imp->mainLayout->addWidget(_imp->destPageLabel, 2, 0, 1, 1); _imp->mainLayout->addWidget(_imp->destPageCombo, 2, 1, 1, 1); _imp->mainLayout->addWidget(_imp->groupLabel, 2, 2, 1, 1); _imp->mainLayout->addWidget(_imp->groupCombo, 2, 3, 1, 1); _imp->mainLayout->addWidget(_imp->buttons, 3, 0, 1, 3); QTimer::singleShot( 25, _imp->nodeSelectionCombo, SLOT(showCompleter()) ); }
void NodeAnimPrivate::computeGroupRange() { NodeGuiPtr nodeUI = nodeGui.lock(); NodePtr node = nodeUI->getNode(); if (!node) { return; } AnimationModulePtr isAnimModel = toAnimationModule(model.lock()); if (!isAnimModel) { return; } NodeGroupPtr nodegroup = node->isEffectNodeGroup(); assert(nodegroup); if (!nodegroup) { return; } AnimationModuleTreeView* treeView = isAnimModel->getEditor()->getTreeView(); NodesList nodes = nodegroup->getNodes(); std::set<double> times; for (NodesList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { NodeAnimPtr childAnim = isAnimModel->findNodeAnim(*it); if (!childAnim) { continue; } if (!treeView->isItemVisibleRecursive(childAnim->getTreeItem())) { continue; } childAnim->refreshFrameRange(); RangeD childRange = childAnim->getFrameRange(); times.insert(childRange.min); times.insert(childRange.max); // Also check the child knobs keyframes NodeGuiPtr childGui = childAnim->getNodeGui(); const KnobsVec &knobs = childGui->getNode()->getKnobs(); for (KnobsVec::const_iterator it2 = knobs.begin(); it2 != knobs.end(); ++it2) { if ( !(*it2)->isAnimationEnabled() || !(*it2)->hasAnimation() ) { continue; } else { // For each dimension and for each split view get the first/last keyframe (if any) int nDims = (*it2)->getNDimensions(); std::list<ViewIdx> views = (*it2)->getViewsList(); for (std::list<ViewIdx>::const_iterator it3 = views.begin(); it3 != views.end(); ++it3) { for (int i = 0; i < nDims; ++i) { CurvePtr curve = (*it2)->getCurve(*it3, DimIdx(i)); if (!curve) { continue; } int nKeys = curve->getKeyFramesCount(); if (nKeys > 0) { KeyFrame k; if (curve->getKeyFrameWithIndex(0, &k)) { times.insert( k.getTime() ); } if (curve->getKeyFrameWithIndex(nKeys - 1, &k)) { times.insert( k.getTime() ); } } } } } } // for all knobs } // for all children nodes if (times.size() <= 1) { frameRange.min = 0; frameRange.max = 0; } else { frameRange.min = *times.begin(); frameRange.max = *times.rbegin(); } } // computeGroupRange
void NodeAnimPrivate::computeRetimeRange() { NodeGuiPtr nodeUI = nodeGui.lock(); NodePtr node = nodeUI->getNode(); if (!node) { return; } NodePtr input = node->getInput(0); if (!input) { return; } if (input) { double inputFirst, inputLast; input->getEffectInstance()->getFrameRange_public(0, &inputFirst, &inputLast); U64 hash; FramesNeededMap framesFirst = node->getEffectInstance()->getFramesNeeded_public(inputFirst, ViewIdx(0), false, AbortableRenderInfoPtr(), &hash); FramesNeededMap framesLast = node->getEffectInstance()->getFramesNeeded_public(inputLast, ViewIdx(0), false, AbortableRenderInfoPtr(), &hash); assert( !framesFirst.empty() && !framesLast.empty() ); if ( framesFirst.empty() || framesLast.empty() ) { return; } { const FrameRangesMap& rangeFirst = framesFirst[0]; assert( !rangeFirst.empty() ); if ( rangeFirst.empty() ) { return; } FrameRangesMap::const_iterator it = rangeFirst.find( ViewIdx(0) ); assert( it != rangeFirst.end() ); if ( it == rangeFirst.end() ) { return; } const std::vector<OfxRangeD>& frames = it->second; assert( !frames.empty() ); if ( frames.empty() ) { return; } frameRange.min = (frames.front().min); } { FrameRangesMap& rangeLast = framesLast[0]; assert( !rangeLast.empty() ); if ( rangeLast.empty() ) { return; } FrameRangesMap::const_iterator it = rangeLast.find( ViewIdx(0) ); assert( it != rangeLast.end() ); if ( it == rangeLast.end() ) { return; } const std::vector<OfxRangeD>& frames = it->second; assert( !frames.empty() ); if ( frames.empty() ) { return; } frameRange.max = (frames.front().min); } } } // computeRetimeRange
void NodeGraph::connectCurrentViewerToSelection(int inputNB) { ViewerTab* lastUsedViewer = getLastSelectedViewer(); if (lastUsedViewer) { boost::shared_ptr<NodeCollection> collection = lastUsedViewer->getInternalNode()->getNode()->getGroup(); if (collection && collection->getNodeGraph() != this) { //somehow the group doesn't belong to this nodegraph , pick another one const std::list<ViewerTab*>& tabs = getGui()->getViewersList(); lastUsedViewer = 0; for (std::list<ViewerTab*>::const_iterator it = tabs.begin(); it!=tabs.end(); ++it) { boost::shared_ptr<NodeCollection> otherCollection = (*it)->getInternalNode()->getNode()->getGroup(); if (otherCollection && otherCollection->getNodeGraph() == this) { lastUsedViewer = *it; break; } } } } boost::shared_ptr<InspectorNode> v; if ( lastUsedViewer ) { v = boost::dynamic_pointer_cast<InspectorNode>( lastUsedViewer-> getInternalNode()->getNode() ); } else { CreateNodeArgs args(QString::fromUtf8(PLUGINID_NATRON_VIEWER), eCreateNodeReasonUserCreate, getGroup()); NodePtr viewerNode = getGui()->getApp()->createNode(args); if (!viewerNode) { return; } v = boost::dynamic_pointer_cast<InspectorNode>(viewerNode); } if (!v) { return; } ///if the node is no longer active (i.e: it was deleted by the user), don't do anything. if ( !v->isActivated() ) { return; } ///get a ptr to the NodeGui boost::shared_ptr<NodeGuiI> gui_i = v->getNodeGui(); NodeGuiPtr gui = boost::dynamic_pointer_cast<NodeGui>(gui_i); assert(gui); ///if there's no selected node or the viewer is selected, then try refreshing that input nb if it is connected. bool viewerAlreadySelected = std::find(_imp->_selection.begin(),_imp->_selection.end(),gui) != _imp->_selection.end(); if (_imp->_selection.empty() || (_imp->_selection.size() > 1) || viewerAlreadySelected) { v->setActiveInputAndRefresh(inputNB, false); gui->refreshEdges(); return; } NodeGuiPtr selected = _imp->_selection.front(); if ( !selected->getNode()->canOthersConnectToThisNode() ) { return; } ///if the node doesn't have the input 'inputNb' created yet, populate enough input ///so it can be created. Edge* foundInput = gui->getInputArrow(inputNB); assert(foundInput); ///and push a connect command to the selected node. pushUndoCommand( new ConnectCommand(this,foundInput,foundInput->getSource(),selected) ); ///Set the viewer as the selected node (also wipe the current selection) selectNode(gui,false); } // connectCurrentViewerToSelection
NATRON_NAMESPACE_ENTER ViewerTab::ViewerTab(const std::string& scriptName, const std::list<NodeGuiPtr> & existingNodesContext, const std::list<NodeGuiPtr>& activePluginsContext, Gui* gui, const NodeGuiPtr& node_ui, QWidget* parent) : QWidget(parent) , PanelWidget(scriptName, this, gui) , _imp( new ViewerTabPrivate(this, node_ui) ) { ViewerNodePtr node = node_ui->getNode()->isEffectViewerNode(); installEventFilter(this); setMouseTracking(true); NodePtr internalNode = node->getNode(); QObject::connect( internalNode.get(), SIGNAL(scriptNameChanged(QString)), this, SLOT(onInternalNodeScriptNameChanged(QString)) ); QObject::connect( internalNode.get(), SIGNAL(labelChanged(QString,QString)), this, SLOT(onInternalNodeLabelChanged(QString,QString)) ); QObject::connect( node.get(), SIGNAL(internalViewerCreated()), this, SLOT(onInternalViewerCreated())); _imp->mainLayout = new QVBoxLayout(this); setLayout(_imp->mainLayout); _imp->mainLayout->setSpacing(0); _imp->mainLayout->setContentsMargins(0, 0, 0, 0); QFontMetrics fm(font(), 0); _imp->viewerContainer = new QWidget(this); _imp->viewerLayout = new QHBoxLayout(_imp->viewerContainer); _imp->viewerLayout->setContentsMargins(0, 0, 0, 0); _imp->viewerLayout->setSpacing(0); _imp->viewerSubContainer = new QWidget(_imp->viewerContainer); _imp->viewerSubContainerLayout = new QVBoxLayout(_imp->viewerSubContainer); _imp->viewerSubContainerLayout->setContentsMargins(0, 0, 0, 0); _imp->viewerSubContainerLayout->setSpacing(1); // Info bars QString inputNames[2] = { QString::fromUtf8("A:"), QString::fromUtf8("B:") }; bool infobarvisible = node->isInfoBarVisible(); for (int i = 0; i < 2; ++i) { _imp->infoWidget[i] = new InfoViewerWidget(inputNames[i], this); } // Viewer _imp->viewer = new ViewerGL(this); GuiAppInstancePtr app = gui->getApp(); // Init viewer to project format { Format projectFormat; app->getProject()->getProjectDefaultFormat(&projectFormat); RectD canonicalFormat = projectFormat.toCanonicalFormat(); for (int i = 0; i < 2; ++i) { _imp->viewer->setInfoViewer(_imp->infoWidget[i], i); _imp->viewer->setRegionOfDefinition(canonicalFormat, projectFormat.getPixelAspectRatio(), i); setInfoBarAndViewerResolution(projectFormat, canonicalFormat, projectFormat.getPixelAspectRatio(), i); } _imp->viewer->resetWipeControls(); } _imp->viewerSubContainerLayout->addWidget(_imp->viewer); for (int i = 0; i < 2; ++i) { _imp->viewerSubContainerLayout->addWidget(_imp->infoWidget[i]); _imp->viewer->setInfoViewer(_imp->infoWidget[i], i); if (i == 1 || !infobarvisible) { _imp->infoWidget[i]->hide(); } } manageSlotsForInfoWidget(0, true); _imp->viewerLayout->addWidget(_imp->viewerSubContainer); _imp->mainLayout->addWidget(_imp->viewerContainer); TimeLinePtr timeline = app->getTimeLine(); _imp->timeLineGui = new TimeLineGui(node, timeline, getGui(), this); QObject::connect( _imp->timeLineGui, SIGNAL(boundariesChanged(SequenceTime,SequenceTime)), this, SLOT(onTimelineBoundariesChanged(SequenceTime,SequenceTime)) ); QObject::connect( app->getProject().get(), SIGNAL(frameRangeChanged(int,int)), _imp->timeLineGui, SLOT(onProjectFrameRangeChanged(int,int)) ); _imp->timeLineGui->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); if (!node->isTimelineVisible()) { _imp->timeLineGui->hide(); } //Add some spacing because the timeline might be black as the info _imp->mainLayout->addSpacing( TO_DPIY(5) ); _imp->mainLayout->addWidget(_imp->timeLineGui); double leftBound, rightBound; leftBound = node->getPlaybackInPointKnob()->getValue(); rightBound = node->getPlaybackOutPointKnob()->getValue(); TimeValue projectLeft, projectRight; app->getProject()->getFrameRange(&projectLeft, &projectRight); _imp->timeLineGui->setBoundaries(leftBound, rightBound); onTimelineBoundariesChanged(leftBound, rightBound); _imp->timeLineGui->setFrameRangeEdited(projectLeft != leftBound || projectRight != rightBound);; manageTimelineSlot(false, timeline); QObject::connect( node.get(), SIGNAL(renderStatsAvailable(int,double,RenderStatsMap)), this, SLOT(onRenderStatsAvailable(int,double,RenderStatsMap)) ); QObject::connect( _imp->viewer, SIGNAL(zoomChanged(int)), this, SLOT(updateZoomComboBox(int)) ); QObject::connect( node.get(), SIGNAL(viewerDisconnected()), this, SLOT(disconnectViewer()) ); createNodeViewerInterface(node_ui); setPluginViewerInterface(node_ui); for (std::list<NodeGuiPtr>::const_iterator it = existingNodesContext.begin(); it != existingNodesContext.end(); ++it) { ViewerNodePtr isViewerNode = (*it)->getNode()->isEffectViewerNode(); // For viewers, create the viewer interface separately if (!isViewerNode) { createNodeViewerInterface(*it); } } for (std::list<NodeGuiPtr>::const_iterator it = activePluginsContext.begin(); it != activePluginsContext.end(); ++it) { ViewerNodePtr isViewerNode = (*it)->getNode()->isEffectViewerNode(); // For viewers, create the viewer interface separately if (!isViewerNode) { setPluginViewerInterface(*it); } } setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); _imp->viewerNode.lock()->setUiContext( getViewer() ); QTimer::singleShot( 25, _imp->timeLineGui, SLOT(recenterOnBounds()) ); //Refresh the viewport lock state const std::list<ViewerTab*>& viewers = getGui()->getViewersList(); if ( !viewers.empty() ) { ViewerTab* other = viewers.front(); if ( other->getInternalNode()->isViewersSynchroEnabled() ) { double left, bottom, factor, par; other->getViewer()->getProjection(&left, &bottom, &factor, &par); _imp->viewer->setProjection(left, bottom, factor, par); node->setViewersSynchroEnabled(true); } } _imp->cachedFramesThread.reset(new CachedFramesThread(this)); _imp->cachedFramesThread->start(); }
void NodeAnimPrivate::computeRetimeRange() { NodeGuiPtr nodeUI = nodeGui.lock(); NodePtr node = nodeUI->getNode(); if (!node) { return; } NodePtr input = node->getInput(0); if (!input) { return; } if (input) { RangeD inputRange = {0, 0}; { GetFrameRangeResultsPtr results; ActionRetCodeEnum stat = input->getEffectInstance()->getFrameRange_public(&results); if (!isFailureRetCode(stat)) { results->getFrameRangeResults(&inputRange); } } FramesNeededMap framesFirst, framesLast; { GetFramesNeededResultsPtr results; ActionRetCodeEnum stat = node->getEffectInstance()->getFramesNeeded_public(TimeValue(inputRange.min), ViewIdx(0), &results); if (!isFailureRetCode(stat)) { results->getFramesNeeded(&framesFirst); } stat = node->getEffectInstance()->getFramesNeeded_public(TimeValue(inputRange.max), ViewIdx(0), &results); if (!isFailureRetCode(stat)) { results->getFramesNeeded(&framesLast); } } assert( !framesFirst.empty() && !framesLast.empty() ); if ( framesFirst.empty() || framesLast.empty() ) { return; } { const FrameRangesMap& rangeFirst = framesFirst[0]; assert( !rangeFirst.empty() ); if ( rangeFirst.empty() ) { return; } FrameRangesMap::const_iterator it = rangeFirst.find( ViewIdx(0) ); assert( it != rangeFirst.end() ); if ( it == rangeFirst.end() ) { return; } const std::vector<OfxRangeD>& frames = it->second; assert( !frames.empty() ); if ( frames.empty() ) { return; } frameRange.min = (frames.front().min); } { FrameRangesMap& rangeLast = framesLast[0]; assert( !rangeLast.empty() ); if ( rangeLast.empty() ) { return; } FrameRangesMap::const_iterator it = rangeLast.find( ViewIdx(0) ); assert( it != rangeLast.end() ); if ( it == rangeLast.end() ) { return; } const std::vector<OfxRangeD>& frames = it->second; assert( !frames.empty() ); if ( frames.empty() ) { return; } frameRange.max = (frames.front().min); } } } // computeRetimeRange
GCC_DIAG_UNUSED_PRIVATE_FIELD_ON #include "Engine/EffectInstance.h" #include "Engine/Node.h" #include "Engine/NodeGroup.h" #include "Engine/Settings.h" #include "Engine/Utils.h" // convertFromPlainText #include "Gui/BackdropGui.h" #include "Gui/Edge.h" #include "Gui/GuiApplicationManager.h" #include "Gui/GuiMacros.h" #include "Gui/NodeGui.h" #include "Gui/TabWidget.h" #include "Global/QtCompat.h" NATRON_NAMESPACE_ENTER void NodeGraph::checkForHints(bool shiftdown, bool controlDown, const NodeGuiPtr& selectedNode, const QRectF& visibleSceneR) { NodePtr internalNode = selectedNode->getNode(); if (!internalNode) { return; } bool doMergeHints = shiftdown && controlDown; bool doConnectionHints = controlDown; //Ignore hints for backdrops BackdropGuiPtr isBd = toBackdropGui( selectedNode ); if (isBd) { return; } if (!doMergeHints) { ///for nodes already connected don't show hint if ( ( internalNode->getNInputs() == 0) && internalNode->hasOutputConnected() ) { doConnectionHints = false; } else if ( ( internalNode->getNInputs() > 0) && internalNode->hasAllInputsConnected() && internalNode->hasOutputConnected() ) { doConnectionHints = false; } } if (!doConnectionHints) { return; } QRectF selectedNodeBbox = selectedNode->boundingRectWithEdges(); //selectedNode->mapToParent( selectedNode->boundingRect() ).boundingRect(); double tolerance = 10; selectedNodeBbox.adjust(-tolerance, -tolerance, tolerance, tolerance); NodeGuiPtr nodeToShowMergeRect; NodePtr selectedNodeInternalNode = selectedNode->getNode(); bool selectedNodeIsReader = selectedNodeInternalNode->getEffectInstance()->isReader() || selectedNodeInternalNode->getNInputs() == 0; Edge* edge = 0; std::set<NodeGuiPtr> nodesWithinRect; getNodesWithinViewportRect(visibleWidgetRect(), &nodesWithinRect); { for (std::set<NodeGuiPtr>::iterator it = nodesWithinRect.begin(); it != nodesWithinRect.end(); ++it) { OutputNodesMap outputs; internalNode->getOutputs(outputs); OutputNodesMap::const_iterator foundAsOutput = outputs.find((*it)->getNode()); if (foundAsOutput != outputs.end()) { continue; } QRectF nodeBbox = (*it)->boundingRectWithEdges(); if ( ( (*it) != selectedNode ) && (*it)->isVisible() && nodeBbox.intersects(visibleSceneR) ) { if (doMergeHints) { //QRectF nodeRect = (*it)->mapToParent((*it)->boundingRect()).boundingRect(); NodePtr internalNode = (*it)->getNode(); if ( !internalNode->isOutputNode() && nodeBbox.intersects(selectedNodeBbox) ) { bool nHasInput = internalNode->hasInputConnected(); int nMaxInput = internalNode->getNInputs(); bool selectedHasInput = selectedNodeInternalNode->hasInputConnected(); int selectedMaxInput = selectedNodeInternalNode->getNInputs(); double nPAR = internalNode->getEffectInstance()->getAspectRatio(-1); double selectedPAR = selectedNodeInternalNode->getEffectInstance()->getAspectRatio(-1); double nFPS = internalNode->getEffectInstance()->getFrameRate(); double selectedFPS = selectedNodeInternalNode->getEffectInstance()->getFrameRate(); bool isValid = true; if ( (selectedPAR != nPAR) || (std::abs(nFPS - selectedFPS) > 0.01) ) { if (nHasInput || selectedHasInput) { isValid = false; } else if ( !nHasInput && (nMaxInput == 0) && !selectedHasInput && (selectedMaxInput == 0) ) { isValid = false; } } if (isValid) { nodeToShowMergeRect = (*it)->shared_from_this(); } } else { (*it)->setMergeHintActive(false); } } else { //!doMergeHints edge = (*it)->hasEdgeNearbyRect(selectedNodeBbox); ///if the edge input is the selected node don't continue if ( edge && ( edge->getSource() == selectedNode) ) { edge = 0; } if ( edge && edge->isOutputEdge() ) { if (selectedNodeIsReader) { continue; } int prefInput = selectedNodeInternalNode->getPreferredInputForConnection(); if (prefInput == -1) { edge = 0; } else { Node::CanConnectInputReturnValue ret = selectedNodeInternalNode->canConnectInput(edge->getSource()->getNode(), prefInput); if (ret != Node::eCanConnectInput_ok) { edge = 0; } } } if ( edge && !edge->isOutputEdge() ) { if ( (*it)->getNode()->getEffectInstance()->isReader() || ( (*it)->getNode()->getNInputs() == 0 ) ) { edge = 0; continue; } //Check that the edge can connect to the selected node { Node::CanConnectInputReturnValue ret = edge->getDest()->getNode()->canConnectInput( selectedNodeInternalNode, edge->getInputNumber() ); if ( (ret == Node::eCanConnectInput_inputAlreadyConnected) && !selectedNodeIsReader ) { ret = Node::eCanConnectInput_ok; } if (ret != Node::eCanConnectInput_ok) { edge = 0; } } //Check that the selected node can connect to the input of the edge if (edge) { NodeGuiPtr edgeHasSource = edge->getSource(); if (edgeHasSource) { int prefInput = selectedNodeInternalNode->getPreferredInputForConnection(); if (prefInput != -1) { Node::CanConnectInputReturnValue ret = selectedNodeInternalNode->canConnectInput(edgeHasSource->getNode(), prefInput); if ( (ret == Node::eCanConnectInput_inputAlreadyConnected) && !selectedNodeIsReader ) { ret = Node::eCanConnectInput_ok; } if (ret != Node::eCanConnectInput_ok) { edge = 0; } } } } } if (edge) { edge->setUseHighlight(true); break; } } // doMergeHints } } } // QMutexLocker l(&_imp->_nodesMutex); if ( _imp->_highLightedEdge && ( _imp->_highLightedEdge != edge) ) { _imp->_highLightedEdge->setUseHighlight(false); _imp->_hintInputEdge->hide(); _imp->_hintOutputEdge->hide(); } _imp->_highLightedEdge = edge; if ( edge && edge->getSource() && edge->getDest() ) { ///setup the hints edge ///find out if the node is already connected to what the edge is connected bool alreadyConnected = false; const std::vector<NodeWPtr> & inpNodes = selectedNode->getNode()->getInputs(); for (std::size_t i = 0; i < inpNodes.size(); ++i) { if ( inpNodes[i].lock() == edge->getSource()->getNode() ) { alreadyConnected = true; break; } } if ( !_imp->_hintInputEdge->isVisible() ) { if (!alreadyConnected) { int prefInput = selectedNode->getNode()->getPreferredInputForConnection(); _imp->_hintInputEdge->setInputNumber(prefInput != -1 ? prefInput : 0); _imp->_hintInputEdge->setSourceAndDestination(edge->getSource(), selectedNode); _imp->_hintInputEdge->setVisible(true); } _imp->_hintOutputEdge->setInputNumber( edge->getInputNumber() ); _imp->_hintOutputEdge->setSourceAndDestination( selectedNode, edge->getDest() ); _imp->_hintOutputEdge->setVisible(true); } else { if (!alreadyConnected) { _imp->_hintInputEdge->initLine(); } _imp->_hintOutputEdge->initLine(); } } else if (edge) { ///setup only 1 of the hints edge if ( _imp->_highLightedEdge && !_imp->_hintInputEdge->isVisible() ) { if ( edge->isOutputEdge() ) { int prefInput = selectedNode->getNode()->getPreferredInputForConnection(); if (prefInput != -1) { _imp->_hintInputEdge->setInputNumber(prefInput); _imp->_hintInputEdge->setSourceAndDestination(edge->getSource(), selectedNode); _imp->_hintInputEdge->setVisible(true); } } else { _imp->_hintInputEdge->setInputNumber( edge->getInputNumber() ); _imp->_hintInputEdge->setSourceAndDestination( selectedNode, edge->getDest() ); _imp->_hintInputEdge->setVisible(true); } } else if ( _imp->_highLightedEdge && _imp->_hintInputEdge->isVisible() ) { _imp->_hintInputEdge->initLine(); } } else if (nodeToShowMergeRect) { nodeToShowMergeRect->setMergeHintActive(true); selectedNode->setMergeHintActive(true); _imp->_mergeHintNode = nodeToShowMergeRect; } else { selectedNode->setMergeHintActive(false); _imp->_mergeHintNode.reset(); } } // NodeGraph::checkForHints