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
ViewerTab::~ViewerTab() { Gui* gui = getGui(); if (gui) { NodeGraph* graph = 0; ViewerNodePtr internalNode = getInternalNode(); ViewerInstancePtr viewerNode = internalNode ? internalNode->getInternalViewerNode() : ViewerInstancePtr(); if (viewerNode) { NodeCollectionPtr collection = viewerNode->getNode()->getGroup(); if (collection) { NodeGroupPtr isGrp = toNodeGroup(collection); if (isGrp) { NodeGraphI* graph_i = isGrp->getNodeGraph(); if (graph_i) { graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); } } else { graph = gui->getNodeGraph(); } } internalNode->invalidateUiContext(); } else { graph = gui->getNodeGraph(); } assert(graph); GuiAppInstancePtr app = gui->getApp(); if ( app && !app->isClosing() && graph && (graph->getLastSelectedViewer() == this) ) { graph->setLastSelectedViewer(0); } } _imp->nodesContext.clear(); }
std::vector<NodeAnimPtr > AnimationModule::getChildrenNodes(const NodeAnimPtr& node) const { std::vector<NodeAnimPtr > children; AnimatedItemTypeEnum nodeType = node->getItemType(); if (nodeType == eAnimatedItemTypeGroup) { // If the node is a group, make all its children to be a child in the tree view NodeGroupPtr nodeGroup = node->getInternalNode()->isEffectNodeGroup(); assert(nodeGroup); NodesList nodes = nodeGroup->getNodes(); for (NodesList::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { NodePtr childNode = (*it); NodeAnimPtr isInDopeSheet = findNodeAnim( childNode ); if (isInDopeSheet) { children.push_back(isInDopeSheet); } } } else if ( node->isTimeNode() ) { // If the node is a time node, all input nodes recursively are considered to be a child std::list<NodePtr> markedNodes; _imp->getInputs_recursive(node->getInternalNode(), markedNodes, &children); } return children; }
void Gui::createGroupGui(const NodePtr & group, const CreateNodeArgs& args) { NodeGroupPtr isGrp = toNodeGroup( group->getEffectInstance()->shared_from_this() ); assert(isGrp); NodeCollectionPtr collection = boost::dynamic_pointer_cast<NodeCollection>(isGrp); assert(collection); TabWidget* where = 0; if (_imp->_lastFocusedGraph) { TabWidget* isTab = dynamic_cast<TabWidget*>( _imp->_lastFocusedGraph->parentWidget() ); if (isTab) { where = isTab; } else { std::list<TabWidgetI*> panes = getApp()->getTabWidgetsSerialization(); assert( !panes.empty() ); where = dynamic_cast<TabWidget*>(panes.front()); } } QGraphicsScene* scene = new QGraphicsScene(this); scene->setItemIndexMethod(QGraphicsScene::NoIndex); std::string newName = isGrp->getNode()->getFullyQualifiedName(); for (std::size_t i = 0; i < newName.size(); ++i) { if (newName[i] == '.') { newName[i] = '_'; } } newName += "_NodeGraph"; std::string label = tr(" Node Graph").toStdString(); NodeGraph::makeFullyQualifiedLabel(group, &label); NodeGraph* nodeGraph = new NodeGraph(this, collection, newName, scene, this); nodeGraph->setLabel(label); nodeGraph->setObjectName( QString::fromUtf8( group->getLabel().c_str() ) ); _imp->_groups.push_back(nodeGraph); SERIALIZATION_NAMESPACE::NodeSerializationPtr serialization = args.getPropertyUnsafe<SERIALIZATION_NAMESPACE::NodeSerializationPtr>(kCreateNodeArgsPropNodeSerialization); bool showSubGraph = args.getPropertyUnsafe<bool>(kCreateNodeArgsPropSubGraphOpened); if ( showSubGraph && where && !serialization ) { where->appendTab(nodeGraph, nodeGraph); QTimer::singleShot( 25, nodeGraph, SLOT(centerOnAllNodes()) ); } else { nodeGraph->setVisible(false); } }
NodeAnimPtr AnimationModule::getGroupNodeAnim(const NodeAnimPtr& node) const { NodePtr internalNode = node->getInternalNode(); if (!internalNode) { return NodeAnimPtr(); } NodeGroupPtr parentGroup = toNodeGroup( internalNode->getGroup() ); NodeAnimPtr parentGroupNodeAnim; if (parentGroup) { parentGroupNodeAnim = findNodeAnim( parentGroup->getNode() ); } return parentGroupNodeAnim; }
void NodePrivate::runOnNodeDeleteCB() { if (_publicInterface->getScriptName_mt_safe().empty()) { return; } std::string cb = _publicInterface->getApp()->getProject()->getOnNodeDeleteCB(); NodeCollectionPtr group = _publicInterface->getGroup(); if (!group) { return; } std::string callbackFunction; if (figureOutCallbackName(cb, &callbackFunction)) { runOnNodeDeleteCBInternal(callbackFunction); } // If this is a group, run the node deleted callback on itself { cb = effect->getBeforeNodeRemovalCallback(); if (!cb.empty()) { if (figureOutCallbackName(cb, &callbackFunction)) { runOnNodeDeleteCBInternal(callbackFunction); } } } // if there's a parent group, run the node deletec callback on the parent NodeGroupPtr isParentGroup = toNodeGroup(group); if (isParentGroup) { NodePtr grpNode = isParentGroup->getNode(); if (grpNode) { cb = isParentGroup->getBeforeNodeRemovalCallback(); if (figureOutCallbackName(cb, &callbackFunction)) { runOnNodeDeleteCBInternal(callbackFunction); } } } }
void GuiAppInstance::onGroupCreationFinished(const NodePtr& node, const NodeSerializationPtr& serialization, bool autoConnect) { NodeGuiIPtr node_gui_i = node->getNodeGui(); if (autoConnect && !serialization && node_gui_i) { NodeGraph* graph = 0; NodeCollectionPtr collection = node->getGroup(); assert(collection); NodeGroupPtr isGrp = toNodeGroup(collection); if (isGrp) { NodeGraphI* graph_i = isGrp->getNodeGraph(); assert(graph_i); graph = dynamic_cast<NodeGraph*>(graph_i); } else { graph = _imp->_gui->getNodeGraph(); } assert(graph); if (!graph) { throw std::logic_error(""); } NodesGuiList selectedNodes = graph->getSelectedNodes(); NodeGuiPtr selectedNode; if ( !selectedNodes.empty() ) { selectedNode = selectedNodes.front(); if (toBackdropGui(selectedNode) ) { selectedNode.reset(); } } NodeGuiPtr nodeGui = boost::dynamic_pointer_cast<NodeGui>(node_gui_i); graph->moveNodesForIdealPosition(nodeGui, selectedNode, true); } AppInstance::onGroupCreationFinished(node, serialization, autoConnect); /*std::list<ViewerInstancePtr> viewers; node->hasViewersConnected(&viewers); for (std::list<ViewerInstancePtr>::iterator it2 = viewers.begin(); it2 != viewers.end(); ++it2) { (*it2)->renderCurrentFrame(false); }*/ }
void NodePrivate::runOnNodeCreatedCB(bool userEdited) { std::string cb = _publicInterface->getApp()->getProject()->getOnNodeCreatedCB(); NodeCollectionPtr group = _publicInterface->getGroup(); if (!group) { return; } std::string callbackFunction; if (figureOutCallbackName(cb, &callbackFunction)) { runOnNodeCreatedCBInternal(callbackFunction, userEdited); } // If this is a group, run the node created callback on itself cb = effect->getAfterNodeCreatedCallback(); if (!cb.empty()) { if (figureOutCallbackName(cb, &callbackFunction)) { runOnNodeCreatedCBInternal(callbackFunction, userEdited); } } // if there's a parent group, run the node created callback on the parent NodeGroupPtr isParentGroup = toNodeGroup(group); if (isParentGroup) { NodePtr grpNode = isParentGroup->getNode(); if (grpNode) { cb = isParentGroup->getAfterNodeCreatedCallback(); if (figureOutCallbackName(cb, &callbackFunction)) { runOnNodeCreatedCBInternal(callbackFunction, userEdited); } } } }
void NodePrivate::runChangedParamCallback(const std::string& cb, const KnobIPtr& k, bool userEdited) { std::vector<std::string> args; std::string error; if ( !k || (k->getName() == "onParamChanged") ) { return; } std::string callbackFunction; if (!figureOutCallbackName(cb, &callbackFunction)) { return; } try { NATRON_PYTHON_NAMESPACE::getFunctionArguments(callbackFunction, &error, &args); } catch (const std::exception& e) { _publicInterface->getApp()->appendToScriptEditor( tr("Failed to run onParamChanged callback: %1").arg( QString::fromUtf8( e.what() ) ).toStdString() ); return; } if ( !error.empty() ) { _publicInterface->getApp()->appendToScriptEditor( tr("Failed to run onParamChanged callback: %1").arg( QString::fromUtf8( error.c_str() ) ).toStdString() ); return; } std::string signatureError; signatureError.append( tr("The param changed callback supports the following signature(s):").toStdString() ); signatureError.append("\n- callback(thisParam,thisNode,thisGroup,app,userEdited)"); if (args.size() != 5) { _publicInterface->getApp()->appendToScriptEditor( tr("Failed to run onParamChanged callback: %1").arg( QString::fromUtf8( signatureError.c_str() ) ).toStdString() ); return; } if ( ( (args[0] != "thisParam") || (args[1] != "thisNode") || (args[2] != "thisGroup") || (args[3] != "app") || (args[4] != "userEdited") ) ) { _publicInterface->getApp()->appendToScriptEditor( tr("Failed to run onParamChanged callback: %1").arg( QString::fromUtf8( signatureError.c_str() ) ).toStdString() ); return; } std::string appID = _publicInterface->getApp()->getAppIDString(); assert(k); std::string thisNodeVar = appID + "."; thisNodeVar.append( _publicInterface->getFullyQualifiedName() ); NodeCollectionPtr collection = _publicInterface->getGroup(); assert(collection); if (!collection) { return; } std::string thisGroupVar; NodeGroupPtr isParentGrp = toNodeGroup(collection); if (isParentGrp) { std::string nodeName = isParentGrp->getNode()->getFullyQualifiedName(); std::string nodeFullName = appID + "." + nodeName; thisGroupVar = nodeFullName; } else { thisGroupVar = appID; } bool alreadyDefined = false; PyObject* nodeObj = NATRON_PYTHON_NAMESPACE::getAttrRecursive(thisNodeVar, NATRON_PYTHON_NAMESPACE::getMainModule(), &alreadyDefined); if (!nodeObj || !alreadyDefined) { return; } if (!PyObject_HasAttrString( nodeObj, k->getName().c_str() ) ) { return; } std::stringstream ss; ss << callbackFunction << "(" << thisNodeVar << "." << k->getName() << "," << thisNodeVar << "," << thisGroupVar << "," << appID << ","; if (userEdited) { ss << "True"; } else { ss << "False"; } ss << ")\n"; std::string script = ss.str(); std::string err; std::string output; if ( !NATRON_PYTHON_NAMESPACE::interpretPythonScript(script, &err, &output) ) { _publicInterface->getApp()->appendToScriptEditor( tr("Failed to execute onParamChanged callback: %1").arg( QString::fromUtf8( err.c_str() ) ).toStdString() ); } else { if ( !output.empty() ) { _publicInterface->getApp()->appendToScriptEditor(output); } } } // runChangedParamCallback
void NodePrivate::runInputChangedCallback(int index, const std::string& cb) { std::vector<std::string> args; std::string error; std::string callbackFunction; if (!figureOutCallbackName(cb, &callbackFunction)) { return; } try { NATRON_PYTHON_NAMESPACE::getFunctionArguments(callbackFunction, &error, &args); } catch (const std::exception& e) { _publicInterface->getApp()->appendToScriptEditor( std::string("Failed to run onInputChanged callback: ") + e.what() ); return; } if ( !error.empty() ) { _publicInterface->getApp()->appendToScriptEditor("Failed to run onInputChanged callback: " + error); return; } std::string signatureError; signatureError.append("The on input changed callback supports the following signature(s):\n"); signatureError.append("- callback(inputIndex,thisNode,thisGroup,app)"); if (args.size() != 4) { _publicInterface->getApp()->appendToScriptEditor("Failed to run onInputChanged callback: " + signatureError); return; } if ( (args[0] != "inputIndex") || (args[1] != "thisNode") || (args[2] != "thisGroup") || (args[3] != "app") ) { _publicInterface->getApp()->appendToScriptEditor("Failed to run onInputChanged callback: " + signatureError); return; } std::string appID = _publicInterface->getApp()->getAppIDString(); NodeCollectionPtr collection = _publicInterface->getGroup(); assert(collection); if (!collection) { return; } std::string thisGroupVar; NodeGroupPtr isParentGrp = toNodeGroup(collection); if (isParentGrp) { std::string nodeName = isParentGrp->getNode()->getFullyQualifiedName(); std::string nodeFullName = appID + "." + nodeName; thisGroupVar = nodeFullName; } else { thisGroupVar = appID; } std::stringstream ss; ss << callbackFunction << "(" << index << "," << appID << "." << _publicInterface->getFullyQualifiedName() << "," << thisGroupVar << "," << appID << ")\n"; std::string script = ss.str(); std::string output; if ( !NATRON_PYTHON_NAMESPACE::interpretPythonScript(script, &error, &output) ) { _publicInterface->getApp()->appendToScriptEditor( tr("Failed to execute callback: %1").arg( QString::fromUtf8( error.c_str() ) ).toStdString() ); } else { if ( !output.empty() ) { _publicInterface->getApp()->appendToScriptEditor(output); } } } //runInputChangedCallback
void moveGroupNode(DopeSheetEditor* model, const NodePtr& node, double dt) { NodeGroupPtr group = node->isEffectNodeGroup(); assert(group); NodesList nodes; group->getNodes_recursive(nodes, true); for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { NodeGuiPtr nodeGui = boost::dynamic_pointer_cast<NodeGui>( (*it)->getNodeGui() ); assert(nodeGui); std::string pluginID = (*it)->getPluginID(); NodeGroupPtr isChildGroup = (*it)->isEffectNodeGroup(); // Move readers #ifndef NATRON_ENABLE_IO_META_NODES if ( ReadNode::isBundledReader( pluginID, node->getApp()->wasProjectCreatedWithLowerCaseIDs() ) ) { #else if (pluginID == PLUGINID_NATRON_READ) { #endif moveReader(*it, dt); } else if (pluginID == PLUGINID_OFX_TIMEOFFSET) { moveTimeOffset(*it, dt); } else if (pluginID == PLUGINID_OFX_FRAMERANGE) { moveFrameRange(*it, dt); } else if (isChildGroup) { moveGroupNode(model, *it, dt); } // Move keyframes const KnobsVec &knobs = (*it)->getKnobs(); for (KnobsVec::const_iterator knobIt = knobs.begin(); knobIt != knobs.end(); ++knobIt) { const KnobIPtr& knob = *knobIt; if ( !knob->hasAnimation() ) { continue; } for (int dim = 0; dim < knob->getDimension(); ++dim) { if ( !knob->isAnimated( dim, ViewIdx(0) ) ) { continue; } KeyFrameSet keyframes = knob->getCurve(ViewIdx(0), dim)->getKeyFrames_mt_safe(); for (KeyFrameSet::iterator kfIt = keyframes.begin(); kfIt != keyframes.end(); ++kfIt) { KeyFrame kf = (*kfIt); KeyFrame fake; knob->moveValueAtTime(eCurveChangeReasonDopeSheet, kf.getTime(), ViewSpec::all(), dim, dt, 0, &fake); } } } } } // moveGroupNode NATRON_NAMESPACE_ANONYMOUS_EXIT ////////////////////////// DSMoveKeysCommand ////////////////////////// DSMoveKeysAndNodesCommand::DSMoveKeysAndNodesCommand(const DSKeyPtrList &keys, const std::vector<DSNodePtr >& nodes, double dt, DopeSheetEditor *model, QUndoCommand *parent) : QUndoCommand(parent), _keys(keys), _nodes(), _dt(dt), _model(model) { setText( tr("Move selected keys") ); std::set<NodePtr > nodesSet; for (std::vector<DSNodePtr >::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { DopeSheetItemType type = (*it)->getItemType(); if ( (type != eDopeSheetItemTypeReader) && ( type != eDopeSheetItemTypeGroup) && ( type != eDopeSheetItemTypeTimeOffset) && ( type != eDopeSheetItemTypeFrameRange) ) { //Note that Retime nodes cannot be moved continue; } _nodes.push_back(*it); nodesSet.insert( (*it)->getInternalNode() ); NodeGroupPtr isGroup = (*it)->getInternalNode()->isEffectNodeGroup(); if (isGroup) { NodesList recurseNodes; isGroup->getNodes_recursive(recurseNodes, true); for (NodesList::iterator it = recurseNodes.begin(); it != recurseNodes.end(); ++it) { nodesSet.insert(*it); } } } for (DSKeyPtrList::iterator it = _keys.begin(); it != _keys.end(); ++it) { KnobHolderPtr holder = (*it)->getContext()->getInternalKnob()->getHolder(); assert(holder); EffectInstancePtr isEffect = toEffectInstance(holder); if (isEffect) { nodesSet.insert( isEffect->getNode() ); } } for (std::set<NodePtr >::iterator it = nodesSet.begin(); it != nodesSet.end(); ++it) { _allDifferentNodes.push_back(*it); } } void DSMoveKeysAndNodesCommand::undo() { moveSelection(-_dt); }
void Gui::removeViewerTab(ViewerTab* tab, bool initiatedFromNode, bool deleteData) { assert(tab); unregisterTab(tab); if (tab == _imp->_activeViewer) { _imp->_activeViewer = 0; } NodeGraph* graph = 0; NodeGroupPtr isGrp; NodeCollectionPtr collection; if ( tab->getInternalNode() && tab->getInternalNode()->getNode() ) { NodeCollectionPtr collection = tab->getInternalNode()->getNode()->getGroup(); isGrp = toNodeGroup(collection); } if (isGrp) { NodeGraphI* graph_i = isGrp->getNodeGraph(); assert(graph_i); graph = dynamic_cast<NodeGraph*>(graph_i); } else { graph = getNodeGraph(); } assert(graph); if (!graph) { throw std::logic_error(""); } ViewerTab* lastSelectedViewer = graph->getLastSelectedViewer(); if (lastSelectedViewer == tab) { bool foundOne = false; NodesList nodes; if (collection) { nodes = collection->getNodes(); } for (NodesList::iterator it = nodes.begin(); it != nodes.end(); ++it) { ViewerNodePtr isViewer = (*it)->isEffectViewerNode(); if ( !isViewer || ( isViewer == tab->getInternalNode() ) || !(*it)->isActivated() ) { continue; } OpenGLViewerI* viewerI = isViewer->getUiContext(); assert(viewerI); ViewerGL* glViewer = dynamic_cast<ViewerGL*>(viewerI); assert(glViewer); if (glViewer) { graph->setLastSelectedViewer( glViewer->getViewerTab() ); } foundOne = true; break; } if (!foundOne) { graph->setLastSelectedViewer(0); } } ViewerNodePtr viewerNode = tab->getInternalNode(); ViewerInstancePtr internalViewer; if (viewerNode) { internalViewer = viewerNode->getInternalViewerNode(); } if (internalViewer) { internalViewer->abortAnyEvaluation(); if (getApp()->getLastViewerUsingTimeline() == internalViewer) { getApp()->discardLastViewerUsingTimeline(); } } if (!initiatedFromNode) { assert(_imp->_nodeGraphArea); ///call the deleteNode which will call this function again when the node will be deactivated. NodePtr internalNode = tab->getInternalNode()->getNode(); NodeGuiIPtr guiI = internalNode->getNodeGui(); NodeGuiPtr gui = boost::dynamic_pointer_cast<NodeGui>(guiI); assert(gui); NodeGraphI* graph_i = internalNode->getGroup()->getNodeGraph(); assert(graph_i); NodeGraph* graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); if (graph) { graph->removeNode(gui); } } else { tab->hide(); TabWidget* container = dynamic_cast<TabWidget*>( tab->parentWidget() ); if (container) { container->removeTab(tab, false); } if (deleteData) { QMutexLocker l(&_imp->_viewerTabsMutex); std::list<ViewerTab*>::iterator it = std::find(_imp->_viewerTabs.begin(), _imp->_viewerTabs.end(), tab); if ( it != _imp->_viewerTabs.end() ) { _imp->_viewerTabs.erase(it); } tab->notifyGuiClosingPublic(); tab->deleteLater(); } } Q_EMIT viewersChanged(); } // Gui::removeViewerTab
void GuiAppInstance::createNodeGui(const NodePtr &node, const NodePtr& parentMultiInstance, const CreateNodeArgs& args) { NodeCollectionPtr group = node->getGroup(); NodeGraph* graph; if (group) { NodeGraphI* graph_i = group->getNodeGraph(); assert(graph_i); graph = dynamic_cast<NodeGraph*>(graph_i); assert(graph); } else { graph = _imp->_gui->getNodeGraph(); } if (!graph) { throw std::logic_error(""); } NodesGuiList selectedNodes = graph->getSelectedNodes(); NodeGuiPtr nodegui = _imp->_gui->createNodeGUI(node, args); assert(nodegui); if (parentMultiInstance && nodegui) { nodegui->hideGui(); NodeGuiIPtr parentNodeGui_i = parentMultiInstance->getNodeGui(); assert(parentNodeGui_i); nodegui->setParentMultiInstance( boost::dynamic_pointer_cast<NodeGui>(parentNodeGui_i) ); } bool isViewer = node->isEffectViewerInstance() != 0; if (isViewer) { _imp->_gui->createViewerGui(node); } // Must be done after the viewer gui has been created _imp->_gui->createNodeViewerInterface(nodegui); NodeGroupPtr isGroup = node->isEffectNodeGroup(); if ( isGroup && isGroup->isSubGraphUserVisible() ) { _imp->_gui->createGroupGui(node, args); } ///Don't initialize inputs if it is a multi-instance child since it is not part of the graph if (!parentMultiInstance) { nodegui->initializeInputs(); } NodeSerializationPtr serialization = args.getProperty<NodeSerializationPtr >(kCreateNodeArgsPropNodeSerialization); if ( !serialization && !isViewer ) { ///we make sure we can have a clean preview. node->computePreviewImage( getTimeLine()->currentFrame() ); triggerAutoSave(); } ///only move main instances if ( node->getParentMultiInstanceName().empty() && !serialization) { bool autoConnect = args.getProperty<bool>(kCreateNodeArgsPropAutoConnect); if ( selectedNodes.empty() || serialization) { autoConnect = false; } double xPosHint = serialization ? INT_MIN : args.getProperty<double>(kCreateNodeArgsPropNodeInitialPosition, 0); double yPosHint = serialization ? INT_MIN : args.getProperty<double>(kCreateNodeArgsPropNodeInitialPosition, 1); if ( (xPosHint != INT_MIN) && (yPosHint != INT_MIN) && (!autoConnect) ) { QPointF pos = nodegui->mapToParent( nodegui->mapFromScene( QPointF(xPosHint, yPosHint) ) ); nodegui->refreshPosition( pos.x(), pos.y(), true ); } else { BackdropGuiPtr isBd = toBackdropGui(nodegui); if (!isBd) { NodeGuiPtr selectedNode; if ( !serialization && (selectedNodes.size() == 1) ) { selectedNode = selectedNodes.front(); BackdropGuiPtr isBdGui = toBackdropGui(selectedNode); if (isBdGui) { selectedNode.reset(); } } nodegui->getDagGui()->moveNodesForIdealPosition(nodegui, selectedNode, autoConnect); } } } } // createNodeGui
bool Project::restoreGroupFromSerialization(const SERIALIZATION_NAMESPACE::NodeSerializationList & serializedNodes, const NodeCollectionPtr& group, std::list<std::pair<NodePtr, SERIALIZATION_NAMESPACE::NodeSerializationPtr > >* createdNodesOut) { bool mustShowErrorsLog = false; NodeGroupPtr isGrp = toNodeGroup(group); QString groupName; if (isGrp) { groupName = QString::fromUtf8( isGrp->getNode()->getLabel().c_str() ); } else { groupName = tr("top-level"); } group->getApplication()->updateProjectLoadStatus( tr("Creating nodes in group: %1").arg(groupName) ); std::list<std::pair<NodePtr, SERIALIZATION_NAMESPACE::NodeSerializationPtr > > createdNodes; // Loop over all node serialization and create them first for (SERIALIZATION_NAMESPACE::NodeSerializationList::const_iterator it = serializedNodes.begin(); it != serializedNodes.end(); ++it) { NodePtr node = appPTR->createNodeForProjectLoading(*it, group); if (!node) { QString text( tr("ERROR: The node %1 version %2.%3" " was found in the script but does not" " exist in the loaded plug-ins.") .arg( QString::fromUtf8( (*it)->_pluginID.c_str() ) ) .arg((*it)->_pluginMajorVersion).arg((*it)->_pluginMinorVersion) ); appPTR->writeToErrorLog_mt_safe(tr("Project"), QDateTime::currentDateTime(), text); mustShowErrorsLog = true; continue; } else { if (node->getPluginID() == PLUGINID_NATRON_STUB) { // If the node could not be created and we made a stub instead, warn the user QString text( tr("WARNING: The node %1 (%2 version %3.%4) " "was found in the script but the plug-in could not be found. It has been replaced by a pass-through node instead.") .arg( QString::fromUtf8( (*it)->_nodeScriptName.c_str() ) ) .arg( QString::fromUtf8( (*it)->_pluginID.c_str() ) ) .arg((*it)->_pluginMajorVersion) .arg((*it)->_pluginMinorVersion)); appPTR->writeToErrorLog_mt_safe(tr("Project"), QDateTime::currentDateTime(), text); mustShowErrorsLog = true; } else if ( (*it)->_pluginMajorVersion != -1 && (node->getMajorVersion() != (int)(*it)->_pluginMajorVersion) ) { // If the node has a IOContainer don't do this check: when loading older projects that had a // ReadOIIO node for example in version 2, we would now create a new Read meta-node with version 1 instead QString text( tr("WARNING: The node %1 (%2 version %3.%4) " "was found in the script but was loaded " "with version %5.%6 instead.") .arg( QString::fromUtf8( (*it)->_nodeScriptName.c_str() ) ) .arg( QString::fromUtf8( (*it)->_pluginID.c_str() ) ) .arg((*it)->_pluginMajorVersion) .arg((*it)->_pluginMinorVersion) .arg( node->getPlugin()->getPropertyUnsafe<unsigned int>(kNatronPluginPropVersion, 0) ) .arg( node->getPlugin()->getPropertyUnsafe<unsigned int>(kNatronPluginPropVersion, 1) ) ); appPTR->writeToErrorLog_mt_safe(tr("Project"), QDateTime::currentDateTime(), text); } } assert(node); createdNodes.push_back(std::make_pair(node, *it)); } // for all node serialization group->getApplication()->updateProjectLoadStatus( tr("Restoring graph links in group: %1").arg(groupName) ); // Connect the nodes together for (std::list<std::pair<NodePtr, SERIALIZATION_NAMESPACE::NodeSerializationPtr > >::const_iterator it = createdNodes.begin(); it != createdNodes.end(); ++it) { // Loop over the inputs map // This is a map <input label, input node name> // // When loading projects before Natron 2.2, the inputs contain both masks and inputs. // restoreInputs(it->first, it->second->_inputs, createdNodes, false /*isMasks*/); // After Natron 2.2, masks are saved separatly restoreInputs(it->first, it->second->_masks, createdNodes, true /*isMasks*/); } // for (std::list< NodeSerializationPtr >::const_iterator it = serializedNodes.begin(); it != serializedNodes.end(); ++it) { if (createdNodesOut) { *createdNodesOut = createdNodes; } // If we created all sub-groups recursively, then we are in the top-level group. We may now restore all links if (!group->getApplication()->isCreatingNode()) { // Now that we created all nodes. There may be cross-graph link(s) and they can only be truely restored now. restoreLinksRecursive(group, serializedNodes, createdNodesOut); } return !mustShowErrorsLog; } // ProjectPrivate::restoreGroupFromSerialization
void NodeGraph::mouseMoveEvent(QMouseEvent* e) { QPointF newPos = mapToScene( e->pos() ); QPointF lastMousePosScene = mapToScene( _imp->_lastMousePos.x(), _imp->_lastMousePos.y() ); double dx, dy; { QPointF newPosRoot = _imp->_root->mapFromScene(newPos); QPointF lastMousePosRoot = _imp->_root->mapFromScene(lastMousePosScene); dx = newPosRoot.x() - lastMousePosRoot.x(); dy = newPosRoot.y() - lastMousePosRoot.y(); } _imp->_hasMovedOnce = true; bool mustUpdate = true; NodeCollectionPtr collection = getGroup(); NodeGroupPtr isGroup = toNodeGroup(collection); bool isGroupEditable = true; bool groupEdited = true; if (isGroup) { isGroupEditable = isGroup->isSubGraphEditable(); groupEdited = isGroup->isSubGraphEditedByUser(); } if (!groupEdited && isGroupEditable) { ///check if user is nearby unlock // see NodeGraph::paintEvent() QPoint pixPos = _imp->getPyPlugUnlockPos(); int pixW = _imp->unlockIcon.width(); int pixH = _imp->unlockIcon.height(); QRect pixRect(pixPos.x(), pixPos.y(), pixW, pixH); pixRect.adjust(-2, -2, 2, 2); QRect selRect = pixRect; selRect.adjust(-3, -3, 3, 3); if ( selRect.contains( e->pos() ) ) { assert(isGroup); QPoint pos = mapToGlobal( e->pos() ); // Unfortunately, the timeout delay for the tooltip is hardcoded in Qt 4, and the last parameter to showText doesn't seem to influence anything // Can not fix https://github.com/MrKepzie/Natron/issues/1151 (at least in Qt4) QToolTip::showText( pos, NATRON_NAMESPACE::convertFromPlainText(QCoreApplication::translate("NodeGraph", "Clicking the unlock button will convert the PyPlug to a regular group saved in the project and dettach it from the script.\n" "Any modification will not be written to the Python script. Subsequent loading of the project will no longer load this group from the python script."), NATRON_NAMESPACE::WhiteSpaceNormal), this, selRect); e->accept(); return; } } QRectF sceneR = visibleSceneRect(); bool mustUpdateNavigator = false; ///Apply actions switch (_imp->_evtState) { case eEventStateDraggingArrow: { QPointF np = _imp->_arrowSelected->mapFromScene(newPos); if ( _imp->_arrowSelected->isOutputEdge() ) { _imp->_arrowSelected->dragDest(np); } else { _imp->_arrowSelected->dragSource(np); } checkAndStartAutoScrollTimer(newPos); mustUpdate = true; if (_imp->cursorSet) { _imp->cursorSet = false; unsetCursor(); } break; } case eEventStateDraggingNode: { mustUpdate = true; mustUpdateNavigator = true; bool controlDown = modifierHasControl(e); bool shiftdown = modifierHasShift(e); moveSelectedNodesBy(shiftdown, controlDown, lastMousePosScene, newPos, sceneR, true); _imp->cursorSet = true; setCursor( QCursor(Qt::ClosedHandCursor) ); break; } case eEventStateMovingArea: { mustUpdateNavigator = true; moveRootInternal(dx, dy); mustUpdate = true; _imp->cursorSet = true; setCursor( QCursor(Qt::SizeAllCursor) ); break; } case eEventStateResizingBackdrop: { mustUpdateNavigator = true; assert(_imp->_backdropResized.lock()); QPointF p = _imp->_backdropResized.lock()->scenePos(); int w = newPos.x() - p.x(); int h = newPos.y() - p.y(); checkAndStartAutoScrollTimer(newPos); mustUpdate = true; pushUndoCommand( new ResizeBackdropCommand(_imp->_backdropResized.lock(), w, h) ); _imp->cursorSet = true; setCursor( QCursor(Qt::SizeFDiagCursor) ); break; } case eEventStateSelectionRect: { QPointF startDrag = _imp->_lastSelectionStartPointScene; QPointF cur = newPos; double xmin = std::min( cur.x(), startDrag.x() ); double xmax = std::max( cur.x(), startDrag.x() ); double ymin = std::min( cur.y(), startDrag.y() ); double ymax = std::max( cur.y(), startDrag.y() ); checkAndStartAutoScrollTimer(newPos); QRectF selRect(xmin, ymin, xmax - xmin, ymax - ymin); _imp->_selectionRect = selRect; mustUpdate = true; _imp->cursorSet = true; setCursor( QCursor(Qt::CrossCursor) ); break; } case eEventStateDraggingNavigator: { _imp->cursorSet = true; setCursor( QCursor(Qt::ClosedHandCursor) ); QPointF mousePosSceneCoordinates; bool insideNavigator = isNearbyNavigator(e->pos(), mousePosSceneCoordinates); if (insideNavigator) { _imp->_refreshOverlays = true; centerOn(mousePosSceneCoordinates); _imp->_lastMousePos = e->pos(); update(); return; } break; } case eEventStateZoomingArea: { int delta = 2 * ( ( e->x() - _imp->_lastMousePos.x() ) - ( e->y() - _imp->_lastMousePos.y() ) ); setTransformationAnchor(QGraphicsView::AnchorViewCenter); wheelEventInternal(modCASIsControl(e), delta); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); mustUpdate = true; _imp->cursorSet = true; setCursor( QCursor(Qt::SizeAllCursor) ); break; } case eEventStateNone: default: { mustUpdate = false; // Test if mouse is inside the navigator QPointF mousePosSceneCoordinates; bool insideNavigator = isNearbyNavigator(e->pos(), mousePosSceneCoordinates); if (insideNavigator) { _imp->cursorSet = true; setCursor( QCursor(Qt::OpenHandCursor) ); } else if (!groupEdited) { if (_imp->cursorSet) { _imp->cursorSet = false; unsetCursor(); } } else { // Set cursor // The cursor should clearly indicate when will happen if mouse is pressed NodeGuiPtr nearbyNode; Edge* nearbyEdge = NULL; NearbyItemEnum nearbyItemCode = hasItemNearbyMouse(e->pos(), &nearbyNode, &nearbyEdge); switch (nearbyItemCode) { case eNearbyItemNode: case eNearbyItemBackdropFrame: case eNearbyItemEdgeBendPoint: { _imp->cursorSet = true; setCursor( QCursor(Qt::OpenHandCursor) ); break; } case eNearbyItemBackdropResizeHandle: { _imp->cursorSet = true; setCursor( QCursor(Qt::SizeFDiagCursor) ); break; } case eNearbyItemNone: { _imp->cursorSet = true; setCursor( QCursor(Qt::CrossCursor) ); break; } case eNearbyItemNodeEdge: default: { if (_imp->cursorSet) { _imp->cursorSet = false; unsetCursor(); } break; } } } break; } } // switch _imp->_lastMousePos = e->pos(); if (mustUpdateNavigator) { _imp->_refreshOverlays = true; mustUpdate = true; } if (mustUpdate) { update(); } TabWidget* tab = getParentPane() ; if (tab && _imp->_evtState == eEventStateNone) { // If the Viewer is in a tab, send the tab widget the event directly qApp->sendEvent(tab, e); } QGraphicsView::mouseMoveEvent(e); } // mouseMoveEvent
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 NodeGraph::mouseMoveEvent(QMouseEvent* e) { QPointF newPos = mapToScene( e->pos() ); QPointF lastMousePosScene = mapToScene( _imp->_lastMousePos.x(), _imp->_lastMousePos.y() ); double dx, dy; { QPointF newPosRoot = _imp->_root->mapFromScene(newPos); QPointF lastMousePosRoot = _imp->_root->mapFromScene(lastMousePosScene); dx = newPosRoot.x() - lastMousePosRoot.x(); dy = newPosRoot.y() - lastMousePosRoot.y(); } _imp->_hasMovedOnce = true; bool mustUpdate = true; NodeCollectionPtr collection = getGroup(); NodeGroupPtr isGroup = toNodeGroup(collection); bool isGroupEditable = true; bool groupEdited = true; if (isGroup) { isGroupEditable = isGroup->isSubGraphEditable(); groupEdited = isGroup->getNode()->hasPyPlugBeenEdited(); } if (!groupEdited && isGroupEditable) { ///check if user is nearby unlock int iw = _imp->unlockIcon.width(); int ih = _imp->unlockIcon.height(); int w = width(); if ( ( e->x() >= (w - iw - 10 - 15) ) && ( e->x() <= (w - 10 + 15) ) && ( e->y() >= (10 - 15) ) && ( e->y() <= (10 + ih + 15) ) ) { assert(isGroup); QPoint pos = mapToGlobal( e->pos() ); QToolTip::showText( pos, GuiUtils::convertFromPlainText(QCoreApplication::translate("NodeGraph", "Clicking the unlock button will convert the PyPlug to a regular group saved in the project and dettach it from the script.\n" "Any modification will not be written to the Python script. Subsequent loading of the project will no longer load this group from the python script."), Qt::WhiteSpaceNormal) ); } } QRectF sceneR = visibleSceneRect(); if ( groupEdited && (_imp->_evtState != eEventStateSelectionRect) && (_imp->_evtState != eEventStateDraggingArrow) ) { // Set cursor std::set<NodeGuiPtr> visibleNodes; getNodesWithinViewportRect(visibleWidgetRect(), &visibleNodes); NodeGuiPtr selected; Edge* selectedEdge = 0; bool optionalInputsAutoHidden = areOptionalInputsAutoHidden(); for (std::set<NodeGuiPtr>::iterator it = visibleNodes.begin(); it != visibleNodes.end(); ++it) { QPointF evpt = (*it)->mapFromScene(newPos); QRectF bbox = (*it)->mapToScene( (*it)->boundingRect() ).boundingRect(); if ( (*it)->isActive() && bbox.intersects(sceneR) ) { if ( (*it)->contains(evpt) ) { selected = (*it)->shared_from_this(); if (optionalInputsAutoHidden) { (*it)->refreshEdgesVisility(true); } else { break; } } else { Edge* edge = (*it)->hasEdgeNearbyPoint(newPos); if (edge) { selectedEdge = edge; if (!optionalInputsAutoHidden) { break; } } else if ( optionalInputsAutoHidden && !(*it)->getIsSelected() ) { (*it)->refreshEdgesVisility(false); } } } } if (selected) { _imp->cursorSet = true; setCursor( QCursor(Qt::OpenHandCursor) ); } else if (selectedEdge) { } else if (!selectedEdge && !selected) { if (_imp->cursorSet) { _imp->cursorSet = false; unsetCursor(); } } } bool mustUpdateNavigator = false; ///Apply actions switch (_imp->_evtState) { case eEventStateDraggingArrow: { QPointF np = _imp->_arrowSelected->mapFromScene(newPos); if ( _imp->_arrowSelected->isOutputEdge() ) { _imp->_arrowSelected->dragDest(np); } else { _imp->_arrowSelected->dragSource(np); } checkAndStartAutoScrollTimer(newPos); mustUpdate = true; break; } case eEventStateDraggingNode: { mustUpdate = true; mustUpdateNavigator = true; bool controlDown = modifierHasControl(e); bool shiftdown = modifierHasShift(e); moveSelectedNodesBy(shiftdown, controlDown, lastMousePosScene, newPos, sceneR, true); break; } case eEventStateMovingArea: { mustUpdateNavigator = true; moveRootInternal(dx, dy); _imp->cursorSet = true; setCursor( QCursor(Qt::SizeAllCursor) ); mustUpdate = true; break; } case eEventStateResizingBackdrop: { mustUpdateNavigator = true; assert(_imp->_backdropResized); QPointF p = _imp->_backdropResized->scenePos(); int w = newPos.x() - p.x(); int h = newPos.y() - p.y(); checkAndStartAutoScrollTimer(newPos); mustUpdate = true; pushUndoCommand( new ResizeBackdropCommand(_imp->_backdropResized, w, h) ); break; } case eEventStateSelectionRect: { QPointF startDrag = _imp->_lastSelectionStartPointScene; QPointF cur = newPos; double xmin = std::min( cur.x(), startDrag.x() ); double xmax = std::max( cur.x(), startDrag.x() ); double ymin = std::min( cur.y(), startDrag.y() ); double ymax = std::max( cur.y(), startDrag.y() ); checkAndStartAutoScrollTimer(newPos); QRectF selRect(xmin, ymin, xmax - xmin, ymax - ymin); _imp->_selectionRect = selRect; mustUpdate = true; break; } case eEventStateDraggingNavigator: { QPointF mousePosSceneCoordinates; bool insideNavigator = isNearbyNavigator(e->pos(), mousePosSceneCoordinates); if (insideNavigator) { _imp->_refreshOverlays = true; centerOn(mousePosSceneCoordinates); _imp->_lastMousePos = e->pos(); update(); return; } break; } case eEventStateZoomingArea: { int delta = 2 * ( ( e->x() - _imp->_lastMousePos.x() ) - ( e->y() - _imp->_lastMousePos.y() ) ); setTransformationAnchor(QGraphicsView::AnchorViewCenter); wheelEventInternal(modCASIsControl(e), delta); setTransformationAnchor(QGraphicsView::AnchorUnderMouse); mustUpdate = true; break; } default: mustUpdate = false; break; } // switch _imp->_lastMousePos = e->pos(); if (mustUpdateNavigator) { _imp->_refreshOverlays = true; mustUpdate = true; } if (mustUpdate) { update(); } QGraphicsView::mouseMoveEvent(e); } // mouseMoveEvent