Vuo  2.1.0
VuoCommandConnect.cc
Go to the documentation of this file.
1 
10 #include "VuoCommandCommon.hh"
11 #include "VuoCommandConnect.hh"
12 
13 #include "VuoCable.hh"
14 #include "VuoCompilerCable.hh"
16 #include "VuoCompilerNode.hh"
17 #include "VuoCompilerType.hh"
18 #include "VuoEditorComposition.hh"
19 #include "VuoEditorWindow.hh"
20 #include "VuoInputEditorManager.hh"
21 #include "VuoNodeClass.hh"
22 #include "VuoPort.hh"
23 #include "VuoRendererCable.hh"
24 #include "VuoRendererPort.hh"
26 #include "VuoStringUtilities.hh"
27 #include "VuoGenericType.hh"
28 
33  VuoRendererPort *targetPort,
34  VuoRendererCable *displacedCable,
35  VuoRendererPort *portToUnpublish,
36  VuoEditorWindow *window,
37  VuoInputEditorManager *inputEditorManager)
38  : VuoCommandCommon(window)
39 {
40  setText(QApplication::translate("VuoEditorWindow", "Cable Connection"));
41  this->window = window;
42 
43  // Normally we would take the composition's "Before" snapshot here, but in the case
44  // of a cable connection we need to reconstruct what the composition looked like before
45  // the cable drag began, so we do this a little bit later within the constructor.
46 
47  this->cableInProgress = cableInProgress;
48  this->displacedCable = displacedCable;
49  this->fromPortForAddedCable = NULL;
50  this->toPortForAddedCable = NULL;
51  this->addedNode = NULL;
52  this->inputEditorManager = inputEditorManager;
53 
54  // @todo: Let cable deletion handle port unpublication.
55  VuoPort *internalPortToUnpublish = (portToUnpublish? portToUnpublish->getBase() : NULL);
56  vector<VuoRendererPublishedPort *> externalPortsToUnpublish = (portToUnpublish? portToUnpublish->getPublishedPortsConnectedByDataCarryingCables() : vector<VuoRendererPublishedPort *>());
57 
58  // Start of command content.
59  {
60  VuoEditorComposition *composition = window->getComposition();
61  VuoRendererCable *addedCable = NULL;
62 
63  // Completing a connection to an input port
64  if (targetPort->getInput())
65  {
66  // Re-connecting an existing cable to a new input port
67  if (cableInProgress->getFloatingEndpointPreviousToPort() &&
68  cableInProgress->getFloatingEndpointPreviousToPort()->hasRenderer() &&
69  cableInProgress->getFloatingEndpointPreviousToPort()->getRenderer()->scene())
70  {
71  revertedFromPortForCable[cableInProgress] = cableInProgress->getBase()->getFromPort();
72  revertedToPortForCable[cableInProgress] = cableInProgress->getFloatingEndpointPreviousToPort();
73 
74  // Mark the previous 'To' port's constant value to be updated to match
75  // the port's last value in the running composition, if applicable.
76  if (cableInProgress->effectivelyCarriesData())
77  {
78  VuoPort *toPort = cableInProgress->getBase()->getToPort();
79  if (!toPort)
80  toPort = cableInProgress->getFloatingEndpointPreviousToPort();
81 
82  if (toPort && toPort->hasRenderer())
83  {
84  VuoCompilerInputEventPort *eventPort = static_cast<VuoCompilerInputEventPort *>(toPort->getCompiler());
85  if (eventPort)
86  {
87  if (inputEditorManager->doesTypeAllowOfflineSerialization(eventPort->getDataVuoType()))
88  {
89  revertedConstantForPort[toPort] = eventPort->getData()->getInitialValue();
90 
91  json_object *currentRunningValue = composition->getPortValueInRunningComposition(toPort);
92  updatedConstantForPort[toPort] = (currentRunningValue? json_object_to_json_string_ext(currentRunningValue, JSON_C_TO_STRING_PLAIN) :
93  revertedConstantForPort[toPort]);
94  }
95  }
96  }
97  }
98  }
99 
100  // Connecting the cable for the first time
101  else
102  {
103  revertedFromPortForCable[cableInProgress] = NULL;
104  revertedToPortForCable[cableInProgress] = NULL;
105  }
106 
107  updatedFromPortForCable[cableInProgress] = cableInProgress->getBase()->getFromPort();
108  updatedToPortForCable[cableInProgress] = targetPort->getBase();
109 
110  // Displacing some other cable
111  if (displacedCable)
112  {
113  revertedFromPortForCable[displacedCable] = displacedCable->getBase()->getFromPort();
114  revertedToPortForCable[displacedCable] = displacedCable->getBase()->getToPort();
115 
116  updatedFromPortForCable[displacedCable] = NULL;
117  updatedToPortForCable[displacedCable] = NULL;
118  }
119  }
120 
121  // Completing a ("backwards") connection to an output port
122  else
123  {
124  // Only possibility: Connecting the cable for the first time
125  revertedFromPortForCable[cableInProgress] = NULL;
126  revertedToPortForCable[cableInProgress] = NULL;
127 
128  updatedFromPortForCable[cableInProgress] = targetPort->getBase();
129  updatedToPortForCable[cableInProgress] = cableInProgress->getBase()->getToPort();
130  }
131 
132  // Reconstruct the state of the composition before the beginning of the cable drag
133  // that concluded with this connection, for the composition's "Before" snapshot.
134  {
135  VuoPort *currentFromPortForCableInProgress = cableInProgress->getBase()->getFromPort();
136  VuoPort *currentToPortForCableInProgress = cableInProgress->getBase()->getToPort();
137  bool currentAlwaysEventOnlyStatusForCableInProgress = cableInProgress->getBase()->getCompiler()->getAlwaysEventOnly();
138 
139  bool mustReconstructRevertedSnapshot = ((currentFromPortForCableInProgress != revertedFromPortForCable[cableInProgress]) ||
140  (currentToPortForCableInProgress != revertedToPortForCable[cableInProgress]));
141 
142  if (mustReconstructRevertedSnapshot)
143  {
144  VuoCommandCommon::updateCable(cableInProgress, revertedFromPortForCable[cableInProgress], revertedToPortForCable[cableInProgress], composition, true);
145  cableInProgress->getBase()->getCompiler()->setAlwaysEventOnly(cableInProgress->getPreviouslyAlwaysEventOnly());
146  }
147 
148  this->revertedSnapshot = window->getComposition()->takeSnapshot();
149 
150  if (mustReconstructRevertedSnapshot)
151  {
152  cableInProgress->getBase()->getCompiler()->setAlwaysEventOnly(currentAlwaysEventOnlyStatusForCableInProgress);
153  VuoCommandCommon::updateCable(cableInProgress, currentFromPortForCableInProgress, currentToPortForCableInProgress, composition, true);
154  }
155  }
156 
157  // Connect a "Make List" node if the connection leaves a list input port without an incoming data cable.
158  if (targetPort->getInput())
159  {
160  VuoPort *previousInputPort = cableInProgress->getFloatingEndpointPreviousToPort();
161  if (previousInputPort && previousInputPort->hasRenderer() && previousInputPort->getRenderer()->scene() &&
162  (previousInputPort != targetPort->getBase()))
163  {
164  VuoCompilerInputEventPort *inputEventPort = dynamic_cast<VuoCompilerInputEventPort *>(previousInputPort->getCompiler());
165  if (inputEventPort && VuoCompilerType::isListType(inputEventPort->getDataType()) && !inputEventPort->hasConnectedDataCable())
166  {
167  VuoNode *toNode = previousInputPort->getRenderer()->getUnderlyingParentNode()->getBase();
168  VuoRendererCable *cable = NULL;
169  VuoRendererNode *makeListNode = composition->createAndConnectMakeListNode(toNode, previousInputPort, cable);
170 
171  addedNode = makeListNode;
172  addedCable = cable;
173  fromPortForAddedCable = addedCable->getBase()->getFromPort();
174  toPortForAddedCable = previousInputPort;
175  }
176  }
177  }
178 
179  // If connecting the first data+event cable from the output port of a "Share Value" node to an input port
180  // with a constant value, propagate the constant value back to the "Share Value" node's input port.
181  inventorySharedValueToBackpropagate();
182 
183  this->operationInvolvesGenericPort = modifiedComponentsIncludeGenericPorts();
184 
185  // Disconnect the displaced cable, if applicable.
186  if (displacedCable)
187  VuoCommandCommon::updateCable(displacedCable, updatedFromPortForCable[displacedCable], updatedToPortForCable[displacedCable], composition);
188 
189  // Unpublish the published port, if applicable.
190  if (internalPortToUnpublish)
191  {
192  bool unpublishIsolatedExternalPorts = false;
193  foreach (VuoRendererPublishedPort *externalPortToUnpublish, externalPortsToUnpublish)
194  VuoCommandCommon::unpublishInternalExternalPortCombination(internalPortToUnpublish, dynamic_cast<VuoPublishedPort *>(externalPortToUnpublish->getBase()), composition, unpublishIsolatedExternalPorts);
195  }
196 
197  // Connect the new cable
198  VuoCommandCommon::updateCable(cableInProgress, updatedFromPortForCable[cableInProgress], updatedToPortForCable[cableInProgress], composition);
199 
200  // Add the "Make List" node and cable, if applicable.
201  if (addedNode)
202  {
203  VuoCommandCommon::addCable(addedCable, fromPortForAddedCable, toPortForAddedCable, composition);
204  composition->addNode(addedNode->getBase());
205  }
206 
207  // Collapse any typecasts possible.
208  composition->collapseTypecastNodes();
209 
210  // For each deleted data+event cable, set the 'To' port's constant value to the last value
211  // that flowed through the cable before its disconnection, if applicable.
212  for (map<VuoPort *, string>::iterator i = updatedConstantForPort.begin(); i != updatedConstantForPort.end(); ++i)
213  {
214  VuoPort *toPort = i->first;
215 
216  if (revertedConstantForPort[toPort] != updatedConstantForPort[toPort])
217  {
218  composition->updatePortConstant(static_cast<VuoCompilerInputEventPort *>(toPort->getCompiler()), updatedConstantForPort[toPort], false);
219  updatedPortIDs.insert(window->getComposition()->getIdentifierForStaticPort(toPort));
220  }
221  }
222  }
223  // End of command content.
224 
225  this->updatedSnapshot = window->getComposition()->takeSnapshot();
226 
227  setDescription("Connect %s:%s -> %s:%s",
228  cableInProgress->getBase()->getFromNode() && cableInProgress->getBase()->getFromNode()->hasCompiler()?
229  cableInProgress->getBase()->getFromNode()->getCompiler()->getIdentifier().c_str() : "?",
230  cableInProgress->getBase()->getFromPort()?
231  cableInProgress->getBase()->getFromPort()->getClass()->getName().c_str() : "?",
232  cableInProgress->getBase()->getToNode() && cableInProgress->getBase()->getToNode()->hasCompiler()?
233  cableInProgress->getBase()->getToNode()->getCompiler()->getIdentifier().c_str() : "?",
234  cableInProgress->getBase()->getToPort()?
235  cableInProgress->getBase()->getToPort()->getClass()->getName().c_str() : "?");
236 }
237 
242 {
244 }
245 
250 {
252 
253  window->resetCompositionWithSnapshot(revertedSnapshot);
254 
255  if (operationInvolvesGenericPort)
257 
258  window->coalesceSnapshots(updatedSnapshot, revertedSnapshot);
259 
260  foreach (string updatedPortID, updatedPortIDs)
261  window->coalesceInternalPortConstantsToSync(updatedPortID);
262 }
263 
268 {
270 
271  window->resetCompositionWithSnapshot(updatedSnapshot);
272 
273  if (operationInvolvesGenericPort)
275 
276  window->coalesceSnapshots(revertedSnapshot, updatedSnapshot);
277 
278  foreach (string updatedPortID, updatedPortIDs)
279  window->coalesceInternalPortConstantsToSync(updatedPortID);
280 }
281 
287 bool VuoCommandConnect::modifiedComponentsIncludeGenericPorts()
288 {
289  bool revertedFromPortIsGeneric = (revertedFromPortForCable[cableInProgress] && dynamic_cast<VuoGenericType *>(revertedFromPortForCable[cableInProgress]->getRenderer()->getDataType()));
290  bool revertedToPortIsGeneric = (revertedToPortForCable[cableInProgress] && dynamic_cast<VuoGenericType *>(revertedToPortForCable[cableInProgress]->getRenderer()->getDataType()));
291  bool updatedFromPortIsGeneric = (updatedFromPortForCable[cableInProgress] && dynamic_cast<VuoGenericType *>(updatedFromPortForCable[cableInProgress]->getRenderer()->getDataType()));
292  bool updatedToPortIsGeneric = (updatedToPortForCable[cableInProgress] && dynamic_cast<VuoGenericType *>(updatedToPortForCable[cableInProgress]->getRenderer()->getDataType()));
293 
294  bool revertedDisplacedFromPortIsGeneric = (revertedFromPortForCable[displacedCable] && dynamic_cast<VuoGenericType *>(revertedFromPortForCable[displacedCable]->getRenderer()->getDataType()));
295  bool revertedDisplacedToPortIsGeneric = (revertedToPortForCable[displacedCable] && dynamic_cast<VuoGenericType *>(revertedToPortForCable[displacedCable]->getRenderer()->getDataType()));
296  bool updatedDisplacedFromPortIsGeneric = (updatedFromPortForCable[displacedCable] && dynamic_cast<VuoGenericType *>(updatedFromPortForCable[displacedCable]->getRenderer()->getDataType()));
297  bool updatedDisplacedToPortIsGeneric = (updatedToPortForCable[displacedCable] && dynamic_cast<VuoGenericType *>(updatedToPortForCable[displacedCable]->getRenderer()->getDataType()));
298 
299  bool addedNodeIsGeneric = (addedNode && addedNode->hasGenericPort());
300  bool fromPortForAddedCableIsGeneric = (fromPortForAddedCable && dynamic_cast<VuoGenericType *>(fromPortForAddedCable->getRenderer()->getDataType()));
301  bool toPortForAddedCableIsGeneric = (toPortForAddedCable && dynamic_cast<VuoGenericType *>(toPortForAddedCable->getRenderer()->getDataType()));
302 
303  return (revertedFromPortIsGeneric || revertedToPortIsGeneric || updatedFromPortIsGeneric || updatedToPortIsGeneric ||
304  revertedDisplacedFromPortIsGeneric || revertedDisplacedToPortIsGeneric || updatedDisplacedFromPortIsGeneric || updatedDisplacedToPortIsGeneric ||
305  addedNodeIsGeneric || fromPortForAddedCableIsGeneric || toPortForAddedCableIsGeneric);
306 }
307 
318 void VuoCommandConnect::inventorySharedValueToBackpropagate()
319 {
320 
321  VuoPort *fromPort = updatedFromPortForCable[cableInProgress];
322  VuoPort *toPort = updatedToPortForCable[cableInProgress];
323  bool fromPortIsSharedValue = (fromPort &&
325  "vuo.data.share."));
326  bool toPortHasCopyableConstant = (toPort &&
327  toPort->getRenderer()->getDataType() &&
328  toPort->hasCompiler() &&
329  inputEditorManager->doesTypeAllowOfflineSerialization(static_cast<VuoCompilerInputEventPort *>(toPort->getCompiler())->getDataVuoType()));
330  bool isReconnection = cableInProgress->getFloatingEndpointPreviousToPort();
331 
332  bool fromPortHasOtherDataConnections = false;
333  if (fromPort)
334  {
335  vector<VuoCable *> connectedCables = fromPort->getConnectedCables(true);
336  foreach (VuoCable *cable, connectedCables)
337  {
338  if (cable->hasRenderer() &&
339  (cable->getRenderer() != cableInProgress) &&
340  (cable->getRenderer()->effectivelyCarriesData()))
341  {
342  fromPortHasOtherDataConnections = true;
343  break;
344  }
345  }
346  }
347 
348  VuoPort *shareValueInputPort = (fromPort? fromPort->getRenderer()->getUnderlyingParentNode()->getBase()->getInputPortWithName("value") : NULL);
349  bool shareValueInputPortConstantNotYetSet = shareValueInputPort &&
350  shareValueInputPort->getRenderer()->isConstant() &&
351  shareValueInputPort->getRenderer()->getConstantAsString().empty();
352 
353  if (fromPortIsSharedValue && toPortHasCopyableConstant && !isReconnection && !fromPortHasOtherDataConnections && cableInProgress->effectivelyCarriesData() && shareValueInputPortConstantNotYetSet)
354  {
355  revertedConstantForPort[shareValueInputPort] = shareValueInputPort->getRenderer()->getConstantAsString();
356  updatedConstantForPort[shareValueInputPort] = toPort->getRenderer()->getConstantAsString();
357  }
358 }