60#include <ApplicationServices/ApplicationServices.h>
61#include <objc/objc-runtime.h>
64const qreal VuoEditorComposition::nodeMoveRate = 15;
65const qreal VuoEditorComposition::nodeMoveRateMultiplier = 4;
67const qreal VuoEditorComposition::showEventsModeUpdateInterval = 1000/20.;
68const int VuoEditorComposition::initialChangeNodeSuggestionCount = 10;
77 VuoEditorComposition_Pro();
80 this->window = window;
82 inputEditorManager = NULL;
83 activeProtocol = NULL;
85 runningComposition = NULL;
86 runningCompositionActiveDriver = NULL;
87 runningCompositionLibraries = NULL;
88 stopRequested =
false;
89 duplicateOnNextMouseMove =
false;
90 duplicationDragInProgress =
false;
91 duplicationCancelled =
false;
92 cursorPosBeforeDuplicationDragMove = QPointF(0,0);
93 cableInProgress = NULL;
94 cableInProgressWasNew =
false;
95 cableInProgressShouldBeWireless =
false;
96 portWithDragInitiated = NULL;
97 cableWithYankInitiated = NULL;
98 menuSelectionInProgress =
false;
99 previousNearbyItem = NULL;
100 dragStickinessDisabled =
false;
101 ignoreApplicationStateChangeEvents =
false;
102 popoverEventsEnabled =
true;
103 runCompositionQueue = dispatch_queue_create(
"org.vuo.editor.run", NULL);
104 activePortPopoversQueue = dispatch_queue_create(
"org.vuo.editor.popovers", NULL);
106 errorMarkingUpdatesEnabled =
true;
107 triggerPortToRefire =
"";
109 contextMenuDeleteSelected =
new QAction(NULL);
110 contextMenuHideSelectedCables =
new QAction(NULL);
111 contextMenuRenameSelected =
new QAction(NULL);
112 contextMenuRefactorSelected =
new QAction(NULL);
113 contextMenuPublishPort =
new QAction(NULL);
114 contextMenuDeleteCables =
new QAction(NULL);
115 contextMenuHideCables =
new QAction(NULL);
116 contextMenuUnhideCables =
new QAction(NULL);
117 contextMenuFireEvent =
new QAction(NULL);
118 contextMenuAddInputPort =
new QAction(NULL);
119 contextMenuRemoveInputPort =
new QAction(NULL);
120 contextMenuSetPortConstant =
new QAction(NULL);
121 contextMenuEditSelectedComments =
new QAction(NULL);
123 contextMenuChangeNode = NULL;
124 contextMenuSpecializeGenericType = NULL;
126 contextMenuFireEvent->setText(tr(
"Fire Event"));
127 contextMenuHideSelectedCables->setText(tr(
"Hide"));
128 contextMenuRenameSelected->setText(tr(
"Rename…"));
129 contextMenuRefactorSelected->setText(tr(
"Package as Subcomposition"));
130 contextMenuAddInputPort->setText(tr(
"Add Input Port"));
131 contextMenuRemoveInputPort->setText(tr(
"Remove Input Port"));
132 contextMenuSetPortConstant->setText(tr(
"Edit Value…"));
133 contextMenuEditSelectedComments->setText(tr(
"Edit…"));
140 connect(contextMenuDeleteCables, &QAction::triggered,
this, &VuoEditorComposition::deleteConnectedCables);
141 connect(contextMenuHideCables, &QAction::triggered,
this, &VuoEditorComposition::hideConnectedCables);
142 connect(contextMenuUnhideCables, &QAction::triggered,
this, &VuoEditorComposition::unhideConnectedCables);
143 connect(contextMenuFireEvent, &QAction::triggered,
this,
static_cast<void (
VuoEditorComposition::*)()
>(&VuoEditorComposition::fireTriggerPortEvent));
144 connect(contextMenuAddInputPort, &QAction::triggered,
this, &VuoEditorComposition::addInputPort);
145 connect(contextMenuRemoveInputPort, &QAction::triggered,
this, &VuoEditorComposition::removeInputPort);
146 connect(contextMenuEditSelectedComments, &QAction::triggered,
this, &VuoEditorComposition::editSelectedComments);
151 connect(contextMenuSetPortConstant, &QAction::triggered,
this, &VuoEditorComposition::setPortConstant, Qt::QueuedConnection);
158 QAction *action =
new QAction(label,
this);
159 connect(action, &QAction::triggered, [=](){
162 contextMenuThrottlingActions.append(action);
173 QAction *action =
new QAction(label,
this);
174 connect(action, &QAction::triggered, [=](){
180 QColor fill(0,0,0,0);
182 if (tint != VuoNode::TintNone)
192 p.drawEllipse(3, 3, 10, 10);
194 action->setIcon(*icon);
198 contextMenuTintActions.append(action);
200 addTintAction(tr(
"Yellow"), VuoNode::TintYellow);
201 addTintAction(tr(
"Tangerine"), VuoNode::TintTangerine);
202 addTintAction(tr(
"Orange"), VuoNode::TintOrange);
203 addTintAction(tr(
"Magenta"), VuoNode::TintMagenta);
204 addTintAction(tr(
"Violet"), VuoNode::TintViolet);
205 addTintAction(tr(
"Blue"), VuoNode::TintBlue);
206 addTintAction(tr(
"Cyan"), VuoNode::TintCyan);
207 addTintAction(tr(
"Green"), VuoNode::TintGreen);
208 addTintAction(tr(
"Lime"), VuoNode::TintLime);
209 addTintAction(tr(
"None"), VuoNode::TintNone);
213 this->refreshComponentAlphaLevelTimer =
new QTimer(
this);
214 this->refreshComponentAlphaLevelTimer->setObjectName(
"VuoEditorComposition::refreshComponentAlphaLevelTimer");
215 refreshComponentAlphaLevelTimer->setInterval(showEventsModeUpdateInterval);
233 connect(
static_cast<VuoEditor *
>(qApp), &VuoEditor::focusChanged,
this, &VuoEditorComposition::updatePopoversForActiveWindowChange, Qt::QueuedConnection);
235 setPopoversHideOnDeactivate(
true);
238 setPopoversHideOnDeactivate(
false);
250 this->compiler = compiler;
266 this->moduleManager = moduleManager;
275 return moduleManager;
285 this->inputEditorManager = inputEditorManager;
295 return this->inputEditorManager;
312 setCustomConstantsForNewNode(rn);
329 __block
bool isAllowed =
true;
332 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
334 bool nodeIsThisComposition = (compositionModuleKey == nodeClass->
getBase()->
getClassName());
337 auto iter = std::find_if(dependencies.begin(), dependencies.end(), [=](
const string &d){ return d == compositionModuleKey; });
338 bool nodeContainsThisComposition = (iter != dependencies.end());
340 isAllowed = ! (nodeIsThisComposition || nodeContainsThisComposition);
349 compiler->
createNode(nodeClass, title, x, y));
367 vector<string> inputPortClassNames;
368 vector<string> outputPortClassNames;
373 inputPortClassNames.push_back(portClass->
getName());
376 outputPortClassNames.push_back(portClass->
getName());
380 dummyNodeClass->
newNode(modelNode) :
389void VuoEditorComposition::setCustomConstantsForNewNode(
VuoRendererNode *newNode)
397 QString currentYear = QString::number(QDateTime::currentDateTime().date().year());
421 disablePortPopovers(rn);
446 map<VuoCable *, VuoPort *> cablesToTransferFromPort;
447 map<VuoCable *, VuoPort *> cablesToTransferToPort;
448 set<VuoCable *> cablesToRemove;
452 vector<VuoRendererInputDrawer *> attachedDrawers;
453 vector<VuoRendererNode *> collapsedTypecasts;
455 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
461 collapsedTypecasts.push_back(typecastNode);
469 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
474 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
479 attachedDrawers.push_back(attachedDrawer);
486 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferFromPort.begin(); i != cablesToTransferFromPort.end(); ++i)
487 i->first->getRenderer()->setFrom(newNode, i->second);
488 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferToPort.begin(); i != cablesToTransferToPort.end(); ++i)
489 i->first->getRenderer()->setTo(newNode, i->second);
490 foreach (
VuoCable *cable, cablesToRemove)
507 if (! (oldDataType == newDataType && oldDataType && !
dynamic_cast<VuoGenericType *
>(oldDataType)) )
511 string oldConstantValue;
513 oldConstantValue = oldInputPort->getRenderer()->getConstantAsString();
515 oldConstantValue = oldInputPort->getRawInitialValue();
547 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
560 disablePortPopovers(oldNode);
588 if (cableHidden && emitHiddenCableNotification)
600 if (cableHidden && emitHiddenCableNotification)
626 set<VuoRendererNode *> &createdNodes,
627 set<VuoRendererCable *> &createdCables)
641 foreach (QGraphicsItem *component, addedComponents)
644 if (rn && !createButDoNotAdd)
648 return addedComponents;
660 set<QGraphicsItem *> dependentAttachments;
664 for(
unsigned int i = 0; i < inputPorts.size(); ++i)
666 set<VuoRendererInputAttachment *> portUpstreamAttachments = inputPorts[i]->getRenderer()->getAllUnderlyingUpstreamInputAttachments();
667 dependentAttachments.insert(portUpstreamAttachments.begin(), portUpstreamAttachments.end());
672 if (nodeAsAttachment && includeCoattachments)
675 dependentAttachments.insert(coattachment->
getRenderer());
678 return dependentAttachments;
692 set<string> selectedNodeIDs;
699 set<string> selectedCommentIDs;
706 set<string> selectedCableIDs;
716 foreach (QGraphicsItem *item, items())
723 if (!currentNodeID.empty() && (selectedNodeIDs.find(currentNodeID) != selectedNodeIDs.end()))
724 item->setSelected(
true);
732 if (!currentCommentID.empty() && (selectedCommentIDs.find(currentCommentID) != selectedCommentIDs.end()))
733 item->setSelected(
true);
741 if (!currentCableID.empty() && (selectedCableIDs.find(currentCableID) != selectedCableIDs.end()))
742 item->setSelected(
true);
765 if ((portName ==
"expression") &&
789 if (inputVariablesBeforeEditing != inputVariablesAfterEditing)
795 QList<QGraphicsItem *> attachmentsToRemove;
802 attachmentsToRemove.append(attachment);
814 if (oldValueList && oldDictionary && oldKeyList)
816 set<VuoRendererNode *> nodesToAdd;
817 set<VuoRendererCable *> cablesToAdd;
835 undoStack->push(
new VuoCommandReplaceNode(oldValueList, newValueList, editorWindow,
"Set Port Constant",
false,
false));
836 undoStack->push(
new VuoCommandReplaceNode(oldKeyList, newKeyList, editorWindow,
"Set Port Constant",
false,
true));
837 undoStack->push(
new VuoCommandReplaceNode(oldDictionary, newDictionary, editorWindow,
"Set Port Constant",
false,
true));
841 cable->
setFrom(
nullptr,
nullptr);
842 cable->
setTo(
nullptr,
nullptr);
861 if (commandDescription.empty())
864 commandDescription =
"Reset";
866 commandDescription =
"Delete";
869 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
878 if (commandDescription.empty())
879 commandDescription =
"Delete";
881 QList<QGraphicsItem *> selectedNodes;
883 selectedNodes.append(node);
923void VuoEditorComposition::insertNode()
925 QAction *sender = (QAction *)QObject::sender();
926 QPair<QPointF, QString> pair = sender->data().value<QPair<QPointF, QString> >();
928 QList<QGraphicsItem *> newNodes;
935 newNodes.append(newNode);
943void VuoEditorComposition::insertComment()
945 QAction *sender = (QAction *)QObject::sender();
946 QPointF scenePos = sender->data().value<QPointF>();
954void VuoEditorComposition::insertSubcomposition()
956 QAction *sender = (QAction *)QObject::sender();
957 QPointF scenePos = sender->data().value<QPointF>();
968 QAction *sender = (QAction *)QObject::sender();
983void VuoEditorComposition::deleteConnectedCables()
985 QAction *sender = (QAction *)QObject::sender();
988 QList<QGraphicsItem *> cablesToRemove;
1019void VuoEditorComposition::hideConnectedCables()
1021 QAction *sender = (QAction *)QObject::sender();
1023 set<VuoRendererCable *> cablesToHide;
1054void VuoEditorComposition::unhideConnectedCables()
1056 QAction *sender = (QAction *)QObject::sender();
1058 set<VuoRendererCable *> cablesToUnhide;
1087void VuoEditorComposition::fireTriggerPortEvent()
1089 QAction *sender = (QAction *)QObject::sender();
1091 fireTriggerPortEvent(port->
getBase());
1107 if (triggerPortToRefire.empty())
1110 VuoPort *triggerPort =
nullptr;
1123 if (portID != this->triggerPortToRefire)
1125 this->triggerPortToRefire = portID;
1134void VuoEditorComposition::setPortConstant()
1136 QAction *sender = (QAction *)QObject::sender();
1147void VuoEditorComposition::setPortConstantToValue(
VuoRendererPort *port,
string value)
1157void VuoEditorComposition::specializeGenericPortType()
1159 QAction *sender = (QAction *)QObject::sender();
1160 QList<QVariant> portAndSpecializedType= sender->data().toList();
1162 QString specializedTypeName = portAndSpecializedType[1].toString();
1174 emit
specializePort(port, specializedTypeName.toUtf8().constData());
1181void VuoEditorComposition::unspecializePortType()
1183 QAction *sender = (QAction *)QObject::sender();
1207 portToUnspecialize,
true);
1208 map<VuoNode *, set<VuoPort *> > portsToUnspecializeForNode;
1209 for (
VuoPort *connectedPort : connectedPotentiallyGenericPorts)
1215 if (isPortCurrentlyRevertible(connectedPort->getRenderer()))
1216 portsToUnspecializeForNode[node].insert(connectedPort);
1219 for (map<
VuoNode *, set<VuoPort *> >::iterator i = portsToUnspecializeForNode.begin(); i != portsToUnspecializeForNode.end(); ++i)
1222 set<VuoPort *> ports = i->second;
1224 if (shouldOutputNodesToReplace)
1227 set<VuoPortClass *> portClasses;
1228 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1229 portClasses.insert((*j)->getClass());
1232 nodesToReplace[node] = unspecializedNodeClassName;
1237 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1242 bool areEndsCompatible =
false;
1245 areEndsCompatible =
true;
1250 if (portOnOtherEnd && isPortCurrentlyRevertible(portOnOtherEnd->
getRenderer()))
1255 if (specializedNodeClassOnOtherEnd)
1258 if (! typeOnOtherEnd ||
dynamic_cast<VuoGenericType *
>(typeOnOtherEnd))
1259 areEndsCompatible =
true;
1264 if (! areEndsCompatible && (cable != cableInProgress))
1265 cablesToDelete.insert(cable);
1274void VuoEditorComposition::fireTriggerPortEvent(
VuoPort *port)
1282 string runningTriggerPortIdentifier =
"";
1283 bool isTriggerPort =
false;
1291 isTriggerPort =
true;
1309 runningTriggerPortIdentifier.c_str());
1312 bool manuallyFirableInputPortChanged = (oldManuallyFirableInputPort != newManuallyFirableInputPort);
1315 auto fireIfRunning = ^void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
1317 dispatch_async(topLevelComposition->runCompositionQueue, ^{
1318 if (topLevelComposition->isRunningThreadUnsafe())
1320 topLevelComposition->runner->fireTriggerPortEvent(thisCompositionIdentifier, runningTriggerPortIdentifier);
1325 if (! (this->showEventsMode && isTriggerPort) )
1326 this->animatePort(port->getRenderer());
1331 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1332 if (
this == topLevelComposition || ! manuallyFirableInputPortChanged)
1336 if (! newSnapshot.empty() && manuallyFirableInputPortChanged)
1337 updateRunningComposition(oldSnapshot, newSnapshot);
1339 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1345 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
1347 moduleManager->doNextTimeNodeClassIsLoaded(nodeClassName, ^{
1348 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1349 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1353 if (! newSnapshot.empty())
1354 updateRunningComposition(oldSnapshot, newSnapshot);
1359 setTriggerPortToRefire(port);
1366void VuoEditorComposition::addInputPort()
1368 QAction *sender = (QAction *)QObject::sender();
1377void VuoEditorComposition::removeInputPort()
1379 QAction *sender = (QAction *)QObject::sender();
1388void VuoEditorComposition::swapNode()
1390 QAction *sender = (QAction *)QObject::sender();
1391 QList<QVariant> nodeAndReplacementType= sender->data().toList();
1393 QString newNodeClassName = nodeAndReplacementType[1].toString();
1404 for (set<VuoRendererNode *>::iterator i = selectedNodes.begin(); i != selectedNodes.end(); ++i)
1414void VuoEditorComposition::editSelectedComments()
1418 for (set<VuoRendererComment *>::iterator i = selectedComments.begin(); i != selectedComments.end(); ++i)
1427 set<VuoRendererCable *> internalCables;
1429 for (QList<QGraphicsItem *>::iterator i = subcompositionComponents.begin(); i != subcompositionComponents.end(); ++i)
1431 QGraphicsItem *compositionComponent = *i;
1436 for (set<VuoCable *>::iterator cable = connectedCables.begin(); cable != connectedCables.end(); ++cable)
1438 VuoNode *fromNode = (*cable)->getFromNode();
1439 VuoNode *toNode = (*cable)->getToNode();
1441 if (fromNode && toNode && subcompositionComponents.contains(fromNode->
getRenderer()) && subcompositionComponents.contains(toNode->
getRenderer()))
1442 internalCables.insert((*cable)->getRenderer());
1447 return internalCables;
1455 return cableInProgress;
1464 return cableInProgressWasNew;
1472 return menuSelectionInProgress;
1480 QList<QGraphicsItem *> compositionComponents = items();
1481 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1483 QGraphicsItem *compositionComponent = *i;
1487 compositionComponent->setSelected(
true);
1496 QList<QGraphicsItem *> compositionComponents = items();
1497 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1499 QGraphicsItem *compositionComponent = *i;
1502 rcomment->setSelected(
true);
1511 QList<QGraphicsItem *> compositionComponents = items();
1512 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1514 QGraphicsItem *compositionComponent = *i;
1515 compositionComponent->setSelected(
false);
1522void VuoEditorComposition::openSelectedEditableNodes()
1527 QString actionText, sourcePath;
1540 moveItemsBy(selectedNodes, selectedComments, dx, dy,
false);
1546void VuoEditorComposition::moveNodesBy(set<VuoRendererNode *> nodes, qreal dx, qreal dy,
bool movedByDragging)
1548 set<VuoRendererComment *> comments;
1549 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1555void VuoEditorComposition::moveCommentsBy(set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1557 set<VuoRendererNode *> nodes;
1558 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1564void VuoEditorComposition::moveItemsBy(set<VuoRendererNode *> nodes, set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1566 emit
itemsMoved(nodes, comments, dx, dy, movedByDragging);
1568 for (set<VuoRendererNode *>::iterator it = nodes.begin(); it != nodes.end(); ++it)
1569 (*it)->updateConnectedCableGeometry();
1575void VuoEditorComposition::resizeCommentBy(
VuoRendererComment *comment, qreal dx, qreal dy)
1585 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1586 set<VuoRendererNode *> selectedNodes;
1587 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1589 QGraphicsItem *compositionComponent = *i;
1592 selectedNodes.insert(rn);
1595 return selectedNodes;
1603 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1604 set<VuoRendererComment *> selectedComments;
1605 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1607 QGraphicsItem *compositionComponent = *i;
1610 selectedComments.insert(rc);
1613 return selectedComments;
1621 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1622 set<VuoRendererCable *> selectedCables;
1623 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1625 QGraphicsItem *compositionComponent = *i;
1628 selectedCables.insert(rc);
1631 return selectedCables;
1639 const QMimeData *mimeData =
event->mimeData();
1640 bool disablePortHoverHighlighting =
true;
1643 if (mimeData->hasFormat(
"text/uri-list"))
1645 QList<QUrl> urls = mimeData->urls();
1649 if (portAtDropLocation)
1651 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1653 disablePortHoverHighlighting =
false;
1655 event->setDropAction(Qt::IgnoreAction);
1659 bool dragIncludesDroppableFile =
false;
1660 foreach (QUrl url, urls)
1662 char *urlZ = strdup(url.path().toUtf8().constData());
1663 bool isSupportedDragNDropFile =
1677 if (isSupportedDragNDropFile)
1679 dragIncludesDroppableFile =
true;
1684 if (!dragIncludesDroppableFile)
1685 event->setDropAction(Qt::IgnoreAction);
1692 else if (mimeData->hasFormat(
"text/plain") || mimeData->hasFormat(
"text/scsv"))
1693 event->acceptProposedAction();
1697 event->setDropAction(Qt::IgnoreAction);
1701 updateHoverHighlighting(event->scenePos(), disablePortHoverHighlighting);
1709 event->acceptProposedAction();
1725 const QMimeData *mimeData =
event->mimeData();
1728 if (mimeData->hasFormat(
"text/uri-list"))
1736 if (topCompositionPath.empty())
1738 QDir compositionDir(QDir(topCompositionPath.c_str()).canonicalPath());
1743 QList<QGraphicsItem *> newNodes;
1744 QList<QUrl> urls = mimeData->urls();
1748 if (portAtDropLocation)
1750 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1753 QString filePath = (useAbsoluteFilePaths? urls[0].path() : compositionDir.relativeFilePath(urls[0].path()));
1754 string constantValue =
"\"" + string(filePath.toUtf8().constData()) +
"\"";
1765 const int ySpacingForNewNodes = 11;
1766 int yOffsetForPreviousNewNode = -1 * ySpacingForNewNodes;
1768 foreach (QUrl url, urls)
1770 QStringList targetNodeClassNames;
1771 char *urlZ = strdup(url.path().toUtf8().constData());
1774 targetNodeClassNames +=
"vuo.image.fetch";
1778 targetNodeClassNames +=
"vuo.video.play";
1780 targetNodeClassNames +=
"vuo.video.decodeImage";
1783 targetNodeClassNames +=
"vuo.scene.fetch";
1785 targetNodeClassNames +=
"vuo.audio.file.play";
1787 targetNodeClassNames +=
"vuo.image.project.dome";
1789 targetNodeClassNames +=
"vuo.rss.fetch";
1791 targetNodeClassNames +=
"vuo.tree.fetch.json";
1793 targetNodeClassNames +=
"vuo.tree.fetch.xml";
1795 targetNodeClassNames +=
"vuo.table.fetch";
1797 targetNodeClassNames +=
"vuo.data.fetch";
1799 targetNodeClassNames +=
"vuo.app.launch";
1801 targetNodeClassNames +=
"vuo.file.list";
1805 QString selectedNodeClassName =
"";
1806 if (targetNodeClassNames.size() == 1)
1807 selectedNodeClassName = targetNodeClassNames[0];
1808 else if (targetNodeClassNames.size() > 1)
1810 QMenu nodeMenu(views()[0]->viewport());
1811 nodeMenu.setSeparatorsCollapsible(
false);
1813 foreach (QString nodeClassName, targetNodeClassNames)
1816 string nodeTitle = (nodeClass? nodeClass->
getBase()->
getDefaultTitle() : nodeClassName.toUtf8().constData());
1818 QAction *nodeAction = nodeMenu.addAction(tr(
"Insert \"%1\" Node").arg(nodeTitle.c_str()));
1819 nodeAction->setData(nodeClassName);
1822 menuSelectionInProgress =
true;
1823 QAction *selectedNode = nodeMenu.exec(QCursor::pos());
1824 menuSelectionInProgress =
false;
1826 selectedNodeClassName = (selectedNode? selectedNode->data().toString().toUtf8().constData() :
"");
1829 if (!selectedNodeClassName.isEmpty())
1832 event->scenePos().x(),
1833 event->scenePos().y() + yOffsetForPreviousNewNode + ySpacingForNewNodes);
1842 QString filePath = (useAbsoluteFilePaths? url.path() : compositionDir.relativeFilePath(url.path()));
1845 newNodes.append(newNode);
1847 yOffsetForPreviousNewNode += newNode->
boundingRect().height();
1848 yOffsetForPreviousNewNode += ySpacingForNewNodes;
1854 if (newNodes.size() > 0)
1866 else if (mimeData->hasFormat(
"text/scsv"))
1868 event->setDropAction(Qt::CopyAction);
1871 QByteArray scsvData =
event->mimeData()->data(
"text/scsv");
1872 QString scsvText = QString::fromUtf8(scsvData);
1873 QStringList nodeClassNames = scsvText.split(
';');
1878 int snapDelta = nextYPos - startPos.y();
1880 QList<QGraphicsItem *> newNodes;
1881 for (QStringList::iterator i = nodeClassNames.begin(); i != nodeClassNames.end(); ++i)
1889 int prevYPos = nextYPos;
1892 if (nextYPos <= prevYPos+newNode->boundingRect().height())
1895 newNodes.append((QGraphicsItem *)newNode);
1903 else if (mimeData->hasFormat(
"text/plain"))
1905 event->setDropAction(Qt::CopyAction);
1908 QList<QGraphicsItem *> newNodes;
1909 QStringList dropItems =
event->mimeData()->text().split(
'\n');
1910 QString nodeClassName = dropItems[0];
1914 QPoint hotSpot = (dropItems.size() >= 3? QPoint(dropItems[1].toInt(), dropItems[2].toInt()) : QPoint(0,0));
1916 event->scenePos().x()-hotSpot.x()+1,
1919 newNodes.append(newNode);
1936 QGraphicsScene::sendEvent(nearbyItem, event);
1941 QGraphicsScene::mouseDoubleClickEvent(event);
1949 QPointF scenePos =
event->scenePos();
1952 if (event->button() == Qt::LeftButton)
1957 if (duplicationCancelled)
1959 QGraphicsItem *itemClickedDirectly = itemAt(scenePos, views()[0]->transform());
1965 duplicationCancelled =
false;
1966 correctForCancelledDuplication(event);
1977 mousePressEventNonLeftButton(event);
1986 bool eventHandled =
false;
2000 cableYankedDirectly = currentCable;
2008 if ((event->modifiers() & Qt::ControlModifier) && (currentPort->
getInput() || isTriggerPort))
2009 fireTriggerPortEvent(currentPort->
getBase());
2013 portWithDragInitiated = currentPort;
2014 cableWithYankInitiated = cableYankedDirectly;
2018 eventHandled =
true;
2025 duplicateOnNextMouseMove =
true;
2026 duplicationDragInProgress =
true;
2032 QGraphicsScene::mousePressEvent(event);
2033 eventHandled =
true;
2039 if (event->modifiers() & Qt::ControlModifier)
2040 nearbyItem->setSelected(! nearbyItem->isSelected());
2045 nearbyItem->setSelected(
true);
2049 eventHandled =
true;
2054 QGraphicsScene::mousePressEvent(event);
2061void VuoEditorComposition::mousePressEventNonLeftButton(QGraphicsSceneMouseEvent *event)
2070 if (event->button() == Qt::RightButton)
2072 QGraphicsSceneContextMenuEvent
contextMenuEvent(QEvent::GraphicsSceneContextMenu);
2081 QGraphicsScene::mousePressEvent(event);
2092void VuoEditorComposition::correctForCancelledDuplication(QGraphicsSceneMouseEvent *event)
2096 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
2097 pressEvent.setScenePos(event->scenePos());
2098 pressEvent.setButton(Qt::LeftButton);
2099 QApplication::sendEvent(
this, &pressEvent);
2101 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
2102 releaseEvent.setScenePos(event->scenePos());
2103 releaseEvent.setButton(Qt::LeftButton);
2104 QApplication::sendEvent(
this, &releaseEvent);
2116 Qt::KeyboardModifiers modifiers =
event->modifiers();
2117 bool optionKeyPressed = (modifiers & Qt::AltModifier);
2118 bool shiftKeyPressed = (modifiers & Qt::ShiftModifier);
2129 bool creatingNewCable =
false;
2130 bool disconnectingExistingCable =
false;
2131 bool duplicatingExistingCable =
false;
2145 fromPort = fixedPort;
2146 fromNode = fixedNode;
2147 creatingNewCable =
true;
2159 creatingNewCable =
true;
2166 if (optionKeyPressed)
2168 duplicatingExistingCable =
true;
2172 disconnectingExistingCable =
true;
2180 if (creatingNewCable)
2193 cableInProgressWasNew =
true;
2194 cableInProgressShouldBeWireless =
false;
2201 highlightEligibleEndpointsForCable(cableInProgress);
2206 else if (disconnectingExistingCable)
2209 if (cableYankedDirectly)
2210 cableInProgress = cableYankedDirectly->
getBase();
2216 cableInProgressWasNew =
false;
2227 highlightEligibleEndpointsForCable(cableInProgress);
2229 cableInProgress->
getRenderer()->setSelected(
true);
2232 else if (duplicatingExistingCable)
2236 if (cableYankedDirectly)
2237 cableToDuplicate = cableYankedDirectly->
getBase();
2254 cableInProgressWasNew =
true;
2255 cableInProgressShouldBeWireless = cableToDuplicate->
hasCompiler() &&
2263 highlightEligibleEndpointsForCable(cableInProgress);
2265 cableInProgress->
getRenderer()->setSelected(
true);
2270 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2283 if (event->button() == Qt::LeftButton)
2285 portWithDragInitiated = NULL;
2286 cableWithYankInitiated = NULL;
2287 duplicateOnNextMouseMove =
false;
2288 duplicationDragInProgress =
false;
2289 dragStickinessDisabled =
false;
2296 bool cableDragEnding = cableInProgress;
2297 if (cableDragEnding)
2298 concludeCableDrag(event);
2310 if ((event->modifiers() == Qt::NoModifier) &&
2311 (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2318 enablePopoverForNode(typecastNode);
2323 enableInactivePopoverForPort(port);
2329 if (!cableDragEnding && !node)
2333 if (!cableDragEnding)
2334 QGraphicsScene::mouseReleaseEvent(event);
2340 QGraphicsScene::mouseReleaseEvent(event);
2350void VuoEditorComposition::concludeCableDrag(QGraphicsSceneMouseEvent *event)
2358 concludePublishedCableDrag(event);
2362 if (hasFeedbackErrors())
2380 bool completedCableConnection =
false;
2383 VuoCable *dataCableToDisplace = NULL;
2387 string typecastToInsert =
"";
2391 string specializedTypeName =
"";
2408 bool draggingPreviouslyPublishedCable = (!cableInProgressWasNew &&
2410 ((cableInProgress->
getToPort() == NULL) &&
2413 if (fixedPort && targetPort)
2421 if (recreatingSameConnection)
2431 bool preexistingCableWithMatchingDataCarryingStatus = (preexistingCable?
2433 cableInProgressExpectedToCarryData) :
2439 if (preexistingCable && !preexistingCableWithMatchingDataCarryingStatus &&
2450 if (!preexistingCableWithMatchingDataCarryingStatus &&
2452 selectBridgingSolution(fixedPort, targetPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert)))
2463 if (portToSpecialize == targetPort)
2464 portToSpecialize = adjustedTargetPort;
2466 targetPort = adjustedTargetPort;
2470 if (cableInProgressExpectedToCarryData &&
2472 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2473 typecastNodeToDelete = uncollapsedTypecast;
2478 if (preexistingCable)
2479 cableToReplace = preexistingCable;
2483 if (cableInProgressExpectedToCarryData)
2487 for (vector<VuoCable *>::iterator cable = previousConnectedCables.begin(); (! dataCableToDisplace) && (cable != previousConnectedCables.end()); ++cable)
2488 if ((((*cable)->getRenderer()->effectivelyCarriesData()) && (*cable) != cableToReplace))
2489 dataCableToDisplace = *cable;
2496 if (publishedDataConnections.size() > 0)
2497 portToUnpublish = targetPort;
2501 completedCableConnection =
true;
2505 else if (!preexistingCableWithMatchingDataCarryingStatus &&
2507 selectBridgingSolution(targetPort, fixedPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert)))
2510 completedCableConnection =
true;
2515 if (completedCableConnection)
2519 if (draggingPreviouslyPublishedCable)
2538 cableInProgressCopy->
setFrom(cableInProgressFromNode, cableInProgressFromPort);
2539 cableInProgressCopy->
setTo(cableInProgressToNode, cableInProgressToPort);
2546 cableInProgress = cableInProgressCopy;
2547 cableInProgressWasNew =
true;
2551 pair<VuoRendererCable *, VuoRendererCable *> cableArgs = std::make_pair((dataCableToDisplace? dataCableToDisplace->
getRenderer() : NULL),
2552 (cableToReplace? cableToReplace->
getRenderer() : NULL));
2553 pair<string, string> typeArgs = std::make_pair(typecastToInsert, specializedTypeName);
2554 pair<VuoRendererPort *, VuoRendererPort *> portArgs = std::make_pair(portToUnpublish, portToSpecialize);
2559 typecastNodeToDelete,
2566 if (cableInProgress)
2570 cableInProgress = NULL;
2584void VuoEditorComposition::concludePublishedCableDrag(QGraphicsSceneMouseEvent *event)
2594 string typecastToInsert =
"";
2598 string specializedTypeName =
"";
2615 if (internalInputPort)
2618 forceEventOnlyPublication =
true;
2624 if (internalInputPort &&
2630 internalInputPort->
getBase()) &&
2633 if (recreatingSameConnection)
2641 ||
selectBridgingSolution(publishedInputPort, internalInputPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert))
2646 bool cableToReplaceHasMatchingDataCarryingStatus = (cableToReplace?
2648 cableInProgressExpectedToCarryData) :
2653 if (cableToReplace && cableToReplaceHasMatchingDataCarryingStatus)
2659 else if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus &&
2675 if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus)
2677 QList<QGraphicsItem *> removedComponents;
2678 removedComponents.append(cableToReplace->
getRenderer());
2687 if (typecastPort && typecastPort->scene())
2695 if (cableInProgressExpectedToCarryData &&
2697 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2699 QList<QGraphicsItem *> removedComponents;
2700 removedComponents.append(uncollapsedTypecast);
2707 forceEventOnlyPublication,
2708 (portToSpecialize? portToSpecialize->
getBase() : NULL),
2709 specializedTypeName,
2735 if (internalOutputPort)
2738 forceEventOnlyPublication =
true;
2744 if (internalOutputPort &&
2745 publishedOutputPort)
2751 ||
selectBridgingSolution(internalOutputPort, publishedOutputPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert))
2755 forceEventOnlyPublication,
2756 portToSpecialize? portToSpecialize->
getBase() : NULL,
2757 specializedTypeName,
2776 if (! cableInProgress)
2779 if (cableInProgressWasNew)
2783 QList<QGraphicsItem *> removedComponents;
2784 removedComponents.append(cableInProgress->
getRenderer());
2788 cableInProgress = NULL;
2801 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2806 cableInProgress = NULL;
2808 else if (cableInProgress)
2823 bool leftMouseButtonPressed = (
event->buttons() & Qt::LeftButton);
2839 if (cableInProgress || (! leftMouseButtonPressed))
2840 updateHoverHighlighting(event->scenePos());
2844 if (leftMouseButtonPressed)
2847 if ((! dragStickinessDisabled) && (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2851 dragStickinessDisabled =
true;
2855 if (portWithDragInitiated || cableWithYankInitiated)
2857 initiateCableDrag(portWithDragInitiated, cableWithYankInitiated, event);
2858 portWithDragInitiated = NULL;
2859 cableWithYankInitiated = NULL;
2864 if (cableInProgress)
2874 else if (duplicationDragInProgress)
2876 if (duplicateOnNextMouseMove)
2879 duplicateOnNextMouseMove =
false;
2886 QPointF delta = newPos - cursorPosBeforeDuplicationDragMove;
2888 cursorPosBeforeDuplicationDragMove = newPos;
2892 QGraphicsScene::mouseMoveEvent(event);
2897 QGraphicsScene::mouseMoveEvent(event);
2905void VuoEditorComposition::updateHoverHighlighting(QPointF scenePos,
bool disablePortHoverHighlighting)
2919 bool hoveringOverNodeHeader =
false;
2920 if (cableInProgress)
2929 hoveringOverNodeHeader =
true;
2935 if (! hoveringOverNodeHeader)
2946 if (item != previousNearbyItem)
2954 if (previousNearbyItem)
2962 if (! cableInProgress)
2968 else if (typecastPort && !disablePortHoverHighlighting)
2970 else if (port && !disablePortHoverHighlighting)
2973 if (!cableInProgress)
2984 if (cableInProgress)
2987 cableInProgress->getFromPort()->getRenderer() :
2988 cableInProgress->getToPort()->getRenderer());
2990 QList< QPair<VuoRendererPort *, bool> > updatedPorts;
2991 if (hoveringOverNodeHeader)
2992 updatedPorts.append( QPair<VuoRendererPort *, bool>(port,
true) );
2993 if (previousNode && previousPort)
2994 updatedPorts.append( QPair<VuoRendererPort *, bool>(previousPort, !cableInProgress->getRenderer()->effectivelyCarriesData()) );
2996 QPair<VuoRendererPort *, bool> p;
2997 foreach (p, updatedPorts)
3002 if (typecastParentPort)
3003 updatedPort = typecastParentPort;
3005 updateEligibilityHighlightingForPort(updatedPort, fixedPort, p.second, types);
3008 updateEligibilityHighlightingForNode(potentialDrawer);
3015 if (targetPort || previousTargetPort)
3017 if (cableInProgress)
3018 cableInProgress->getRenderer()->setFloatingEndpointAboveEventPort(targetPort && (!targetPort->
getDataType() || hoveringOverNodeHeader));
3023 previousNearbyItem = item;
3031 else if (port && !disablePortHoverHighlighting)
3034 if (!cableInProgress)
3040 else if (makeListDrawer)
3050 if (previousNearbyItem)
3068 else if (typecastPort)
3077 previousNearbyItem = NULL;
3086 if ((event->key() != Qt::Key_Alt) && (event->key() != Qt::Key_Shift) && (event->key() != Qt::Key_Escape))
3089 Qt::KeyboardModifiers modifiers =
event->modifiers();
3090 qreal adjustedNodeMoveRate = nodeMoveRate;
3091 if (modifiers & Qt::ShiftModifier)
3093 adjustedNodeMoveRate *= nodeMoveRateMultiplier;
3096 switch (event->key()) {
3097 case Qt::Key_Backspace:
3102 case Qt::Key_Delete:
3114 if (modifiers & Qt::ControlModifier)
3115 openSelectedEditableNodes();
3132 if (cableInProgress)
3134 cableInProgress->getRenderer()->updateGeometry();
3135 cableInProgress->getCompiler()->setAlwaysEventOnly(
true);
3136 highlightEligibleEndpointsForCable(cableInProgress);
3146 case Qt::Key_Return:
3149 QGraphicsScene::keyPressEvent(event);
3151 if (!event->isAccepted())
3158 if (selectedComments.empty() && !selectedNodes.empty())
3165 case Qt::Key_Escape:
3167 if (duplicateOnNextMouseMove || duplicationDragInProgress)
3171 duplicateOnNextMouseMove =
false;
3172 duplicationDragInProgress =
false;
3173 duplicationCancelled =
true;
3175 else if (cableInProgress)
3184 QGraphicsScene::keyPressEvent(event);
3195 switch (event->key()) {
3198 if (cableInProgress)
3200 cableInProgress->getRenderer()->updateGeometry();
3201 cableInProgress->getCompiler()->setAlwaysEventOnly(
false);
3202 highlightEligibleEndpointsForCable(cableInProgress);
3214 QGraphicsScene::keyReleaseEvent(event);
3229 contextMenu.setSeparatorsCollapsible(
false);
3234 QAction *insertNodeSection =
new QAction(tr(
"Insert Node"), NULL);
3235 insertNodeSection->setEnabled(
false);
3236 contextMenu.addAction(insertNodeSection);
3238 QString spacer(
" ");
3242 QMenu *shareMenu =
new QMenu(&contextMenu);
3243 shareMenu->setSeparatorsCollapsible(
false);
3244 shareMenu->setTitle(spacer + tr(
"Share"));
3245 contextMenu.addMenu(shareMenu);
3248 QAction *action =
new QAction(tr(
"Share Value"), NULL);
3249 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share"))));
3250 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3251 shareMenu->addAction(action);
3255 QAction *action =
new QAction(tr(
"Share List"), NULL);
3256 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share.list"))));
3257 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3258 shareMenu->addAction(action);
3262 QAction *action =
new QAction(tr(
"Share Event"), NULL);
3263 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.share"))));
3264 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3265 shareMenu->addAction(action);
3271 QMenu *holdMenu =
new QMenu(&contextMenu);
3272 holdMenu->setSeparatorsCollapsible(
false);
3273 holdMenu->setTitle(spacer + tr(
"Hold"));
3274 contextMenu.addMenu(holdMenu);
3277 QAction *action =
new QAction(tr(
"Hold Value"), NULL);
3278 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold2"))));
3279 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3280 holdMenu->addAction(action);
3284 QAction *action =
new QAction(tr(
"Hold List"), NULL);
3285 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold.list2"))));
3286 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3287 holdMenu->addAction(action);
3293 QMenu *allowMenu =
new QMenu(&contextMenu);
3294 allowMenu->setSeparatorsCollapsible(
false);
3295 allowMenu->setTitle(spacer + tr(
"Allow"));
3296 contextMenu.addMenu(allowMenu);
3299 QAction *action =
new QAction(tr(
"Allow First Event"), NULL);
3300 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirst"))));
3301 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3302 allowMenu->addAction(action);
3306 QAction *action =
new QAction(tr(
"Allow First Value"), NULL);
3307 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirstValue"))));
3308 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3309 allowMenu->addAction(action);
3313 QAction *action =
new QAction(tr(
"Allow Periodic Events"), NULL);
3314 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.time.allowPeriodic"))));
3315 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3316 allowMenu->addAction(action);
3320 QAction *action =
new QAction(tr(
"Allow Changes"), NULL);
3321 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowChanges2"))));
3322 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3323 allowMenu->addAction(action);
3327 contextMenu.addSeparator();
3329 QAction *contextMenuInsertComment =
new QAction(NULL);
3330 contextMenuInsertComment->setText(tr(
"Insert Comment"));
3331 contextMenuInsertComment->setData(QVariant::fromValue(event->scenePos()));
3332 connect(contextMenuInsertComment, &QAction::triggered,
this, &VuoEditorComposition::insertComment);
3333 contextMenu.addAction(contextMenuInsertComment);
3335 QAction *contextMenuInsertSubcomposition =
new QAction(NULL);
3336 contextMenuInsertSubcomposition->setText(tr(
"Insert Subcomposition"));
3337 contextMenuInsertSubcomposition->setData(QVariant::fromValue(event->scenePos()));
3338 connect(contextMenuInsertSubcomposition, &QAction::triggered,
this, &VuoEditorComposition::insertSubcomposition);
3339 contextMenu.addAction(contextMenuInsertSubcomposition);
3344 QAction *contextMenuDeleteSelectedSnapshot =
new QAction(NULL);
3351 if (port->
isConstant() && inputEditorManager)
3355 if (inputEditorLoadedForPortDataType)
3357 contextMenuSetPortConstant->setData(QVariant::fromValue(port));
3358 contextMenu.addAction(contextMenuSetPortConstant);
3360 inputEditorLoadedForPortDataType->deleteLater();
3366 contextMenuPublishPort->setText(tr(
"Publish Port"));
3367 contextMenuPublishPort->setData(QVariant::fromValue(port));
3368 contextMenu.addAction(contextMenuPublishPort);
3373 vector<VuoRendererPublishedPort *> externalPublishedPorts = port->
getPublishedPorts();
3374 bool hasExternalPublishedPortWithMultipleInternalPorts =
false;
3375 bool hasExternalPublishedPortBelongingToActiveProtocol =
false;
3379 hasExternalPublishedPortWithMultipleInternalPorts =
true;
3382 hasExternalPublishedPortBelongingToActiveProtocol =
true;
3389 if (!hasExternalPublishedPortWithMultipleInternalPorts &&!hasExternalPublishedPortBelongingToActiveProtocol)
3391 contextMenuPublishPort->setText(tr(
"Delete Published Port"));
3392 contextMenuPublishPort->setData(QVariant::fromValue(port));
3393 contextMenu.addAction(contextMenuPublishPort);
3398 if (isTriggerPort || port->
getInput())
3400 __block
bool isTopLevelCompositionRunning =
false;
3401 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
3403 isTopLevelCompositionRunning = topLevelComposition->
isRunning();
3406 if (isTopLevelCompositionRunning)
3408 contextMenuFireEvent->setData(QVariant::fromValue(port));
3409 contextMenu.addAction(contextMenuFireEvent);
3415 QMenu *contextMenuThrottling =
new QMenu(&contextMenu);
3416 contextMenuThrottling->setSeparatorsCollapsible(
false);
3417 contextMenuThrottling->setTitle(tr(
"Set Event Throttling"));
3419 foreach (QAction *action, contextMenuThrottlingActions)
3421 contextMenuThrottling->addAction(action);
3422 action->setData(QVariant::fromValue(port));
3423 action->setCheckable(
true);
3426 contextMenu.addMenu(contextMenuThrottling);
3432 if (contextMenuSpecializeGenericType)
3433 contextMenuSpecializeGenericType->deleteLater();
3436 contextMenuSpecializeGenericType->setSeparatorsCollapsible(
false);
3437 contextMenuSpecializeGenericType->setTitle(tr(
"Set Data Type"));
3438 contextMenuSpecializeGenericType->setToolTipsVisible(
true);
3440 populateSpecializePortMenu(contextMenuSpecializeGenericType, port,
true);
3441 contextMenu.addMenu(contextMenuSpecializeGenericType);
3446 int numVisibleDirectlyConnectedCables = 0;
3447 int numHidableDirectlyConnectedCables = 0;
3448 int numUnhidableDirectlyConnectedCables = 0;
3454 numVisibleDirectlyConnectedCables++;
3456 numHidableDirectlyConnectedCables++;
3460 numUnhidableDirectlyConnectedCables++;
3463 int numVisibleChildPortConnectedCables = 0;
3464 int numHidableChildPortConnectedCables = 0;
3465 int numUnhidableChildPortConnectedCables = 0;
3475 numVisibleChildPortConnectedCables++;
3478 numHidableChildPortConnectedCables++;
3481 numUnhidableChildPortConnectedCables++;
3486 int numVisibleConnectedCables = numVisibleDirectlyConnectedCables + numVisibleChildPortConnectedCables;
3489 int numHidableConnectedCables = numHidableDirectlyConnectedCables + numHidableChildPortConnectedCables;
3492 int numUnhidableConnectedCables = numUnhidableDirectlyConnectedCables + numUnhidableChildPortConnectedCables;
3494 if ((!
renderHiddenCables && ((numHidableConnectedCables >= 1) || (numUnhidableConnectedCables >= 1)))
3495 || (numVisibleConnectedCables >= 1))
3497 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3498 contextMenu.addSeparator();
3503 if (numHidableConnectedCables >= 1)
3508 int numApparentlyHidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3509 contextMenuHideCables->setText(numApparentlyHidableConnectedCables > 1?
"Hide Cables" :
"Hide Cable");
3510 contextMenuHideCables->setData(QVariant::fromValue(port));
3511 contextMenu.addAction(contextMenuHideCables);
3514 if (numUnhidableConnectedCables >= 1)
3516 int numApparentlyUnhidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3517 contextMenuUnhideCables->setText(numApparentlyUnhidableConnectedCables > 1?
"Unhide Cables" :
"Unhide Cable");
3518 contextMenuUnhideCables->setData(QVariant::fromValue(port));
3519 contextMenu.addAction(contextMenuUnhideCables);
3523 if (numVisibleConnectedCables >= 1)
3525 contextMenuDeleteCables->setText(numVisibleConnectedCables > 1?
"Delete Cables" :
"Delete Cable");
3526 contextMenuDeleteCables->setData(QVariant::fromValue(port));
3527 contextMenu.addAction(contextMenuDeleteCables);
3534 if (! item->isSelected())
3537 item->setSelected(
true);
3540 QList<QGraphicsItem *> selectedComponents = selectedItems();
3541 bool onlyCommentsSelected =
true;
3542 bool onlyCablesSelected =
true;
3543 bool selectionContainsMissingNode =
false;
3544 foreach (QGraphicsItem *item, selectedComponents)
3547 onlyCommentsSelected =
false;
3550 onlyCablesSelected =
false;
3553 selectionContainsMissingNode =
true;
3556 contextMenuDeleteSelectedSnapshot->setText(contextMenuDeleteSelected->text());
3564 if (onlyCommentsSelected)
3565 contextMenu.addAction(contextMenuEditSelectedComments);
3570 contextMenu.addSeparator();
3573 contextMenu.addAction(contextMenuRefactorSelected);
3575 contextMenu.addSeparator();
3578 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3588 contextMenu.addAction(contextMenuHideSelectedCables);
3591 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3606 contextMenuAddInputPort->setData(QVariant::fromValue(node));
3607 contextMenuRemoveInputPort->setData(QVariant::fromValue(node));
3610 contextMenuRemoveInputPort->setEnabled(listItemCount >= 1);
3612 contextMenu.addAction(contextMenuAddInputPort);
3613 contextMenu.addAction(contextMenuRemoveInputPort);
3615 contextMenu.addSeparator();
3619 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3630 if (originalGenericNodeClass)
3631 nodeClass = originalGenericNodeClass->
getBase();
3634 QString actionText, sourcePath;
3638 int numSelectedNonAttachmentNodes = 0;
3639 QList<QGraphicsItem *> selectedComponents = selectedItems();
3640 foreach (QGraphicsItem *item, selectedComponents)
3643 numSelectedNonAttachmentNodes++;
3646 if ((numSelectedNonAttachmentNodes == 1) && nodeClassIsEditable)
3649 QAction *editAction =
new QAction(NULL);
3650 editAction->setText(actionText);
3651 editAction->setData(QVariant::fromValue(node));
3654 contextMenu.addAction(editAction);
3655 contextMenu.addSeparator();
3660 contextMenu.addAction(contextMenuRenameSelected);
3665 contextMenu.addSeparator();
3668 if (numSelectedNonAttachmentNodes == 1)
3670 if (contextMenuChangeNode)
3671 contextMenuChangeNode->deleteLater();
3674 contextMenuChangeNode->setSeparatorsCollapsible(
false);
3675 contextMenuChangeNode->setTitle(tr(
"Change To"));
3678 if (!contextMenuChangeNode->actions().isEmpty())
3679 contextMenu.addMenu(contextMenuChangeNode);
3683 if (!selectionContainsMissingNode)
3684 contextMenu.addAction(contextMenuRefactorSelected);
3686 if ((numSelectedNonAttachmentNodes == 1) && (nodeClassIsEditable || nodeClassIs3rdParty))
3690 if (!modulePath.isEmpty())
3692 QString enclosingDirUrl =
"file://" + QFileInfo(modulePath).dir().absolutePath();
3693 QAction *openEnclosingFolderAction =
new QAction(NULL);
3694 openEnclosingFolderAction->setText(
"Show in Finder");
3695 openEnclosingFolderAction->setData(QVariant::fromValue(enclosingDirUrl));
3697 contextMenu.addAction(openEnclosingFolderAction);
3701 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3702 contextMenu.addSeparator();
3705 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3711 if (!contextMenu.actions().isEmpty())
3716 menuSelectionInProgress =
true;
3717 contextMenu.exec(event->screenPos());
3718 menuSelectionInProgress =
false;
3721 delete contextMenuDeleteSelectedSnapshot;
3729 QAction *sender = (QAction *)QObject::sender();
3737void VuoEditorComposition::expandSpecializePortMenu()
3739 QAction *sender = (QAction *)QObject::sender();
3742 populateSpecializePortMenu(contextMenuSpecializeGenericType, port,
false);
3743 contextMenuSpecializeGenericType->exec();
3753void VuoEditorComposition::populateSpecializePortMenu(QMenu *menu,
VuoRendererPort *port,
bool limitInitialOptions)
3761 QAction *unspecializeAction = menu->addAction(tr(
"Generic"));
3766 menu->addSeparator();
3768 unspecializeAction->setData(QVariant::fromValue(port));
3769 unspecializeAction->setCheckable(
true);
3770 unspecializeAction->setChecked(genericDataType);
3773 set<string> compatibleTypes;
3774 set<string> compatibleTypesInIsolation;
3777 if (genericDataType)
3785 compatibleTypes = set<string>(compatibleTypesVector.begin(), compatibleTypesVector.end());
3791 compatibleTypes.insert(compatibleTypeNames.begin(), compatibleTypeNames.end());
3796 else if (isPortCurrentlyRevertible(port))
3798 map<VuoNode *, string> nodesToReplace;
3799 set<VuoCable *> cablesToDelete;
3801 if (cablesToDelete.size() >= 1)
3809 compatibleTypes = getRespecializationOptionsForPortInNetwork(port);
3813 if (genericTypeFromPortClass)
3825 if (genericHostPortDataType)
3827 else if (isPortCurrentlyRevertible(hostPort->
getRenderer()))
3837 vector<string> compatibleTypesInIsolationVector;
3838 if (genericTypeFromPortClass)
3845 foreach (
string type, compatibleTypesInIsolationVector)
3852 const int maxTypeCountForFlatMenuDisplay = 10;
3853 if (compatibleTypesInIsolation.size() <= maxTypeCountForFlatMenuDisplay)
3855 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
false,
"", menu);
3863 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true,
"", menu);
3868 QList<QAction *> allNodeSetActionsToAdd;
3869 for (map<
string, set<VuoCompilerType *> >::iterator i = loadedTypesForNodeSet.begin(); i != loadedTypesForNodeSet.end(); ++i)
3871 string nodeSetName = i->first;
3872 if (!nodeSetName.empty())
3873 allNodeSetActionsToAdd +=
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true, nodeSetName, menu);
3876 bool usingExpansionMenu =
false;
3877 if ((menu->actions().size() > 0) && (allNodeSetActionsToAdd.size() > 0))
3879 menu->addSeparator();
3881 if (limitInitialOptions)
3884 QAction *showMoreAction = menu->addAction(tr(
"More…"));
3885 showMoreAction->setData(QVariant::fromValue(port));
3886 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandSpecializePortMenu);
3887 usingExpansionMenu =
true;
3891 if (!usingExpansionMenu)
3895 foreach (QAction *action, menu->actions())
3897 QMenu *specializeSubmenu = action->menu();
3898 if (specializeSubmenu)
3900 foreach (QAction *specializeSubaction, specializeSubmenu->actions())
3901 connect(specializeSubaction, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3903 else if (action == unspecializeAction)
3904 connect(action, &QAction::triggered,
this, &VuoEditorComposition::unspecializePortType);
3906 connect(action, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3918 vector<string> typeOptions;
3920 map<string, VuoCompilerType *> loadedTypes = compiler->
getTypes();
3921 for (map<string, VuoCompilerType *>::iterator i = loadedTypes.begin(); i != loadedTypes.end(); ++i)
3926 (i->first !=
"VuoMathExpressionList"))
3928 typeOptions.push_back(i->first);
3940set<string> VuoEditorComposition::getRespecializationOptionsForPortInNetwork(
VuoRendererPort *port)
3943 return set<string>();
3950 vector<string> compatibleInnerTypeNames;
3951 vector<string> compatibleTypeNames;
3952 for (
VuoPort *connectedPort : connectedGenericPorts)
3957 if (specializedNodeClass)
3965 vector<string> innermostCompatibleTypeNamesForPort;
3966 for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
3969 if (! innermostCompatibleTypeNamesForPort.empty())
3971 if (compatibleInnerTypeNames.empty())
3972 compatibleInnerTypeNames = innermostCompatibleTypeNamesForPort;
3975 for (
int k = compatibleInnerTypeNames.size() - 1; k >= 0; --k)
3976 if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleInnerTypeNames[k]) ==
3977 innermostCompatibleTypeNamesForPort.end())
3978 compatibleInnerTypeNames.erase(compatibleInnerTypeNames.begin() + k);
3987 string typeNameForPort = genericTypeFromPortClass->
getModuleKey();
3991 for (vector<string>::iterator k = compatibleInnerTypeNames.begin(); k != compatibleInnerTypeNames.end(); ++k)
3992 compatibleTypeNames.push_back(prefix + *k);
3994 if (compatibleTypeNames.empty())
4004 return set<string>(compatibleTypeNames.begin(), compatibleTypeNames.end());
4027 set<string> compatibleTypesInIsolation,
4028 set<string> compatibleTypesInContext,
4029 bool limitToNodeSet,
4033 QList<QAction *> actionsToAddToMenu;
4035 map<string, VuoCompilerType *> allTypes = compiler->
getTypes();
4037 QList<VuoCompilerType *> compatibleTypesForNodeSetDisplay;
4038 foreach (
string typeName, compatibleTypesInIsolation)
4041 if (type && (!limitToNodeSet || (loadedTypesForNodeSet[nodeSetName].find(type) != loadedTypesForNodeSet[nodeSetName].end())) &&
4044 (typeName !=
"VuoUrl" && typeName !=
"VuoList_VuoUrl"
4046 && typeName !=
"VuoInteraction" && typeName !=
"VuoList_VuoInteraction"
4047 && typeName !=
"VuoInteractionType" && typeName !=
"VuoList_VuoInteractionType"
4048 && typeName !=
"VuoUuid" && typeName !=
"VuoList_VuoUuid"
4050 && typeName !=
"VuoIconPosition" && typeName !=
"VuoList_VuoIconPosition"
4051 && typeName !=
"VuoMesh" && typeName !=
"VuoList_VuoMesh"
4052 && typeName !=
"VuoWindowProperty" && typeName !=
"VuoList_VuoWindowProperty"
4053 && typeName !=
"VuoWindowReference" && typeName !=
"VuoList_VuoWindowReference"))
4054 compatibleTypesForNodeSetDisplay.append(type);
4057 if (!compatibleTypesForNodeSetDisplay.isEmpty())
4059 QMenu *contextMenuNodeSetTypes = NULL;
4060 QList<QAction *> actionsToAddToNodeSetSubmenu;
4061 bool enabledContentAdded =
false;
4064 if (!nodeSetName.empty())
4066 contextMenuNodeSetTypes =
new QMenu(menu);
4067 contextMenuNodeSetTypes->setSeparatorsCollapsible(
false);
4069 contextMenuNodeSetTypes->setToolTipsVisible(
true);
4070 actionsToAddToMenu.append(contextMenuNodeSetTypes->menuAction());
4076 QList<QVariant> portAndSpecializedType;
4077 portAndSpecializedType.append(QVariant::fromValue(genericPort));
4078 portAndSpecializedType.append(typeName.c_str());
4080 QAction *specializeAction;
4084 if (!nodeSetName.empty())
4086 specializeAction =
new QAction(typeTitle, contextMenuNodeSetTypes);
4087 actionsToAddToNodeSetSubmenu.append(specializeAction);
4093 specializeAction =
new QAction(typeTitle, menu);
4094 actionsToAddToMenu.append(specializeAction);
4097 specializeAction->setData(QVariant(portAndSpecializedType));
4098 specializeAction->setCheckable(
true);
4101 if (compatibleTypesInContext.find(typeName) == compatibleTypesInContext.end())
4103 specializeAction->setEnabled(
false);
4105 specializeAction->setToolTip(tr(
"To change to this type, disconnect the cable from this port and from this node's other ports of the same type."));
4108 enabledContentAdded =
true;
4111 if (contextMenuNodeSetTypes)
4115 if (!enabledContentAdded)
4116 contextMenuNodeSetTypes->setEnabled(
false);
4120 QList<QAction *> actionListWithPromotions = promoteSingletonsFromSubmenus(actionsToAddToMenu);
4121 return actionListWithPromotions;
4128QList<QAction *> VuoEditorComposition::promoteSingletonsFromSubmenus(QList<QAction *> actionList)
4130 QList<QAction *> modifiedActionList;
4131 foreach (QAction *action, actionList)
4133 if (action->menu() && (action->menu()->actions().size() == 1))
4135 QAction *singleSubaction = action->menu()->actions().first();
4136 action->menu()->removeAction(singleSubaction);
4137 modifiedActionList.append(singleSubaction);
4140 modifiedActionList.append(action);
4143 return modifiedActionList;
4151 std::sort(actionList.begin(), actionList.end(), nodeSetMenuActionLessThan);
4152 foreach (QAction *action, actionList)
4153 menu->addAction(action);
4159void VuoEditorComposition::updatePopoversForActiveWindowChange(QWidget *old, QWidget *now)
4172void VuoEditorComposition::updatePopoversForApplicationStateChange(
bool active)
4174 if (ignoreApplicationStateChangeEvents)
4178 if (activeWindow && (activeWindow->
getComposition() ==
this) && (!activeWindow->isMinimized()))
4192 return findNearbyComponent(scenePos, VuoEditorComposition::targetTypePort, limitPortCollisionRange);
4200 QGraphicsItem *item =
findNearbyComponent(scenePos, VuoEditorComposition::targetTypeNodeHeader);
4218 bool limitPortCollisionRange)
4224 bool ignoreComments;
4228 case VuoEditorComposition::targetTypePort:
4230 ignoreCables =
true;
4232 ignorePorts =
false;
4233 ignoreComments =
true;
4236 case VuoEditorComposition::targetTypeNodeHeader:
4238 ignoreCables =
true;
4241 ignoreComments =
true;
4246 ignoreCables =
false;
4247 ignoreNodes =
false;
4248 ignorePorts =
false;
4249 ignoreComments =
false;
4257 QGraphicsItem *topmostItemUnderCursor = itemAt(scenePos, views()[0]->transform());
4258 if (topmostItemUnderCursor && (!topmostItemUnderCursor->isEnabled()))
4259 topmostItemUnderCursor = NULL;
4263 QRectF searchRect(scenePos.x()-0.5*rectLength, scenePos.y()-0.5*rectLength, rectLength, rectLength);
4264 QList<QGraphicsItem *> itemsInRange = items(searchRect);
4265 for (QList<QGraphicsItem *>::iterator i = itemsInRange.begin(); i != itemsInRange.end(); ++i)
4267 if (!(*i)->isEnabled())
4274 bool makeListDragHandle =
4277 if (makeListDragHandle &&
4278 ((! topmostItemUnderCursor) ||
4279 (topmostItemUnderCursor == makeListDrawer) ||
4280 (topmostItemUnderCursor->zValue() < makeListDrawer->zValue())))
4282 return makeListDrawer;
4293 ((! topmostItemUnderCursor) ||
4294 (topmostItemUnderCursor == port) ||
4296 (topmostItemUnderCursor->zValue() < port->zValue()))
4298 ((! limitPortCollisionRange) ||
4312 ((! topmostItemUnderCursor) ||
4313 (topmostItemUnderCursor == cable) ||
4314 (topmostItemUnderCursor->zValue() < cable->zValue())))
4321 if (! ignoreComments)
4328 ((! topmostItemUnderCursor) ||
4329 (topmostItemUnderCursor == (*i)) ||
4330 (topmostItemUnderCursor->zValue() < (*i)->zValue())))
4337 if (targetType == VuoEditorComposition::targetTypeNodeHeader)
4343 headerRect = node->mapToScene(headerRect).
boundingRect();
4344 if (headerRect.intersects(searchRect) && scenePos.y() <= headerRect.bottom())
4356 return topmostItemUnderCursor;
4361 return ((
VuoRendererPort *)(topmostItemUnderCursor))->getRenderedParentNode();
4378 if (! cableInProgress)
4381 VuoPort *fromPort = cableInProgress->getFromPort();
4382 VuoPort *toPort = cableInProgress->getToPort();
4383 VuoPort *fixedPort = (fromPort? fromPort: toPort);
4398 vector<VuoRendererPort *> portList;
4413 if (portList.size() > firstPortIndex)
4415 targetPort = portList[firstPortIndex];
4421 for (
int i = firstPortIndex; i < portList.size(); ++i)
4425 firstPortWithoutWall = portList[i];
4429 if (firstPortWithoutWall)
4430 targetPort = firstPortWithoutWall;
4439 if (portList.size() > firstPortIndex)
4440 targetPort = portList[firstPortIndex];
4461 QRectF boundingRect;
4462 foreach (QGraphicsItem *item, items())
4466 boundingRect |= item->sceneBoundingRect();
4469 return boundingRect;
4478 QRectF boundingRect;
4480 foreach (QGraphicsItem *item, selectedItems())
4484 boundingRect |= item->sceneBoundingRect();
4487 return boundingRect;
4496 QRectF boundingRect;
4498 foreach (QGraphicsItem *item, selectedItems())
4502 boundingRect |= item->mapToScene(item->childrenBoundingRect()).boundingRect();
4512 boundingRect |= drawer->mapToScene(drawer->childrenBoundingRect()).boundingRect();
4517 return boundingRect;
4544 if (updateInRunningComposition)
4553 if (updateInRunningComposition)
4579 if (!errorMarkingUpdatesEnabled)
4582 errorMarkingUpdatesEnabled =
false;
4599 set<VuoCompilerCable *> potentialCables;
4601 if (targetPort && cableInProgress)
4607 if (cableInProgress->getFromNode())
4609 fromNode = cableInProgress->getFromNode();
4610 fromPort = cableInProgress->getFromPort();
4612 toPort = targetPort->
getBase();
4617 fromPort = targetPort->
getBase();
4618 toNode = cableInProgress->getToNode();
4619 toPort = cableInProgress->getToPort();
4624 potentialCable->
setAlwaysEventOnly(! cableInProgress->getRenderer()->effectivelyCarriesData() ||
4625 cableInProgress->getRenderer()->isFloatingEndpointAboveEventPort());
4629 potentialCables.insert(potentialCable);
4640 VUserLog(
"%s: Showing error popover: %s",
4648 set<VuoRendererNode *> nodesToMark;
4649 set<VuoRendererCable *> cablesToMark;
4651 set<VuoNode *> problemNodes = issue.
getNodes();
4652 foreach (
VuoNode *node, problemNodes)
4656 set<VuoCable *> problemCables = issue.
getCables();
4657 foreach (
VuoCable *cable, problemCables)
4659 VuoCable *cableToMark = (cable->
getCompiler() == potentialCable ? cableInProgress : cable);
4668 errorPopovers.insert(errorPopover);
4672 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
4676 if (targetPort && cableInProgress && nodesToMark.find(targetPort->
getRenderedParentNode()) != nodesToMark.end())
4680 else if (! nodesToMark.empty())
4683 qreal topY = viewportRect.bottom();
4689 QPointF scenePos = node->scenePos();
4690 if (viewportRect.contains(scenePos) && (scenePos.y() < topY))
4692 topmostVisibleNode = node;
4693 topY = scenePos.y();
4697 if (topmostVisibleNode)
4698 nearbyNode = topmostVisibleNode;
4707 VUserLog(
"Warning: no nearby node (no marked nodes).");
4712 const QPoint offsetFromNode(0,10);
4713 QPoint popoverTopLeftInScene = (nearbyNode?
4714 (nearbyNode->scenePos().toPoint() +
4717 QPoint(viewportRect.center().x() - 0.5*errorPopover->sizeHint().width(),
4718 viewportRect.center().y() - 0.5*errorPopover->sizeHint().height()));
4722 const int margin = 5;
4723 popoverTopLeftInScene = (QPoint(fmin(popoverTopLeftInScene.x(), viewportRect.bottomRight().x() - errorPopover->sizeHint().width() - margin),
4724 fmin(popoverTopLeftInScene.y(), viewportRect.bottomRight().y() - errorPopover->sizeHint().height() - margin)));
4726 popoverTopLeftInScene = (QPoint(fmax(popoverTopLeftInScene.x(), viewportRect.topLeft().x() + margin),
4727 fmax(popoverTopLeftInScene.y(), viewportRect.topLeft().y() + margin)));
4729 QPoint popoverTopLeftInView = views()[0]->mapFromScene(popoverTopLeftInScene);
4730 QPoint popoverTopLeftGlobal = views()[0]->mapToGlobal(popoverTopLeftInView);
4732 errorPopover->move(popoverTopLeftGlobal);
4733 errorPopover->show();
4741 delete potentialCable;
4743 errorMarkingUpdatesEnabled =
true;
4749bool VuoEditorComposition::hasFeedbackErrors(
void)
4751 return this->errorMark;
4759 if (hasFeedbackErrors())
4768void VuoEditorComposition::buildComposition(
string compositionSnapshot,
const set<string> &dependenciesUninstalled)
4774 if (! dependenciesUninstalled.empty())
4776 vector<string> dependenciesRemovedVec(dependenciesUninstalled.begin(), dependenciesUninstalled.end());
4778 throw VuoException(
"Some modules that the composition needs were uninstalled: " + dependenciesStr);
4781 delete runningComposition;
4782 runningComposition = NULL;
4786 if (runningCompositionActiveDriver)
4790 string dir, file, ext;
4792 linkedCompositionPath = dir + file +
".dylib";
4797 compiler->
compileComposition(runningComposition, compiledCompositionPath,
true, issues);
4801 remove(compiledCompositionPath.c_str());
4807 delete runningComposition;
4808 runningComposition = NULL;
4820bool VuoEditorComposition::isRunningThreadUnsafe(
void)
4822 return runner != NULL && ! stopRequested && ! runner->
isStopped();
4832 __block
bool running;
4833 dispatch_sync(runCompositionQueue, ^{
4834 running = isRunningThreadUnsafe();
4860 if (matchingComposition->showEventsMode)
4864 stopRequested =
false;
4865 dispatch_async(runCompositionQueue, ^{
4868 runningCompositionLibraries = std::make_shared<VuoRunningCompositionLibraries>();
4870 buildComposition(compositionSnapshot);
4884 if (matchingComposition->showEventsMode)
4885 this->runner->subscribeToEventTelemetry(matchingCompositionIdentifier);
4887 dispatch_sync(activePortPopoversQueue, ^{
4888 for (
auto i : matchingComposition->activePortPopovers)
4890 string portID = i.first;
4891 updateDataInPortPopoverFromRunningTopLevelComposition(matchingComposition, matchingCompositionIdentifier, portID);
4912 stopRequested =
true;
4913 dispatch_async(runCompositionQueue, ^{
4917 runner->waitUntilStopped();
4922 linkedCompositionPath =
"";
4924 runningCompositionLibraries =
nullptr;
4926 delete runningComposition;
4927 runningComposition = NULL;
4935 subcompositionRouter->applyToAllLinkedCompositions(
this, ^
void (
VuoEditorComposition *matchingComposition,
string matchingCompositionIdentifier)
4937 if (matchingComposition->showEventsMode)
4940 dispatch_sync(activePortPopoversQueue, ^{
4941 for (
auto i : matchingComposition->activePortPopovers)
4943 VuoPortPopover *popover = i.second;
4944 popover->setCompositionRunning(false);
4967 dispatch_async(runCompositionQueue, ^{
4968 if (isRunningThreadUnsafe())
4974 runningCompositionLibraries->enqueueLibraryContainingDependencyToUnload(moduleKey);
4977 string oldBuiltCompositionSnapshot = oldCompositionSnapshot;
4979 if (previouslyActiveDriver)
4982 previouslyActiveDriver->applyToComposition(oldBuiltComposition, compiler);
4986 buildComposition(newCompositionSnapshot, dependenciesUninstalled);
4988 string compositionDiff = diffInfo->
diff(oldBuiltCompositionSnapshot, runningComposition, compiler);
4991 catch (exception &e)
4993 VUserLog(
"Composition stopped itself: %s", e.what());
4998 VUserLog(
"Composition stopped itself.");
5004 dispatch_async(dispatch_get_main_queue(), ^{
5023 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, reloadSubcompositionIfUnsaved);
5041 if (
this == topLevelComposition)
5043 dispatch_async(runCompositionQueue, ^{
5044 if (isRunningThreadUnsafe())
5046 json_object *constantObject = json_tokener_parse(constant.c_str());
5047 runner->
setInputPortValue(thisCompositionIdentifier, runningPortID, constantObject);
5055 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
5057 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
5084 if (runningPortIdentifier.empty())
5090 if (
this == topLevelComposition)
5092 dispatch_async(runCompositionQueue, ^{
5093 if (isRunningThreadUnsafe())
5095 json_object *constantObject = json_tokener_parse(constant.c_str());
5096 runner->
setInputPortValue(thisCompositionIdentifier, runningPortIdentifier, constantObject);
5104 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
5106 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
5119 dispatch_async(runCompositionQueue, ^{
5120 if (isRunningThreadUnsafe())
5122 json_object *constantObject = json_tokener_parse(constant.c_str());
5124 foreach (
string subcompositionIdentifier, subcompositionIdentifiers)
5126 runner->
setInputPortValue(subcompositionIdentifier, portIdentifier, constantObject);
5137 dispatch_async(runCompositionQueue, ^{
5138 if (isRunningThreadUnsafe())
5143 json_object *constantObject = json_tokener_parse(constant.c_str());
5144 map<VuoRunner::Port *, json_object *> m;
5145 m[publishedPort] = constantObject;
5147 json_object_put(constantObject);
5159 return contextMenuDeleteSelected;
5169 QMenu *contextMenuTints =
new QMenu(parent);
5170 contextMenuTints->setSeparatorsCollapsible(
false);
5171 contextMenuTints->setTitle(tr(
"Tint"));
5172 foreach (QAction *tintAction, contextMenuTintActions)
5173 contextMenuTints->addAction(tintAction);
5174 contextMenuTints->insertSeparator(contextMenuTintActions.last());
5176 return contextMenuTints;
5182void VuoEditorComposition::expandChangeNodeMenu()
5184 QAction *sender = (QAction *)QObject::sender();
5189 int currentMatchesListed = contextMenuChangeNode->actions().size()-1;
5190 if (currentMatchesListed <= initialChangeNodeSuggestionCount)
5193 int verticalSpacePerItem = 21;
5197 int targetMatches = availableVerticalSpace/verticalSpacePerItem - 2;
5206 contextMenuChangeNode->exec();
5221 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
5222 vector<VuoCompilerNodeClass *> loadedNodeClasses;
5223 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
5224 loadedNodeClasses.push_back(i->second);
5227 vector<string> bestMatches;
5228 map<string, double> matchScores;
5229 matchScores[
""] = 0;
5231 int targetMatchCount = (matchLimit > 0? matchLimit : loadedNodeClasses.size());
5232 for (
int i = 0; i < targetMatchCount; ++i)
5233 bestMatches.push_back(
"");
5236 bool overflow =
false;
5239 string originalGenericNodeClassName;
5243 originalGenericNodeClassName = nodeClass->
getClassName();
5248 if (loadedNodeClassName == originalGenericNodeClassName)
5251 bool canSwapNondestructively = canSwapWithoutBreakingCables(node, loadedNodeClass->
getBase());
5252 double matchScore = (canSwapNondestructively? calculateNodeSimilarity(nodeClass, loadedNodeClass->
getBase()) : 0);
5253 int highestIndexWithCompetitiveScore = -1;
5254 for (
int i = targetMatchCount-1; (i >= 0) && (highestIndexWithCompetitiveScore == -1); --i)
5255 if (matchScore <= matchScores[bestMatches[i] ])
5256 highestIndexWithCompetitiveScore = i;
5258 if (highestIndexWithCompetitiveScore < targetMatchCount-1)
5260 if (matchScores[bestMatches[targetMatchCount-1] ] > 0)
5263 for (
int j = targetMatchCount-2; j > highestIndexWithCompetitiveScore; --j)
5264 bestMatches[j+1] = bestMatches[j];
5266 bestMatches[highestIndexWithCompetitiveScore+1] = loadedNodeClassName;
5267 matchScores[loadedNodeClassName] = matchScore;
5271 for (
int i = 0; i < targetMatchCount; ++i)
5273 if (matchScores[bestMatches[i] ] > 0)
5278 matchDisplayText += QString(
" (%1)").arg(bestMatches[i].c_str());
5280 QAction *changeAction = menu->addAction(matchDisplayText);
5282 QList<QVariant> currentNodeAndNewClass;
5283 currentNodeAndNewClass.append(QVariant::fromValue(node));
5284 currentNodeAndNewClass.append(bestMatches[i].c_str());
5285 changeAction->setData(QVariant(currentNodeAndNewClass));
5286 connect(changeAction, &QAction::triggered,
this, &VuoEditorComposition::swapNode);
5293 QAction *showMoreAction = menu->addAction(tr(
"More…"));
5294 showMoreAction->setData(QVariant::fromValue(node));
5295 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandChangeNodeMenu);
5306 map<string, int> requiredInputs;
5307 bool inputEventSourceRequired =
false;
5310 bool hasDrawerWithNoIncomingCables =
false;
5311 bool hasDrawerWithNoIncomingDataCables =
false;
5318 hasDrawerWithNoIncomingCables =
true;
5319 hasDrawerWithNoIncomingDataCables =
true;
5320 vector<VuoRendererPort *> childPorts = inputDrawer->
getDrawerPorts();
5324 hasDrawerWithNoIncomingCables =
false;
5326 hasDrawerWithNoIncomingDataCables =
false;
5331 if (!hasDrawerWithNoIncomingDataCables)
5338 requiredInputs[typeKey] = ((requiredInputs.find(typeKey) == requiredInputs.end())? 1 : requiredInputs[typeKey]+1);
5343 if (hasIncomingCables && !hasDrawerWithNoIncomingCables)
5344 inputEventSourceRequired =
true;
5348 map<string, int> requiredOutputs;
5349 bool outputEventSourceRequired =
false;
5361 requiredOutputs[typeKey] = ((requiredOutputs.find(typeKey) == requiredOutputs.end())? 1 : requiredOutputs[typeKey]+1);
5365 outputEventSourceRequired =
true;
5369 bool inputEventSourceAvailable =
false;
5370 map<string, int> availableInputs;
5378 availableInputs[typeKey] = ((availableInputs.find(typeKey) == availableInputs.end())? 1 : availableInputs[typeKey]+1);
5383 inputEventSourceAvailable =
true;
5386 bool outputEventSourceAvailable =
false;
5387 map<string, int> availableOutputs;
5395 availableOutputs[typeKey] = ((availableOutputs.find(typeKey) == availableOutputs.end())? 1 : availableOutputs[typeKey]+1);
5400 outputEventSourceAvailable =
true;
5403 for (std::map<string,int>::iterator it=requiredInputs.begin(); it!=requiredInputs.end(); ++it)
5405 string typeKey = it->first;
5406 int typeRequiredCount = it->second;
5407 if (availableInputs[typeKey] < typeRequiredCount)
5412 for (std::map<string,int>::iterator it=requiredOutputs.begin(); it!=requiredOutputs.end(); ++it)
5414 string typeKey = it->first;
5415 int typeRequiredCount = it->second;
5416 if (availableOutputs[typeKey] < typeRequiredCount)
5420 if (inputEventSourceRequired && !inputEventSourceAvailable)
5423 if (outputEventSourceRequired && !outputEventSourceAvailable)
5433bool VuoEditorComposition::isPortCurrentlyRevertible(
VuoRendererPort *port)
5439 if (!specializedNodeClass)
5444 if (!originalGenericType)
5453 if (hostPort && (!isPortCurrentlyRevertible(hostPort->
getRenderer())))
5480 string publishedPortName = ((! name.empty())?
5491 bool performedMerge =
false;
5494 publishedPort = (isPublishedInput ?
5500 if (isPublishedInput && portType && type && !forceEventOnlyPublication)
5508 performedMerge =
true;
5515 if (! performedMerge)
5518 if (isPublishedInput && type)
5533 if (! existingPublishedCable)
5539 if (mergePerformed != NULL)
5540 *mergePerformed = performedMerge;
5542 return rendererPublishedPort;
5554 if (creatingPublishedInputCable)
5557 VuoPort *fromPort = externalPort;
5560 VuoPort *toPort = internalPort;
5567 publishedCable->
setFrom(fromNode, fromPort);
5574 VuoPort *fromPort = internalPort;
5577 VuoPort *toPort = externalPort;
5584 publishedCable->
setTo(toNode, toPort);
5587 if (forceEventOnlyPublication)
5590 return publishedCable;
5606 vector<VuoPublishedPort *> publishedPortsToAdd;
5607 map<VuoPublishedPort *, string> publishedPortsToRename;
5610 VuoProtocol *previousActiveProtocol = this->activeProtocol;
5611 bool removingPreviousProtocol = previousActiveProtocol && (previousActiveProtocol != protocol);
5613 bool portChangesMadeDuringProtocolRemoval =
false;
5614 if (removingPreviousProtocol)
5617 if (portChangesMadeDuringProtocolRemoval && !useUndoStack)
5619 VUserLog(
"Warning: Unexpected combination: Removing protocol ports, but useUndoStack=false");
5620 useUndoStack =
true;
5624 this->activeProtocol = protocol;
5629 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5631 string portName = i->first;
5632 string portType = i->second;
5634 bool compositionHadCompatiblePort =
false;
5636 if (preexistingPublishedPort)
5640 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5641 (!preexistingType && (portType ==
"")));
5644 compositionHadCompatiblePort =
true;
5649 compositionHadCompatiblePort =
false;
5654 if (!compositionHadCompatiblePort)
5664 publishedPortsToAdd.push_back(publishedPort);
5671 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5673 string portName = i->first;
5674 string portType = i->second;
5676 bool compositionHadCompatiblePort =
false;
5678 if (preexistingPublishedPort)
5681 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5682 (!preexistingType && (portType ==
"")));
5685 compositionHadCompatiblePort =
true;
5690 compositionHadCompatiblePort =
false;
5695 if (!compositionHadCompatiblePort)
5705 publishedPortsToAdd.push_back(publishedPort);
5713 bool undoStackMacroBegunAlready = (removingPreviousProtocol && portChangesMadeDuringProtocolRemoval);
5714 if (!publishedPortsToRename.empty() || !publishedPortsToAdd.empty() || undoStackMacroBegunAlready)
5716 set<VuoPublishedPort *> publishedPortsToRemove;
5717 bool beginUndoStackMacro = !undoStackMacroBegunAlready;
5718 bool endUndoStackMacro =
true;
5719 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5733string VuoEditorComposition::getNonProtocolVariantForPortName(
string portName)
5735 string modifiedPortName = portName;
5736 if (modifiedPortName.length() > 0)
5737 modifiedPortName[0] = toupper(modifiedPortName[0]);
5738 modifiedPortName =
"some" + modifiedPortName;
5740 return modifiedPortName;
5764 set<VuoPublishedPort *> publishedPortsToRemove;
5765 map<VuoPublishedPort *, string> publishedPortsToRename;
5768 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5770 string portName = i->first;
5771 string portType = i->second;
5774 if (preexistingPublishedPort)
5776 bool portCompatibleAcrossProtocolTransition =
false;
5777 if (replacementProtocol)
5780 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5782 string replacementPortName = i->first;
5783 string replacementPortType = i->second;
5785 if ((portName == replacementPortName) && (portType == replacementPortType))
5786 portCompatibleAcrossProtocolTransition =
true;
5791 publishedPortsToRemove.insert(preexistingPublishedPort);
5792 else if (!portCompatibleAcrossProtocolTransition)
5798 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5800 string portName = i->first;
5801 string portType = i->second;
5804 if (preexistingPublishedPort)
5806 bool portCompatibleAcrossProtocolTransition =
false;
5807 if (replacementProtocol)
5810 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5812 string replacementPortName = i->first;
5813 string replacementPortType = i->second;
5815 if ((portName == replacementPortName) && (portType == replacementPortType))
5816 portCompatibleAcrossProtocolTransition =
true;
5821 publishedPortsToRemove.insert(preexistingPublishedPort);
5822 else if (!portCompatibleAcrossProtocolTransition)
5829 if (!publishedPortsToRemove.empty())
5830 publishedPortsToRename.clear();
5832 bool portChangesRequired = (!publishedPortsToRename.empty() || !publishedPortsToRemove.empty());
5833 if (portChangesRequired)
5835 vector<VuoPublishedPort *> publishedPortsToAdd;
5836 bool beginUndoStackMacro =
true;
5837 bool endUndoStackMacro = !replacementProtocol;
5838 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5843 return portChangesRequired;
5854 if ((activeProtocol != protocol) || !activeProtocol)
5857 activeProtocol = NULL;
5860 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5862 string portName = i->first;
5863 string portType = i->second;
5866 if (preexistingPublishedPort)
5871 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5873 string portName = i->first;
5874 string portType = i->second;
5877 if (preexistingPublishedPort)
5890 return activeProtocol;
5899 if (!activeProtocol)
5902 return static_cast<VuoEditor *
>(qApp)->getBuiltInDriverForProtocol(activeProtocol);
5935 return removalResult;
5962void VuoEditorComposition::highlightEligibleEndpointsForCable(
VuoCable *cable)
5975 highlightInternalPortsConnectableToPort(fixedPort, cable->
getRenderer());
5989 QList<QGraphicsItem *> compositionComponents = items();
5990 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5992 QGraphicsItem *compositionComponent = *i;
5998 for(vector<VuoPort *>::iterator inputPort = inputPorts.begin(); inputPort != inputPorts.end(); ++inputPort)
5999 updateEligibilityHighlightingForPort((*inputPort)->getRenderer(), port, !cable->
effectivelyCarriesData(), types);
6003 for(vector<VuoPort *>::iterator outputPort = outputPorts.begin(); outputPort != outputPorts.end(); ++outputPort)
6004 updateEligibilityHighlightingForPort((*outputPort)->getRenderer(), port, !cable->
effectivelyCarriesData(), types);
6009 if (rc && rc != cable)
6011 QGraphicsItem::CacheMode normalCacheMode = rc->cacheMode();
6012 rc->setCacheMode(QGraphicsItem::NoCache);
6030 rc->setCacheMode(normalCacheMode);
6035 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
6036 updateEligibilityHighlightingForNode(
dynamic_cast<VuoRendererNode *
>(*i));
6043void VuoEditorComposition::updateEligibilityHighlightingForPort(
VuoRendererPort *portToHighlight,
6045 bool eventOnlyConnection,
6046 map<string, VuoCompilerType *> &types)
6048 QGraphicsItem::CacheMode normalCacheMode = portToHighlight->cacheMode();
6049 portToHighlight->setCacheMode(QGraphicsItem::NoCache);
6057 if (typecastPortToHighlight)
6060 portToHighlight->setCacheMode(normalCacheMode);
6062 if (typecastPortToHighlight)
6063 updateEligibilityHighlightingForPort(typecastPortToHighlight->
getChildPort(), fixedPort, eventOnlyConnection, types);
6084 bool forwardConnection;
6087 fromPort = fixedPort;
6088 toPort = portToHighlight;
6089 forwardConnection =
true;
6093 fromPort = portToHighlight;
6095 forwardConnection =
false;
6098 bool directConnectionPossible;
6102 if (fixedExternalPublishedPort && externalPublishedPortToHighlight)
6103 directConnectionPossible =
false;
6104 else if (fixedExternalPublishedPort && !externalPublishedPortToHighlight)
6106 else if (!fixedExternalPublishedPort && externalPublishedPortToHighlight)
6112 if (directConnectionPossible)
6116 else if (fixedPort == portToHighlight)
6139bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6141 bool eventOnlyConnection,
6142 bool forwardConnection)
6145 string respecializedTypeName =
"";
6147 return canConnectDirectlyWithRespecializationNondestructively(fromPort,
6149 eventOnlyConnection,
6151 &portToRespecialize,
6152 respecializedTypeName);
6165bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6167 bool eventOnlyConnection,
6168 bool forwardConnection,
6170 string &respecializedTypeName)
6172 *portToRespecialize = NULL;
6173 respecializedTypeName =
"";
6175 bool canConnectWithRespecialization = canConnectDirectlyWithRespecialization(fromPort,
6177 eventOnlyConnection,
6180 respecializedTypeName);
6181 if (!canConnectWithRespecialization)
6184 if (canConnectWithRespecialization && !portToRespecialize)
6187 bool nondestructive = portCanBeUnspecializedNondestructively((*portToRespecialize)->getBase());
6188 if (!nondestructive)
6190 *portToRespecialize = NULL;
6191 respecializedTypeName =
"";
6193 return nondestructive;
6201bool VuoEditorComposition::portCanBeUnspecializedNondestructively(
VuoPort *portToUnspecialize)
6203 map<VuoNode *, string> nodesToReplace;
6204 set<VuoCable *> cablesToDelete;
6209 if (cablesToDelete.empty())
6212 else if ((cablesToDelete.size() == 1) && ((*(cablesToDelete.begin()))->getToPort() == portToUnspecialize))
6237bool VuoEditorComposition::canConnectDirectlyWithRespecialization(
VuoRendererPort *fromPort,
6239 bool eventOnlyConnection,
6240 bool forwardConnection,
6242 string &respecializedTypeName)
6246 *portToRespecialize = NULL;
6247 respecializedTypeName =
"";
6257 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6258 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6260 if (!(fromPortIsEnabledOutput && toPortIsEnabledInput))
6266 if (!(currentFromDataType && currentToDataType))
6281 if (fromSpecializedNodeClass)
6293 if (toSpecializedNodeClass)
6302 bool fromPortIsGeneric = currentFromGenericType;
6303 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6304 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6306 bool toPortIsGeneric = currentToGenericType;
6307 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6308 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6311 set<string> compatibleTypes;
6312 string specializedType =
"";
6316 if ((fromPortIsStatic && toPortIsSpecialized) || (fromPortIsSpecialized && toPortIsStatic))
6320 portToTryToRespecialize = (fromPortIsSpecialized? fromPort : toPort);
6324 else if ((fromPortIsSpecialized || toPortIsSpecialized) && !fromPortIsStatic && !toPortIsStatic)
6327 bool dragSourceIsGeneric = (forwardConnection? fromPortIsGeneric : toPortIsGeneric);
6329 VuoRendererPort *dragDestination = (forwardConnection? toPort : fromPort);
6330 bool dragDestinationIsGeneric = (forwardConnection? toPortIsGeneric : fromPortIsGeneric);
6347 if (!dragSourceIsGeneric && !dragDestinationIsGeneric)
6350 portToTryToRespecialize = dragDestination;
6358 if (portToTryToRespecialize)
6359 compatibleTypes = getRespecializationOptionsForPortInNetwork(portToTryToRespecialize);
6361 bool portsAreCompatible = (compatibleTypes.find(specializedType) != compatibleTypes.end());
6363 if (portsAreCompatible)
6365 *portToRespecialize = portToTryToRespecialize;
6366 respecializedTypeName = specializedType;
6369 return portsAreCompatible;
6378void VuoEditorComposition::updateEligibilityHighlightingForNode(
VuoRendererNode *node)
6393 QGraphicsItem::CacheMode normalCacheMode = drawer->cacheMode();
6394 drawer->setCacheMode(QGraphicsItem::NoCache);
6399 drawer->setCacheMode(normalCacheMode);
6408 QGraphicsItem::CacheMode normalCacheMode = hostPort->cacheMode();
6409 hostPort->setCacheMode(QGraphicsItem::NoCache);
6411 hostPort->setCacheMode(normalCacheMode);
6445 bool toPortIsDragDestination,
6447 string &specializedTypeName,
6448 string &typecastToInsert)
6450 *portToSpecialize = NULL;
6451 specializedTypeName =
"";
6453 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6454 map<string, string> specializedTypeNameForTypecast;
6457 vector<string> candidateTypecasts =
findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast, types);
6458 bool solutionSelected = selectBridgingSolutionFromOptions(candidateTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, typecastToInsert);
6460 if (!solutionSelected)
6463 if (portToSpecializeForTypecast.find(typecastToInsert) != portToSpecializeForTypecast.end())
6464 *portToSpecialize = portToSpecializeForTypecast[typecastToInsert];
6465 if (specializedTypeNameForTypecast.find(typecastToInsert) != specializedTypeNameForTypecast.end())
6466 specializedTypeName = specializedTypeNameForTypecast[typecastToInsert];
6489bool VuoEditorComposition::selectBridgingSolutionFromOptions(vector<string> suitableTypecasts,
6490 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6491 map<string, string> specializedTypeNameForTypecast,
6492 string &selectedTypecast)
6494 if (suitableTypecasts.empty())
6496 selectedTypecast =
"";
6500 else if (suitableTypecasts.size() == 1)
6502 selectedTypecast = suitableTypecasts[0];
6507 return promptForBridgingSelectionFromOptions(suitableTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, selectedTypecast);
6517 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6518 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6520 return (fromPortIsEnabledOutput && toPortIsEnabledInput &&
6532 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6548 if (toNodeUsesIndex)
6575 bool toPortIsDragDestination,
6576 map<string, VuoCompilerType *> &types)
6578 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6579 map<string, string> specializedTypeNameForTypecast;
6580 return findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast, types);
6594 bool toPortIsDragDestination,
6595 map<string, VuoRendererPort *> &portToSpecializeForTypecast,
6596 map<string, string> &specializedTypeNameForTypecast,
6597 map<string, VuoCompilerType *> &types)
6604 const bool limitCombinations =
true;
6606 portToSpecializeForTypecast.clear();
6607 specializedTypeNameForTypecast.clear();
6608 vector<string> suitableTypecasts;
6610 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6611 return suitableTypecasts;
6616 return suitableTypecasts;
6621 string specializedTypeName =
"";
6624 suitableTypecasts.push_back(
"");
6625 portToSpecializeForTypecast[
""] = portToSpecialize;
6626 specializedTypeNameForTypecast[
""] = specializedTypeName;
6628 return suitableTypecasts;
6635 if (!(currentFromDataType && currentToDataType))
6636 return suitableTypecasts;
6646 if (fromSpecializedNodeClass)
6658 if (toSpecializedNodeClass)
6669 bool fromPortIsGeneric = currentFromGenericType;
6670 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6671 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6673 bool toPortIsGeneric = currentToGenericType;
6674 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6675 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6678 if (fromPortIsGeneric && toPortIsGeneric)
6679 return suitableTypecasts;
6682 else if (fromPortIsStatic && toPortIsStatic)
6684 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6686 return suitableTypecasts;
6691 bool specializeToPort =
true;
6692 if (toPortIsGeneric)
6693 specializeToPort =
true;
6694 else if (fromPortIsGeneric)
6695 specializeToPort =
false;
6696 else if (fromPortIsSpecialized && toPortIsStatic)
6697 specializeToPort =
false;
6698 else if (fromPortIsStatic && toPortIsSpecialized)
6699 specializeToPort =
true;
6700 else if (fromPortIsSpecialized && toPortIsSpecialized)
6701 specializeToPort = toPortIsDragDestination;
6705 set<string> compatibleTypes;
6706 if (specializeToPort && (toPortIsGeneric || (toPortIsSpecialized && portCanBeUnspecializedNondestructively(toPort->
getBase()))))
6707 compatibleTypes = getRespecializationOptionsForPortInNetwork(toPort);
6708 else if (!specializeToPort && (fromPortIsGeneric || (fromPortIsSpecialized && portCanBeUnspecializedNondestructively(fromPort->
getBase()))))
6709 compatibleTypes = getRespecializationOptionsForPortInNetwork(fromPort);
6715 if (limitCombinations)
6717 vector<string> limitedSuitableTypecasts;
6720 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6723 foreach (
string typecastName, limitedSuitableTypecasts)
6725 portToSpecializeForTypecast[typecastName] = specializeToPort? toPort : fromPort;
6726 specializedTypeNameForTypecast[typecastName] = specializeToPort? currentToDataType->
getModuleKey() :
6733 if (compatibleTypes.find(fixedDataType) != compatibleTypes.end())
6735 limitedSuitableTypecasts.push_back(
"");
6736 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6737 specializedTypeNameForTypecast[
""] = fixedDataType;
6740 if (limitedSuitableTypecasts.size() >= 1)
6741 return limitedSuitableTypecasts;
6744 foreach (
string compatibleTypeName, compatibleTypes)
6746 VuoCompilerType *compatibleSpecializedType = types[compatibleTypeName];
6747 if (!compatibleSpecializedType)
6748 compatibleSpecializedType = compiler->
getType(compatibleTypeName);
6750 if (compatibleSpecializedType)
6752 VuoType *candidateFromType = specializeToPort? currentFromDataType : compatibleSpecializedType->
getBase();
6753 VuoType *candidateToType = specializeToPort? compatibleSpecializedType->
getBase() : currentToDataType;
6756 if (candidateFromType == candidateToType)
6758 suitableTypecasts.push_back(
"");
6759 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6760 specializedTypeNameForTypecast[
""] = compatibleSpecializedType->
getBase()->
getModuleKey();
6763 if (portsPassSanityCheckToTypeconvert(fromPort,
6769 foreach (
string typecast, suitableTypecastsForCurrentTypes)
6771 suitableTypecasts.push_back(typecast);
6772 portToSpecializeForTypecast[typecast] = specializeToPort? toPort : fromPort;
6773 specializedTypeNameForTypecast[typecast] = compatibleSpecializedType->
getBase()->
getModuleKey();
6779 return suitableTypecasts;
6794bool VuoEditorComposition::promptForBridgingSelectionFromOptions(vector<string> suitableTypecasts,
6795 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6796 map<string, string> specializedTypeNameForTypecast,
6797 string &selectedTypecast)
6799 QMenu typecastMenu(views()[0]->viewport());
6800 typecastMenu.setSeparatorsCollapsible(
false);
6801 QString spacer(
" ");
6804 set <pair<VuoRendererPort *, string> > specializationDetails;
6805 vector<string> typeconversionOptionsRequiringNoSpecialization;
6806 foreach (
string typecastClassName, suitableTypecasts)
6808 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typecastClassName];
6809 string specializedTypeName = specializedTypeNameForTypecast[typecastClassName];
6810 specializationDetails.insert(std::make_pair(portToSpecialize,
6811 specializedTypeName));
6813 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6814 if (portAlreadyHasTargetType)
6815 typeconversionOptionsRequiringNoSpecialization.push_back(typecastClassName);
6821 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()))
6823 QString menuText = getDisplayTextForSpecializationOption(portToSpecializeForTypecast[
""], specializedTypeNameForTypecast[
""]);
6824 QAction *typecastAction = typecastMenu.addAction(menuText);
6825 typecastAction->setData(QVariant(
""));
6828 bool foundSpecializationOptionsRequiringNoTypeconversion = typecastMenu.actions().size() >= 1;
6829 bool foundTypeconversionOptionsRequiringNoSpecialization = typeconversionOptionsRequiringNoSpecialization.size() >= 1;
6832 bool includingTypeconvertWithNoSpecializationHeader = foundSpecializationOptionsRequiringNoTypeconversion;
6833 if (foundTypeconversionOptionsRequiringNoSpecialization)
6835 if (foundSpecializationOptionsRequiringNoTypeconversion)
6836 typecastMenu.addSeparator();
6838 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6839 string specializedTypeName = specializedTypeNameForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6841 if (portToSpecialize && !specializedTypeName.empty())
6843 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6844 QAction *typecastAction = typecastMenu.addAction(menuText);
6845 typecastAction->setEnabled(
false);
6846 includingTypeconvertWithNoSpecializationHeader =
true;
6850 foreach (
string typecastClassName, typeconversionOptionsRequiringNoSpecialization)
6856 typecastAction->setData(QVariant(typecastClassName.c_str()));
6861 for (set<pair<VuoRendererPort *, string> >::iterator i = specializationDetails.begin(); i != specializationDetails.end(); ++i)
6864 string specializedTypeName = i->second;
6867 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()) &&
6868 (portToSpecializeForTypecast[
""] == portToSpecialize) &&
6869 (specializedTypeNameForTypecast[
""] == specializedTypeName))
6875 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6876 if (portAlreadyHasTargetType)
6881 if (typecastMenu.actions().size() >= 1)
6882 typecastMenu.addSeparator();
6884 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6885 QAction *typecastAction = typecastMenu.addAction(menuText);
6886 typecastAction->setEnabled(
false);
6889 foreach (
string typecastClassName, suitableTypecasts)
6891 if ((portToSpecializeForTypecast[typecastClassName] == portToSpecialize) &&
6892 (specializedTypeNameForTypecast[typecastClassName] == specializedTypeName))
6898 typecastAction->setData(QVariant(typecastClassName.c_str()));
6904 menuSelectionInProgress =
true;
6905 QAction *selectedTypecastAction = typecastMenu.exec(QCursor::pos());
6906 menuSelectionInProgress =
false;
6908 selectedTypecast = (selectedTypecastAction? selectedTypecastAction->data().toString().toUtf8().constData() :
"");
6909 return selectedTypecastAction;
6915QString VuoEditorComposition::getDisplayTextForSpecializationOption(
VuoRendererPort *portToSpecialize,
string specializedTypeName)
6917 if (!portToSpecialize || specializedTypeName.empty())
6920 bool isInput = portToSpecialize && portToSpecialize->
getInput();
6921 QString typeDisplayName = compiler->
getType(specializedTypeName)?
6923 specializedTypeName.c_str();
6925 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6927 QString displayText;
6928 if (portAlreadyHasTargetType)
6933 displayText = tr(
"Keep Input Port as %1");
6938 displayText = tr(
"Keep Output Port as %1");
6946 displayText = tr(
"Change Input Port to %1");
6951 displayText = tr(
"Change Output Port to %1");
6955 return displayText.arg(typeDisplayName);
6974 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
6975 if (topLevelComposition->isRunningThreadUnsafe())
6977 portValue = isInput ?
6978 topLevelComposition->runner->getInputPortValue(thisCompositionIdentifier, runningPortIdentifier) :
6979 topLevelComposition->runner->getOutputPortValue(thisCompositionIdentifier, runningPortIdentifier);
6983 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, getPortValue);
6991string VuoEditorComposition::getIdentifierForRunningPort(
VuoPort *runningPort)
7010 return dynamic_cast<VuoPublishedPort *
>(staticPort)->getClass()->getName();
7016 string nodeIdentifier =
"";
7026 if (staticPort->
hasCompiler() && !nodeIdentifier.empty())
7070 if (candidateInputPort == port)
7074 if (candidateOutputPort == port)
7090 map<string, VuoPortPopover *>::iterator popover = activePortPopovers.find(portID);
7091 if (popover != activePortPopovers.end())
7092 return popover->second;
7105void VuoEditorComposition::enableInactivePopoverForPort(
VuoRendererPort *rp)
7108 bool popoverJustClosedAtLastEvent = portsWithPopoversClosedAtLastEvent.find(portID) != portsWithPopoversClosedAtLastEvent.end();
7109 if (!popoverJustClosedAtLastEvent)
7118 if (!popoverEventsEnabled)
7124 VUserLog(
"%s: Open popover for %s",
7128 dispatch_sync(runCompositionQueue, ^{
7130 dispatch_sync(activePortPopoversQueue, ^{
7132 if (activePortPopovers.find(portID) == activePortPopovers.end())
7136 VuoPortPopover *popover = new VuoPortPopover(port, this, views()[0]->viewport());
7138 connect(popover, &VuoPortPopover::popoverClosedForPort, this, &VuoEditorComposition::disablePopoverForPortThreadSafe);
7139 connect(popover, &VuoPortPopover::popoverDetachedFromPort, [=]{
7140 VUserLog(
"%s: Detach popover for %s",
7141 window->getWindowTitleWithoutPlaceholder().toUtf8().data(),
7153 const int cutoffMargin = 16;
7154 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
7155 if (portLeftInScene.x() + popover->size().width() + cutoffMargin > viewportRect.right())
7156 portLeftInScene = QPoint(viewportRect.right() - popover->size().width() - cutoffMargin, portLeftInScene.y());
7157 if (portLeftInScene.y() + popover->size().height() + cutoffMargin > viewportRect.bottom())
7158 portLeftInScene = QPoint(portLeftInScene.x(), viewportRect.bottom() - popover->size().height() - cutoffMargin);
7160 QPoint popoverLeftInView = views()[0]->mapFromScene(portLeftInScene);
7162 const QPoint offset = QPoint(12, 6);
7164 QPoint popoverTopLeft = popoverLeftInView + offset;
7165 popover->move(popoverTopLeft);
7168 activePortPopovers[portID] = popover;
7170 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7197 VUserLog(
"%s: Close popover for %s",
7202 map<string, VuoPortPopover *>::iterator i = activePortPopovers.find(portID);
7203 if (i != activePortPopovers.end())
7205 popover = i->second;
7206 activePortPopovers.erase(i);
7212 popover->deleteLater();
7215 bool isInput =
false;
7223 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7224 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7226 dispatch_async(topLevelComposition->runCompositionQueue, ^{
7227 if (topLevelComposition->isRunningThreadUnsafe())
7230 topLevelComposition->runner->unsubscribeFromInputPortTelemetry(thisCompositionIdentifier, portID) :
7231 topLevelComposition->runner->unsubscribeFromOutputPortTelemetry(thisCompositionIdentifier, portID));
7241void VuoEditorComposition::disablePopoverForPortThreadSafe(
string portID)
7243 dispatch_sync(activePortPopoversQueue, ^{
7253 disablePortPopovers();
7264 errorPopover->hide();
7265 errorPopover->deleteLater();
7268 errorPopovers.clear();
7277 dispatch_sync(activePortPopoversQueue, ^{
7278 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7279 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7281 string portID = i->first;
7282 bool shouldDisable = false;
7285 shouldDisable = true;
7288 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7289 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7304 dispatch_sync(activePortPopoversQueue, ^{
7305 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7306 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7308 string portID = i->first;
7310 bool foundPort = identifierCache->doForPortWithIdentifier(portID, [](VuoPort *port) {});
7323 if (recordWhichPopoversClosed)
7324 portsWithPopoversClosedAtLastEvent.clear();
7326 dispatch_sync(activePortPopoversQueue, ^{
7327 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7328 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7330 string portID = i->first;
7331 bool shouldDisable = false;
7334 shouldDisable = true;
7337 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7338 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7348 portsWithPopoversClosedAtLastEvent.insert(portID);
7360 moveDetachedPortPopoversBy(dx, dy);
7361 moveErrorPopoversBy(dx, dy);
7367void VuoEditorComposition::moveErrorPopoversBy(
int dx,
int dy)
7370 errorPopover->move(errorPopover->pos().x()+dx, errorPopover->pos().y()+dy);
7376void VuoEditorComposition::moveDetachedPortPopoversBy(
int dx,
int dy)
7378 dispatch_sync(activePortPopoversQueue, ^{
7379 map<string, VuoPortPopover *> portPopovers = activePortPopovers;
7380 for (map<string, VuoPortPopover *>::iterator i = portPopovers.begin(); i != portPopovers.end(); ++i)
7382 VuoPortPopover *popover = i->second;
7383 if (popover && popover->getDetached())
7384 popover->move(popover->pos().x()+dx, popover->pos().y()+dy);
7392void VuoEditorComposition::setPopoversHideOnDeactivate(
bool shouldHide)
7394 dispatch_sync(activePortPopoversQueue, ^{
7395 auto portPopovers = activePortPopovers;
7396 for (
auto i : portPopovers)
7401 id nsWindow = (id)VuoPopover::getWindowForPopover(popover);
7402 ((void (*)(id, SEL, BOOL))objc_msgSend)(nsWindow, sel_getUid(
"setHidesOnDeactivate:"), shouldHide);
7414 dispatch_sync(activePortPopoversQueue, ^{
7415 for (map<string, VuoPortPopover *>::iterator i = activePortPopovers.begin(); i != activePortPopovers.end(); ++i)
7417 string portID = i->first;
7418 VuoPortPopover *popover = i->second;
7419 bool shouldUpdate = false;
7422 shouldUpdate = true;
7425 identifierCache->doForPortWithIdentifier(portID, [&shouldUpdate, node](VuoPort *port) {
7426 shouldUpdate = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7431 QMetaObject::invokeMethod(popover,
"updateTextAndResize", Qt::QueuedConnection);
7448 string popoverCompositionIdentifier,
7459 string portSummary = (isInput ?
7463 dispatch_async(popoverComposition->activePortPopoversQueue, ^{
7464 VuoPortPopover *popover = popoverComposition->getActivePopoverForPort(portID);
7467 QMetaObject::invokeMethod(popover,
"updateDataValueImmediately", Qt::QueuedConnection, Q_ARG(QString, portSummary.c_str()));
7468 QMetaObject::invokeMethod(popover,
"setCompositionRunning", Qt::QueuedConnection, Q_ARG(bool, true), Q_ARG(bool, false));
7482 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7484 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7485 if (topLevelComposition->isRunningThreadUnsafe())
7486 topLevelComposition->updateDataInPortPopoverFromRunningTopLevelComposition(this, thisCompositionIdentifier, portID);
7496 bool receivedEvent,
bool receivedData,
string dataSummary)
7500 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7501 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7503 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7504 Q_ARG(bool, receivedEvent),
7505 Q_ARG(bool, receivedData),
7506 Q_ARG(QString, dataSummary.c_str()));
7509 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7517 bool sentEvent,
bool sentData,
string dataSummary)
7521 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7522 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7524 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7525 Q_ARG(bool, sentEvent),
7526 Q_ARG(bool, sentData),
7527 Q_ARG(QString, dataSummary.c_str()));
7530 if (matchingComposition->showEventsMode && sentEvent)
7535 port->getRenderer()->setFiredEvent();
7536 matchingComposition->animatePort(port->getRenderer());
7541 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7552 dispatch_async(matchingComposition->runCompositionQueue, ^{
7553 if (matchingComposition->isRunningThreadUnsafe())
7555 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7556 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7558 QMetaObject::invokeMethod(popover,
"incrementDroppedEventCount", Qt::QueuedConnection);
7563 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7574 if (matchingComposition->showEventsMode)
7576 dispatch_async(this->runCompositionQueue, ^{
7577 if (this->isRunningThreadUnsafe())
7586 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7597 if (matchingComposition->showEventsMode)
7599 dispatch_async(this->runCompositionQueue, ^{
7600 if (this->isRunningThreadUnsafe())
7609 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7638 return showEventsMode;
7646 this->showEventsMode = showEventsMode;
7654 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7655 if (topLevelComposition->isRunningThreadUnsafe())
7656 topLevelComposition->runner->subscribeToEventTelemetry(thisCompositionIdentifier);
7659 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, subscribe);
7667 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7668 if (topLevelComposition->isRunningThreadUnsafe())
7669 topLevelComposition->runner->unsubscribeFromEventTelemetry(thisCompositionIdentifier);
7672 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, unsubscribe);
7708QGraphicsItemAnimation * VuoEditorComposition::setUpAnimationForPort(QGraphicsItemAnimation *animation,
VuoRendererPort *port)
7716 animatedPort->setZValue(VuoRendererItem::triggerAnimationZValue);
7719 animation->setItem(animatedPort);
7720 animation->setScaleAt(0.0, 1, 1);
7721 animation->setScaleAt(0.999, 3, 3);
7722 animation->setScaleAt(1.0, 1, 1);
7724 QTimeLine *animationTimeline = animation->timeLine();
7725 animationTimeline->setFrameRange(0, 100);
7726 animationTimeline->setUpdateInterval(showEventsModeUpdateInterval);
7727 animationTimeline->setCurveShape(QTimeLine::LinearCurve);
7729 preparedAnimations.insert(animation);
7730 animationForTimeline[animation->timeLine()] = animation;
7732 connect(animationTimeline, &QTimeLine::valueChanged,
this, &VuoEditorComposition::updatePortAnimation);
7733 connect(animationTimeline, &QTimeLine::finished,
this, &VuoEditorComposition::endPortAnimation);
7743 dispatch_async(dispatch_get_main_queue(), ^{
7744 QGraphicsItemAnimation *animation = getAvailableAnimationForPort(port);
7750 if (animation->timeLine()->state() == QTimeLine::Running)
7751 animation->timeLine()->setCurrentTime(0);
7755 animatedPort->setPos(port->pos());
7756 animatedPort->setVisible(
true);
7757 animation->timeLine()->start();
7766QGraphicsItemAnimation * VuoEditorComposition::getAvailableAnimationForPort(
VuoRendererPort *port)
7768 vector<QGraphicsItemAnimation *> animations = port->
getAnimations();
7770 QGraphicsItemAnimation *mostAdvancedAnimation = NULL;
7771 qreal maxPercentAdvanced = -1;
7773 for (
int i = 0; i < animations.size(); ++i)
7775 QGraphicsItemAnimation *animation = animations[i];
7776 bool animationPrepared = (preparedAnimations.find(animation) != preparedAnimations.end());
7777 bool animationRunning = (animation->timeLine()->state() == QTimeLine::Running);
7779 if (! animationPrepared)
7780 return setUpAnimationForPort(animation, port);
7782 else if (! animationRunning)
7787 qreal percentAdvanced = animation->timeLine()->currentValue();
7788 if (percentAdvanced > maxPercentAdvanced)
7790 mostAdvancedAnimation = animation;
7791 maxPercentAdvanced = percentAdvanced;
7797 return (maxPercentAdvanced >= 0.5? mostAdvancedAnimation : NULL);
7805void VuoEditorComposition::updatePortAnimation(qreal value)
7807 QTimeLine *animationTimeline = (QTimeLine *)sender();
7808 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7810 const qreal multiplier = 1000.;
7818void VuoEditorComposition::endPortAnimation(
void)
7820 QTimeLine *animationTimeline = (QTimeLine *)sender();
7821 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7823 animatedPort->setVisible(
false);
7829void VuoEditorComposition::setDisableDragStickiness(
bool disable)
7831 this->dragStickinessDisabled = disable;
7839 this->ignoreApplicationStateChangeEvents = ignore;
7849 this->popoverEventsEnabled = enable;
7858 refreshComponentAlphaLevelTimer->start();
7866 refreshComponentAlphaLevelTimer->stop();
7889 if (!activeProtocol)
7896 if (!
getBase()->
getCompiler()->getCachedGraph()->mayEventsReachPublishedOutputPorts())
7898 QString errorHeadline = tr(
"<b>This composition doesn't send any images to <code>outputImage</code>.</b>");
7899 QString errorDetails = tr(
"<p>To export, your composition should use the data and events from the published input ports "
7900 "to output a stream of images through the <code>outputImage</code> published output port.</p>");
7902 if (isExportingMovie)
7903 errorDetails.append(
"<p>Alternatively, you can record a realtime movie by running the composition and selecting File > Start Recording.</p>");
7906 QMessageBox messageBox(window);
7907 messageBox.setWindowFlags(Qt::Sheet);
7908 messageBox.setWindowModality(Qt::WindowModal);
7910 messageBox.setTextFormat(Qt::RichText);
7912 messageBox.setStandardButtons(QMessageBox::Help | QMessageBox::Ok);
7913 messageBox.setButtonText(QMessageBox::Help, tr(
"Open an Example"));
7914 messageBox.setButtonText(QMessageBox::Ok, tr(
"OK"));
7915 messageBox.setDefaultButton(QMessageBox::Ok);
7917 messageBox.setText(errorHeadline);
7918 messageBox.setInformativeText(
"<style>p{" + fonts->
getCSS(fonts->
dialogBodyFont()) +
"}</style>" + errorDetails);
7920 if (messageBox.exec() == QMessageBox::Help)
7922 map<QString, QString> examples =
static_cast<VuoEditor *
>(qApp)->getExampleCompositionsForProtocol(activeProtocol);
7923 map<QString, QString>::iterator i = examples.begin();
7924 if (i != examples.end())
7954 string dir, file, ext;
7968 if (! customizedName.empty())
7969 return QString::fromStdString(customizedName);
7987 string fileNameContentPart = (fileNameParts.size() >= 2 && fileNameParts[fileNameParts.size()-1] ==
"vuo"?
7988 fileNameParts[fileNameParts.size()-2] :
7989 (fileNameParts.size() >= 1? fileNameParts[fileNameParts.size()-1] :
""));
7992 if (QRegExp(
"\\s").indexIn(fileNameContentPart.c_str()) != -1)
7994 string formattedName = fileNameContentPart;
7995 if (formattedName.size() >= 1)
7996 formattedName[0] = toupper(formattedName[0]);
7998 return QString(formattedName.c_str());
8012 QStringList wordsInName = nodeSetName.split(QRegularExpression(
"\\."));
8013 if (wordsInName.size() < 2 || wordsInName[0] !=
"vuo")
8019 map<QString, QString> wordsToReformat;
8020 wordsToReformat[
"artnet"] =
"Art-Net";
8021 wordsToReformat[
"bcf2000"] =
"BCF2000";
8022 wordsToReformat[
"hid"] =
"HID";
8023 wordsToReformat[
"midi"] =
"MIDI";
8024 wordsToReformat[
"ndi"] =
"NDI";
8025 wordsToReformat[
"osc"] =
"OSC";
8026 wordsToReformat[
"rss"] =
"RSS";
8027 wordsToReformat[
"ui"] =
"UI";
8028 wordsToReformat[
"url"] =
"URL";
8030 QString nodeSetDisplayName =
"";
8031 for (
int i = 1; i < wordsInName.size(); ++i)
8033 QString currentWord = wordsInName[i];
8034 if (currentWord.size() >= 1)
8036 if (wordsToReformat.find(currentWord.toLower()) != wordsToReformat.end())
8037 currentWord = wordsToReformat.at(currentWord.toLower());
8039 currentWord[0] = currentWord[0].toUpper();
8041 nodeSetDisplayName += currentWord;
8043 if (i < wordsInName.size()-1)
8044 nodeSetDisplayName +=
" ";
8047 return nodeSetDisplayName;
8060 string formattedTypeName =
"";
8068 formattedTypeName =
"List of " + formattedInnerTypeName +
" elements";
8074 return formattedTypeName.c_str();
8093 return "Transform2D";
8095 return "Transform3D";
8103bool VuoEditorComposition::nodeSetMenuActionLessThan(QAction *action1, QAction *action2)
8105 QString item1Text = action1->text();
8106 QString item2Text = action2->text();
8109 const QString listPrefix =
"List of ";
8110 const QString builtInTypePrefix =
"Vuo";
8112 if (item1Text.startsWith(listPrefix))
8114 item1Text.remove(0, listPrefix.length());
8115 if (item1Text.startsWith(builtInTypePrefix))
8116 item1Text.remove(0, builtInTypePrefix.length());
8119 if (item2Text.startsWith(listPrefix))
8121 item2Text.remove(0, listPrefix.length());
8122 if (item2Text.startsWith(builtInTypePrefix))
8123 item2Text.remove(0, builtInTypePrefix.length());
8127 return (item1Text.compare(item2Text, Qt::CaseInsensitive) < 0);
8134bool VuoEditorComposition::itemHigherOnCanvas(QGraphicsItem *item1, QGraphicsItem *item2)
8136 qreal item1Y = item1->scenePos().y();
8137 qreal item2Y = item2->scenePos().y();
8139 qreal item1X = item1->scenePos().x();
8140 qreal item2X = item2->scenePos().x();
8142 if (item1Y == item2Y)
8143 return (item1X < item2X);
8145 return item1Y < item2Y;
8158 string originalGenericNode1ClassName, originalGenericNode2ClassName;
8174 vector<string> node1Keywords = node1->
getKeywords();
8175 vector<string> node2Keywords = node2->
getKeywords();
8187 node1Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8191 node2Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8193 set<string> node1KeywordSet(node1Keywords.begin(), node1Keywords.end());
8194 set<string> node2KeywordSet(node2Keywords.begin(), node2Keywords.end());
8196 set<string> nodeKeywordsIntersection;
8197 std::set_intersection(node1KeywordSet.begin(), node1KeywordSet.end(),
8198 node2KeywordSet.begin(), node2KeywordSet.end(),
8199 std::inserter(nodeKeywordsIntersection, nodeKeywordsIntersection.end()));
8201 set<string> nodeKeywordsUnion = node1KeywordSet;
8202 nodeKeywordsUnion.insert(node2KeywordSet.begin(), node2KeywordSet.end());
8205 if (nodeKeywordsUnion.size() == 0)
8209 double nodeSimilarity = nodeKeywordsIntersection.size()/(1.0*nodeKeywordsUnion.size());
8211 return nodeSimilarity;
8230VuoEditorComposition::~VuoEditorComposition()
8232 dispatch_sync(runCompositionQueue, ^{});
8233 dispatch_release(runCompositionQueue);
8235 preparedAnimations.clear();
8236 animationForTimeline.clear();
8238 delete identifierCache;
8264 vector<VuoRendererPort *> sortedPortsToPublish;
8265 foreach (
string portID, portsToPublish)
8269 sortedPortsToPublish.push_back(port->
getRenderer());
8271 std::sort(sortedPortsToPublish.begin(), sortedPortsToPublish.end(), itemHigherOnCanvas);
8273 map<string, string> publishedPortNames;
8280 string publishedPortName = (!specializedPublishedPortName.empty()?
8281 specializedPublishedPortName :
8290 return publishedPortNames;
8318void VuoEditorComposition::repositionPopover()
8323 const int cutoffMargin = 16;
8324 if (popover->pos().x()+popover->size().width()+cutoffMargin > views()[0]->viewport()->rect().right())
8325 popover->move(QPoint(views()[0]->viewport()->rect().right()-popover->size().width()-cutoffMargin, popover->pos().y()));
8327 if (popover->pos().y()+popover->size().height()+cutoffMargin > views()[0]->viewport()->rect().bottom())
8328 popover->move(QPoint(popover->pos().x(), views()[0]->viewport()->rect().bottom()-popover->size().height()-cutoffMargin));