60 #include <ApplicationServices/ApplicationServices.h>
61 #include <objc/objc-runtime.h>
64 const qreal VuoEditorComposition::nodeMoveRate = 15;
65 const qreal VuoEditorComposition::nodeMoveRateMultiplier = 4;
67 const qreal VuoEditorComposition::showEventsModeUpdateInterval = 1000/20.;
68 const int VuoEditorComposition::initialChangeNodeSuggestionCount = 10;
80 VuoEditorComposition_Pro();
83 this->window = window;
85 inputEditorManager = NULL;
86 activeProtocol = NULL;
88 runningComposition = NULL;
89 runningCompositionActiveDriver = NULL;
90 runningCompositionLibraries = NULL;
91 stopRequested =
false;
92 duplicateOnNextMouseMove =
false;
93 duplicationDragInProgress =
false;
94 duplicationCancelled =
false;
95 cursorPosBeforeDuplicationDragMove = QPointF(0,0);
96 cableInProgress = NULL;
97 cableInProgressWasNew =
false;
98 cableInProgressShouldBeWireless =
false;
99 portWithDragInitiated = NULL;
100 cableWithYankInitiated = NULL;
101 menuSelectionInProgress =
false;
102 previousNearbyItem = NULL;
103 dragStickinessDisabled =
false;
104 ignoreApplicationStateChangeEvents =
false;
105 popoverEventsEnabled =
true;
106 runCompositionQueue = dispatch_queue_create(
"org.vuo.editor.run", NULL);
107 activePortPopoversQueue = dispatch_queue_create(
"org.vuo.editor.popovers", NULL);
109 errorMarkingUpdatesEnabled =
true;
110 triggerPortToRefire =
"";
112 contextMenuDeleteSelected =
new QAction(NULL);
113 contextMenuHideSelectedCables =
new QAction(NULL);
114 contextMenuRenameSelected =
new QAction(NULL);
115 contextMenuRefactorSelected =
new QAction(NULL);
116 contextMenuPublishPort =
new QAction(NULL);
117 contextMenuDeleteCables =
new QAction(NULL);
118 contextMenuHideCables =
new QAction(NULL);
119 contextMenuUnhideCables =
new QAction(NULL);
120 contextMenuFireEvent =
new QAction(NULL);
121 contextMenuAddInputPort =
new QAction(NULL);
122 contextMenuRemoveInputPort =
new QAction(NULL);
123 contextMenuSetPortConstant =
new QAction(NULL);
124 contextMenuEditSelectedComments =
new QAction(NULL);
126 contextMenuChangeNode = NULL;
127 contextMenuSpecializeGenericType = NULL;
129 contextMenuFireEvent->setText(tr(
"Fire Event"));
130 contextMenuHideSelectedCables->setText(tr(
"Hide"));
131 contextMenuRenameSelected->setText(tr(
"Rename…"));
132 contextMenuRefactorSelected->setText(tr(
"Package as Subcomposition"));
133 contextMenuAddInputPort->setText(tr(
"Add Input Port"));
134 contextMenuRemoveInputPort->setText(tr(
"Remove Input Port"));
135 contextMenuSetPortConstant->setText(tr(
"Edit Value…"));
136 contextMenuEditSelectedComments->setText(tr(
"Edit…"));
143 connect(contextMenuDeleteCables, &QAction::triggered,
this, &VuoEditorComposition::deleteConnectedCables);
144 connect(contextMenuHideCables, &QAction::triggered,
this, &VuoEditorComposition::hideConnectedCables);
145 connect(contextMenuUnhideCables, &QAction::triggered,
this, &VuoEditorComposition::unhideConnectedCables);
146 connect(contextMenuFireEvent, &QAction::triggered,
this,
static_cast<void (
VuoEditorComposition::*)()
>(&VuoEditorComposition::fireTriggerPortEvent));
147 connect(contextMenuAddInputPort, &QAction::triggered,
this, &VuoEditorComposition::addInputPort);
148 connect(contextMenuRemoveInputPort, &QAction::triggered,
this, &VuoEditorComposition::removeInputPort);
149 connect(contextMenuEditSelectedComments, &QAction::triggered,
this, &VuoEditorComposition::editSelectedComments);
154 connect(contextMenuSetPortConstant, &QAction::triggered,
this, &VuoEditorComposition::setPortConstant, Qt::QueuedConnection);
161 QAction *action =
new QAction(label,
this);
162 connect(action, &QAction::triggered, [=](){
165 contextMenuThrottlingActions.append(action);
176 QAction *action =
new QAction(label,
this);
177 connect(action, &QAction::triggered, [=](){
183 QColor fill(0,0,0,0);
185 if (tint != VuoNode::TintNone)
195 p.drawEllipse(3, 3, 10, 10);
197 action->setIcon(*icon);
201 contextMenuTintActions.append(action);
203 addTintAction(tr(
"Yellow"), VuoNode::TintYellow);
204 addTintAction(tr(
"Tangerine"), VuoNode::TintTangerine);
205 addTintAction(tr(
"Orange"), VuoNode::TintOrange);
206 addTintAction(tr(
"Magenta"), VuoNode::TintMagenta);
207 addTintAction(tr(
"Violet"), VuoNode::TintViolet);
208 addTintAction(tr(
"Blue"), VuoNode::TintBlue);
209 addTintAction(tr(
"Cyan"), VuoNode::TintCyan);
210 addTintAction(tr(
"Green"), VuoNode::TintGreen);
211 addTintAction(tr(
"Lime"), VuoNode::TintLime);
212 addTintAction(tr(
"None"), VuoNode::TintNone);
216 this->refreshComponentAlphaLevelTimer =
new QTimer(
this);
217 this->refreshComponentAlphaLevelTimer->setObjectName(
"VuoEditorComposition::refreshComponentAlphaLevelTimer");
218 refreshComponentAlphaLevelTimer->setInterval(showEventsModeUpdateInterval);
220 setShowEventsMode(
false);
236 connect(
static_cast<VuoEditor *
>(qApp), &VuoEditor::focusChanged,
this, &VuoEditorComposition::updatePopoversForActiveWindowChange, Qt::QueuedConnection);
238 setPopoversHideOnDeactivate(
true);
241 setPopoversHideOnDeactivate(
false);
253 this->compiler = compiler;
269 this->moduleManager = moduleManager;
278 return moduleManager;
288 this->inputEditorManager = inputEditorManager;
298 return this->inputEditorManager;
315 setCustomConstantsForNewNode(rn);
332 __block
bool isAllowed =
true;
335 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
337 bool nodeIsThisComposition = (compositionModuleKey == nodeClass->
getBase()->
getClassName());
340 auto iter = std::find_if(dependencies.begin(), dependencies.end(), [=](
const string &d){ return d == compositionModuleKey; });
341 bool nodeContainsThisComposition = (iter != dependencies.end());
343 isAllowed = ! (nodeIsThisComposition || nodeContainsThisComposition);
352 compiler->
createNode(nodeClass, title, x, y));
370 vector<string> inputPortClassNames;
371 vector<string> outputPortClassNames;
376 inputPortClassNames.push_back(portClass->
getName());
379 outputPortClassNames.push_back(portClass->
getName());
383 dummyNodeClass->
newNode(modelNode) :
392 void VuoEditorComposition::setCustomConstantsForNewNode(
VuoRendererNode *newNode)
400 QString currentYear = QString::number(QDateTime::currentDateTime().date().year());
424 disablePortPopovers(rn);
449 map<VuoCable *, VuoPort *> cablesToTransferFromPort;
450 map<VuoCable *, VuoPort *> cablesToTransferToPort;
451 set<VuoCable *> cablesToRemove;
455 vector<VuoRendererInputDrawer *> attachedDrawers;
456 vector<VuoRendererNode *> collapsedTypecasts;
458 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
464 collapsedTypecasts.push_back(typecastNode);
472 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
477 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
482 attachedDrawers.push_back(attachedDrawer);
489 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferFromPort.begin(); i != cablesToTransferFromPort.end(); ++i)
490 i->first->getRenderer()->setFrom(newNode, i->second);
491 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferToPort.begin(); i != cablesToTransferToPort.end(); ++i)
492 i->first->getRenderer()->setTo(newNode, i->second);
493 foreach (
VuoCable *cable, cablesToRemove)
510 if (! (oldDataType == newDataType && oldDataType && !
dynamic_cast<VuoGenericType *
>(oldDataType)) )
514 string oldConstantValue;
516 oldConstantValue = oldInputPort->getRenderer()->getConstantAsString();
518 oldConstantValue = oldInputPort->getRawInitialValue();
550 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
563 disablePortPopovers(oldNode);
591 if (cableHidden && emitHiddenCableNotification)
603 if (cableHidden && emitHiddenCableNotification)
629 set<VuoRendererNode *> &createdNodes,
630 set<VuoRendererCable *> &createdCables)
644 foreach (QGraphicsItem *component, addedComponents)
647 if (rn && !createButDoNotAdd)
651 return addedComponents;
663 set<QGraphicsItem *> dependentAttachments;
667 for(
unsigned int i = 0; i < inputPorts.size(); ++i)
669 set<VuoRendererInputAttachment *> portUpstreamAttachments = inputPorts[i]->getRenderer()->getAllUnderlyingUpstreamInputAttachments();
670 dependentAttachments.insert(portUpstreamAttachments.begin(), portUpstreamAttachments.end());
675 if (nodeAsAttachment && includeCoattachments)
678 dependentAttachments.insert(coattachment->
getRenderer());
681 return dependentAttachments;
695 set<string> selectedNodeIDs;
702 set<string> selectedCommentIDs;
709 set<string> selectedCableIDs;
719 foreach (QGraphicsItem *item, items())
726 if (!currentNodeID.empty() && (selectedNodeIDs.find(currentNodeID) != selectedNodeIDs.end()))
727 item->setSelected(
true);
735 if (!currentCommentID.empty() && (selectedCommentIDs.find(currentCommentID) != selectedCommentIDs.end()))
736 item->setSelected(
true);
744 if (!currentCableID.empty() && (selectedCableIDs.find(currentCableID) != selectedCableIDs.end()))
745 item->setSelected(
true);
768 if ((portName ==
"expression") &&
792 if (inputVariablesBeforeEditing != inputVariablesAfterEditing)
798 QList<QGraphicsItem *> attachmentsToRemove;
805 attachmentsToRemove.append(attachment);
817 if (oldValueList && oldDictionary && oldKeyList)
819 set<VuoRendererNode *> nodesToAdd;
820 set<VuoRendererCable *> cablesToAdd;
838 undoStack->push(
new VuoCommandReplaceNode(oldValueList, newValueList, editorWindow,
"Set Port Constant",
false,
false));
839 undoStack->push(
new VuoCommandReplaceNode(oldKeyList, newKeyList, editorWindow,
"Set Port Constant",
false,
true));
840 undoStack->push(
new VuoCommandReplaceNode(oldDictionary, newDictionary, editorWindow,
"Set Port Constant",
false,
true));
844 cable->
setFrom(
nullptr,
nullptr);
845 cable->
setTo(
nullptr,
nullptr);
864 if (commandDescription.empty())
867 commandDescription =
"Reset";
869 commandDescription =
"Delete";
872 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
881 if (commandDescription.empty())
882 commandDescription =
"Delete";
884 QList<QGraphicsItem *> selectedNodes;
886 selectedNodes.append(node);
926 void VuoEditorComposition::insertNode()
928 QAction *sender = (QAction *)QObject::sender();
929 QPair<QPointF, QString> pair = sender->data().value<QPair<QPointF, QString> >();
931 QList<QGraphicsItem *> newNodes;
938 newNodes.append(newNode);
946 void VuoEditorComposition::insertComment()
948 QAction *sender = (QAction *)QObject::sender();
949 QPointF scenePos = sender->data().value<QPointF>();
957 void VuoEditorComposition::insertSubcomposition()
959 QAction *sender = (QAction *)QObject::sender();
960 QPointF scenePos = sender->data().value<QPointF>();
971 QAction *sender = (QAction *)QObject::sender();
986 void VuoEditorComposition::deleteConnectedCables()
988 QAction *sender = (QAction *)QObject::sender();
991 QList<QGraphicsItem *> cablesToRemove;
1022 void VuoEditorComposition::hideConnectedCables()
1024 QAction *sender = (QAction *)QObject::sender();
1026 set<VuoRendererCable *> cablesToHide;
1057 void VuoEditorComposition::unhideConnectedCables()
1059 QAction *sender = (QAction *)QObject::sender();
1061 set<VuoRendererCable *> cablesToUnhide;
1090 void VuoEditorComposition::fireTriggerPortEvent()
1092 QAction *sender = (QAction *)QObject::sender();
1094 fireTriggerPortEvent(port->
getBase());
1110 if (triggerPortToRefire.empty())
1113 VuoPort *triggerPort =
nullptr;
1126 if (portID != this->triggerPortToRefire)
1128 this->triggerPortToRefire = portID;
1137 void VuoEditorComposition::setPortConstant()
1139 QAction *sender = (QAction *)QObject::sender();
1150 void VuoEditorComposition::setPortConstantToValue(
VuoRendererPort *port,
string value)
1160 void VuoEditorComposition::specializeGenericPortType()
1162 QAction *sender = (QAction *)QObject::sender();
1163 QList<QVariant> portAndSpecializedType= sender->data().toList();
1165 QString specializedTypeName = portAndSpecializedType[1].toString();
1177 emit
specializePort(port, specializedTypeName.toUtf8().constData());
1184 void VuoEditorComposition::unspecializePortType()
1186 QAction *sender = (QAction *)QObject::sender();
1210 portToUnspecialize,
true);
1211 map<VuoNode *, set<VuoPort *> > portsToUnspecializeForNode;
1212 for (
VuoPort *connectedPort : connectedPotentiallyGenericPorts)
1218 if (isPortCurrentlyRevertible(connectedPort->getRenderer()))
1219 portsToUnspecializeForNode[node].insert(connectedPort);
1222 for (map<
VuoNode *, set<VuoPort *> >::iterator i = portsToUnspecializeForNode.begin(); i != portsToUnspecializeForNode.end(); ++i)
1225 set<VuoPort *> ports = i->second;
1227 if (shouldOutputNodesToReplace)
1230 set<VuoPortClass *> portClasses;
1231 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1232 portClasses.insert((*j)->getClass());
1235 nodesToReplace[node] = unspecializedNodeClassName;
1240 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1245 bool areEndsCompatible =
false;
1248 areEndsCompatible =
true;
1253 if (portOnOtherEnd && isPortCurrentlyRevertible(portOnOtherEnd->
getRenderer()))
1258 if (specializedNodeClassOnOtherEnd)
1261 if (! typeOnOtherEnd ||
dynamic_cast<VuoGenericType *
>(typeOnOtherEnd))
1262 areEndsCompatible =
true;
1267 if (! areEndsCompatible && (cable != cableInProgress))
1268 cablesToDelete.insert(cable);
1277 void VuoEditorComposition::fireTriggerPortEvent(
VuoPort *port)
1285 string runningTriggerPortIdentifier =
"";
1286 bool isTriggerPort =
false;
1294 isTriggerPort =
true;
1312 runningTriggerPortIdentifier.c_str());
1315 bool manuallyFirableInputPortChanged = (oldManuallyFirableInputPort != newManuallyFirableInputPort);
1318 auto fireIfRunning = ^void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
1320 dispatch_async(topLevelComposition->runCompositionQueue, ^{
1321 if (topLevelComposition->isRunningThreadUnsafe())
1323 topLevelComposition->runner->fireTriggerPortEvent(thisCompositionIdentifier, runningTriggerPortIdentifier);
1328 if (! (this->showEventsMode && isTriggerPort) )
1329 this->animatePort(port->getRenderer());
1334 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1335 if (
this == topLevelComposition || ! manuallyFirableInputPortChanged)
1339 if (! newSnapshot.empty() && manuallyFirableInputPortChanged)
1340 updateRunningComposition(oldSnapshot, newSnapshot);
1342 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1348 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
1350 moduleManager->doNextTimeNodeClassIsLoaded(nodeClassName, ^{
1351 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1352 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1356 if (! newSnapshot.empty())
1357 updateRunningComposition(oldSnapshot, newSnapshot);
1362 setTriggerPortToRefire(port);
1369 void VuoEditorComposition::addInputPort()
1371 QAction *sender = (QAction *)QObject::sender();
1380 void VuoEditorComposition::removeInputPort()
1382 QAction *sender = (QAction *)QObject::sender();
1391 void VuoEditorComposition::swapNode()
1393 QAction *sender = (QAction *)QObject::sender();
1394 QList<QVariant> nodeAndReplacementType= sender->data().toList();
1396 QString newNodeClassName = nodeAndReplacementType[1].toString();
1407 for (set<VuoRendererNode *>::iterator i = selectedNodes.begin(); i != selectedNodes.end(); ++i)
1417 void VuoEditorComposition::editSelectedComments()
1421 for (set<VuoRendererComment *>::iterator i = selectedComments.begin(); i != selectedComments.end(); ++i)
1430 set<VuoRendererCable *> internalCables;
1432 for (QList<QGraphicsItem *>::iterator i = subcompositionComponents.begin(); i != subcompositionComponents.end(); ++i)
1434 QGraphicsItem *compositionComponent = *i;
1439 for (set<VuoCable *>::iterator cable = connectedCables.begin(); cable != connectedCables.end(); ++cable)
1441 VuoNode *fromNode = (*cable)->getFromNode();
1442 VuoNode *toNode = (*cable)->getToNode();
1444 if (fromNode && toNode && subcompositionComponents.contains(fromNode->
getRenderer()) && subcompositionComponents.contains(toNode->
getRenderer()))
1445 internalCables.insert((*cable)->getRenderer());
1450 return internalCables;
1458 return cableInProgress;
1467 return cableInProgressWasNew;
1475 return menuSelectionInProgress;
1483 QList<QGraphicsItem *> compositionComponents = items();
1484 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1486 QGraphicsItem *compositionComponent = *i;
1490 compositionComponent->setSelected(
true);
1499 QList<QGraphicsItem *> compositionComponents = items();
1500 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1502 QGraphicsItem *compositionComponent = *i;
1505 rcomment->setSelected(
true);
1514 QList<QGraphicsItem *> compositionComponents = items();
1515 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1517 QGraphicsItem *compositionComponent = *i;
1518 compositionComponent->setSelected(
false);
1525 void VuoEditorComposition::openSelectedEditableNodes()
1530 QString actionText, sourcePath;
1543 moveItemsBy(selectedNodes, selectedComments, dx, dy,
false);
1549 void VuoEditorComposition::moveNodesBy(set<VuoRendererNode *> nodes, qreal dx, qreal dy,
bool movedByDragging)
1551 set<VuoRendererComment *> comments;
1552 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1558 void VuoEditorComposition::moveCommentsBy(set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1560 set<VuoRendererNode *> nodes;
1561 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1567 void VuoEditorComposition::moveItemsBy(set<VuoRendererNode *> nodes, set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1569 emit
itemsMoved(nodes, comments, dx, dy, movedByDragging);
1571 for (set<VuoRendererNode *>::iterator it = nodes.begin(); it != nodes.end(); ++it)
1572 (*it)->updateConnectedCableGeometry();
1578 void VuoEditorComposition::resizeCommentBy(
VuoRendererComment *comment, qreal dx, qreal dy)
1588 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1589 set<VuoRendererNode *> selectedNodes;
1590 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1592 QGraphicsItem *compositionComponent = *i;
1595 selectedNodes.insert(rn);
1598 return selectedNodes;
1606 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1607 set<VuoRendererComment *> selectedComments;
1608 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1610 QGraphicsItem *compositionComponent = *i;
1613 selectedComments.insert(rc);
1616 return selectedComments;
1624 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1625 set<VuoRendererCable *> selectedCables;
1626 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1628 QGraphicsItem *compositionComponent = *i;
1631 selectedCables.insert(rc);
1634 return selectedCables;
1642 const QMimeData *mimeData =
event->mimeData();
1643 bool disablePortHoverHighlighting =
true;
1646 if (mimeData->hasFormat(
"text/uri-list"))
1648 QList<QUrl> urls = mimeData->urls();
1652 if (portAtDropLocation)
1654 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1656 disablePortHoverHighlighting =
false;
1658 event->setDropAction(Qt::IgnoreAction);
1662 bool dragIncludesDroppableFile =
false;
1663 foreach (QUrl url, urls)
1665 char *urlZ = strdup(url.path().toUtf8().constData());
1666 bool isSupportedDragNDropFile =
1680 if (isSupportedDragNDropFile)
1682 dragIncludesDroppableFile =
true;
1687 if (!dragIncludesDroppableFile)
1688 event->setDropAction(Qt::IgnoreAction);
1695 else if (mimeData->hasFormat(
"text/plain") || mimeData->hasFormat(
"text/scsv"))
1696 event->acceptProposedAction();
1700 event->setDropAction(Qt::IgnoreAction);
1704 updateHoverHighlighting(event->scenePos(), disablePortHoverHighlighting);
1712 event->acceptProposedAction();
1728 const QMimeData *mimeData =
event->mimeData();
1731 if (mimeData->hasFormat(
"text/uri-list"))
1739 if (topCompositionPath.empty())
1741 QDir compositionDir(QDir(topCompositionPath.c_str()).canonicalPath());
1746 QList<QGraphicsItem *> newNodes;
1747 QList<QUrl> urls = mimeData->urls();
1751 if (portAtDropLocation)
1753 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1756 QString filePath = (useAbsoluteFilePaths? urls[0].path() : compositionDir.relativeFilePath(urls[0].path()));
1757 string constantValue =
"\"" + string(filePath.toUtf8().constData()) +
"\"";
1768 const int ySpacingForNewNodes = 11;
1769 int yOffsetForPreviousNewNode = -1 * ySpacingForNewNodes;
1771 foreach (QUrl url, urls)
1773 QStringList targetNodeClassNames;
1774 char *urlZ = strdup(url.path().toUtf8().constData());
1777 targetNodeClassNames +=
"vuo.image.fetch";
1781 targetNodeClassNames +=
"vuo.video.play";
1783 targetNodeClassNames +=
"vuo.video.decodeImage";
1786 targetNodeClassNames +=
"vuo.scene.fetch";
1788 targetNodeClassNames +=
"vuo.audio.file.play";
1790 targetNodeClassNames +=
"vuo.image.project.dome";
1792 targetNodeClassNames +=
"vuo.rss.fetch";
1794 targetNodeClassNames +=
"vuo.tree.fetch.json";
1796 targetNodeClassNames +=
"vuo.tree.fetch.xml";
1798 targetNodeClassNames +=
"vuo.table.fetch";
1800 targetNodeClassNames +=
"vuo.data.fetch";
1802 targetNodeClassNames +=
"vuo.app.launch";
1804 targetNodeClassNames +=
"vuo.file.list";
1808 QString selectedNodeClassName =
"";
1809 if (targetNodeClassNames.size() == 1)
1810 selectedNodeClassName = targetNodeClassNames[0];
1811 else if (targetNodeClassNames.size() > 1)
1813 QMenu nodeMenu(views()[0]->viewport());
1814 nodeMenu.setSeparatorsCollapsible(
false);
1816 foreach (QString nodeClassName, targetNodeClassNames)
1819 string nodeTitle = (nodeClass? nodeClass->
getBase()->
getDefaultTitle() : nodeClassName.toUtf8().constData());
1821 QAction *nodeAction = nodeMenu.addAction(tr(
"Insert \"%1\" Node").arg(nodeTitle.c_str()));
1822 nodeAction->setData(nodeClassName);
1825 menuSelectionInProgress =
true;
1826 QAction *selectedNode = nodeMenu.exec(QCursor::pos());
1827 menuSelectionInProgress =
false;
1829 selectedNodeClassName = (selectedNode? selectedNode->data().toString().toUtf8().constData() :
"");
1832 if (!selectedNodeClassName.isEmpty())
1835 event->scenePos().x(),
1836 event->scenePos().y() + yOffsetForPreviousNewNode + ySpacingForNewNodes);
1845 QString filePath = (useAbsoluteFilePaths? url.path() : compositionDir.relativeFilePath(url.path()));
1848 newNodes.append(newNode);
1850 yOffsetForPreviousNewNode += newNode->
boundingRect().height();
1851 yOffsetForPreviousNewNode += ySpacingForNewNodes;
1857 if (newNodes.size() > 0)
1869 else if (mimeData->hasFormat(
"text/scsv"))
1871 event->setDropAction(Qt::CopyAction);
1874 QByteArray scsvData =
event->mimeData()->data(
"text/scsv");
1875 QString scsvText = QString::fromUtf8(scsvData);
1876 QStringList nodeClassNames = scsvText.split(
';');
1881 int snapDelta = nextYPos - startPos.y();
1883 QList<QGraphicsItem *> newNodes;
1884 for (QStringList::iterator i = nodeClassNames.begin(); i != nodeClassNames.end(); ++i)
1892 int prevYPos = nextYPos;
1895 if (nextYPos <= prevYPos+newNode->boundingRect().height())
1898 newNodes.append((QGraphicsItem *)newNode);
1906 else if (mimeData->hasFormat(
"text/plain"))
1908 event->setDropAction(Qt::CopyAction);
1911 QList<QGraphicsItem *> newNodes;
1912 QStringList dropItems =
event->mimeData()->text().split(
'\n');
1913 QString nodeClassName = dropItems[0];
1917 QPoint hotSpot = (dropItems.size() >= 3? QPoint(dropItems[1].toInt(), dropItems[2].toInt()) : QPoint(0,0));
1919 event->scenePos().x()-hotSpot.x()+1,
1922 newNodes.append(newNode);
1939 QGraphicsScene::sendEvent(nearbyItem, event);
1944 QGraphicsScene::mouseDoubleClickEvent(event);
1952 QPointF scenePos =
event->scenePos();
1955 if (event->button() == Qt::LeftButton)
1960 if (duplicationCancelled)
1962 QGraphicsItem *itemClickedDirectly = itemAt(scenePos, views()[0]->transform());
1968 duplicationCancelled =
false;
1969 correctForCancelledDuplication(event);
1980 mousePressEventNonLeftButton(event);
1989 bool eventHandled =
false;
2003 cableYankedDirectly = currentCable;
2011 if ((event->modifiers() & Qt::ControlModifier) && (currentPort->
getInput() || isTriggerPort))
2012 fireTriggerPortEvent(currentPort->
getBase());
2016 portWithDragInitiated = currentPort;
2017 cableWithYankInitiated = cableYankedDirectly;
2021 eventHandled =
true;
2028 duplicateOnNextMouseMove =
true;
2029 duplicationDragInProgress =
true;
2035 QGraphicsScene::mousePressEvent(event);
2036 eventHandled =
true;
2042 if (event->modifiers() & Qt::ControlModifier)
2043 nearbyItem->setSelected(! nearbyItem->isSelected());
2048 nearbyItem->setSelected(
true);
2052 eventHandled =
true;
2057 QGraphicsScene::mousePressEvent(event);
2064 void VuoEditorComposition::mousePressEventNonLeftButton(QGraphicsSceneMouseEvent *event)
2073 if (event->button() == Qt::RightButton)
2075 QGraphicsSceneContextMenuEvent
contextMenuEvent(QEvent::GraphicsSceneContextMenu);
2084 QGraphicsScene::mousePressEvent(event);
2095 void VuoEditorComposition::correctForCancelledDuplication(QGraphicsSceneMouseEvent *event)
2099 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
2100 pressEvent.setScenePos(event->scenePos());
2101 pressEvent.setButton(Qt::LeftButton);
2102 QApplication::sendEvent(
this, &pressEvent);
2104 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
2105 releaseEvent.setScenePos(event->scenePos());
2106 releaseEvent.setButton(Qt::LeftButton);
2107 QApplication::sendEvent(
this, &releaseEvent);
2119 Qt::KeyboardModifiers modifiers =
event->modifiers();
2120 bool optionKeyPressed = (modifiers & Qt::AltModifier);
2121 bool shiftKeyPressed = (modifiers & Qt::ShiftModifier);
2132 bool creatingNewCable =
false;
2133 bool disconnectingExistingCable =
false;
2134 bool duplicatingExistingCable =
false;
2148 fromPort = fixedPort;
2149 fromNode = fixedNode;
2150 creatingNewCable =
true;
2162 creatingNewCable =
true;
2169 if (optionKeyPressed)
2171 duplicatingExistingCable =
true;
2175 disconnectingExistingCable =
true;
2183 if (creatingNewCable)
2196 cableInProgressWasNew =
true;
2197 cableInProgressShouldBeWireless =
false;
2204 highlightEligibleEndpointsForCable(cableInProgress);
2209 else if (disconnectingExistingCable)
2212 if (cableYankedDirectly)
2213 cableInProgress = cableYankedDirectly->
getBase();
2219 cableInProgressWasNew =
false;
2230 highlightEligibleEndpointsForCable(cableInProgress);
2232 cableInProgress->
getRenderer()->setSelected(
true);
2235 else if (duplicatingExistingCable)
2239 if (cableYankedDirectly)
2240 cableToDuplicate = cableYankedDirectly->
getBase();
2257 cableInProgressWasNew =
true;
2258 cableInProgressShouldBeWireless = cableToDuplicate->
hasCompiler() &&
2266 highlightEligibleEndpointsForCable(cableInProgress);
2268 cableInProgress->
getRenderer()->setSelected(
true);
2273 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2286 if (event->button() == Qt::LeftButton)
2288 portWithDragInitiated = NULL;
2289 cableWithYankInitiated = NULL;
2290 duplicateOnNextMouseMove =
false;
2291 duplicationDragInProgress =
false;
2292 dragStickinessDisabled =
false;
2299 bool cableDragEnding = cableInProgress;
2300 if (cableDragEnding)
2301 concludeCableDrag(event);
2313 if ((event->modifiers() == Qt::NoModifier) &&
2314 (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2321 enablePopoverForNode(typecastNode);
2326 enableInactivePopoverForPort(port);
2332 if (!cableDragEnding && !node)
2336 if (!cableDragEnding)
2337 QGraphicsScene::mouseReleaseEvent(event);
2343 QGraphicsScene::mouseReleaseEvent(event);
2353 void VuoEditorComposition::concludeCableDrag(QGraphicsSceneMouseEvent *event)
2361 concludePublishedCableDrag(event);
2365 if (hasFeedbackErrors())
2383 bool completedCableConnection =
false;
2386 VuoCable *dataCableToDisplace = NULL;
2390 string typecastToInsert =
"";
2394 string specializedTypeName =
"";
2411 bool draggingPreviouslyPublishedCable = (!cableInProgressWasNew &&
2413 ((cableInProgress->
getToPort() == NULL) &&
2416 if (fixedPort && targetPort)
2424 if (recreatingSameConnection)
2434 bool preexistingCableWithMatchingDataCarryingStatus = (preexistingCable?
2436 cableInProgressExpectedToCarryData) :
2442 if (preexistingCable && !preexistingCableWithMatchingDataCarryingStatus &&
2453 if (!preexistingCableWithMatchingDataCarryingStatus &&
2455 selectBridgingSolution(fixedPort, targetPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert)))
2466 if (portToSpecialize == targetPort)
2467 portToSpecialize = adjustedTargetPort;
2469 targetPort = adjustedTargetPort;
2473 if (cableInProgressExpectedToCarryData &&
2475 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2476 typecastNodeToDelete = uncollapsedTypecast;
2481 if (preexistingCable)
2482 cableToReplace = preexistingCable;
2486 if (cableInProgressExpectedToCarryData)
2490 for (vector<VuoCable *>::iterator cable = previousConnectedCables.begin(); (! dataCableToDisplace) && (cable != previousConnectedCables.end()); ++cable)
2491 if ((((*cable)->getRenderer()->effectivelyCarriesData()) && (*cable) != cableToReplace))
2492 dataCableToDisplace = *cable;
2499 if (publishedDataConnections.size() > 0)
2500 portToUnpublish = targetPort;
2504 completedCableConnection =
true;
2508 else if (!preexistingCableWithMatchingDataCarryingStatus &&
2510 selectBridgingSolution(targetPort, fixedPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert)))
2513 completedCableConnection =
true;
2518 if (completedCableConnection)
2522 if (draggingPreviouslyPublishedCable)
2541 cableInProgressCopy->
setFrom(cableInProgressFromNode, cableInProgressFromPort);
2542 cableInProgressCopy->
setTo(cableInProgressToNode, cableInProgressToPort);
2549 cableInProgress = cableInProgressCopy;
2550 cableInProgressWasNew =
true;
2554 pair<VuoRendererCable *, VuoRendererCable *> cableArgs = std::make_pair((dataCableToDisplace? dataCableToDisplace->
getRenderer() : NULL),
2555 (cableToReplace? cableToReplace->
getRenderer() : NULL));
2556 pair<string, string> typeArgs = std::make_pair(typecastToInsert, specializedTypeName);
2557 pair<VuoRendererPort *, VuoRendererPort *> portArgs = std::make_pair(portToUnpublish, portToSpecialize);
2562 typecastNodeToDelete,
2569 if (cableInProgress)
2573 cableInProgress = NULL;
2587 void VuoEditorComposition::concludePublishedCableDrag(QGraphicsSceneMouseEvent *event)
2597 string typecastToInsert =
"";
2601 string specializedTypeName =
"";
2618 if (internalInputPort)
2621 forceEventOnlyPublication =
true;
2627 if (internalInputPort &&
2633 internalInputPort->
getBase()) &&
2636 if (recreatingSameConnection)
2644 ||
selectBridgingSolution(publishedInputPort, internalInputPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert))
2649 bool cableToReplaceHasMatchingDataCarryingStatus = (cableToReplace?
2651 cableInProgressExpectedToCarryData) :
2656 if (cableToReplace && cableToReplaceHasMatchingDataCarryingStatus)
2662 else if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus &&
2678 if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus)
2680 QList<QGraphicsItem *> removedComponents;
2681 removedComponents.append(cableToReplace->
getRenderer());
2690 if (typecastPort && typecastPort->scene())
2698 if (cableInProgressExpectedToCarryData &&
2700 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2702 QList<QGraphicsItem *> removedComponents;
2703 removedComponents.append(uncollapsedTypecast);
2710 forceEventOnlyPublication,
2711 (portToSpecialize? portToSpecialize->
getBase() : NULL),
2712 specializedTypeName,
2738 if (internalOutputPort)
2741 forceEventOnlyPublication =
true;
2747 if (internalOutputPort &&
2748 publishedOutputPort)
2754 ||
selectBridgingSolution(internalOutputPort, publishedOutputPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert))
2758 forceEventOnlyPublication,
2759 portToSpecialize? portToSpecialize->
getBase() : NULL,
2760 specializedTypeName,
2779 if (! cableInProgress)
2782 if (cableInProgressWasNew)
2786 QList<QGraphicsItem *> removedComponents;
2787 removedComponents.append(cableInProgress->
getRenderer());
2791 cableInProgress = NULL;
2804 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2809 cableInProgress = NULL;
2811 else if (cableInProgress)
2826 bool leftMouseButtonPressed = (
event->buttons() & Qt::LeftButton);
2842 if (cableInProgress || (! leftMouseButtonPressed))
2843 updateHoverHighlighting(event->scenePos());
2847 if (leftMouseButtonPressed)
2850 if ((! dragStickinessDisabled) && (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2854 dragStickinessDisabled =
true;
2858 if (portWithDragInitiated || cableWithYankInitiated)
2860 initiateCableDrag(portWithDragInitiated, cableWithYankInitiated, event);
2861 portWithDragInitiated = NULL;
2862 cableWithYankInitiated = NULL;
2867 if (cableInProgress)
2877 else if (duplicationDragInProgress)
2879 if (duplicateOnNextMouseMove)
2882 duplicateOnNextMouseMove =
false;
2889 QPointF delta = newPos - cursorPosBeforeDuplicationDragMove;
2891 cursorPosBeforeDuplicationDragMove = newPos;
2895 QGraphicsScene::mouseMoveEvent(event);
2900 QGraphicsScene::mouseMoveEvent(event);
2908 void VuoEditorComposition::updateHoverHighlighting(QPointF scenePos,
bool disablePortHoverHighlighting)
2922 bool hoveringOverNodeHeader =
false;
2923 if (cableInProgress)
2932 hoveringOverNodeHeader =
true;
2938 if (! hoveringOverNodeHeader)
2949 if (item != previousNearbyItem)
2957 if (previousNearbyItem)
2965 if (! cableInProgress)
2971 else if (typecastPort && !disablePortHoverHighlighting)
2973 else if (port && !disablePortHoverHighlighting)
2976 if (!cableInProgress)
2987 if (cableInProgress)
2990 cableInProgress->getFromPort()->getRenderer() :
2991 cableInProgress->getToPort()->getRenderer());
2993 QList< QPair<VuoRendererPort *, bool> > updatedPorts;
2994 if (hoveringOverNodeHeader)
2995 updatedPorts.append( QPair<VuoRendererPort *, bool>(port,
true) );
2996 if (previousNode && previousPort)
2997 updatedPorts.append( QPair<VuoRendererPort *, bool>(previousPort, !cableInProgress->getRenderer()->effectivelyCarriesData()) );
2999 QPair<VuoRendererPort *, bool> p;
3000 foreach (p, updatedPorts)
3005 if (typecastParentPort)
3006 updatedPort = typecastParentPort;
3008 updateEligibilityHighlightingForPort(updatedPort, fixedPort, p.second, types);
3011 updateEligibilityHighlightingForNode(potentialDrawer);
3018 if (targetPort || previousTargetPort)
3020 if (cableInProgress)
3021 cableInProgress->getRenderer()->setFloatingEndpointAboveEventPort(targetPort && (!targetPort->
getDataType() || hoveringOverNodeHeader));
3026 previousNearbyItem = item;
3034 else if (port && !disablePortHoverHighlighting)
3037 if (!cableInProgress)
3043 else if (makeListDrawer)
3053 if (previousNearbyItem)
3071 else if (typecastPort)
3080 previousNearbyItem = NULL;
3089 if ((event->key() != Qt::Key_Alt) && (event->key() != Qt::Key_Shift) && (event->key() != Qt::Key_Escape))
3092 Qt::KeyboardModifiers modifiers =
event->modifiers();
3093 qreal adjustedNodeMoveRate = nodeMoveRate;
3094 if (modifiers & Qt::ShiftModifier)
3096 adjustedNodeMoveRate *= nodeMoveRateMultiplier;
3099 switch (event->key()) {
3100 case Qt::Key_Backspace:
3105 case Qt::Key_Delete:
3117 if (modifiers & Qt::ControlModifier)
3118 openSelectedEditableNodes();
3135 if (cableInProgress)
3137 cableInProgress->getRenderer()->updateGeometry();
3138 cableInProgress->getCompiler()->setAlwaysEventOnly(
true);
3139 highlightEligibleEndpointsForCable(cableInProgress);
3149 case Qt::Key_Return:
3152 QGraphicsScene::keyPressEvent(event);
3154 if (!event->isAccepted())
3161 if (selectedComments.empty() && !selectedNodes.empty())
3168 case Qt::Key_Escape:
3170 if (duplicateOnNextMouseMove || duplicationDragInProgress)
3174 duplicateOnNextMouseMove =
false;
3175 duplicationDragInProgress =
false;
3176 duplicationCancelled =
true;
3178 else if (cableInProgress)
3187 QGraphicsScene::keyPressEvent(event);
3198 switch (event->key()) {
3201 if (cableInProgress)
3203 cableInProgress->getRenderer()->updateGeometry();
3204 cableInProgress->getCompiler()->setAlwaysEventOnly(
false);
3205 highlightEligibleEndpointsForCable(cableInProgress);
3217 QGraphicsScene::keyReleaseEvent(event);
3232 contextMenu.setSeparatorsCollapsible(
false);
3237 QAction *insertNodeSection =
new QAction(tr(
"Insert Node"), NULL);
3238 insertNodeSection->setEnabled(
false);
3239 contextMenu.addAction(insertNodeSection);
3241 QString spacer(
" ");
3245 QMenu *shareMenu =
new QMenu(&contextMenu);
3246 shareMenu->setSeparatorsCollapsible(
false);
3247 shareMenu->setTitle(spacer + tr(
"Share"));
3248 contextMenu.addMenu(shareMenu);
3251 QAction *action =
new QAction(tr(
"Share Value"), NULL);
3252 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share"))));
3253 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3254 shareMenu->addAction(action);
3258 QAction *action =
new QAction(tr(
"Share List"), NULL);
3259 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share.list"))));
3260 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3261 shareMenu->addAction(action);
3265 QAction *action =
new QAction(tr(
"Share Event"), NULL);
3266 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.share"))));
3267 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3268 shareMenu->addAction(action);
3274 QMenu *holdMenu =
new QMenu(&contextMenu);
3275 holdMenu->setSeparatorsCollapsible(
false);
3276 holdMenu->setTitle(spacer + tr(
"Hold"));
3277 contextMenu.addMenu(holdMenu);
3280 QAction *action =
new QAction(tr(
"Hold Value"), NULL);
3281 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold2"))));
3282 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3283 holdMenu->addAction(action);
3287 QAction *action =
new QAction(tr(
"Hold List"), NULL);
3288 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold.list2"))));
3289 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3290 holdMenu->addAction(action);
3296 QMenu *allowMenu =
new QMenu(&contextMenu);
3297 allowMenu->setSeparatorsCollapsible(
false);
3298 allowMenu->setTitle(spacer + tr(
"Allow"));
3299 contextMenu.addMenu(allowMenu);
3302 QAction *action =
new QAction(tr(
"Allow First Event"), NULL);
3303 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirst"))));
3304 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3305 allowMenu->addAction(action);
3309 QAction *action =
new QAction(tr(
"Allow First Value"), NULL);
3310 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirstValue"))));
3311 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3312 allowMenu->addAction(action);
3316 QAction *action =
new QAction(tr(
"Allow Periodic Events"), NULL);
3317 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.time.allowPeriodic"))));
3318 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3319 allowMenu->addAction(action);
3323 QAction *action =
new QAction(tr(
"Allow Changes"), NULL);
3324 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowChanges2"))));
3325 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3326 allowMenu->addAction(action);
3330 contextMenu.addSeparator();
3332 QAction *contextMenuInsertComment =
new QAction(NULL);
3333 contextMenuInsertComment->setText(tr(
"Insert Comment"));
3334 contextMenuInsertComment->setData(qVariantFromValue(event->scenePos()));
3335 connect(contextMenuInsertComment, &QAction::triggered,
this, &VuoEditorComposition::insertComment);
3336 contextMenu.addAction(contextMenuInsertComment);
3338 QAction *contextMenuInsertSubcomposition =
new QAction(NULL);
3339 contextMenuInsertSubcomposition->setText(tr(
"Insert Subcomposition"));
3340 contextMenuInsertSubcomposition->setData(qVariantFromValue(event->scenePos()));
3341 connect(contextMenuInsertSubcomposition, &QAction::triggered,
this, &VuoEditorComposition::insertSubcomposition);
3342 contextMenu.addAction(contextMenuInsertSubcomposition);
3347 QAction *contextMenuDeleteSelectedSnapshot =
new QAction(NULL);
3354 if (port->
isConstant() && inputEditorManager)
3358 if (inputEditorLoadedForPortDataType)
3360 contextMenuSetPortConstant->setData(qVariantFromValue((
void *)port));
3361 contextMenu.addAction(contextMenuSetPortConstant);
3363 inputEditorLoadedForPortDataType->deleteLater();
3369 contextMenuPublishPort->setText(tr(
"Publish Port"));
3370 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3371 contextMenu.addAction(contextMenuPublishPort);
3376 vector<VuoRendererPublishedPort *> externalPublishedPorts = port->
getPublishedPorts();
3377 bool hasExternalPublishedPortWithMultipleInternalPorts =
false;
3378 bool hasExternalPublishedPortBelongingToActiveProtocol =
false;
3382 hasExternalPublishedPortWithMultipleInternalPorts =
true;
3385 hasExternalPublishedPortBelongingToActiveProtocol =
true;
3392 if (!hasExternalPublishedPortWithMultipleInternalPorts &&!hasExternalPublishedPortBelongingToActiveProtocol)
3394 contextMenuPublishPort->setText(tr(
"Delete Published Port"));
3395 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3396 contextMenu.addAction(contextMenuPublishPort);
3401 if (isTriggerPort || port->
getInput())
3403 __block
bool isTopLevelCompositionRunning =
false;
3404 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
3406 isTopLevelCompositionRunning = topLevelComposition->
isRunning();
3409 if (isTopLevelCompositionRunning)
3411 contextMenuFireEvent->setData(qVariantFromValue((
void *)port));
3412 contextMenu.addAction(contextMenuFireEvent);
3418 QMenu *contextMenuThrottling =
new QMenu(&contextMenu);
3419 contextMenuThrottling->setSeparatorsCollapsible(
false);
3420 contextMenuThrottling->setTitle(tr(
"Set Event Throttling"));
3422 foreach (QAction *action, contextMenuThrottlingActions)
3424 contextMenuThrottling->addAction(action);
3425 action->setData(qVariantFromValue(port));
3426 action->setCheckable(
true);
3429 contextMenu.addMenu(contextMenuThrottling);
3435 if (contextMenuSpecializeGenericType)
3436 contextMenuSpecializeGenericType->deleteLater();
3439 contextMenuSpecializeGenericType->setSeparatorsCollapsible(
false);
3440 contextMenuSpecializeGenericType->setTitle(tr(
"Set Data Type"));
3441 contextMenuSpecializeGenericType->setToolTipsVisible(
true);
3443 populateSpecializePortMenu(contextMenuSpecializeGenericType, port,
true);
3444 contextMenu.addMenu(contextMenuSpecializeGenericType);
3449 int numVisibleDirectlyConnectedCables = 0;
3450 int numHidableDirectlyConnectedCables = 0;
3451 int numUnhidableDirectlyConnectedCables = 0;
3457 numVisibleDirectlyConnectedCables++;
3459 numHidableDirectlyConnectedCables++;
3463 numUnhidableDirectlyConnectedCables++;
3466 int numVisibleChildPortConnectedCables = 0;
3467 int numHidableChildPortConnectedCables = 0;
3468 int numUnhidableChildPortConnectedCables = 0;
3478 numVisibleChildPortConnectedCables++;
3481 numHidableChildPortConnectedCables++;
3484 numUnhidableChildPortConnectedCables++;
3489 int numVisibleConnectedCables = numVisibleDirectlyConnectedCables + numVisibleChildPortConnectedCables;
3492 int numHidableConnectedCables = numHidableDirectlyConnectedCables + numHidableChildPortConnectedCables;
3495 int numUnhidableConnectedCables = numUnhidableDirectlyConnectedCables + numUnhidableChildPortConnectedCables;
3497 if ((!
renderHiddenCables && ((numHidableConnectedCables >= 1) || (numUnhidableConnectedCables >= 1)))
3498 || (numVisibleConnectedCables >= 1))
3500 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3501 contextMenu.addSeparator();
3506 if (numHidableConnectedCables >= 1)
3511 int numApparentlyHidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3512 contextMenuHideCables->setText(numApparentlyHidableConnectedCables > 1?
"Hide Cables" :
"Hide Cable");
3513 contextMenuHideCables->setData(qVariantFromValue((
void *)port));
3514 contextMenu.addAction(contextMenuHideCables);
3517 if (numUnhidableConnectedCables >= 1)
3519 int numApparentlyUnhidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3520 contextMenuUnhideCables->setText(numApparentlyUnhidableConnectedCables > 1?
"Unhide Cables" :
"Unhide Cable");
3521 contextMenuUnhideCables->setData(qVariantFromValue((
void *)port));
3522 contextMenu.addAction(contextMenuUnhideCables);
3526 if (numVisibleConnectedCables >= 1)
3528 contextMenuDeleteCables->setText(numVisibleConnectedCables > 1?
"Delete Cables" :
"Delete Cable");
3529 contextMenuDeleteCables->setData(qVariantFromValue((
void *)port));
3530 contextMenu.addAction(contextMenuDeleteCables);
3537 if (! item->isSelected())
3540 item->setSelected(
true);
3543 QList<QGraphicsItem *> selectedComponents = selectedItems();
3544 bool onlyCommentsSelected =
true;
3545 bool onlyCablesSelected =
true;
3546 bool selectionContainsMissingNode =
false;
3547 foreach (QGraphicsItem *item, selectedComponents)
3550 onlyCommentsSelected =
false;
3553 onlyCablesSelected =
false;
3556 selectionContainsMissingNode =
true;
3559 contextMenuDeleteSelectedSnapshot->setText(contextMenuDeleteSelected->text());
3567 if (onlyCommentsSelected)
3568 contextMenu.addAction(contextMenuEditSelectedComments);
3573 contextMenu.addSeparator();
3576 contextMenu.addAction(contextMenuRefactorSelected);
3578 contextMenu.addSeparator();
3581 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3591 contextMenu.addAction(contextMenuHideSelectedCables);
3594 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3609 contextMenuAddInputPort->setData(qVariantFromValue((
void *)node));
3610 contextMenuRemoveInputPort->setData(qVariantFromValue((
void *)node));
3613 contextMenuRemoveInputPort->setEnabled(listItemCount >= 1);
3615 contextMenu.addAction(contextMenuAddInputPort);
3616 contextMenu.addAction(contextMenuRemoveInputPort);
3618 contextMenu.addSeparator();
3622 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3633 if (originalGenericNodeClass)
3634 nodeClass = originalGenericNodeClass->
getBase();
3637 QString actionText, sourcePath;
3641 int numSelectedNonAttachmentNodes = 0;
3642 QList<QGraphicsItem *> selectedComponents = selectedItems();
3643 foreach (QGraphicsItem *item, selectedComponents)
3646 numSelectedNonAttachmentNodes++;
3649 if ((numSelectedNonAttachmentNodes == 1) && nodeClassIsEditable)
3652 QAction *editAction =
new QAction(NULL);
3653 editAction->setText(actionText);
3654 editAction->setData(qVariantFromValue(node));
3657 contextMenu.addAction(editAction);
3658 contextMenu.addSeparator();
3663 contextMenu.addAction(contextMenuRenameSelected);
3668 contextMenu.addSeparator();
3671 if (numSelectedNonAttachmentNodes == 1)
3673 if (contextMenuChangeNode)
3674 contextMenuChangeNode->deleteLater();
3677 contextMenuChangeNode->setSeparatorsCollapsible(
false);
3678 contextMenuChangeNode->setTitle(tr(
"Change To"));
3681 if (!contextMenuChangeNode->actions().isEmpty())
3682 contextMenu.addMenu(contextMenuChangeNode);
3686 if (!selectionContainsMissingNode)
3687 contextMenu.addAction(contextMenuRefactorSelected);
3689 if ((numSelectedNonAttachmentNodes == 1) && (nodeClassIsEditable || nodeClassIs3rdParty))
3693 if (!modulePath.isEmpty())
3695 QString enclosingDirUrl =
"file://" + QFileInfo(modulePath).dir().absolutePath();
3696 QAction *openEnclosingFolderAction =
new QAction(NULL);
3697 openEnclosingFolderAction->setText(
"Show in Finder");
3698 openEnclosingFolderAction->setData(qVariantFromValue(enclosingDirUrl));
3700 contextMenu.addAction(openEnclosingFolderAction);
3704 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3705 contextMenu.addSeparator();
3708 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3714 if (!contextMenu.actions().isEmpty())
3719 menuSelectionInProgress =
true;
3720 contextMenu.exec(event->screenPos());
3721 menuSelectionInProgress =
false;
3724 delete contextMenuDeleteSelectedSnapshot;
3732 QAction *sender = (QAction *)QObject::sender();
3740 void VuoEditorComposition::expandSpecializePortMenu()
3742 QAction *sender = (QAction *)QObject::sender();
3745 populateSpecializePortMenu(contextMenuSpecializeGenericType, port,
false);
3746 contextMenuSpecializeGenericType->exec();
3756 void VuoEditorComposition::populateSpecializePortMenu(QMenu *menu,
VuoRendererPort *port,
bool limitInitialOptions)
3764 QAction *unspecializeAction = menu->addAction(tr(
"Generic"));
3769 menu->addSeparator();
3771 unspecializeAction->setData(qVariantFromValue((
void *)port));
3772 unspecializeAction->setCheckable(
true);
3773 unspecializeAction->setChecked(genericDataType);
3776 set<string> compatibleTypes;
3777 set<string> compatibleTypesInIsolation;
3780 if (genericDataType)
3788 compatibleTypes = set<string>(compatibleTypesVector.begin(), compatibleTypesVector.end());
3794 compatibleTypes.insert(compatibleTypeNames.begin(), compatibleTypeNames.end());
3799 else if (isPortCurrentlyRevertible(port))
3801 map<VuoNode *, string> nodesToReplace;
3802 set<VuoCable *> cablesToDelete;
3804 if (cablesToDelete.size() >= 1)
3810 port->getUnderlyingParentNode()->getBase()->getNodeClass()->getCompiler());
3812 compatibleTypes = getRespecializationOptionsForPortInNetwork(port);
3816 if (genericTypeFromPortClass)
3828 if (genericHostPortDataType)
3830 else if (isPortCurrentlyRevertible(hostPort->
getRenderer()))
3840 vector<string> compatibleTypesInIsolationVector;
3841 if (genericTypeFromPortClass)
3848 foreach (
string type, compatibleTypesInIsolationVector)
3855 const int maxTypeCountForFlatMenuDisplay = 10;
3856 if (compatibleTypesInIsolation.size() <= maxTypeCountForFlatMenuDisplay)
3858 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
false,
"", menu);
3866 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true,
"", menu);
3871 QList<QAction *> allNodeSetActionsToAdd;
3872 for (map<
string, set<VuoCompilerType *> >::iterator i = loadedTypesForNodeSet.begin(); i != loadedTypesForNodeSet.end(); ++i)
3874 string nodeSetName = i->first;
3875 if (!nodeSetName.empty())
3876 allNodeSetActionsToAdd +=
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true, nodeSetName, menu);
3879 bool usingExpansionMenu =
false;
3880 if ((menu->actions().size() > 0) && (allNodeSetActionsToAdd.size() > 0))
3882 menu->addSeparator();
3884 if (limitInitialOptions)
3887 QAction *showMoreAction = menu->addAction(tr(
"More…"));
3888 showMoreAction->setData(qVariantFromValue(
static_cast<void *
>(port)));
3889 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandSpecializePortMenu);
3890 usingExpansionMenu =
true;
3894 if (!usingExpansionMenu)
3898 foreach (QAction *action, menu->actions())
3900 QMenu *specializeSubmenu = action->menu();
3901 if (specializeSubmenu)
3903 foreach (QAction *specializeSubaction, specializeSubmenu->actions())
3904 connect(specializeSubaction, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3906 else if (action == unspecializeAction)
3907 connect(action, &QAction::triggered,
this, &VuoEditorComposition::unspecializePortType);
3909 connect(action, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3921 vector<string> typeOptions;
3923 map<string, VuoCompilerType *> loadedTypes = compiler->
getTypes();
3924 for (map<string, VuoCompilerType *>::iterator i = loadedTypes.begin(); i != loadedTypes.end(); ++i)
3929 (i->first !=
"VuoMathExpressionList"))
3931 typeOptions.push_back(i->first);
3943 set<string> VuoEditorComposition::getRespecializationOptionsForPortInNetwork(
VuoRendererPort *port)
3946 return set<string>();
3953 vector<string> compatibleInnerTypeNames;
3954 vector<string> compatibleTypeNames;
3955 for (
VuoPort *connectedPort : connectedGenericPorts)
3960 if (specializedNodeClass)
3968 vector<string> innermostCompatibleTypeNamesForPort;
3969 for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
3972 if (! innermostCompatibleTypeNamesForPort.empty())
3974 if (compatibleInnerTypeNames.empty())
3975 compatibleInnerTypeNames = innermostCompatibleTypeNamesForPort;
3978 for (
int k = compatibleInnerTypeNames.size() - 1; k >= 0; --k)
3979 if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleInnerTypeNames[k]) ==
3980 innermostCompatibleTypeNamesForPort.end())
3981 compatibleInnerTypeNames.erase(compatibleInnerTypeNames.begin() + k);
3990 string typeNameForPort = genericTypeFromPortClass->
getModuleKey();
3994 for (vector<string>::iterator k = compatibleInnerTypeNames.begin(); k != compatibleInnerTypeNames.end(); ++k)
3995 compatibleTypeNames.push_back(prefix + *k);
3997 if (compatibleTypeNames.empty())
4007 return set<string>(compatibleTypeNames.begin(), compatibleTypeNames.end());
4030 set<string> compatibleTypesInIsolation,
4031 set<string> compatibleTypesInContext,
4032 bool limitToNodeSet,
4036 QList<QAction *> actionsToAddToMenu;
4038 map<string, VuoCompilerType *> allTypes = compiler->
getTypes();
4040 QList<VuoCompilerType *> compatibleTypesForNodeSetDisplay;
4041 foreach (
string typeName, compatibleTypesInIsolation)
4044 if ((!limitToNodeSet || (loadedTypesForNodeSet[nodeSetName].find(type) != loadedTypesForNodeSet[nodeSetName].end())) &&
4047 (typeName !=
"VuoUrl" && typeName !=
"VuoList_VuoUrl"
4049 && typeName !=
"VuoInteraction" && typeName !=
"VuoList_VuoInteraction"
4050 && typeName !=
"VuoInteractionType" && typeName !=
"VuoList_VuoInteractionType"
4051 && typeName !=
"VuoUuid" && typeName !=
"VuoList_VuoUuid"
4053 && typeName !=
"VuoIconPosition" && typeName !=
"VuoList_VuoIconPosition"
4054 && typeName !=
"VuoMesh" && typeName !=
"VuoList_VuoMesh"
4055 && typeName !=
"VuoWindowProperty" && typeName !=
"VuoList_VuoWindowProperty"
4056 && typeName !=
"VuoWindowReference" && typeName !=
"VuoList_VuoWindowReference"))
4057 compatibleTypesForNodeSetDisplay.append(type);
4060 if (!compatibleTypesForNodeSetDisplay.isEmpty())
4062 QMenu *contextMenuNodeSetTypes = NULL;
4063 QList<QAction *> actionsToAddToNodeSetSubmenu;
4064 bool enabledContentAdded =
false;
4067 if (!nodeSetName.empty())
4069 contextMenuNodeSetTypes =
new QMenu(menu);
4070 contextMenuNodeSetTypes->setSeparatorsCollapsible(
false);
4072 contextMenuNodeSetTypes->setToolTipsVisible(
true);
4073 actionsToAddToMenu.append(contextMenuNodeSetTypes->menuAction());
4079 QList<QVariant> portAndSpecializedType;
4080 portAndSpecializedType.append(qVariantFromValue((
void *)genericPort));
4081 portAndSpecializedType.append(typeName.c_str());
4083 QAction *specializeAction;
4087 if (!nodeSetName.empty())
4089 specializeAction =
new QAction(typeTitle, contextMenuNodeSetTypes);
4090 actionsToAddToNodeSetSubmenu.append(specializeAction);
4096 specializeAction =
new QAction(typeTitle, menu);
4097 actionsToAddToMenu.append(specializeAction);
4100 specializeAction->setData(QVariant(portAndSpecializedType));
4101 specializeAction->setCheckable(
true);
4102 specializeAction->setChecked(genericPort && (genericPort->getDataType()->getModuleKey() == type->
getBase()->
getModuleKey()));
4104 if (compatibleTypesInContext.find(typeName) == compatibleTypesInContext.end())
4106 specializeAction->setEnabled(
false);
4108 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."));
4111 enabledContentAdded =
true;
4114 if (contextMenuNodeSetTypes)
4118 if (!enabledContentAdded)
4119 contextMenuNodeSetTypes->setEnabled(
false);
4123 QList<QAction *> actionListWithPromotions = promoteSingletonsFromSubmenus(actionsToAddToMenu);
4124 return actionListWithPromotions;
4131 QList<QAction *> VuoEditorComposition::promoteSingletonsFromSubmenus(QList<QAction *> actionList)
4133 QList<QAction *> modifiedActionList;
4134 foreach (QAction *action, actionList)
4136 if (action->menu() && (action->menu()->actions().size() == 1))
4138 QAction *singleSubaction = action->menu()->actions().first();
4139 action->menu()->removeAction(singleSubaction);
4140 modifiedActionList.append(singleSubaction);
4143 modifiedActionList.append(action);
4146 return modifiedActionList;
4154 std::sort(actionList.begin(), actionList.end(), nodeSetMenuActionLessThan);
4155 foreach (QAction *action, actionList)
4156 menu->addAction(action);
4162 void VuoEditorComposition::updatePopoversForActiveWindowChange(QWidget *old, QWidget *now)
4175 void VuoEditorComposition::updatePopoversForApplicationStateChange(
bool active)
4177 if (ignoreApplicationStateChangeEvents)
4181 if (activeWindow && (activeWindow->
getComposition() ==
this) && (!activeWindow->isMinimized()))
4195 return findNearbyComponent(scenePos, VuoEditorComposition::targetTypePort, limitPortCollisionRange);
4203 QGraphicsItem *item =
findNearbyComponent(scenePos, VuoEditorComposition::targetTypeNodeHeader);
4221 bool limitPortCollisionRange)
4227 bool ignoreComments;
4231 case VuoEditorComposition::targetTypePort:
4233 ignoreCables =
true;
4235 ignorePorts =
false;
4236 ignoreComments =
true;
4239 case VuoEditorComposition::targetTypeNodeHeader:
4241 ignoreCables =
true;
4244 ignoreComments =
true;
4249 ignoreCables =
false;
4250 ignoreNodes =
false;
4251 ignorePorts =
false;
4252 ignoreComments =
false;
4260 QGraphicsItem *topmostItemUnderCursor = itemAt(scenePos, views()[0]->transform());
4261 if (topmostItemUnderCursor && (!topmostItemUnderCursor->isEnabled()))
4262 topmostItemUnderCursor = NULL;
4266 QRectF searchRect(scenePos.x()-0.5*rectLength, scenePos.y()-0.5*rectLength, rectLength, rectLength);
4267 QList<QGraphicsItem *> itemsInRange = items(searchRect);
4268 for (QList<QGraphicsItem *>::iterator i = itemsInRange.begin(); i != itemsInRange.end(); ++i)
4270 if (!(*i)->isEnabled())
4277 bool makeListDragHandle =
4280 if (makeListDragHandle &&
4281 ((! topmostItemUnderCursor) ||
4282 (topmostItemUnderCursor == makeListDrawer) ||
4283 (topmostItemUnderCursor->zValue() < makeListDrawer->zValue())))
4285 return makeListDrawer;
4296 ((! topmostItemUnderCursor) ||
4297 (topmostItemUnderCursor == port) ||
4299 (topmostItemUnderCursor->zValue() < port->zValue()))
4301 ((! limitPortCollisionRange) ||
4315 ((! topmostItemUnderCursor) ||
4316 (topmostItemUnderCursor == cable) ||
4317 (topmostItemUnderCursor->zValue() < cable->zValue())))
4324 if (! ignoreComments)
4331 ((! topmostItemUnderCursor) ||
4332 (topmostItemUnderCursor == (*i)) ||
4333 (topmostItemUnderCursor->zValue() < (*i)->zValue())))
4340 if (targetType == VuoEditorComposition::targetTypeNodeHeader)
4346 headerRect = node->mapToScene(headerRect).
boundingRect();
4347 if (headerRect.intersects(searchRect) && scenePos.y() <= headerRect.bottom())
4359 return topmostItemUnderCursor;
4364 return ((
VuoRendererPort *)(topmostItemUnderCursor))->getRenderedParentNode();
4381 if (! cableInProgress)
4384 VuoPort *fromPort = cableInProgress->getFromPort();
4385 VuoPort *toPort = cableInProgress->getToPort();
4386 VuoPort *fixedPort = (fromPort? fromPort: toPort);
4401 vector<VuoRendererPort *> portList;
4416 if (portList.size() > firstPortIndex)
4418 targetPort = portList[firstPortIndex];
4424 for (
int i = firstPortIndex; i < portList.size(); ++i)
4428 firstPortWithoutWall = portList[i];
4432 if (firstPortWithoutWall)
4433 targetPort = firstPortWithoutWall;
4442 if (portList.size() > firstPortIndex)
4443 targetPort = portList[firstPortIndex];
4464 QRectF boundingRect;
4465 foreach (QGraphicsItem *item, items())
4469 boundingRect |= item->sceneBoundingRect();
4472 return boundingRect;
4481 QRectF boundingRect;
4483 foreach (QGraphicsItem *item, selectedItems())
4487 boundingRect |= item->sceneBoundingRect();
4490 return boundingRect;
4499 QRectF boundingRect;
4501 foreach (QGraphicsItem *item, selectedItems())
4505 boundingRect |= item->mapToScene(item->childrenBoundingRect()).boundingRect();
4515 boundingRect |= drawer->mapToScene(drawer->childrenBoundingRect()).boundingRect();
4520 return boundingRect;
4547 if (updateInRunningComposition)
4556 if (updateInRunningComposition)
4582 if (!errorMarkingUpdatesEnabled)
4585 errorMarkingUpdatesEnabled =
false;
4602 set<VuoCompilerCable *> potentialCables;
4604 if (targetPort && cableInProgress)
4610 if (cableInProgress->getFromNode())
4612 fromNode = cableInProgress->getFromNode();
4613 fromPort = cableInProgress->getFromPort();
4615 toPort = targetPort->
getBase();
4620 fromPort = targetPort->
getBase();
4621 toNode = cableInProgress->getToNode();
4622 toPort = cableInProgress->getToPort();
4627 potentialCable->
setAlwaysEventOnly(! cableInProgress->getRenderer()->effectivelyCarriesData() ||
4628 cableInProgress->getRenderer()->isFloatingEndpointAboveEventPort());
4632 potentialCables.insert(potentialCable);
4643 VUserLog(
"%s: Showing error popover: %s",
4651 set<VuoRendererNode *> nodesToMark;
4652 set<VuoRendererCable *> cablesToMark;
4654 set<VuoNode *> problemNodes = issue.
getNodes();
4655 foreach (
VuoNode *node, problemNodes)
4659 set<VuoCable *> problemCables = issue.
getCables();
4660 foreach (
VuoCable *cable, problemCables)
4662 VuoCable *cableToMark = (cable->
getCompiler() == potentialCable ? cableInProgress : cable);
4671 errorPopovers.insert(errorPopover);
4675 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
4679 if (targetPort && cableInProgress && nodesToMark.find(targetPort->
getRenderedParentNode()) != nodesToMark.end())
4683 else if (! nodesToMark.empty())
4686 qreal topY = viewportRect.bottom();
4692 QPointF scenePos = node->scenePos();
4693 if (viewportRect.contains(scenePos) && (scenePos.y() < topY))
4695 topmostVisibleNode = node;
4696 topY = scenePos.y();
4700 if (topmostVisibleNode)
4701 nearbyNode = topmostVisibleNode;
4710 VUserLog(
"Warning: no nearby node (no marked nodes).");
4715 const QPoint offsetFromNode(0,10);
4716 QPoint popoverTopLeftInScene = (nearbyNode?
4717 (nearbyNode->scenePos().toPoint() +
4720 QPoint(viewportRect.center().x() - 0.5*errorPopover->sizeHint().width(),
4721 viewportRect.center().y() - 0.5*errorPopover->sizeHint().height()));
4725 const int margin = 5;
4726 popoverTopLeftInScene = (QPoint(fmin(popoverTopLeftInScene.x(), viewportRect.bottomRight().x() - errorPopover->sizeHint().width() - margin),
4727 fmin(popoverTopLeftInScene.y(), viewportRect.bottomRight().y() - errorPopover->sizeHint().height() - margin)));
4729 popoverTopLeftInScene = (QPoint(fmax(popoverTopLeftInScene.x(), viewportRect.topLeft().x() + margin),
4730 fmax(popoverTopLeftInScene.y(), viewportRect.topLeft().y() + margin)));
4732 QPoint popoverTopLeftInView = views()[0]->mapFromScene(popoverTopLeftInScene);
4733 QPoint popoverTopLeftGlobal = views()[0]->mapToGlobal(popoverTopLeftInView);
4735 errorPopover->move(popoverTopLeftGlobal);
4736 errorPopover->show();
4744 delete potentialCable;
4746 errorMarkingUpdatesEnabled =
true;
4752 bool VuoEditorComposition::hasFeedbackErrors(
void)
4754 return this->errorMark;
4762 if (hasFeedbackErrors())
4771 void VuoEditorComposition::buildComposition(
string compositionSnapshot,
const set<string> &dependenciesUninstalled)
4777 if (! dependenciesUninstalled.empty())
4779 vector<string> dependenciesRemovedVec(dependenciesUninstalled.begin(), dependenciesUninstalled.end());
4781 throw VuoException(
"Some modules that the composition needs were uninstalled: " + dependenciesStr);
4784 delete runningComposition;
4785 runningComposition = NULL;
4789 if (runningCompositionActiveDriver)
4793 string dir, file, ext;
4795 linkedCompositionPath = dir + file +
".dylib";
4800 compiler->
compileComposition(runningComposition, compiledCompositionPath,
true, issues);
4804 remove(compiledCompositionPath.c_str());
4810 delete runningComposition;
4811 runningComposition = NULL;
4823 bool VuoEditorComposition::isRunningThreadUnsafe(
void)
4825 return runner != NULL && ! stopRequested && ! runner->
isStopped();
4835 __block
bool running;
4836 dispatch_sync(runCompositionQueue, ^{
4837 running = isRunningThreadUnsafe();
4863 if (matchingComposition->showEventsMode)
4867 stopRequested =
false;
4868 dispatch_async(runCompositionQueue, ^{
4871 runningCompositionLibraries = std::make_shared<VuoRunningCompositionLibraries>();
4873 buildComposition(compositionSnapshot);
4887 if (matchingComposition->showEventsMode)
4888 this->runner->subscribeToEventTelemetry(matchingCompositionIdentifier);
4890 dispatch_sync(activePortPopoversQueue, ^{
4891 for (
auto i : matchingComposition->activePortPopovers)
4893 string portID = i.first;
4894 updateDataInPortPopoverFromRunningTopLevelComposition(matchingComposition, matchingCompositionIdentifier, portID);
4915 stopRequested =
true;
4916 dispatch_async(runCompositionQueue, ^{
4920 runner->waitUntilStopped();
4925 linkedCompositionPath =
"";
4927 runningCompositionLibraries =
nullptr;
4929 delete runningComposition;
4930 runningComposition = NULL;
4938 subcompositionRouter->applyToAllLinkedCompositions(
this, ^
void (
VuoEditorComposition *matchingComposition,
string matchingCompositionIdentifier)
4940 if (matchingComposition->showEventsMode)
4943 dispatch_sync(activePortPopoversQueue, ^{
4944 for (
auto i : matchingComposition->activePortPopovers)
4946 VuoPortPopover *popover = i.second;
4947 popover->setCompositionRunning(false);
4970 dispatch_async(runCompositionQueue, ^{
4971 if (isRunningThreadUnsafe())
4977 runningCompositionLibraries->enqueueLibraryContainingDependencyToUnload(moduleKey);
4980 string oldBuiltCompositionSnapshot = oldCompositionSnapshot;
4982 if (previouslyActiveDriver)
4985 previouslyActiveDriver->applyToComposition(oldBuiltComposition, compiler);
4989 buildComposition(newCompositionSnapshot, dependenciesUninstalled);
4991 string compositionDiff = diffInfo->
diff(oldBuiltCompositionSnapshot, runningComposition, compiler);
4994 catch (exception &e)
4996 VUserLog(
"Composition stopped itself: %s", e.what());
5001 VUserLog(
"Composition stopped itself.");
5007 dispatch_async(dispatch_get_main_queue(), ^{
5026 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, reloadSubcompositionIfUnsaved);
5044 if (
this == topLevelComposition)
5046 dispatch_async(runCompositionQueue, ^{
5047 if (isRunningThreadUnsafe())
5049 json_object *constantObject = json_tokener_parse(constant.c_str());
5050 runner->
setInputPortValue(thisCompositionIdentifier, runningPortID, constantObject);
5058 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
5060 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
5087 if (runningPortIdentifier.empty())
5093 if (
this == topLevelComposition)
5095 dispatch_async(runCompositionQueue, ^{
5096 if (isRunningThreadUnsafe())
5098 json_object *constantObject = json_tokener_parse(constant.c_str());
5099 runner->
setInputPortValue(thisCompositionIdentifier, runningPortIdentifier, constantObject);
5107 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
5109 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
5122 dispatch_async(runCompositionQueue, ^{
5123 if (isRunningThreadUnsafe())
5125 json_object *constantObject = json_tokener_parse(constant.c_str());
5127 foreach (
string subcompositionIdentifier, subcompositionIdentifiers)
5129 runner->
setInputPortValue(subcompositionIdentifier, portIdentifier, constantObject);
5140 dispatch_async(runCompositionQueue, ^{
5141 if (isRunningThreadUnsafe())
5146 json_object *constantObject = json_tokener_parse(constant.c_str());
5147 map<VuoRunner::Port *, json_object *> m;
5148 m[publishedPort] = constantObject;
5161 return contextMenuDeleteSelected;
5171 QMenu *contextMenuTints =
new QMenu(parent);
5172 contextMenuTints->setSeparatorsCollapsible(
false);
5173 contextMenuTints->setTitle(tr(
"Tint"));
5174 foreach (QAction *tintAction, contextMenuTintActions)
5175 contextMenuTints->addAction(tintAction);
5176 contextMenuTints->insertSeparator(contextMenuTintActions.last());
5178 return contextMenuTints;
5184 void VuoEditorComposition::expandChangeNodeMenu()
5186 QAction *sender = (QAction *)QObject::sender();
5191 int currentMatchesListed = contextMenuChangeNode->actions().size()-1;
5192 if (currentMatchesListed <= initialChangeNodeSuggestionCount)
5195 int verticalSpacePerItem = 21;
5199 int targetMatches = availableVerticalSpace/verticalSpacePerItem - 2;
5208 contextMenuChangeNode->exec();
5223 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
5224 vector<VuoCompilerNodeClass *> loadedNodeClasses;
5225 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
5226 loadedNodeClasses.push_back(i->second);
5229 vector<string> bestMatches;
5230 map<string, double> matchScores;
5231 matchScores[
""] = 0;
5233 int targetMatchCount = (matchLimit > 0? matchLimit : loadedNodeClasses.size());
5234 for (
int i = 0; i < targetMatchCount; ++i)
5235 bestMatches.push_back(
"");
5238 bool overflow =
false;
5241 string originalGenericNodeClassName;
5245 originalGenericNodeClassName = nodeClass->
getClassName();
5250 if (loadedNodeClassName == originalGenericNodeClassName)
5253 bool canSwapNondestructively = canSwapWithoutBreakingCables(node, loadedNodeClass->
getBase());
5254 double matchScore = (canSwapNondestructively? calculateNodeSimilarity(nodeClass, loadedNodeClass->
getBase()) : 0);
5255 int highestIndexWithCompetitiveScore = -1;
5256 for (
int i = targetMatchCount-1; (i >= 0) && (highestIndexWithCompetitiveScore == -1); --i)
5257 if (matchScore <= matchScores[bestMatches[i] ])
5258 highestIndexWithCompetitiveScore = i;
5260 if (highestIndexWithCompetitiveScore < targetMatchCount-1)
5262 if (matchScores[bestMatches[targetMatchCount-1] ] > 0)
5265 for (
int j = targetMatchCount-2; j > highestIndexWithCompetitiveScore; --j)
5266 bestMatches[j+1] = bestMatches[j];
5268 bestMatches[highestIndexWithCompetitiveScore+1] = loadedNodeClassName;
5269 matchScores[loadedNodeClassName] = matchScore;
5273 for (
int i = 0; i < targetMatchCount; ++i)
5275 if (matchScores[bestMatches[i] ] > 0)
5280 matchDisplayText += QString(
" (%1)").arg(bestMatches[i].c_str());
5282 QAction *changeAction = menu->addAction(matchDisplayText);
5284 QList<QVariant> currentNodeAndNewClass;
5285 currentNodeAndNewClass.append(qVariantFromValue((
void *)node));
5286 currentNodeAndNewClass.append(bestMatches[i].c_str());
5287 changeAction->setData(QVariant(currentNodeAndNewClass));
5288 connect(changeAction, &QAction::triggered,
this, &VuoEditorComposition::swapNode);
5295 QAction *showMoreAction = menu->addAction(tr(
"More…"));
5296 showMoreAction->setData(qVariantFromValue(
static_cast<void *
>(node)));
5297 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandChangeNodeMenu);
5308 map<string, int> requiredInputs;
5309 bool inputEventSourceRequired =
false;
5312 bool hasDrawerWithNoIncomingCables =
false;
5313 bool hasDrawerWithNoIncomingDataCables =
false;
5320 hasDrawerWithNoIncomingCables =
true;
5321 hasDrawerWithNoIncomingDataCables =
true;
5322 vector<VuoRendererPort *> childPorts = inputDrawer->
getDrawerPorts();
5326 hasDrawerWithNoIncomingCables =
false;
5328 hasDrawerWithNoIncomingDataCables =
false;
5333 if (!hasDrawerWithNoIncomingDataCables)
5340 requiredInputs[typeKey] = ((requiredInputs.find(typeKey) == requiredInputs.end())? 1 : requiredInputs[typeKey]+1);
5345 if (hasIncomingCables && !hasDrawerWithNoIncomingCables)
5346 inputEventSourceRequired =
true;
5350 map<string, int> requiredOutputs;
5351 bool outputEventSourceRequired =
false;
5363 requiredOutputs[typeKey] = ((requiredOutputs.find(typeKey) == requiredOutputs.end())? 1 : requiredOutputs[typeKey]+1);
5367 outputEventSourceRequired =
true;
5371 bool inputEventSourceAvailable =
false;
5372 map<string, int> availableInputs;
5380 availableInputs[typeKey] = ((availableInputs.find(typeKey) == availableInputs.end())? 1 : availableInputs[typeKey]+1);
5385 inputEventSourceAvailable =
true;
5388 bool outputEventSourceAvailable =
false;
5389 map<string, int> availableOutputs;
5397 availableOutputs[typeKey] = ((availableOutputs.find(typeKey) == availableOutputs.end())? 1 : availableOutputs[typeKey]+1);
5402 outputEventSourceAvailable =
true;
5405 for (std::map<string,int>::iterator it=requiredInputs.begin(); it!=requiredInputs.end(); ++it)
5407 string typeKey = it->first;
5408 int typeRequiredCount = it->second;
5409 if (availableInputs[typeKey] < typeRequiredCount)
5414 for (std::map<string,int>::iterator it=requiredOutputs.begin(); it!=requiredOutputs.end(); ++it)
5416 string typeKey = it->first;
5417 int typeRequiredCount = it->second;
5418 if (availableOutputs[typeKey] < typeRequiredCount)
5422 if (inputEventSourceRequired && !inputEventSourceAvailable)
5425 if (outputEventSourceRequired && !outputEventSourceAvailable)
5435 bool VuoEditorComposition::isPortCurrentlyRevertible(
VuoRendererPort *port)
5441 if (!specializedNodeClass)
5446 if (!originalGenericType)
5455 if (hostPort && (!isPortCurrentlyRevertible(hostPort->
getRenderer())))
5482 string publishedPortName = ((! name.empty())?
5493 bool performedMerge =
false;
5496 publishedPort = (isPublishedInput ?
5502 if (isPublishedInput && portType && type && !forceEventOnlyPublication)
5510 performedMerge =
true;
5517 if (! performedMerge)
5520 if (isPublishedInput && type)
5535 if (! existingPublishedCable)
5541 if (mergePerformed != NULL)
5542 *mergePerformed = performedMerge;
5544 return rendererPublishedPort;
5556 if (creatingPublishedInputCable)
5559 VuoPort *fromPort = externalPort;
5562 VuoPort *toPort = internalPort;
5569 publishedCable->
setFrom(fromNode, fromPort);
5576 VuoPort *fromPort = internalPort;
5579 VuoPort *toPort = externalPort;
5586 publishedCable->
setTo(toNode, toPort);
5589 if (forceEventOnlyPublication)
5592 return publishedCable;
5608 vector<VuoPublishedPort *> publishedPortsToAdd;
5609 map<VuoPublishedPort *, string> publishedPortsToRename;
5612 VuoProtocol *previousActiveProtocol = this->activeProtocol;
5613 bool removingPreviousProtocol = previousActiveProtocol && (previousActiveProtocol != protocol);
5615 bool portChangesMadeDuringProtocolRemoval =
false;
5616 if (removingPreviousProtocol)
5619 if (portChangesMadeDuringProtocolRemoval && !useUndoStack)
5621 VUserLog(
"Warning: Unexpected combination: Removing protocol ports, but useUndoStack=false");
5622 useUndoStack =
true;
5626 this->activeProtocol = protocol;
5631 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5633 string portName = i->first;
5634 string portType = i->second;
5636 bool compositionHadCompatiblePort =
false;
5638 if (preexistingPublishedPort)
5642 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5643 (!preexistingType && (portType ==
"")));
5646 compositionHadCompatiblePort =
true;
5651 compositionHadCompatiblePort =
false;
5656 if (!compositionHadCompatiblePort)
5665 publishedPortsToAdd.push_back(publishedPort);
5672 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5674 string portName = i->first;
5675 string portType = i->second;
5677 bool compositionHadCompatiblePort =
false;
5679 if (preexistingPublishedPort)
5682 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5683 (!preexistingType && (portType ==
"")));
5686 compositionHadCompatiblePort =
true;
5691 compositionHadCompatiblePort =
false;
5696 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);
5733 string 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;
5962 void 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));
6043 void 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)
6139 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6141 bool eventOnlyConnection,
6142 bool forwardConnection)
6145 string respecializedTypeName =
"";
6147 return canConnectDirectlyWithRespecializationNondestructively(fromPort,
6149 eventOnlyConnection,
6151 &portToRespecialize,
6152 respecializedTypeName);
6165 bool 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;
6201 bool 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))
6237 bool 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;
6378 void 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];
6489 bool 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);
6749 VuoType *candidateFromType = specializeToPort? currentFromDataType : compatibleSpecializedType->
getBase();
6750 VuoType *candidateToType = specializeToPort? compatibleSpecializedType->
getBase() : currentToDataType;
6752 if (compatibleSpecializedType)
6755 if (candidateFromType == candidateToType)
6757 suitableTypecasts.push_back(
"");
6758 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6759 specializedTypeNameForTypecast[
""] = compatibleSpecializedType->
getBase()->
getModuleKey();
6762 if (portsPassSanityCheckToTypeconvert(fromPort,
6768 foreach (
string typecast, suitableTypecastsForCurrentTypes)
6770 suitableTypecasts.push_back(typecast);
6771 portToSpecializeForTypecast[typecast] = specializeToPort? toPort : fromPort;
6772 specializedTypeNameForTypecast[typecast] = compatibleSpecializedType->
getBase()->
getModuleKey();
6778 return suitableTypecasts;
6793 bool VuoEditorComposition::promptForBridgingSelectionFromOptions(vector<string> suitableTypecasts,
6794 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6795 map<string, string> specializedTypeNameForTypecast,
6796 string &selectedTypecast)
6798 QMenu typecastMenu(views()[0]->viewport());
6799 typecastMenu.setSeparatorsCollapsible(
false);
6800 QString spacer(
" ");
6803 set <pair<VuoRendererPort *, string> > specializationDetails;
6804 vector<string> typeconversionOptionsRequiringNoSpecialization;
6805 foreach (
string typecastClassName, suitableTypecasts)
6807 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typecastClassName];
6808 string specializedTypeName = specializedTypeNameForTypecast[typecastClassName];
6809 specializationDetails.insert(std::make_pair(portToSpecialize,
6810 specializedTypeName));
6812 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6813 if (portAlreadyHasTargetType)
6814 typeconversionOptionsRequiringNoSpecialization.push_back(typecastClassName);
6820 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()))
6822 QString menuText = getDisplayTextForSpecializationOption(portToSpecializeForTypecast[
""], specializedTypeNameForTypecast[
""]);
6823 QAction *typecastAction = typecastMenu.addAction(menuText);
6824 typecastAction->setData(QVariant(
""));
6827 bool foundSpecializationOptionsRequiringNoTypeconversion = typecastMenu.actions().size() >= 1;
6828 bool foundTypeconversionOptionsRequiringNoSpecialization = typeconversionOptionsRequiringNoSpecialization.size() >= 1;
6831 bool includingTypeconvertWithNoSpecializationHeader = foundSpecializationOptionsRequiringNoTypeconversion;
6832 if (foundTypeconversionOptionsRequiringNoSpecialization)
6834 if (foundSpecializationOptionsRequiringNoTypeconversion)
6835 typecastMenu.addSeparator();
6837 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6838 string specializedTypeName = specializedTypeNameForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6840 if (portToSpecialize && !specializedTypeName.empty())
6842 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6843 QAction *typecastAction = typecastMenu.addAction(menuText);
6844 typecastAction->setEnabled(
false);
6845 includingTypeconvertWithNoSpecializationHeader =
true;
6849 foreach (
string typecastClassName, typeconversionOptionsRequiringNoSpecialization)
6855 typecastAction->setData(QVariant(typecastClassName.c_str()));
6860 for (set<pair<VuoRendererPort *, string> >::iterator i = specializationDetails.begin(); i != specializationDetails.end(); ++i)
6863 string specializedTypeName = i->second;
6866 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()) &&
6867 (portToSpecializeForTypecast[
""] == portToSpecialize) &&
6868 (specializedTypeNameForTypecast[
""] == specializedTypeName))
6874 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6875 if (portAlreadyHasTargetType)
6880 if (typecastMenu.actions().size() >= 1)
6881 typecastMenu.addSeparator();
6883 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6884 QAction *typecastAction = typecastMenu.addAction(menuText);
6885 typecastAction->setEnabled(
false);
6888 foreach (
string typecastClassName, suitableTypecasts)
6890 if ((portToSpecializeForTypecast[typecastClassName] == portToSpecialize) &&
6891 (specializedTypeNameForTypecast[typecastClassName] == specializedTypeName))
6897 typecastAction->setData(QVariant(typecastClassName.c_str()));
6903 menuSelectionInProgress =
true;
6904 QAction *selectedTypecastAction = typecastMenu.exec(QCursor::pos());
6905 menuSelectionInProgress =
false;
6907 selectedTypecast = (selectedTypecastAction? selectedTypecastAction->data().toString().toUtf8().constData() :
"");
6908 return selectedTypecastAction;
6914 QString VuoEditorComposition::getDisplayTextForSpecializationOption(
VuoRendererPort *portToSpecialize,
string specializedTypeName)
6916 if (!portToSpecialize || specializedTypeName.empty())
6919 bool isInput = portToSpecialize && portToSpecialize->
getInput();
6920 QString typeDisplayName = compiler->
getType(specializedTypeName)?
6922 specializedTypeName.c_str();
6924 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6926 QString displayText;
6927 if (portAlreadyHasTargetType)
6932 displayText = tr(
"Keep Input Port as %1");
6937 displayText = tr(
"Keep Output Port as %1");
6945 displayText = tr(
"Change Input Port to %1");
6950 displayText = tr(
"Change Output Port to %1");
6954 return displayText.arg(typeDisplayName);
6973 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
6974 if (topLevelComposition->isRunningThreadUnsafe())
6976 portValue = isInput ?
6977 topLevelComposition->runner->getInputPortValue(thisCompositionIdentifier, runningPortIdentifier) :
6978 topLevelComposition->runner->getOutputPortValue(thisCompositionIdentifier, runningPortIdentifier);
6982 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, getPortValue);
6990 string VuoEditorComposition::getIdentifierForRunningPort(
VuoPort *runningPort)
7009 return dynamic_cast<VuoPublishedPort *
>(staticPort)->getClass()->getName();
7015 string nodeIdentifier =
"";
7025 if (staticPort->
hasCompiler() && !nodeIdentifier.empty())
7069 if (candidateInputPort == port)
7073 if (candidateOutputPort == port)
7089 map<string, VuoPortPopover *>::iterator popover = activePortPopovers.find(portID);
7090 if (popover != activePortPopovers.end())
7091 return popover->second;
7104 void VuoEditorComposition::enableInactivePopoverForPort(
VuoRendererPort *rp)
7107 bool popoverJustClosedAtLastEvent = portsWithPopoversClosedAtLastEvent.find(portID) != portsWithPopoversClosedAtLastEvent.end();
7108 if (!popoverJustClosedAtLastEvent)
7117 if (!popoverEventsEnabled)
7123 VUserLog(
"%s: Open popover for %s",
7127 dispatch_sync(runCompositionQueue, ^{
7129 dispatch_sync(activePortPopoversQueue, ^{
7131 if (activePortPopovers.find(portID) == activePortPopovers.end())
7135 VuoPortPopover *popover = new VuoPortPopover(port, this, views()[0]->viewport());
7137 connect(popover, &VuoPortPopover::popoverClosedForPort, this, &VuoEditorComposition::disablePopoverForPortThreadSafe);
7138 connect(popover, &VuoPortPopover::popoverDetachedFromPort, [=]{
7139 VUserLog(
"%s: Detach popover for %s",
7140 window->getWindowTitleWithoutPlaceholder().toUtf8().data(),
7152 const int cutoffMargin = 16;
7153 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
7154 if (portLeftInScene.x() + popover->size().width() + cutoffMargin > viewportRect.right())
7155 portLeftInScene = QPoint(viewportRect.right() - popover->size().width() - cutoffMargin, portLeftInScene.y());
7156 if (portLeftInScene.y() + popover->size().height() + cutoffMargin > viewportRect.bottom())
7157 portLeftInScene = QPoint(portLeftInScene.x(), viewportRect.bottom() - popover->size().height() - cutoffMargin);
7159 QPoint popoverLeftInView = views()[0]->mapFromScene(portLeftInScene);
7161 const QPoint offset = QPoint(12, 6);
7163 QPoint popoverTopLeft = popoverLeftInView + offset;
7164 popover->move(popoverTopLeft);
7167 activePortPopovers[portID] = popover;
7169 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7196 VUserLog(
"%s: Close popover for %s",
7201 map<string, VuoPortPopover *>::iterator i = activePortPopovers.find(portID);
7202 if (i != activePortPopovers.end())
7204 popover = i->second;
7205 activePortPopovers.erase(i);
7211 popover->deleteLater();
7214 bool isInput =
false;
7222 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7223 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7225 dispatch_async(topLevelComposition->runCompositionQueue, ^{
7226 if (topLevelComposition->isRunningThreadUnsafe())
7229 topLevelComposition->runner->unsubscribeFromInputPortTelemetry(thisCompositionIdentifier, portID) :
7230 topLevelComposition->runner->unsubscribeFromOutputPortTelemetry(thisCompositionIdentifier, portID));
7240 void VuoEditorComposition::disablePopoverForPortThreadSafe(
string portID)
7242 dispatch_sync(activePortPopoversQueue, ^{
7252 disablePortPopovers();
7263 errorPopover->hide();
7264 errorPopover->deleteLater();
7267 errorPopovers.clear();
7276 dispatch_sync(activePortPopoversQueue, ^{
7277 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7278 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7280 string portID = i->first;
7281 bool shouldDisable = false;
7284 shouldDisable = true;
7287 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7288 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7303 dispatch_sync(activePortPopoversQueue, ^{
7304 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7305 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7307 string portID = i->first;
7309 bool foundPort = identifierCache->doForPortWithIdentifier(portID, [](VuoPort *port) {});
7322 if (recordWhichPopoversClosed)
7323 portsWithPopoversClosedAtLastEvent.clear();
7325 dispatch_sync(activePortPopoversQueue, ^{
7326 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7327 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7329 string portID = i->first;
7330 bool shouldDisable = false;
7333 shouldDisable = true;
7336 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7337 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7347 portsWithPopoversClosedAtLastEvent.insert(portID);
7359 moveDetachedPortPopoversBy(dx, dy);
7360 moveErrorPopoversBy(dx, dy);
7366 void VuoEditorComposition::moveErrorPopoversBy(
int dx,
int dy)
7369 errorPopover->move(errorPopover->pos().x()+dx, errorPopover->pos().y()+dy);
7375 void VuoEditorComposition::moveDetachedPortPopoversBy(
int dx,
int dy)
7377 dispatch_sync(activePortPopoversQueue, ^{
7378 map<string, VuoPortPopover *> portPopovers = activePortPopovers;
7379 for (map<string, VuoPortPopover *>::iterator i = portPopovers.begin(); i != portPopovers.end(); ++i)
7381 VuoPortPopover *popover = i->second;
7382 if (popover && popover->getDetached())
7383 popover->move(popover->pos().x()+dx, popover->pos().y()+dy);
7391 void VuoEditorComposition::setPopoversHideOnDeactivate(
bool shouldHide)
7393 dispatch_sync(activePortPopoversQueue, ^{
7394 auto portPopovers = activePortPopovers;
7395 for (
auto i : portPopovers)
7400 id nsWindow = (id)VuoPopover::getWindowForPopover(popover);
7401 ((void (*)(id, SEL, BOOL))objc_msgSend)(nsWindow, sel_getUid(
"setHidesOnDeactivate:"), shouldHide);
7413 dispatch_sync(activePortPopoversQueue, ^{
7414 for (map<string, VuoPortPopover *>::iterator i = activePortPopovers.begin(); i != activePortPopovers.end(); ++i)
7416 string portID = i->first;
7417 VuoPortPopover *popover = i->second;
7418 bool shouldUpdate = false;
7421 shouldUpdate = true;
7424 identifierCache->doForPortWithIdentifier(portID, [&shouldUpdate, node](VuoPort *port) {
7425 shouldUpdate = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7430 QMetaObject::invokeMethod(popover,
"updateTextAndResize", Qt::QueuedConnection);
7447 string popoverCompositionIdentifier,
7458 string portSummary = (isInput ?
7462 dispatch_async(popoverComposition->activePortPopoversQueue, ^{
7463 VuoPortPopover *popover = popoverComposition->getActivePopoverForPort(portID);
7466 QMetaObject::invokeMethod(popover,
"updateDataValueImmediately", Qt::QueuedConnection, Q_ARG(QString, portSummary.c_str()));
7467 QMetaObject::invokeMethod(popover,
"setCompositionRunning", Qt::QueuedConnection, Q_ARG(bool, true), Q_ARG(bool, false));
7481 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7483 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7484 if (topLevelComposition->isRunningThreadUnsafe())
7485 topLevelComposition->updateDataInPortPopoverFromRunningTopLevelComposition(this, thisCompositionIdentifier, portID);
7495 bool receivedEvent,
bool receivedData,
string dataSummary)
7499 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7500 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7502 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7503 Q_ARG(bool, receivedEvent),
7504 Q_ARG(bool, receivedData),
7505 Q_ARG(QString, dataSummary.c_str()));
7508 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7516 bool sentEvent,
bool sentData,
string dataSummary)
7520 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7521 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7523 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7524 Q_ARG(bool, sentEvent),
7525 Q_ARG(bool, sentData),
7526 Q_ARG(QString, dataSummary.c_str()));
7529 if (matchingComposition->showEventsMode && sentEvent)
7534 port->getRenderer()->setFiredEvent();
7535 matchingComposition->animatePort(port->getRenderer());
7540 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7551 dispatch_async(matchingComposition->runCompositionQueue, ^{
7552 if (matchingComposition->isRunningThreadUnsafe())
7554 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7555 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7557 QMetaObject::invokeMethod(popover,
"incrementDroppedEventCount", Qt::QueuedConnection);
7562 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7573 if (matchingComposition->showEventsMode)
7575 dispatch_async(this->runCompositionQueue, ^{
7576 if (this->isRunningThreadUnsafe())
7585 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7596 if (matchingComposition->showEventsMode)
7598 dispatch_async(this->runCompositionQueue, ^{
7599 if (this->isRunningThreadUnsafe())
7608 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7637 return showEventsMode;
7645 this->showEventsMode = showEventsMode;
7653 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7654 if (topLevelComposition->isRunningThreadUnsafe())
7655 topLevelComposition->runner->subscribeToEventTelemetry(thisCompositionIdentifier);
7658 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, subscribe);
7666 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7667 if (topLevelComposition->isRunningThreadUnsafe())
7668 topLevelComposition->runner->unsubscribeFromEventTelemetry(thisCompositionIdentifier);
7671 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, unsubscribe);
7707 QGraphicsItemAnimation * VuoEditorComposition::setUpAnimationForPort(QGraphicsItemAnimation *animation,
VuoRendererPort *port)
7715 animatedPort->setZValue(VuoRendererItem::triggerAnimationZValue);
7718 animation->setItem(animatedPort);
7719 animation->setScaleAt(0.0, 1, 1);
7720 animation->setScaleAt(0.999, 3, 3);
7721 animation->setScaleAt(1.0, 1, 1);
7723 QTimeLine *animationTimeline = animation->timeLine();
7724 animationTimeline->setFrameRange(0, 100);
7725 animationTimeline->setUpdateInterval(showEventsModeUpdateInterval);
7726 animationTimeline->setCurveShape(QTimeLine::LinearCurve);
7728 preparedAnimations.insert(animation);
7729 animationForTimeline[animation->timeLine()] = animation;
7731 connect(animationTimeline, &QTimeLine::valueChanged,
this, &VuoEditorComposition::updatePortAnimation);
7732 connect(animationTimeline, &QTimeLine::finished,
this, &VuoEditorComposition::endPortAnimation);
7742 dispatch_async(dispatch_get_main_queue(), ^{
7743 QGraphicsItemAnimation *animation = getAvailableAnimationForPort(port);
7749 if (animation->timeLine()->state() == QTimeLine::Running)
7750 animation->timeLine()->setCurrentTime(0);
7754 animatedPort->setPos(port->pos());
7755 animatedPort->setVisible(
true);
7756 animation->timeLine()->start();
7765 QGraphicsItemAnimation * VuoEditorComposition::getAvailableAnimationForPort(
VuoRendererPort *port)
7767 vector<QGraphicsItemAnimation *> animations = port->
getAnimations();
7769 QGraphicsItemAnimation *mostAdvancedAnimation = NULL;
7770 qreal maxPercentAdvanced = -1;
7772 for (
int i = 0; i < animations.size(); ++i)
7774 QGraphicsItemAnimation *animation = animations[i];
7775 bool animationPrepared = (preparedAnimations.find(animation) != preparedAnimations.end());
7776 bool animationRunning = (animation->timeLine()->state() == QTimeLine::Running);
7778 if (! animationPrepared)
7779 return setUpAnimationForPort(animation, port);
7781 else if (! animationRunning)
7786 qreal percentAdvanced = animation->timeLine()->currentValue();
7787 if (percentAdvanced > maxPercentAdvanced)
7789 mostAdvancedAnimation = animation;
7790 maxPercentAdvanced = percentAdvanced;
7796 return (maxPercentAdvanced >= 0.5? mostAdvancedAnimation : NULL);
7804 void VuoEditorComposition::updatePortAnimation(qreal value)
7806 QTimeLine *animationTimeline = (QTimeLine *)sender();
7807 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7809 const qreal multiplier = 1000.;
7817 void VuoEditorComposition::endPortAnimation(
void)
7819 QTimeLine *animationTimeline = (QTimeLine *)sender();
7820 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7822 animatedPort->setVisible(
false);
7828 void VuoEditorComposition::setDisableDragStickiness(
bool disable)
7830 this->dragStickinessDisabled = disable;
7838 this->ignoreApplicationStateChangeEvents = ignore;
7848 this->popoverEventsEnabled = enable;
7857 refreshComponentAlphaLevelTimer->start();
7865 refreshComponentAlphaLevelTimer->stop();
7888 if (!activeProtocol)
7895 if (!
getBase()->
getCompiler()->getCachedGraph()->mayEventsReachPublishedOutputPorts())
7897 QString errorHeadline = tr(
"<b>This composition doesn't send any images to <code>outputImage</code>.</b>");
7898 QString errorDetails = tr(
"<p>To export, your composition should use the data and events from the published input ports "
7899 "to output a stream of images through the <code>outputImage</code> published output port.</p>");
7901 if (isExportingMovie)
7902 errorDetails.append(
"<p>Alternatively, you can record a realtime movie by running the composition and selecting File > Start Recording.</p>");
7905 QMessageBox messageBox(window);
7906 messageBox.setWindowFlags(Qt::Sheet);
7907 messageBox.setWindowModality(Qt::WindowModal);
7909 messageBox.setTextFormat(Qt::RichText);
7911 messageBox.setStandardButtons(QMessageBox::Help | QMessageBox::Ok);
7912 messageBox.setButtonText(QMessageBox::Help, tr(
"Open an Example"));
7913 messageBox.setButtonText(QMessageBox::Ok, tr(
"OK"));
7914 messageBox.setDefaultButton(QMessageBox::Ok);
7916 messageBox.setText(errorHeadline);
7917 messageBox.setInformativeText(
"<style>p{" + fonts->
getCSS(fonts->
dialogBodyFont()) +
"}</style>" + errorDetails);
7919 if (messageBox.exec() == QMessageBox::Help)
7921 map<QString, QString> examples =
static_cast<VuoEditor *
>(qApp)->getExampleCompositionsForProtocol(activeProtocol);
7922 map<QString, QString>::iterator i = examples.begin();
7923 if (i != examples.end())
7953 string dir, file, ext;
7967 if (! customizedName.empty())
7968 return QString::fromStdString(customizedName);
7986 string fileNameContentPart = (fileNameParts.size() >= 2 && fileNameParts[fileNameParts.size()-1] ==
"vuo"?
7987 fileNameParts[fileNameParts.size()-2] :
7988 (fileNameParts.size() >= 1? fileNameParts[fileNameParts.size()-1] :
""));
7991 if (QRegExp(
"\\s").indexIn(fileNameContentPart.c_str()) != -1)
7993 string formattedName = fileNameContentPart;
7994 if (formattedName.size() >= 1)
7995 formattedName[0] = toupper(formattedName[0]);
7997 return QString(formattedName.c_str());
8011 QStringList wordsInName = nodeSetName.split(QRegularExpression(
"\\."));
8012 if (wordsInName.size() < 2 || wordsInName[0] !=
"vuo")
8018 map<QString, QString> wordsToReformat;
8019 wordsToReformat[
"artnet"] =
"Art-Net";
8020 wordsToReformat[
"bcf2000"] =
"BCF2000";
8021 wordsToReformat[
"hid"] =
"HID";
8022 wordsToReformat[
"midi"] =
"MIDI";
8023 wordsToReformat[
"ndi"] =
"NDI";
8024 wordsToReformat[
"osc"] =
"OSC";
8025 wordsToReformat[
"rss"] =
"RSS";
8026 wordsToReformat[
"ui"] =
"UI";
8027 wordsToReformat[
"url"] =
"URL";
8029 QString nodeSetDisplayName =
"";
8030 for (
int i = 1; i < wordsInName.size(); ++i)
8032 QString currentWord = wordsInName[i];
8033 if (currentWord.size() >= 1)
8035 if (wordsToReformat.find(currentWord.toLower()) != wordsToReformat.end())
8036 currentWord = wordsToReformat.at(currentWord.toLower());
8038 currentWord[0] = currentWord[0].toUpper();
8040 nodeSetDisplayName += currentWord;
8042 if (i < wordsInName.size()-1)
8043 nodeSetDisplayName +=
" ";
8046 return nodeSetDisplayName;
8059 string formattedTypeName =
"";
8067 formattedTypeName =
"List of " + formattedInnerTypeName +
" elements";
8073 return formattedTypeName.c_str();
8092 return "Transform2D";
8094 return "Transform3D";
8102 bool VuoEditorComposition::nodeSetMenuActionLessThan(QAction *action1, QAction *action2)
8104 QString item1Text = action1->text();
8105 QString item2Text = action2->text();
8108 const QString listPrefix =
"List of ";
8109 const QString builtInTypePrefix =
"Vuo";
8111 if (item1Text.startsWith(listPrefix))
8113 item1Text.remove(0, listPrefix.length());
8114 if (item1Text.startsWith(builtInTypePrefix))
8115 item1Text.remove(0, builtInTypePrefix.length());
8118 if (item2Text.startsWith(listPrefix))
8120 item2Text.remove(0, listPrefix.length());
8121 if (item2Text.startsWith(builtInTypePrefix))
8122 item2Text.remove(0, builtInTypePrefix.length());
8126 return (item1Text.compare(item2Text, Qt::CaseInsensitive) < 0);
8133 bool VuoEditorComposition::itemHigherOnCanvas(QGraphicsItem *item1, QGraphicsItem *item2)
8135 qreal item1Y = item1->scenePos().y();
8136 qreal item2Y = item2->scenePos().y();
8138 qreal item1X = item1->scenePos().x();
8139 qreal item2X = item2->scenePos().x();
8141 if (item1Y == item2Y)
8142 return (item1X < item2X);
8144 return item1Y < item2Y;
8157 string originalGenericNode1ClassName, originalGenericNode2ClassName;
8173 vector<string> node1Keywords = node1->
getKeywords();
8174 vector<string> node2Keywords = node2->
getKeywords();
8186 node1Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8190 node2Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8192 set<string> node1KeywordSet(node1Keywords.begin(), node1Keywords.end());
8193 set<string> node2KeywordSet(node2Keywords.begin(), node2Keywords.end());
8195 set<string> nodeKeywordsIntersection;
8196 std::set_intersection(node1KeywordSet.begin(), node1KeywordSet.end(),
8197 node2KeywordSet.begin(), node2KeywordSet.end(),
8198 std::inserter(nodeKeywordsIntersection, nodeKeywordsIntersection.end()));
8200 set<string> nodeKeywordsUnion = node1KeywordSet;
8201 nodeKeywordsUnion.insert(node2KeywordSet.begin(), node2KeywordSet.end());
8204 if (nodeKeywordsUnion.size() == 0)
8208 double nodeSimilarity = nodeKeywordsIntersection.size()/(1.0*nodeKeywordsUnion.size());
8210 return nodeSimilarity;
8229 VuoEditorComposition::~VuoEditorComposition()
8231 dispatch_sync(runCompositionQueue, ^{});
8232 dispatch_release(runCompositionQueue);
8234 preparedAnimations.clear();
8235 animationForTimeline.clear();
8237 delete identifierCache;
8263 vector<VuoRendererPort *> sortedPortsToPublish;
8264 foreach (
string portID, portsToPublish)
8268 sortedPortsToPublish.push_back(port->
getRenderer());
8270 std::sort(sortedPortsToPublish.begin(), sortedPortsToPublish.end(), itemHigherOnCanvas);
8272 map<string, string> publishedPortNames;
8279 string publishedPortName = (!specializedPublishedPortName.empty()?
8280 specializedPublishedPortName :
8289 return publishedPortNames;
8317 void VuoEditorComposition::repositionPopover()
8322 const int cutoffMargin = 16;
8323 if (popover->pos().x()+popover->size().width()+cutoffMargin > views()[0]->viewport()->rect().right())
8324 popover->move(QPoint(views()[0]->viewport()->rect().right()-popover->size().width()-cutoffMargin, popover->pos().y()));
8326 if (popover->pos().y()+popover->size().height()+cutoffMargin > views()[0]->viewport()->rect().bottom())
8327 popover->move(QPoint(popover->pos().x(), views()[0]->viewport()->rect().bottom()-popover->size().height()-cutoffMargin));