62#include <ApplicationServices/ApplicationServices.h>
63#include <objc/objc-runtime.h>
66const qreal VuoEditorComposition::nodeMoveRate = 15;
67const qreal VuoEditorComposition::nodeMoveRateMultiplier = 4;
69const qreal VuoEditorComposition::showEventsModeUpdateInterval = 1000/20.;
70const int VuoEditorComposition::initialChangeNodeSuggestionCount = 10;
79 VuoEditorComposition_Pro();
82 this->window = window;
84 inputEditorManager = NULL;
85 activeProtocol = NULL;
87 runningComposition = NULL;
88 runningCompositionActiveDriver = NULL;
89 runningCompositionLibraries = NULL;
90 stopRequested =
false;
91 duplicateOnNextMouseMove =
false;
92 duplicationDragInProgress =
false;
93 duplicationCancelled =
false;
94 cursorPosBeforeDuplicationDragMove = QPointF(0,0);
95 cableInProgress = NULL;
96 cableInProgressWasNew =
false;
97 cableInProgressShouldBeWireless =
false;
98 portWithDragInitiated = NULL;
99 cableWithYankInitiated = NULL;
100 menuSelectionInProgress =
false;
101 previousNearbyItem = NULL;
102 dragStickinessDisabled =
false;
103 ignoreApplicationStateChangeEvents =
false;
104 popoverEventsEnabled =
true;
105 runCompositionQueue = dispatch_queue_create(
"org.vuo.editor.run", NULL);
106 activePortPopoversQueue = dispatch_queue_create(
"org.vuo.editor.popovers", NULL);
108 errorMarkingUpdatesEnabled =
true;
109 triggerPortToRefire =
"";
111 contextMenuDeleteSelected =
new QAction(NULL);
112 contextMenuHideSelectedCables =
new QAction(NULL);
113 contextMenuRenameSelected =
new QAction(NULL);
114 contextMenuRefactorSelected =
new QAction(NULL);
115 contextMenuPublishPort =
new QAction(NULL);
116 contextMenuDeleteCables =
new QAction(NULL);
117 contextMenuHideCables =
new QAction(NULL);
118 contextMenuUnhideCables =
new QAction(NULL);
119 contextMenuFireEvent =
new QAction(NULL);
120 contextMenuAddInputPort =
new QAction(NULL);
121 contextMenuRemoveInputPort =
new QAction(NULL);
122 contextMenuSetPortConstant =
new QAction(NULL);
123 contextMenuEditSelectedComments =
new QAction(NULL);
125 contextMenuChangeNode = NULL;
126 contextMenuSpecializeGenericType = NULL;
128 contextMenuFireEvent->setText(tr(
"Fire Event"));
129 contextMenuHideSelectedCables->setText(tr(
"Hide"));
130 contextMenuRenameSelected->setText(tr(
"Rename…"));
131 contextMenuRefactorSelected->setText(tr(
"Package as Subcomposition"));
132 contextMenuAddInputPort->setText(tr(
"Add Input Port"));
133 contextMenuRemoveInputPort->setText(tr(
"Remove Input Port"));
134 contextMenuSetPortConstant->setText(tr(
"Edit Value…"));
135 contextMenuEditSelectedComments->setText(tr(
"Edit…"));
142 connect(contextMenuDeleteCables, &QAction::triggered,
this, &VuoEditorComposition::deleteConnectedCables);
143 connect(contextMenuHideCables, &QAction::triggered,
this, &VuoEditorComposition::hideConnectedCables);
144 connect(contextMenuUnhideCables, &QAction::triggered,
this, &VuoEditorComposition::unhideConnectedCables);
145 connect(contextMenuFireEvent, &QAction::triggered,
this,
static_cast<void (
VuoEditorComposition::*)()
>(&VuoEditorComposition::fireTriggerPortEvent));
146 connect(contextMenuAddInputPort, &QAction::triggered,
this, &VuoEditorComposition::addInputPort);
147 connect(contextMenuRemoveInputPort, &QAction::triggered,
this, &VuoEditorComposition::removeInputPort);
148 connect(contextMenuEditSelectedComments, &QAction::triggered,
this, &VuoEditorComposition::editSelectedComments);
153 connect(contextMenuSetPortConstant, &QAction::triggered,
this, &VuoEditorComposition::setPortConstant, Qt::QueuedConnection);
160 QAction *action =
new QAction(label,
this);
161 connect(action, &QAction::triggered, [=](){
164 contextMenuThrottlingActions.append(action);
175 QAction *action =
new QAction(label,
this);
176 connect(action, &QAction::triggered, [=](){
182 QColor fill(0,0,0,0);
184 if (tint != VuoNode::TintNone)
194 p.drawEllipse(3, 3, 10, 10);
196 action->setIcon(*icon);
200 contextMenuTintActions.append(action);
202 addTintAction(tr(
"Yellow"), VuoNode::TintYellow);
203 addTintAction(tr(
"Tangerine"), VuoNode::TintTangerine);
204 addTintAction(tr(
"Orange"), VuoNode::TintOrange);
205 addTintAction(tr(
"Magenta"), VuoNode::TintMagenta);
206 addTintAction(tr(
"Violet"), VuoNode::TintViolet);
207 addTintAction(tr(
"Blue"), VuoNode::TintBlue);
208 addTintAction(tr(
"Cyan"), VuoNode::TintCyan);
209 addTintAction(tr(
"Green"), VuoNode::TintGreen);
210 addTintAction(tr(
"Lime"), VuoNode::TintLime);
211 addTintAction(tr(
"None"), VuoNode::TintNone);
215 this->refreshComponentAlphaLevelTimer =
new QTimer(
this);
216 this->refreshComponentAlphaLevelTimer->setObjectName(
"VuoEditorComposition::refreshComponentAlphaLevelTimer");
217 refreshComponentAlphaLevelTimer->setInterval(showEventsModeUpdateInterval);
235 connect(
static_cast<VuoEditor *
>(qApp), &VuoEditor::focusChanged,
this, &VuoEditorComposition::updatePopoversForActiveWindowChange, Qt::QueuedConnection);
237 setPopoversHideOnDeactivate(
true);
240 setPopoversHideOnDeactivate(
false);
252 this->compiler = compiler;
268 this->moduleManager = moduleManager;
277 return moduleManager;
287 this->inputEditorManager = inputEditorManager;
297 return this->inputEditorManager;
314 setCustomConstantsForNewNode(rn);
331 __block
bool isAllowed =
true;
334 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
336 bool nodeIsThisComposition = (compositionModuleKey == nodeClass->
getBase()->
getClassName());
339 auto iter = std::find_if(dependencies.begin(), dependencies.end(), [=](
const string &d){ return d == compositionModuleKey; });
340 bool nodeContainsThisComposition = (iter != dependencies.end());
342 isAllowed = ! (nodeIsThisComposition || nodeContainsThisComposition);
351 compiler->
createNode(nodeClass, title, x, y));
369 vector<string> inputPortClassNames;
370 vector<string> outputPortClassNames;
375 inputPortClassNames.push_back(portClass->
getName());
378 outputPortClassNames.push_back(portClass->
getName());
382 dummyNodeClass->
newNode(modelNode) :
391void VuoEditorComposition::setCustomConstantsForNewNode(
VuoRendererNode *newNode)
399 QString currentYear = QString::number(QDateTime::currentDateTime().date().year());
423 disablePortPopovers(rn);
448 map<VuoCable *, VuoPort *> cablesToTransferFromPort;
449 map<VuoCable *, VuoPort *> cablesToTransferToPort;
450 set<VuoCable *> cablesToRemove;
454 vector<VuoRendererInputDrawer *> attachedDrawers;
455 vector<VuoRendererNode *> collapsedTypecasts;
457 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
463 collapsedTypecasts.push_back(typecastNode);
471 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
476 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
481 attachedDrawers.push_back(attachedDrawer);
488 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferFromPort.begin(); i != cablesToTransferFromPort.end(); ++i)
489 i->first->getRenderer()->setFrom(newNode, i->second);
490 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferToPort.begin(); i != cablesToTransferToPort.end(); ++i)
491 i->first->getRenderer()->setTo(newNode, i->second);
492 foreach (
VuoCable *cable, cablesToRemove)
509 if (! (oldDataType == newDataType && oldDataType && !
dynamic_cast<VuoGenericType *
>(oldDataType)) )
513 string oldConstantValue;
515 oldConstantValue = oldInputPort->getRenderer()->getConstantAsString();
517 oldConstantValue = oldInputPort->getRawInitialValue();
549 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
562 disablePortPopovers(oldNode);
567 string graphvizIdentifier;
568 string graphvizDeclaration;
598 if (cableHidden && emitHiddenCableNotification)
610 if (cableHidden && emitHiddenCableNotification)
636 set<VuoRendererNode *> &createdNodes,
637 set<VuoRendererCable *> &createdCables)
651 foreach (QGraphicsItem *component, addedComponents)
654 if (rn && !createButDoNotAdd)
658 return addedComponents;
670 set<QGraphicsItem *> dependentAttachments;
674 for(
unsigned int i = 0; i < inputPorts.size(); ++i)
676 set<VuoRendererInputAttachment *> portUpstreamAttachments = inputPorts[i]->getRenderer()->getAllUnderlyingUpstreamInputAttachments();
677 dependentAttachments.insert(portUpstreamAttachments.begin(), portUpstreamAttachments.end());
682 if (nodeAsAttachment && includeCoattachments)
685 dependentAttachments.insert(coattachment->
getRenderer());
688 return dependentAttachments;
702 set<string> selectedNodeIDs;
709 set<string> selectedCommentIDs;
716 set<string> selectedCableIDs;
726 foreach (QGraphicsItem *item, items())
733 if (!currentNodeID.empty() && (selectedNodeIDs.find(currentNodeID) != selectedNodeIDs.end()))
734 item->setSelected(
true);
742 if (!currentCommentID.empty() && (selectedCommentIDs.find(currentCommentID) != selectedCommentIDs.end()))
743 item->setSelected(
true);
751 if (!currentCableID.empty() && (selectedCableIDs.find(currentCableID) != selectedCableIDs.end()))
752 item->setSelected(
true);
775 if ((portName ==
"expression") &&
799 if (inputVariablesBeforeEditing != inputVariablesAfterEditing)
805 QList<QGraphicsItem *> attachmentsToRemove;
812 attachmentsToRemove.append(attachment);
824 if (oldValueList && oldDictionary && oldKeyList)
826 set<VuoRendererNode *> nodesToAdd;
827 set<VuoRendererCable *> cablesToAdd;
845 undoStack->push(
new VuoCommandReplaceNode(oldValueList, newValueList, editorWindow,
"Set Port Constant",
false,
false));
846 undoStack->push(
new VuoCommandReplaceNode(oldKeyList, newKeyList, editorWindow,
"Set Port Constant",
false,
true));
847 undoStack->push(
new VuoCommandReplaceNode(oldDictionary, newDictionary, editorWindow,
"Set Port Constant",
false,
true));
851 cable->
setFrom(
nullptr,
nullptr);
852 cable->
setTo(
nullptr,
nullptr);
871 if (commandDescription.empty())
874 commandDescription =
"Reset";
876 commandDescription =
"Delete";
879 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
888 if (commandDescription.empty())
889 commandDescription =
"Delete";
891 QList<QGraphicsItem *> selectedNodes;
893 selectedNodes.append(node);
933void VuoEditorComposition::insertNode()
935 QAction *sender = (QAction *)QObject::sender();
936 QPair<QPointF, QString> pair = sender->data().value<QPair<QPointF, QString> >();
938 QList<QGraphicsItem *> newNodes;
945 newNodes.append(newNode);
953void VuoEditorComposition::insertComment()
955 QAction *sender = (QAction *)QObject::sender();
956 QPointF scenePos = sender->data().value<QPointF>();
964void VuoEditorComposition::insertSubcomposition()
966 QAction *sender = (QAction *)QObject::sender();
967 QPointF scenePos = sender->data().value<QPointF>();
978 QAction *sender = (QAction *)QObject::sender();
993void VuoEditorComposition::deleteConnectedCables()
995 QAction *sender = (QAction *)QObject::sender();
998 QList<QGraphicsItem *> cablesToRemove;
1029void VuoEditorComposition::hideConnectedCables()
1031 QAction *sender = (QAction *)QObject::sender();
1033 set<VuoRendererCable *> cablesToHide;
1064void VuoEditorComposition::unhideConnectedCables()
1066 QAction *sender = (QAction *)QObject::sender();
1068 set<VuoRendererCable *> cablesToUnhide;
1097void VuoEditorComposition::fireTriggerPortEvent()
1099 QAction *sender = (QAction *)QObject::sender();
1101 fireTriggerPortEvent(port->
getBase());
1117 if (triggerPortToRefire.empty())
1120 VuoPort *triggerPort =
nullptr;
1133 if (portID != this->triggerPortToRefire)
1135 this->triggerPortToRefire = portID;
1144void VuoEditorComposition::setPortConstant()
1146 QAction *sender = (QAction *)QObject::sender();
1157void VuoEditorComposition::setPortConstantToValue(
VuoRendererPort *port,
string value)
1167void VuoEditorComposition::specializeGenericPortType()
1169 QAction *sender = (QAction *)QObject::sender();
1170 QList<QVariant> portAndSpecializedType= sender->data().toList();
1172 QString specializedTypeName = portAndSpecializedType[1].toString();
1184 emit
specializePort(port, specializedTypeName.toUtf8().constData());
1191void VuoEditorComposition::unspecializePortType()
1193 QAction *sender = (QAction *)QObject::sender();
1217 portToUnspecialize,
true);
1218 map<VuoNode *, set<VuoPort *> > portsToUnspecializeForNode;
1219 for (pair<VuoNode *, VuoPort *> i : connectedPotentiallyGenericPorts)
1222 VuoPort *connectedPort = i.second;
1226 if (isPortCurrentlyRevertible(connectedPort->
getRenderer()))
1227 portsToUnspecializeForNode[node].insert(connectedPort);
1230 for (map<
VuoNode *, set<VuoPort *> >::iterator i = portsToUnspecializeForNode.begin(); i != portsToUnspecializeForNode.end(); ++i)
1233 set<VuoPort *> ports = i->second;
1235 if (shouldOutputNodesToReplace)
1238 set<VuoPortClass *> portClasses;
1239 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1240 portClasses.insert((*j)->getClass());
1243 nodesToReplace[node] = unspecializedNodeClassName;
1248 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1253 bool areEndsCompatible =
false;
1256 areEndsCompatible =
true;
1261 if (portOnOtherEnd && isPortCurrentlyRevertible(portOnOtherEnd->
getRenderer()))
1266 if (specializedNodeClassOnOtherEnd)
1269 if (! typeOnOtherEnd ||
dynamic_cast<VuoGenericType *
>(typeOnOtherEnd))
1270 areEndsCompatible =
true;
1275 if (! areEndsCompatible && (cable != cableInProgress))
1276 cablesToDelete.insert(cable);
1285void VuoEditorComposition::fireTriggerPortEvent(
VuoPort *port)
1293 string runningTriggerPortIdentifier =
"";
1294 bool isTriggerPort =
false;
1302 isTriggerPort =
true;
1320 runningTriggerPortIdentifier.c_str());
1323 bool manuallyFirableInputPortChanged = (oldManuallyFirableInputPort != newManuallyFirableInputPort);
1326 auto fireIfRunning = ^void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
1328 dispatch_async(topLevelComposition->runCompositionQueue, ^{
1329 if (topLevelComposition->isRunningThreadUnsafe())
1331 topLevelComposition->runner->fireTriggerPortEvent(thisCompositionIdentifier, runningTriggerPortIdentifier);
1336 if (! (this->showEventsMode && isTriggerPort) )
1337 this->animatePort(port->getRenderer());
1342 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1343 if (
this == topLevelComposition || ! manuallyFirableInputPortChanged)
1347 if (! newSnapshot.empty() && manuallyFirableInputPortChanged)
1348 updateRunningComposition(oldSnapshot, newSnapshot);
1350 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1356 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
1358 moduleManager->doNextTimeNodeClassIsLoaded(nodeClassName, ^{
1359 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1360 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1364 if (! newSnapshot.empty())
1365 updateRunningComposition(oldSnapshot, newSnapshot);
1370 setTriggerPortToRefire(port);
1377void VuoEditorComposition::addInputPort()
1379 QAction *sender = (QAction *)QObject::sender();
1388void VuoEditorComposition::removeInputPort()
1390 QAction *sender = (QAction *)QObject::sender();
1399void VuoEditorComposition::swapNode()
1401 QAction *sender = (QAction *)QObject::sender();
1402 QList<QVariant> nodeAndReplacementType= sender->data().toList();
1404 QString newNodeClassName = nodeAndReplacementType[1].toString();
1415 for (set<VuoRendererNode *>::iterator i = selectedNodes.begin(); i != selectedNodes.end(); ++i)
1425void VuoEditorComposition::editSelectedComments()
1429 for (set<VuoRendererComment *>::iterator i = selectedComments.begin(); i != selectedComments.end(); ++i)
1438 set<VuoRendererCable *> internalCables;
1440 for (QList<QGraphicsItem *>::iterator i = subcompositionComponents.begin(); i != subcompositionComponents.end(); ++i)
1442 QGraphicsItem *compositionComponent = *i;
1447 for (set<VuoCable *>::iterator cable = connectedCables.begin(); cable != connectedCables.end(); ++cable)
1449 VuoNode *fromNode = (*cable)->getFromNode();
1450 VuoNode *toNode = (*cable)->getToNode();
1452 if (fromNode && toNode && subcompositionComponents.contains(fromNode->
getRenderer()) && subcompositionComponents.contains(toNode->
getRenderer()))
1453 internalCables.insert((*cable)->getRenderer());
1458 return internalCables;
1466 return cableInProgress;
1475 return cableInProgressWasNew;
1483 return menuSelectionInProgress;
1491 QList<QGraphicsItem *> compositionComponents = items();
1492 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1494 QGraphicsItem *compositionComponent = *i;
1498 compositionComponent->setSelected(
true);
1507 QList<QGraphicsItem *> compositionComponents = items();
1508 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1510 QGraphicsItem *compositionComponent = *i;
1513 rcomment->setSelected(
true);
1522 QList<QGraphicsItem *> compositionComponents = items();
1523 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1525 QGraphicsItem *compositionComponent = *i;
1526 compositionComponent->setSelected(
false);
1533void VuoEditorComposition::openSelectedEditableNodes()
1538 QString actionText, sourcePath;
1551 moveItemsBy(selectedNodes, selectedComments, dx, dy,
false);
1557void VuoEditorComposition::moveNodesBy(set<VuoRendererNode *> nodes, qreal dx, qreal dy,
bool movedByDragging)
1559 set<VuoRendererComment *> comments;
1560 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1566void VuoEditorComposition::moveCommentsBy(set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1568 set<VuoRendererNode *> nodes;
1569 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1575void VuoEditorComposition::moveItemsBy(set<VuoRendererNode *> nodes, set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1577 emit
itemsMoved(nodes, comments, dx, dy, movedByDragging);
1579 for (set<VuoRendererNode *>::iterator it = nodes.begin(); it != nodes.end(); ++it)
1580 (*it)->updateConnectedCableGeometry();
1586void VuoEditorComposition::resizeCommentBy(
VuoRendererComment *comment, qreal dx, qreal dy)
1596 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1597 set<VuoRendererNode *> selectedNodes;
1598 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1600 QGraphicsItem *compositionComponent = *i;
1603 selectedNodes.insert(rn);
1606 return selectedNodes;
1614 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1615 set<VuoRendererComment *> selectedComments;
1616 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1618 QGraphicsItem *compositionComponent = *i;
1621 selectedComments.insert(rc);
1624 return selectedComments;
1632 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1633 set<VuoRendererCable *> selectedCables;
1634 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1636 QGraphicsItem *compositionComponent = *i;
1639 selectedCables.insert(rc);
1642 return selectedCables;
1650 const QMimeData *mimeData =
event->mimeData();
1651 bool disablePortHoverHighlighting =
true;
1654 if (mimeData->hasFormat(
"text/uri-list"))
1656 QList<QUrl> urls = mimeData->urls();
1660 if (portAtDropLocation)
1662 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1664 disablePortHoverHighlighting =
false;
1666 event->setDropAction(Qt::IgnoreAction);
1670 bool dragIncludesDroppableFile =
false;
1671 foreach (QUrl url, urls)
1673 char *urlZ = strdup(url.path().toUtf8().constData());
1674 bool isSupportedDragNDropFile =
1688 if (isSupportedDragNDropFile)
1690 dragIncludesDroppableFile =
true;
1695 if (!dragIncludesDroppableFile)
1696 event->setDropAction(Qt::IgnoreAction);
1703 else if (mimeData->hasFormat(
"text/plain") || mimeData->hasFormat(
"text/scsv"))
1704 event->acceptProposedAction();
1708 event->setDropAction(Qt::IgnoreAction);
1712 updateHoverHighlighting(event->scenePos(), disablePortHoverHighlighting);
1720 event->acceptProposedAction();
1736 const QMimeData *mimeData =
event->mimeData();
1739 if (mimeData->hasFormat(
"text/uri-list"))
1747 if (topCompositionPath.empty())
1749 QDir compositionDir(QDir(topCompositionPath.c_str()).canonicalPath());
1754 QList<QGraphicsItem *> newNodes;
1755 QList<QUrl> urls = mimeData->urls();
1759 if (portAtDropLocation)
1761 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1764 QString filePath = (useAbsoluteFilePaths? urls[0].path() : compositionDir.relativeFilePath(urls[0].path()));
1765 string constantValue =
"\"" + string(filePath.toUtf8().constData()) +
"\"";
1776 const int ySpacingForNewNodes = 11;
1777 int yOffsetForPreviousNewNode = -1 * ySpacingForNewNodes;
1779 foreach (QUrl url, urls)
1781 QStringList targetNodeClassNames;
1782 char *urlZ = strdup(url.path().toUtf8().constData());
1785 targetNodeClassNames +=
"vuo.image.fetch";
1789 targetNodeClassNames +=
"vuo.video.play";
1791 targetNodeClassNames +=
"vuo.video.decodeImage";
1794 targetNodeClassNames +=
"vuo.scene.fetch";
1796 targetNodeClassNames +=
"vuo.audio.file.play";
1798 targetNodeClassNames +=
"vuo.image.project.dome";
1800 targetNodeClassNames +=
"vuo.rss.fetch";
1802 targetNodeClassNames +=
"vuo.tree.fetch.json";
1804 targetNodeClassNames +=
"vuo.tree.fetch.xml";
1806 targetNodeClassNames +=
"vuo.table.fetch";
1808 targetNodeClassNames +=
"vuo.data.fetch";
1810 targetNodeClassNames +=
"vuo.app.launch";
1812 targetNodeClassNames +=
"vuo.file.list";
1816 QString selectedNodeClassName =
"";
1817 if (targetNodeClassNames.size() == 1)
1818 selectedNodeClassName = targetNodeClassNames[0];
1819 else if (targetNodeClassNames.size() > 1)
1821 QMenu nodeMenu(views()[0]->viewport());
1822 nodeMenu.setSeparatorsCollapsible(
false);
1824 foreach (QString nodeClassName, targetNodeClassNames)
1827 string nodeTitle = (nodeClass? nodeClass->
getBase()->
getDefaultTitle() : nodeClassName.toUtf8().constData());
1829 QAction *nodeAction = nodeMenu.addAction(tr(
"Insert \"%1\" Node").arg(nodeTitle.c_str()));
1830 nodeAction->setData(nodeClassName);
1833 menuSelectionInProgress =
true;
1834 QAction *selectedNode = nodeMenu.exec(QCursor::pos());
1835 menuSelectionInProgress =
false;
1837 selectedNodeClassName = (selectedNode? selectedNode->data().toString().toUtf8().constData() :
"");
1840 if (!selectedNodeClassName.isEmpty())
1843 event->scenePos().x(),
1844 event->scenePos().y() + yOffsetForPreviousNewNode + ySpacingForNewNodes);
1853 QString filePath = (useAbsoluteFilePaths? url.path() : compositionDir.relativeFilePath(url.path()));
1856 newNodes.append(newNode);
1858 yOffsetForPreviousNewNode += newNode->
boundingRect().height();
1859 yOffsetForPreviousNewNode += ySpacingForNewNodes;
1865 if (newNodes.size() > 0)
1877 else if (mimeData->hasFormat(
"text/scsv"))
1879 event->setDropAction(Qt::CopyAction);
1882 QByteArray scsvData =
event->mimeData()->data(
"text/scsv");
1883 QString scsvText = QString::fromUtf8(scsvData);
1884 QStringList nodeClassNames = scsvText.split(
';');
1889 int snapDelta = nextYPos - startPos.y();
1891 QList<QGraphicsItem *> newNodes;
1892 for (QStringList::iterator i = nodeClassNames.begin(); i != nodeClassNames.end(); ++i)
1900 int prevYPos = nextYPos;
1903 if (nextYPos <= prevYPos+newNode->boundingRect().height())
1906 newNodes.append((QGraphicsItem *)newNode);
1914 else if (mimeData->hasFormat(
"text/plain"))
1916 event->setDropAction(Qt::CopyAction);
1919 QList<QGraphicsItem *> newNodes;
1920 QStringList dropItems =
event->mimeData()->text().split(
'\n');
1921 QString nodeClassName = dropItems[0];
1925 QPoint hotSpot = (dropItems.size() >= 3? QPoint(dropItems[1].toInt(), dropItems[2].toInt()) : QPoint(0,0));
1927 event->scenePos().x()-hotSpot.x()+1,
1930 newNodes.append(newNode);
1947 QGraphicsScene::sendEvent(nearbyItem, event);
1952 QGraphicsScene::mouseDoubleClickEvent(event);
1960 QPointF scenePos =
event->scenePos();
1963 if (event->button() == Qt::LeftButton)
1968 if (duplicationCancelled)
1970 QGraphicsItem *itemClickedDirectly = itemAt(scenePos, views()[0]->transform());
1976 duplicationCancelled =
false;
1977 correctForCancelledDuplication(event);
1988 mousePressEventNonLeftButton(event);
1997 bool eventHandled =
false;
2011 cableYankedDirectly = currentCable;
2019 if ((event->modifiers() & Qt::ControlModifier) && (currentPort->
getInput() || isTriggerPort))
2020 fireTriggerPortEvent(currentPort->
getBase());
2024 portWithDragInitiated = currentPort;
2025 cableWithYankInitiated = cableYankedDirectly;
2029 eventHandled =
true;
2036 duplicateOnNextMouseMove =
true;
2037 duplicationDragInProgress =
true;
2043 QGraphicsScene::mousePressEvent(event);
2044 eventHandled =
true;
2050 if (event->modifiers() & Qt::ControlModifier)
2051 nearbyItem->setSelected(! nearbyItem->isSelected());
2056 nearbyItem->setSelected(
true);
2060 eventHandled =
true;
2065 QGraphicsScene::mousePressEvent(event);
2072void VuoEditorComposition::mousePressEventNonLeftButton(QGraphicsSceneMouseEvent *event)
2081 if (event->button() == Qt::RightButton)
2083 QGraphicsSceneContextMenuEvent
contextMenuEvent(QEvent::GraphicsSceneContextMenu);
2092 QGraphicsScene::mousePressEvent(event);
2103void VuoEditorComposition::correctForCancelledDuplication(QGraphicsSceneMouseEvent *event)
2107 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
2108 pressEvent.setScenePos(event->scenePos());
2109 pressEvent.setButton(Qt::LeftButton);
2110 QApplication::sendEvent(
this, &pressEvent);
2112 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
2113 releaseEvent.setScenePos(event->scenePos());
2114 releaseEvent.setButton(Qt::LeftButton);
2115 QApplication::sendEvent(
this, &releaseEvent);
2127 Qt::KeyboardModifiers modifiers =
event->modifiers();
2128 bool optionKeyPressed = (modifiers & Qt::AltModifier);
2129 bool shiftKeyPressed = (modifiers & Qt::ShiftModifier);
2140 bool creatingNewCable =
false;
2141 bool disconnectingExistingCable =
false;
2142 bool duplicatingExistingCable =
false;
2156 fromPort = fixedPort;
2157 fromNode = fixedNode;
2158 creatingNewCable =
true;
2170 creatingNewCable =
true;
2177 if (optionKeyPressed)
2179 duplicatingExistingCable =
true;
2183 disconnectingExistingCable =
true;
2191 if (creatingNewCable)
2204 cableInProgressWasNew =
true;
2205 cableInProgressShouldBeWireless =
false;
2212 highlightEligibleEndpointsForCable(cableInProgress);
2217 else if (disconnectingExistingCable)
2220 if (cableYankedDirectly)
2221 cableInProgress = cableYankedDirectly->
getBase();
2227 cableInProgressWasNew =
false;
2238 highlightEligibleEndpointsForCable(cableInProgress);
2240 cableInProgress->
getRenderer()->setSelected(
true);
2243 else if (duplicatingExistingCable)
2247 if (cableYankedDirectly)
2248 cableToDuplicate = cableYankedDirectly->
getBase();
2265 cableInProgressWasNew =
true;
2266 cableInProgressShouldBeWireless = cableToDuplicate->
hasCompiler() &&
2274 highlightEligibleEndpointsForCable(cableInProgress);
2276 cableInProgress->
getRenderer()->setSelected(
true);
2281 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2294 if (event->button() == Qt::LeftButton)
2296 portWithDragInitiated = NULL;
2297 cableWithYankInitiated = NULL;
2298 duplicateOnNextMouseMove =
false;
2299 duplicationDragInProgress =
false;
2300 dragStickinessDisabled =
false;
2307 bool cableDragEnding = cableInProgress;
2308 if (cableDragEnding)
2309 concludeCableDrag(event);
2321 if ((event->modifiers() == Qt::NoModifier) &&
2322 (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2329 enablePopoverForNode(typecastNode);
2334 enableInactivePopoverForPort(port);
2340 if (!cableDragEnding && !node)
2344 if (!cableDragEnding)
2345 QGraphicsScene::mouseReleaseEvent(event);
2351 QGraphicsScene::mouseReleaseEvent(event);
2361void VuoEditorComposition::concludeCableDrag(QGraphicsSceneMouseEvent *event)
2369 concludePublishedCableDrag(event);
2373 if (hasFeedbackErrors())
2391 bool completedCableConnection =
false;
2394 VuoCable *dataCableToDisplace = NULL;
2398 string typecastToInsert =
"";
2402 string specializedTypeName =
"";
2419 bool draggingPreviouslyPublishedCable = (!cableInProgressWasNew &&
2421 ((cableInProgress->
getToPort() == NULL) &&
2424 if (fixedPort && targetPort)
2432 if (recreatingSameConnection)
2442 bool preexistingCableWithMatchingDataCarryingStatus = (preexistingCable?
2444 cableInProgressExpectedToCarryData) :
2450 if (preexistingCable && !preexistingCableWithMatchingDataCarryingStatus &&
2461 if (!preexistingCableWithMatchingDataCarryingStatus &&
2463 selectBridgingSolution(fixedPort, targetPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert)))
2474 if (portToSpecialize == targetPort)
2475 portToSpecialize = adjustedTargetPort;
2477 targetPort = adjustedTargetPort;
2481 if (cableInProgressExpectedToCarryData &&
2483 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2484 typecastNodeToDelete = uncollapsedTypecast;
2489 if (preexistingCable)
2490 cableToReplace = preexistingCable;
2494 if (cableInProgressExpectedToCarryData)
2498 for (vector<VuoCable *>::iterator cable = previousConnectedCables.begin(); (! dataCableToDisplace) && (cable != previousConnectedCables.end()); ++cable)
2499 if ((((*cable)->getRenderer()->effectivelyCarriesData()) && (*cable) != cableToReplace))
2500 dataCableToDisplace = *cable;
2507 if (publishedDataConnections.size() > 0)
2508 portToUnpublish = targetPort;
2512 completedCableConnection =
true;
2516 else if (!preexistingCableWithMatchingDataCarryingStatus &&
2518 selectBridgingSolution(targetPort, fixedPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert)))
2521 completedCableConnection =
true;
2526 if (completedCableConnection)
2530 if (draggingPreviouslyPublishedCable)
2549 cableInProgressCopy->
setFrom(cableInProgressFromNode, cableInProgressFromPort);
2550 cableInProgressCopy->
setTo(cableInProgressToNode, cableInProgressToPort);
2557 cableInProgress = cableInProgressCopy;
2558 cableInProgressWasNew =
true;
2562 pair<VuoRendererCable *, VuoRendererCable *> cableArgs = std::make_pair((dataCableToDisplace? dataCableToDisplace->
getRenderer() : NULL),
2563 (cableToReplace? cableToReplace->getRenderer() : NULL));
2564 pair<string, string> typeArgs = std::make_pair(typecastToInsert, specializedTypeName);
2565 pair<VuoRendererPort *, VuoRendererPort *> portArgs = std::make_pair(portToUnpublish, portToSpecialize);
2570 typecastNodeToDelete,
2577 if (cableInProgress)
2581 cableInProgress = NULL;
2595void VuoEditorComposition::concludePublishedCableDrag(QGraphicsSceneMouseEvent *event)
2605 string typecastToInsert =
"";
2609 string specializedTypeName =
"";
2626 if (internalInputPort)
2629 forceEventOnlyPublication =
true;
2635 if (internalInputPort &&
2641 internalInputPort->
getBase()) &&
2644 if (recreatingSameConnection)
2652 ||
selectBridgingSolution(publishedInputPort, internalInputPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert))
2657 bool cableToReplaceHasMatchingDataCarryingStatus = (cableToReplace?
2659 cableInProgressExpectedToCarryData) :
2664 if (cableToReplace && cableToReplaceHasMatchingDataCarryingStatus)
2670 else if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus &&
2686 if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus)
2688 QList<QGraphicsItem *> removedComponents;
2689 removedComponents.append(cableToReplace->
getRenderer());
2698 if (typecastPort && typecastPort->scene())
2706 if (cableInProgressExpectedToCarryData &&
2708 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2710 QList<QGraphicsItem *> removedComponents;
2711 removedComponents.append(uncollapsedTypecast);
2718 forceEventOnlyPublication,
2719 (portToSpecialize? portToSpecialize->
getBase() : NULL),
2720 specializedTypeName,
2746 if (internalOutputPort)
2749 forceEventOnlyPublication =
true;
2755 if (internalOutputPort &&
2756 publishedOutputPort)
2762 ||
selectBridgingSolution(internalOutputPort, publishedOutputPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert))
2766 forceEventOnlyPublication,
2767 portToSpecialize? portToSpecialize->
getBase() : NULL,
2768 specializedTypeName,
2787 if (! cableInProgress)
2790 if (cableInProgressWasNew)
2794 QList<QGraphicsItem *> removedComponents;
2795 removedComponents.append(cableInProgress->
getRenderer());
2799 cableInProgress = NULL;
2812 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2817 cableInProgress = NULL;
2819 else if (cableInProgress)
2834 bool leftMouseButtonPressed = (
event->buttons() & Qt::LeftButton);
2850 if (cableInProgress || (! leftMouseButtonPressed))
2851 updateHoverHighlighting(event->scenePos());
2855 if (leftMouseButtonPressed)
2858 if ((! dragStickinessDisabled) && (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2862 dragStickinessDisabled =
true;
2866 if (portWithDragInitiated || cableWithYankInitiated)
2868 initiateCableDrag(portWithDragInitiated, cableWithYankInitiated, event);
2869 portWithDragInitiated = NULL;
2870 cableWithYankInitiated = NULL;
2875 if (cableInProgress)
2885 else if (duplicationDragInProgress)
2887 if (duplicateOnNextMouseMove)
2890 duplicateOnNextMouseMove =
false;
2897 QPointF delta = newPos - cursorPosBeforeDuplicationDragMove;
2899 cursorPosBeforeDuplicationDragMove = newPos;
2903 QGraphicsScene::mouseMoveEvent(event);
2908 QGraphicsScene::mouseMoveEvent(event);
2916void VuoEditorComposition::updateHoverHighlighting(QPointF scenePos,
bool disablePortHoverHighlighting)
2928 bool hoveringOverNodeHeader =
false;
2929 if (cableInProgress)
2938 hoveringOverNodeHeader =
true;
2944 if (! hoveringOverNodeHeader)
2955 if (item != previousNearbyItem)
2963 if (previousNearbyItem)
2971 if (! cableInProgress)
2977 else if (typecastPort && !disablePortHoverHighlighting)
2979 else if (port && !disablePortHoverHighlighting)
2982 if (!cableInProgress)
2993 if (cableInProgress)
2996 cableInProgress->getFromPort()->getRenderer() :
2997 cableInProgress->getToPort()->getRenderer());
2999 QList< QPair<VuoRendererPort *, bool> > updatedPorts;
3000 if (hoveringOverNodeHeader)
3001 updatedPorts.append( QPair<VuoRendererPort *, bool>(port,
true) );
3002 if (previousNode && previousPort)
3003 updatedPorts.append( QPair<VuoRendererPort *, bool>(previousPort, !cableInProgress->getRenderer()->effectivelyCarriesData()) );
3005 QPair<VuoRendererPort *, bool> p;
3006 foreach (p, updatedPorts)
3011 if (typecastParentPort)
3012 updatedPort = typecastParentPort;
3014 updateEligibilityHighlightingForPort(updatedPort, fixedPort, p.second);
3017 updateEligibilityHighlightingForNode(potentialDrawer);
3024 if (targetPort || previousTargetPort)
3026 if (cableInProgress)
3027 cableInProgress->getRenderer()->setFloatingEndpointAboveEventPort(targetPort && (!targetPort->
getDataType() || hoveringOverNodeHeader));
3032 previousNearbyItem = item;
3040 else if (port && !disablePortHoverHighlighting)
3043 if (!cableInProgress)
3049 else if (makeListDrawer)
3059 if (previousNearbyItem)
3077 else if (typecastPort)
3086 previousNearbyItem = NULL;
3095 if ((event->key() != Qt::Key_Alt) && (event->key() != Qt::Key_Shift) && (event->key() != Qt::Key_Escape))
3098 Qt::KeyboardModifiers modifiers =
event->modifiers();
3099 qreal adjustedNodeMoveRate = nodeMoveRate;
3100 if (modifiers & Qt::ShiftModifier)
3102 adjustedNodeMoveRate *= nodeMoveRateMultiplier;
3105 switch (event->key()) {
3106 case Qt::Key_Backspace:
3111 case Qt::Key_Delete:
3123 if (modifiers & Qt::ControlModifier)
3124 openSelectedEditableNodes();
3141 if (cableInProgress)
3143 cableInProgress->getRenderer()->updateGeometry();
3144 cableInProgress->getCompiler()->setAlwaysEventOnly(
true);
3145 highlightEligibleEndpointsForCable(cableInProgress);
3155 case Qt::Key_Return:
3158 QGraphicsScene::keyPressEvent(event);
3160 if (!event->isAccepted())
3167 if (selectedComments.empty() && !selectedNodes.empty())
3174 case Qt::Key_Escape:
3176 if (duplicateOnNextMouseMove || duplicationDragInProgress)
3180 duplicateOnNextMouseMove =
false;
3181 duplicationDragInProgress =
false;
3182 duplicationCancelled =
true;
3184 else if (cableInProgress)
3193 QGraphicsScene::keyPressEvent(event);
3204 switch (event->key()) {
3207 if (cableInProgress)
3209 cableInProgress->getRenderer()->updateGeometry();
3210 cableInProgress->getCompiler()->setAlwaysEventOnly(
false);
3211 highlightEligibleEndpointsForCable(cableInProgress);
3223 QGraphicsScene::keyReleaseEvent(event);
3238 contextMenu.setSeparatorsCollapsible(
false);
3243 QAction *insertNodeSection =
new QAction(tr(
"Insert Node"), NULL);
3244 insertNodeSection->setEnabled(
false);
3245 contextMenu.addAction(insertNodeSection);
3247 QString spacer(
" ");
3251 QMenu *shareMenu =
new QMenu(&contextMenu);
3252 shareMenu->setSeparatorsCollapsible(
false);
3253 shareMenu->setTitle(spacer + tr(
"Share"));
3254 contextMenu.addMenu(shareMenu);
3257 QAction *action =
new QAction(tr(
"Share Value"), NULL);
3258 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share"))));
3259 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3260 shareMenu->addAction(action);
3264 QAction *action =
new QAction(tr(
"Share List"), NULL);
3265 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share.list"))));
3266 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3267 shareMenu->addAction(action);
3271 QAction *action =
new QAction(tr(
"Share Event"), NULL);
3272 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.share"))));
3273 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3274 shareMenu->addAction(action);
3280 QMenu *holdMenu =
new QMenu(&contextMenu);
3281 holdMenu->setSeparatorsCollapsible(
false);
3282 holdMenu->setTitle(spacer + tr(
"Hold"));
3283 contextMenu.addMenu(holdMenu);
3286 QAction *action =
new QAction(tr(
"Hold Value"), NULL);
3287 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold2"))));
3288 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3289 holdMenu->addAction(action);
3293 QAction *action =
new QAction(tr(
"Hold List"), NULL);
3294 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold.list2"))));
3295 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3296 holdMenu->addAction(action);
3302 QMenu *allowMenu =
new QMenu(&contextMenu);
3303 allowMenu->setSeparatorsCollapsible(
false);
3304 allowMenu->setTitle(spacer + tr(
"Allow"));
3305 contextMenu.addMenu(allowMenu);
3308 QAction *action =
new QAction(tr(
"Allow First Event"), NULL);
3309 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirst"))));
3310 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3311 allowMenu->addAction(action);
3315 QAction *action =
new QAction(tr(
"Allow First Value"), NULL);
3316 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirstValue"))));
3317 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3318 allowMenu->addAction(action);
3322 QAction *action =
new QAction(tr(
"Allow Periodic Events"), NULL);
3323 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.time.allowPeriodic"))));
3324 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3325 allowMenu->addAction(action);
3329 QAction *action =
new QAction(tr(
"Allow Changes"), NULL);
3330 action->setData(QVariant::fromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowChanges2"))));
3331 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3332 allowMenu->addAction(action);
3336 contextMenu.addSeparator();
3338 QAction *contextMenuInsertComment =
new QAction(NULL);
3339 contextMenuInsertComment->setText(tr(
"Insert Comment"));
3340 contextMenuInsertComment->setData(event->scenePos());
3341 connect(contextMenuInsertComment, &QAction::triggered,
this, &VuoEditorComposition::insertComment);
3342 contextMenu.addAction(contextMenuInsertComment);
3344 QAction *contextMenuInsertSubcomposition =
new QAction(NULL);
3345 contextMenuInsertSubcomposition->setText(tr(
"Insert Subcomposition"));
3346 contextMenuInsertSubcomposition->setData(event->scenePos());
3347 connect(contextMenuInsertSubcomposition, &QAction::triggered,
this, &VuoEditorComposition::insertSubcomposition);
3348 contextMenu.addAction(contextMenuInsertSubcomposition);
3353 QAction *contextMenuDeleteSelectedSnapshot =
new QAction(NULL);
3360 if (port->
isConstant() && inputEditorManager)
3364 if (inputEditorLoadedForPortDataType)
3366 contextMenuSetPortConstant->setData(QVariant::fromValue(port));
3367 contextMenu.addAction(contextMenuSetPortConstant);
3369 inputEditorLoadedForPortDataType->deleteLater();
3375 contextMenuPublishPort->setText(tr(
"Publish Port"));
3376 contextMenuPublishPort->setData(QVariant::fromValue(port));
3377 contextMenu.addAction(contextMenuPublishPort);
3382 vector<VuoRendererPublishedPort *> externalPublishedPorts = port->
getPublishedPorts();
3383 bool hasExternalPublishedPortWithMultipleInternalPorts =
false;
3384 bool hasExternalPublishedPortBelongingToActiveProtocol =
false;
3388 hasExternalPublishedPortWithMultipleInternalPorts =
true;
3391 hasExternalPublishedPortBelongingToActiveProtocol =
true;
3398 if (!hasExternalPublishedPortWithMultipleInternalPorts &&!hasExternalPublishedPortBelongingToActiveProtocol)
3400 contextMenuPublishPort->setText(tr(
"Delete Published Port"));
3401 contextMenuPublishPort->setData(QVariant::fromValue(port));
3402 contextMenu.addAction(contextMenuPublishPort);
3407 if (isTriggerPort || port->
getInput())
3409 __block
bool isTopLevelCompositionRunning =
false;
3410 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
3412 isTopLevelCompositionRunning = topLevelComposition->
isRunning();
3415 if (isTopLevelCompositionRunning)
3417 contextMenuFireEvent->setData(QVariant::fromValue(port));
3418 contextMenu.addAction(contextMenuFireEvent);
3424 QMenu *contextMenuThrottling =
new QMenu(&contextMenu);
3425 contextMenuThrottling->setSeparatorsCollapsible(
false);
3426 contextMenuThrottling->setTitle(tr(
"Set Event Throttling"));
3428 foreach (QAction *action, contextMenuThrottlingActions)
3430 contextMenuThrottling->addAction(action);
3431 action->setData(QVariant::fromValue(port));
3432 action->setCheckable(
true);
3435 contextMenu.addMenu(contextMenuThrottling);
3441 if (contextMenuSpecializeGenericType)
3442 contextMenuSpecializeGenericType->deleteLater();
3445 contextMenuSpecializeGenericType->setSeparatorsCollapsible(
false);
3446 contextMenuSpecializeGenericType->setTitle(tr(
"Set Data Type"));
3447 contextMenuSpecializeGenericType->setToolTipsVisible(
true);
3449 populateSpecializePortMenu(contextMenuSpecializeGenericType, port,
true);
3450 contextMenu.addMenu(contextMenuSpecializeGenericType);
3455 int numVisibleDirectlyConnectedCables = 0;
3456 int numHidableDirectlyConnectedCables = 0;
3457 int numUnhidableDirectlyConnectedCables = 0;
3463 numVisibleDirectlyConnectedCables++;
3465 numHidableDirectlyConnectedCables++;
3469 numUnhidableDirectlyConnectedCables++;
3472 int numVisibleChildPortConnectedCables = 0;
3473 int numHidableChildPortConnectedCables = 0;
3474 int numUnhidableChildPortConnectedCables = 0;
3484 numVisibleChildPortConnectedCables++;
3487 numHidableChildPortConnectedCables++;
3490 numUnhidableChildPortConnectedCables++;
3495 int numVisibleConnectedCables = numVisibleDirectlyConnectedCables + numVisibleChildPortConnectedCables;
3498 int numHidableConnectedCables = numHidableDirectlyConnectedCables + numHidableChildPortConnectedCables;
3501 int numUnhidableConnectedCables = numUnhidableDirectlyConnectedCables + numUnhidableChildPortConnectedCables;
3503 if ((!
renderHiddenCables && ((numHidableConnectedCables >= 1) || (numUnhidableConnectedCables >= 1)))
3504 || (numVisibleConnectedCables >= 1))
3506 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3507 contextMenu.addSeparator();
3512 if (numHidableConnectedCables >= 1)
3517 int numApparentlyHidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3518 contextMenuHideCables->setText(numApparentlyHidableConnectedCables > 1?
"Hide Cables" :
"Hide Cable");
3519 contextMenuHideCables->setData(QVariant::fromValue(port));
3520 contextMenu.addAction(contextMenuHideCables);
3523 if (numUnhidableConnectedCables >= 1)
3525 int numApparentlyUnhidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3526 contextMenuUnhideCables->setText(numApparentlyUnhidableConnectedCables > 1?
"Unhide Cables" :
"Unhide Cable");
3527 contextMenuUnhideCables->setData(QVariant::fromValue(port));
3528 contextMenu.addAction(contextMenuUnhideCables);
3532 if (numVisibleConnectedCables >= 1)
3534 contextMenuDeleteCables->setText(numVisibleConnectedCables > 1?
"Delete Cables" :
"Delete Cable");
3535 contextMenuDeleteCables->setData(QVariant::fromValue(port));
3536 contextMenu.addAction(contextMenuDeleteCables);
3543 if (! item->isSelected())
3546 item->setSelected(
true);
3549 QList<QGraphicsItem *> selectedComponents = selectedItems();
3550 bool onlyCommentsSelected =
true;
3551 bool onlyCablesSelected =
true;
3552 bool selectionContainsMissingNode =
false;
3553 foreach (QGraphicsItem *item, selectedComponents)
3556 onlyCommentsSelected =
false;
3559 onlyCablesSelected =
false;
3562 selectionContainsMissingNode =
true;
3565 contextMenuDeleteSelectedSnapshot->setText(contextMenuDeleteSelected->text());
3573 if (onlyCommentsSelected)
3574 contextMenu.addAction(contextMenuEditSelectedComments);
3579 contextMenu.addSeparator();
3582 contextMenu.addAction(contextMenuRefactorSelected);
3584 contextMenu.addSeparator();
3587 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3597 contextMenu.addAction(contextMenuHideSelectedCables);
3600 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3615 contextMenuAddInputPort->setData(QVariant::fromValue(node));
3616 contextMenuRemoveInputPort->setData(QVariant::fromValue(node));
3619 contextMenuRemoveInputPort->setEnabled(listItemCount >= 1);
3621 contextMenu.addAction(contextMenuAddInputPort);
3622 contextMenu.addAction(contextMenuRemoveInputPort);
3624 contextMenu.addSeparator();
3628 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3639 if (originalGenericNodeClass)
3640 nodeClass = originalGenericNodeClass->
getBase();
3643 QString actionText, sourcePath;
3647 int numSelectedNonAttachmentNodes = 0;
3648 QList<QGraphicsItem *> selectedComponents = selectedItems();
3649 foreach (QGraphicsItem *item, selectedComponents)
3652 numSelectedNonAttachmentNodes++;
3655 if ((numSelectedNonAttachmentNodes == 1) && nodeClassIsEditable)
3658 QAction *editAction =
new QAction(NULL);
3659 editAction->setText(actionText);
3660 editAction->setData(QVariant::fromValue(node));
3663 contextMenu.addAction(editAction);
3664 contextMenu.addSeparator();
3669 contextMenu.addAction(contextMenuRenameSelected);
3674 contextMenu.addSeparator();
3677 if (numSelectedNonAttachmentNodes == 1)
3679 if (contextMenuChangeNode)
3680 contextMenuChangeNode->deleteLater();
3683 contextMenuChangeNode->setSeparatorsCollapsible(
false);
3684 contextMenuChangeNode->setTitle(tr(
"Change To"));
3687 if (!contextMenuChangeNode->actions().isEmpty())
3688 contextMenu.addMenu(contextMenuChangeNode);
3692 if (!selectionContainsMissingNode)
3693 contextMenu.addAction(contextMenuRefactorSelected);
3695 if ((numSelectedNonAttachmentNodes == 1) && (nodeClassIsEditable || nodeClassIs3rdParty))
3699 if (!modulePath.isEmpty())
3701 QString enclosingDirUrl =
"file://" + QFileInfo(modulePath).dir().absolutePath();
3702 QAction *openEnclosingFolderAction =
new QAction(NULL);
3703 openEnclosingFolderAction->setText(
"Show in Finder");
3704 openEnclosingFolderAction->setData(enclosingDirUrl);
3706 contextMenu.addAction(openEnclosingFolderAction);
3710 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3711 contextMenu.addSeparator();
3714 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3720 if (!contextMenu.actions().isEmpty())
3725 menuSelectionInProgress =
true;
3726 contextMenu.exec(event->screenPos());
3727 menuSelectionInProgress =
false;
3730 delete contextMenuDeleteSelectedSnapshot;
3738 QAction *sender = (QAction *)QObject::sender();
3746void VuoEditorComposition::expandSpecializePortMenu()
3748 QAction *sender = (QAction *)QObject::sender();
3751 populateSpecializePortMenu(contextMenuSpecializeGenericType, port,
false);
3752 contextMenuSpecializeGenericType->exec();
3762void VuoEditorComposition::populateSpecializePortMenu(QMenu *menu,
VuoRendererPort *port,
bool limitInitialOptions)
3770 QAction *unspecializeAction = menu->addAction(tr(
"Generic"));
3775 menu->addSeparator();
3777 unspecializeAction->setData(QVariant::fromValue(port));
3778 unspecializeAction->setCheckable(
true);
3779 unspecializeAction->setChecked(genericDataType);
3782 set<string> compatibleTypes;
3783 set<string> compatibleTypesInIsolation;
3786 if (genericDataType)
3794 compatibleTypes = set<string>(compatibleTypesVector.begin(), compatibleTypesVector.end());
3800 compatibleTypes.insert(compatibleTypeNames.begin(), compatibleTypeNames.end());
3805 else if (isPortCurrentlyRevertible(port))
3807 map<VuoNode *, string> nodesToReplace;
3808 set<VuoCable *> cablesToDelete;
3810 if (cablesToDelete.size() >= 1)
3818 compatibleTypes = getRespecializationOptionsForPortInNetwork(port);
3822 if (genericTypeFromPortClass)
3834 if (genericHostPortDataType)
3836 else if (isPortCurrentlyRevertible(hostPort->
getRenderer()))
3846 vector<string> compatibleTypesInIsolationVector;
3847 if (genericTypeFromPortClass)
3854 foreach (
string type, compatibleTypesInIsolationVector)
3861 const int maxTypeCountForFlatMenuDisplay = 10;
3862 if (compatibleTypesInIsolation.size() <= maxTypeCountForFlatMenuDisplay)
3864 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
false,
"", menu);
3872 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true,
"", menu);
3876 QList<QAction *> allNodeSetActionsToAdd;
3877 for (
const string &nodeSet : moduleManager->getKnownNodeSets())
3878 allNodeSetActionsToAdd +=
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes, true, nodeSet, menu);
3880 bool usingExpansionMenu =
false;
3881 if ((menu->actions().size() > 0) && (allNodeSetActionsToAdd.size() > 0))
3883 menu->addSeparator();
3885 if (limitInitialOptions)
3888 QAction *showMoreAction = menu->addAction(tr(
"More…"));
3889 showMoreAction->setData(QVariant::fromValue(port));
3890 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandSpecializePortMenu);
3891 usingExpansionMenu =
true;
3895 if (!usingExpansionMenu)
3899 foreach (QAction *action, menu->actions())
3901 QMenu *specializeSubmenu = action->menu();
3902 if (specializeSubmenu)
3904 foreach (QAction *specializeSubaction, specializeSubmenu->actions())
3905 connect(specializeSubaction, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3907 else if (action == unspecializeAction)
3908 connect(action, &QAction::triggered,
this, &VuoEditorComposition::unspecializePortType);
3910 connect(action, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3925 return vector<string>(types.begin(), types.end());
3929 vector<string> typeOptions;
3931 typeOptions.push_back(i.first);
3942set<string> VuoEditorComposition::getRespecializationOptionsForPortInNetwork(
VuoRendererPort *port)
3949 port->
getBase(), compatibility);
3954 return set<string>(allTypes.begin(), allTypes.end());
3957 return set<string>(compatibleTypes.begin(), compatibleTypes.end());
3980 set<string> compatibleTypesInIsolation,
3981 set<string> compatibleTypesInContext,
3982 bool limitToNodeSet,
3986 QList<QAction *> actionsToAddToMenu;
3988 vector<string> compatibleTypesForNodeSetDisplay;
3989 for (
const string &typeName : compatibleTypesInIsolation)
3994 innermostTypeName !=
"VuoUrl" &&
3996 innermostTypeName !=
"VuoInteraction" &&
3997 innermostTypeName !=
"VuoInteractionType" &&
3998 innermostTypeName !=
"VuoUuid" &&
4000 innermostTypeName !=
"VuoIconPosition" &&
4001 innermostTypeName !=
"VuoMesh" &&
4002 innermostTypeName !=
"VuoWindowProperty" &&
4003 innermostTypeName !=
"VuoWindowReference" &&
4004 (! limitToNodeSet || moduleManager->
getNodeSetForType(innermostTypeName) == nodeSetName))
4005 compatibleTypesForNodeSetDisplay.push_back(typeName);
4008 if (!compatibleTypesForNodeSetDisplay.empty())
4010 QMenu *contextMenuNodeSetTypes = NULL;
4011 QList<QAction *> actionsToAddToNodeSetSubmenu;
4012 bool enabledContentAdded =
false;
4015 if (!nodeSetName.empty())
4017 contextMenuNodeSetTypes =
new QMenu(menu);
4018 contextMenuNodeSetTypes->setSeparatorsCollapsible(
false);
4020 contextMenuNodeSetTypes->setToolTipsVisible(
true);
4021 actionsToAddToMenu.append(contextMenuNodeSetTypes->menuAction());
4024 for (
const string &typeName : compatibleTypesForNodeSetDisplay)
4026 QList<QVariant> portAndSpecializedType;
4027 portAndSpecializedType.append(QVariant::fromValue(genericPort));
4028 portAndSpecializedType.append(typeName.c_str());
4030 QAction *specializeAction;
4034 if (!nodeSetName.empty())
4036 specializeAction =
new QAction(typeTitle, contextMenuNodeSetTypes);
4037 actionsToAddToNodeSetSubmenu.append(specializeAction);
4043 specializeAction =
new QAction(typeTitle, menu);
4044 actionsToAddToMenu.append(specializeAction);
4047 specializeAction->setData(QVariant(portAndSpecializedType));
4048 specializeAction->setCheckable(
true);
4051 if (compatibleTypesInContext.find(typeName) == compatibleTypesInContext.end())
4053 specializeAction->setEnabled(
false);
4055 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."));
4058 enabledContentAdded =
true;
4061 if (contextMenuNodeSetTypes)
4065 if (!enabledContentAdded)
4066 contextMenuNodeSetTypes->setEnabled(
false);
4070 QList<QAction *> actionListWithPromotions = promoteSingletonsFromSubmenus(actionsToAddToMenu);
4071 return actionListWithPromotions;
4078QList<QAction *> VuoEditorComposition::promoteSingletonsFromSubmenus(QList<QAction *> actionList)
4080 QList<QAction *> modifiedActionList;
4081 foreach (QAction *action, actionList)
4083 if (action->menu() && (action->menu()->actions().size() == 1))
4085 QAction *singleSubaction = action->menu()->actions().first();
4086 action->menu()->removeAction(singleSubaction);
4087 modifiedActionList.append(singleSubaction);
4090 modifiedActionList.append(action);
4093 return modifiedActionList;
4101 std::sort(actionList.begin(), actionList.end(),
4102 [](QAction *action1, QAction *action2) { return action1->text().compare(action2->text(), Qt::CaseInsensitive) < 0; });
4104 foreach (QAction *action, actionList)
4105 menu->addAction(action);
4111void VuoEditorComposition::updatePopoversForActiveWindowChange(QWidget *old, QWidget *now)
4124void VuoEditorComposition::updatePopoversForApplicationStateChange(
bool active)
4126 if (ignoreApplicationStateChangeEvents)
4130 if (activeWindow && (activeWindow->
getComposition() ==
this) && (!activeWindow->isMinimized()))
4144 return findNearbyComponent(scenePos, VuoEditorComposition::targetTypePort, limitPortCollisionRange);
4152 QGraphicsItem *item =
findNearbyComponent(scenePos, VuoEditorComposition::targetTypeNodeHeader);
4170 bool limitPortCollisionRange)
4176 bool ignoreComments;
4180 case VuoEditorComposition::targetTypePort:
4182 ignoreCables =
true;
4184 ignorePorts =
false;
4185 ignoreComments =
true;
4188 case VuoEditorComposition::targetTypeNodeHeader:
4190 ignoreCables =
true;
4193 ignoreComments =
true;
4198 ignoreCables =
false;
4199 ignoreNodes =
false;
4200 ignorePorts =
false;
4201 ignoreComments =
false;
4209 QGraphicsItem *topmostItemUnderCursor = itemAt(scenePos, views()[0]->transform());
4210 if (topmostItemUnderCursor && (!topmostItemUnderCursor->isEnabled()))
4211 topmostItemUnderCursor = NULL;
4215 QRectF searchRect(scenePos.x()-0.5*rectLength, scenePos.y()-0.5*rectLength, rectLength, rectLength);
4216 QList<QGraphicsItem *> itemsInRange = items(searchRect);
4217 for (QList<QGraphicsItem *>::iterator i = itemsInRange.begin(); i != itemsInRange.end(); ++i)
4219 if (!(*i)->isEnabled())
4226 bool makeListDragHandle =
4229 if (makeListDragHandle &&
4230 ((! topmostItemUnderCursor) ||
4231 (topmostItemUnderCursor == makeListDrawer) ||
4232 (topmostItemUnderCursor->zValue() < makeListDrawer->zValue())))
4234 return makeListDrawer;
4245 ((! topmostItemUnderCursor) ||
4246 (topmostItemUnderCursor == port) ||
4248 (topmostItemUnderCursor->zValue() < port->zValue()))
4250 ((! limitPortCollisionRange) ||
4264 ((! topmostItemUnderCursor) ||
4265 (topmostItemUnderCursor == cable) ||
4266 (topmostItemUnderCursor->zValue() < cable->zValue())))
4273 if (! ignoreComments)
4280 ((! topmostItemUnderCursor) ||
4281 (topmostItemUnderCursor == (*i)) ||
4282 (topmostItemUnderCursor->zValue() < (*i)->zValue())))
4289 if (targetType == VuoEditorComposition::targetTypeNodeHeader)
4295 headerRect = node->mapToScene(headerRect).
boundingRect();
4296 if (headerRect.intersects(searchRect) && scenePos.y() <= headerRect.bottom())
4308 return topmostItemUnderCursor;
4313 return ((
VuoRendererPort *)(topmostItemUnderCursor))->getRenderedParentNode();
4330 if (! cableInProgress)
4333 VuoPort *fromPort = cableInProgress->getFromPort();
4334 VuoPort *toPort = cableInProgress->getToPort();
4335 VuoPort *fixedPort = (fromPort? fromPort: toPort);
4350 vector<VuoRendererPort *> portList;
4365 if (portList.size() > firstPortIndex)
4367 targetPort = portList[firstPortIndex];
4373 for (
int i = firstPortIndex; i < portList.size(); ++i)
4377 firstPortWithoutWall = portList[i];
4381 if (firstPortWithoutWall)
4382 targetPort = firstPortWithoutWall;
4391 if (portList.size() > firstPortIndex)
4392 targetPort = portList[firstPortIndex];
4413 QRectF boundingRect;
4414 foreach (QGraphicsItem *item, items())
4418 boundingRect |= item->sceneBoundingRect();
4421 return boundingRect;
4430 QRectF boundingRect;
4432 foreach (QGraphicsItem *item, selectedItems())
4436 boundingRect |= item->sceneBoundingRect();
4439 return boundingRect;
4448 QRectF boundingRect;
4450 foreach (QGraphicsItem *item, selectedItems())
4454 boundingRect |= item->mapToScene(item->childrenBoundingRect()).boundingRect();
4464 boundingRect |= drawer->mapToScene(drawer->childrenBoundingRect()).boundingRect();
4469 return boundingRect;
4496 if (updateInRunningComposition)
4505 if (updateInRunningComposition)
4531 if (!errorMarkingUpdatesEnabled)
4534 errorMarkingUpdatesEnabled =
false;
4551 set<VuoCompilerCable *> potentialCables;
4553 if (targetPort && cableInProgress)
4559 if (cableInProgress->getFromNode())
4561 fromNode = cableInProgress->getFromNode();
4562 fromPort = cableInProgress->getFromPort();
4564 toPort = targetPort->
getBase();
4569 fromPort = targetPort->
getBase();
4570 toNode = cableInProgress->getToNode();
4571 toPort = cableInProgress->getToPort();
4576 potentialCable->
setAlwaysEventOnly(! cableInProgress->getRenderer()->effectivelyCarriesData() ||
4577 cableInProgress->getRenderer()->isFloatingEndpointAboveEventPort());
4581 potentialCables.insert(potentialCable);
4592 VUserLog(
"%s: Showing error popover: %s",
4600 set<VuoRendererNode *> nodesToMark;
4601 set<VuoRendererCable *> cablesToMark;
4603 set<VuoNode *> problemNodes = issue.
getNodes();
4604 foreach (
VuoNode *node, problemNodes)
4608 set<VuoCable *> problemCables = issue.
getCables();
4609 foreach (
VuoCable *cable, problemCables)
4611 VuoCable *cableToMark = (cable->
getCompiler() == potentialCable ? cableInProgress : cable);
4620 errorPopovers.insert(errorPopover);
4624 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
4628 if (targetPort && cableInProgress && nodesToMark.find(targetPort->
getRenderedParentNode()) != nodesToMark.end())
4632 else if (! nodesToMark.empty())
4635 qreal topY = viewportRect.bottom();
4641 QPointF scenePos = node->scenePos();
4642 if (viewportRect.contains(scenePos) && (scenePos.y() < topY))
4644 topmostVisibleNode = node;
4645 topY = scenePos.y();
4649 if (topmostVisibleNode)
4650 nearbyNode = topmostVisibleNode;
4659 VUserLog(
"Warning: no nearby node (no marked nodes).");
4664 const QPoint offsetFromNode(0,10);
4665 QPoint popoverTopLeftInScene = (nearbyNode?
4666 (nearbyNode->scenePos().toPoint() +
4669 QPoint(viewportRect.center().x() - 0.5*errorPopover->sizeHint().width(),
4670 viewportRect.center().y() - 0.5*errorPopover->sizeHint().height()));
4674 const int margin = 5;
4675 popoverTopLeftInScene = (QPoint(fmin(popoverTopLeftInScene.x(), viewportRect.bottomRight().x() - errorPopover->sizeHint().width() - margin),
4676 fmin(popoverTopLeftInScene.y(), viewportRect.bottomRight().y() - errorPopover->sizeHint().height() - margin)));
4678 popoverTopLeftInScene = (QPoint(fmax(popoverTopLeftInScene.x(), viewportRect.topLeft().x() + margin),
4679 fmax(popoverTopLeftInScene.y(), viewportRect.topLeft().y() + margin)));
4681 QPoint popoverTopLeftInView = views()[0]->mapFromScene(popoverTopLeftInScene);
4682 QPoint popoverTopLeftGlobal = views()[0]->mapToGlobal(popoverTopLeftInView);
4684 errorPopover->move(popoverTopLeftGlobal);
4685 errorPopover->show();
4693 delete potentialCable;
4695 errorMarkingUpdatesEnabled =
true;
4701bool VuoEditorComposition::hasFeedbackErrors(
void)
4703 return this->errorMark;
4711 if (hasFeedbackErrors())
4720void VuoEditorComposition::buildComposition(
string compositionSnapshot,
const set<string> &dependenciesUninstalled)
4726 if (! dependenciesUninstalled.empty())
4728 vector<string> dependenciesRemovedVec(dependenciesUninstalled.begin(), dependenciesUninstalled.end());
4730 throw VuoException(
"Some modules that the composition needs were uninstalled: " + dependenciesStr);
4733 delete runningComposition;
4734 runningComposition = NULL;
4738 if (runningCompositionActiveDriver)
4742 string dir, file, ext;
4744 linkedCompositionPath = dir + file +
".dylib";
4749 compiler->
compileComposition(runningComposition, compiledCompositionPath,
true, issues);
4753 remove(compiledCompositionPath.c_str());
4759 delete runningComposition;
4760 runningComposition = NULL;
4772bool VuoEditorComposition::isRunningThreadUnsafe(
void)
4774 return runner != NULL && ! stopRequested && ! runner->
isStopped();
4784 __block
bool running;
4785 dispatch_sync(runCompositionQueue, ^{
4786 running = isRunningThreadUnsafe();
4812 if (matchingComposition->showEventsMode)
4816 stopRequested =
false;
4817 dispatch_async(runCompositionQueue, ^{
4820 runningCompositionLibraries = std::make_shared<VuoRunningCompositionLibraries>();
4822 buildComposition(compositionSnapshot);
4836 if (matchingComposition->showEventsMode)
4837 this->runner->subscribeToEventTelemetry(matchingCompositionIdentifier);
4839 dispatch_sync(activePortPopoversQueue, ^{
4840 for (
auto i : matchingComposition->activePortPopovers)
4842 string portID = i.first;
4864 stopRequested =
true;
4865 dispatch_async(runCompositionQueue, ^{
4869 runner->waitUntilStopped();
4874 linkedCompositionPath =
"";
4876 runningCompositionLibraries =
nullptr;
4878 delete runningComposition;
4879 runningComposition = NULL;
4887 subcompositionRouter->applyToAllLinkedCompositions(
this, ^
void (
VuoEditorComposition *matchingComposition,
string matchingCompositionIdentifier)
4889 if (matchingComposition->showEventsMode)
4892 dispatch_sync(activePortPopoversQueue, ^{
4893 for (
auto i : matchingComposition->activePortPopovers)
4919 dispatch_async(runCompositionQueue, ^{
4920 if (isRunningThreadUnsafe())
4926 runningCompositionLibraries->enqueueLibraryContainingDependencyToUnload(moduleKey);
4929 string oldBuiltCompositionSnapshot = oldCompositionSnapshot;
4931 if (previouslyActiveDriver)
4938 buildComposition(newCompositionSnapshot, dependenciesUninstalled);
4940 string compositionDiff = diffInfo->
diff(oldBuiltCompositionSnapshot, runningComposition, compiler);
4943 catch (exception &e)
4945 VUserLog(
"Composition stopped itself: %s", e.what());
4950 VUserLog(
"Composition stopped itself.");
4956 dispatch_async(dispatch_get_main_queue(), ^{
4975 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, reloadSubcompositionIfUnsaved);
4993 if (
this == topLevelComposition)
4995 dispatch_async(runCompositionQueue, ^{
4996 if (isRunningThreadUnsafe())
4998 json_object *constantObject = json_tokener_parse(constant.c_str());
4999 runner->
setInputPortValue(thisCompositionIdentifier, runningPortID, constantObject);
5007 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
5009 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
5036 if (runningPortIdentifier.empty())
5042 if (
this == topLevelComposition)
5044 dispatch_async(runCompositionQueue, ^{
5045 if (isRunningThreadUnsafe())
5047 json_object *constantObject = json_tokener_parse(constant.c_str());
5048 runner->
setInputPortValue(thisCompositionIdentifier, runningPortIdentifier, constantObject);
5056 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
5058 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
5071 dispatch_async(runCompositionQueue, ^{
5072 if (isRunningThreadUnsafe())
5074 json_object *constantObject = json_tokener_parse(constant.c_str());
5076 foreach (
string subcompositionIdentifier, subcompositionIdentifiers)
5078 runner->
setInputPortValue(subcompositionIdentifier, portIdentifier, constantObject);
5089 dispatch_async(runCompositionQueue, ^{
5090 if (isRunningThreadUnsafe())
5095 json_object *constantObject = json_tokener_parse(constant.c_str());
5096 map<VuoRunner::Port *, json_object *> m;
5097 m[publishedPort] = constantObject;
5099 json_object_put(constantObject);
5111 return contextMenuDeleteSelected;
5121 QMenu *contextMenuTints =
new QMenu(parent);
5122 contextMenuTints->setSeparatorsCollapsible(
false);
5123 contextMenuTints->setTitle(tr(
"Tint"));
5124 foreach (QAction *tintAction, contextMenuTintActions)
5125 contextMenuTints->addAction(tintAction);
5126 contextMenuTints->insertSeparator(contextMenuTintActions.last());
5128 return contextMenuTints;
5134void VuoEditorComposition::expandChangeNodeMenu()
5136 QAction *sender = (QAction *)QObject::sender();
5141 int currentMatchesListed = contextMenuChangeNode->actions().size()-1;
5142 if (currentMatchesListed <= initialChangeNodeSuggestionCount)
5145 int verticalSpacePerItem = 21;
5149 int targetMatches = availableVerticalSpace/verticalSpacePerItem - 2;
5158 contextMenuChangeNode->exec();
5173 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
5174 vector<VuoCompilerNodeClass *> loadedNodeClasses;
5175 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
5176 loadedNodeClasses.push_back(i->second);
5179 vector<string> bestMatches;
5180 map<string, double> matchScores;
5181 matchScores[
""] = 0;
5183 int targetMatchCount = (matchLimit > 0? matchLimit : loadedNodeClasses.size());
5184 for (
int i = 0; i < targetMatchCount; ++i)
5185 bestMatches.push_back(
"");
5188 bool overflow =
false;
5191 string originalGenericNodeClassName;
5195 originalGenericNodeClassName = nodeClass->
getClassName();
5200 if (loadedNodeClassName == originalGenericNodeClassName)
5203 bool canSwapNondestructively = canSwapWithoutBreakingCables(node, loadedNodeClass->
getBase());
5204 double matchScore = (canSwapNondestructively? calculateNodeSimilarity(nodeClass, loadedNodeClass->
getBase()) : 0);
5205 int highestIndexWithCompetitiveScore = -1;
5206 for (
int i = targetMatchCount-1; (i >= 0) && (highestIndexWithCompetitiveScore == -1); --i)
5207 if (matchScore <= matchScores[bestMatches[i] ])
5208 highestIndexWithCompetitiveScore = i;
5210 if (highestIndexWithCompetitiveScore < targetMatchCount-1)
5212 if (matchScores[bestMatches[targetMatchCount-1] ] > 0)
5215 for (
int j = targetMatchCount-2; j > highestIndexWithCompetitiveScore; --j)
5216 bestMatches[j+1] = bestMatches[j];
5218 bestMatches[highestIndexWithCompetitiveScore+1] = loadedNodeClassName;
5219 matchScores[loadedNodeClassName] = matchScore;
5223 for (
int i = 0; i < targetMatchCount; ++i)
5225 if (matchScores[bestMatches[i] ] > 0)
5230 matchDisplayText += QString(
" (%1)").arg(bestMatches[i].c_str());
5232 QAction *changeAction = menu->addAction(matchDisplayText);
5234 QList<QVariant> currentNodeAndNewClass;
5235 currentNodeAndNewClass.append(QVariant::fromValue(node));
5236 currentNodeAndNewClass.append(bestMatches[i].c_str());
5237 changeAction->setData(QVariant(currentNodeAndNewClass));
5238 connect(changeAction, &QAction::triggered,
this, &VuoEditorComposition::swapNode);
5245 QAction *showMoreAction = menu->addAction(tr(
"More…"));
5246 showMoreAction->setData(QVariant::fromValue(node));
5247 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandChangeNodeMenu);
5258 map<string, int> requiredInputs;
5259 bool inputEventSourceRequired =
false;
5262 bool hasDrawerWithNoIncomingCables =
false;
5263 bool hasDrawerWithNoIncomingDataCables =
false;
5270 hasDrawerWithNoIncomingCables =
true;
5271 hasDrawerWithNoIncomingDataCables =
true;
5272 vector<VuoRendererPort *> childPorts = inputDrawer->
getDrawerPorts();
5276 hasDrawerWithNoIncomingCables =
false;
5278 hasDrawerWithNoIncomingDataCables =
false;
5283 if (!hasDrawerWithNoIncomingDataCables)
5290 requiredInputs[typeKey] = ((requiredInputs.find(typeKey) == requiredInputs.end())? 1 : requiredInputs[typeKey]+1);
5295 if (hasIncomingCables && !hasDrawerWithNoIncomingCables)
5296 inputEventSourceRequired =
true;
5300 map<string, int> requiredOutputs;
5301 bool outputEventSourceRequired =
false;
5313 requiredOutputs[typeKey] = ((requiredOutputs.find(typeKey) == requiredOutputs.end())? 1 : requiredOutputs[typeKey]+1);
5317 outputEventSourceRequired =
true;
5321 bool inputEventSourceAvailable =
false;
5322 map<string, int> availableInputs;
5330 availableInputs[typeKey] = ((availableInputs.find(typeKey) == availableInputs.end())? 1 : availableInputs[typeKey]+1);
5335 inputEventSourceAvailable =
true;
5338 bool outputEventSourceAvailable =
false;
5339 map<string, int> availableOutputs;
5347 availableOutputs[typeKey] = ((availableOutputs.find(typeKey) == availableOutputs.end())? 1 : availableOutputs[typeKey]+1);
5352 outputEventSourceAvailable =
true;
5355 for (std::map<string,int>::iterator it=requiredInputs.begin(); it!=requiredInputs.end(); ++it)
5357 string typeKey = it->first;
5358 int typeRequiredCount = it->second;
5359 if (availableInputs[typeKey] < typeRequiredCount)
5364 for (std::map<string,int>::iterator it=requiredOutputs.begin(); it!=requiredOutputs.end(); ++it)
5366 string typeKey = it->first;
5367 int typeRequiredCount = it->second;
5368 if (availableOutputs[typeKey] < typeRequiredCount)
5372 if (inputEventSourceRequired && !inputEventSourceAvailable)
5375 if (outputEventSourceRequired && !outputEventSourceAvailable)
5385bool VuoEditorComposition::isPortCurrentlyRevertible(
VuoRendererPort *port)
5391 if (!specializedNodeClass)
5396 if (!originalGenericType)
5405 if (hostPort && (!isPortCurrentlyRevertible(hostPort->
getRenderer())))
5432 string publishedPortName = ((! name.empty())?
5443 bool performedMerge =
false;
5446 publishedPort = (isPublishedInput ?
5452 if (isPublishedInput && portType && type && !forceEventOnlyPublication)
5460 performedMerge =
true;
5467 if (! performedMerge)
5470 if (isPublishedInput && type)
5485 if (! existingPublishedCable)
5491 if (mergePerformed != NULL)
5492 *mergePerformed = performedMerge;
5494 return rendererPublishedPort;
5506 if (creatingPublishedInputCable)
5509 VuoPort *fromPort = externalPort;
5512 VuoPort *toPort = internalPort;
5519 publishedCable->
setFrom(fromNode, fromPort);
5526 VuoPort *fromPort = internalPort;
5529 VuoPort *toPort = externalPort;
5536 publishedCable->
setTo(toNode, toPort);
5539 if (forceEventOnlyPublication)
5542 return publishedCable;
5558 vector<VuoPublishedPort *> publishedPortsToAdd;
5559 map<VuoPublishedPort *, string> publishedPortsToRename;
5562 VuoProtocol *previousActiveProtocol = this->activeProtocol;
5563 bool removingPreviousProtocol = previousActiveProtocol && (previousActiveProtocol != protocol);
5565 bool portChangesMadeDuringProtocolRemoval =
false;
5566 if (removingPreviousProtocol)
5569 if (portChangesMadeDuringProtocolRemoval && !useUndoStack)
5571 VUserLog(
"Warning: Unexpected combination: Removing protocol ports, but useUndoStack=false");
5572 useUndoStack =
true;
5576 this->activeProtocol = protocol;
5581 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5583 string portName = i->first;
5584 string portType = i->second;
5586 bool compositionHadCompatiblePort =
false;
5588 if (preexistingPublishedPort)
5592 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5593 (!preexistingType && (portType ==
"")));
5596 compositionHadCompatiblePort =
true;
5601 compositionHadCompatiblePort =
false;
5606 if (!compositionHadCompatiblePort)
5616 publishedPortsToAdd.push_back(publishedPort);
5623 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5625 string portName = i->first;
5626 string portType = i->second;
5628 bool compositionHadCompatiblePort =
false;
5630 if (preexistingPublishedPort)
5633 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5634 (!preexistingType && (portType ==
"")));
5637 compositionHadCompatiblePort =
true;
5642 compositionHadCompatiblePort =
false;
5647 if (!compositionHadCompatiblePort)
5657 publishedPortsToAdd.push_back(publishedPort);
5665 bool undoStackMacroBegunAlready = (removingPreviousProtocol && portChangesMadeDuringProtocolRemoval);
5666 if (!publishedPortsToRename.empty() || !publishedPortsToAdd.empty() || undoStackMacroBegunAlready)
5668 set<VuoPublishedPort *> publishedPortsToRemove;
5669 bool beginUndoStackMacro = !undoStackMacroBegunAlready;
5670 bool endUndoStackMacro =
true;
5671 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5685string VuoEditorComposition::getNonProtocolVariantForPortName(
string portName)
5687 string modifiedPortName = portName;
5688 if (modifiedPortName.length() > 0)
5689 modifiedPortName[0] = toupper(modifiedPortName[0]);
5690 modifiedPortName =
"some" + modifiedPortName;
5692 return modifiedPortName;
5716 set<VuoPublishedPort *> publishedPortsToRemove;
5717 map<VuoPublishedPort *, string> publishedPortsToRename;
5720 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5722 string portName = i->first;
5723 string portType = i->second;
5726 if (preexistingPublishedPort)
5728 bool portCompatibleAcrossProtocolTransition =
false;
5729 if (replacementProtocol)
5732 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5734 string replacementPortName = i->first;
5735 string replacementPortType = i->second;
5737 if ((portName == replacementPortName) && (portType == replacementPortType))
5738 portCompatibleAcrossProtocolTransition =
true;
5743 publishedPortsToRemove.insert(preexistingPublishedPort);
5744 else if (!portCompatibleAcrossProtocolTransition)
5750 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5752 string portName = i->first;
5753 string portType = i->second;
5756 if (preexistingPublishedPort)
5758 bool portCompatibleAcrossProtocolTransition =
false;
5759 if (replacementProtocol)
5762 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5764 string replacementPortName = i->first;
5765 string replacementPortType = i->second;
5767 if ((portName == replacementPortName) && (portType == replacementPortType))
5768 portCompatibleAcrossProtocolTransition =
true;
5773 publishedPortsToRemove.insert(preexistingPublishedPort);
5774 else if (!portCompatibleAcrossProtocolTransition)
5781 if (!publishedPortsToRemove.empty())
5782 publishedPortsToRename.clear();
5784 bool portChangesRequired = (!publishedPortsToRename.empty() || !publishedPortsToRemove.empty());
5785 if (portChangesRequired)
5787 vector<VuoPublishedPort *> publishedPortsToAdd;
5788 bool beginUndoStackMacro =
true;
5789 bool endUndoStackMacro = !replacementProtocol;
5790 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5795 return portChangesRequired;
5806 if ((activeProtocol != protocol) || !activeProtocol)
5809 activeProtocol = NULL;
5812 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5814 string portName = i->first;
5815 string portType = i->second;
5818 if (preexistingPublishedPort)
5823 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5825 string portName = i->first;
5826 string portType = i->second;
5829 if (preexistingPublishedPort)
5842 return activeProtocol;
5851 if (!activeProtocol)
5854 return static_cast<VuoEditor *
>(qApp)->getBuiltInDriverForProtocol(activeProtocol);
5887 return removalResult;
5914void VuoEditorComposition::highlightEligibleEndpointsForCable(
VuoCable *cable)
5927 highlightInternalPortsConnectableToPort(fixedPort, cable->
getRenderer());
5939 QList<QGraphicsItem *> compositionComponents = items();
5942 map<VuoRendererPort *, VuoRendererColors::HighlightType> highlightForPort;
5944 for (QGraphicsItem *compositionComponent : compositionComponents)
5951 for (
VuoPort *inputPort : inputPorts)
5952 highlightForPort[inputPort->getRenderer()] =
5953 updateEligibilityHighlightingForPort(inputPort->getRenderer(), port, !cable->effectivelyCarriesData());
5957 for (
VuoPort *outputPort : outputPorts)
5958 highlightForPort[outputPort->getRenderer()] =
5959 updateEligibilityHighlightingForPort(outputPort->getRenderer(), port, !cable->effectivelyCarriesData());
5963 for (QGraphicsItem *compositionComponent : compositionComponents)
5967 if (rc && rc != cable)
5969 QGraphicsItem::CacheMode normalCacheMode = rc->cacheMode();
5970 rc->setCacheMode(QGraphicsItem::NoCache);
5987 rc->setCacheMode(normalCacheMode);
5992 for (QGraphicsItem *compositionComponent : compositionComponents)
5993 updateEligibilityHighlightingForNode(dynamic_cast<
VuoRendererNode *>(compositionComponent));
6002 bool eventOnlyConnection)
6004 QGraphicsItem::CacheMode normalCacheMode = portToHighlight->cacheMode();
6005 portToHighlight->setCacheMode(QGraphicsItem::NoCache);
6013 if (typecastPortToHighlight)
6016 portToHighlight->setCacheMode(normalCacheMode);
6018 if (typecastPortToHighlight)
6019 updateEligibilityHighlightingForPort(typecastPortToHighlight->
getChildPort(), fixedPort, eventOnlyConnection);
6041 bool forwardConnection;
6044 fromPort = fixedPort;
6045 toPort = portToHighlight;
6046 forwardConnection =
true;
6050 fromPort = portToHighlight;
6052 forwardConnection =
false;
6055 bool directConnectionPossible;
6059 if (fixedExternalPublishedPort && externalPublishedPortToHighlight)
6060 directConnectionPossible =
false;
6061 else if (fixedExternalPublishedPort && !externalPublishedPortToHighlight)
6063 else if (!fixedExternalPublishedPort && externalPublishedPortToHighlight)
6069 if (directConnectionPossible)
6073 else if (fixedPort == portToHighlight)
6096bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6098 bool eventOnlyConnection,
6099 bool forwardConnection)
6102 string respecializedTypeName =
"";
6104 return canConnectDirectlyWithRespecializationNondestructively(fromPort,
6106 eventOnlyConnection,
6108 &portToRespecialize,
6109 respecializedTypeName);
6122bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6124 bool eventOnlyConnection,
6125 bool forwardConnection,
6127 string &respecializedTypeName)
6129 *portToRespecialize = NULL;
6130 respecializedTypeName =
"";
6132 bool canConnectWithRespecialization = canConnectDirectlyWithRespecialization(fromPort,
6134 eventOnlyConnection,
6137 respecializedTypeName);
6138 if (!canConnectWithRespecialization)
6141 if (canConnectWithRespecialization && !portToRespecialize)
6144 bool nondestructive = portCanBeUnspecializedNondestructively((*portToRespecialize)->getBase());
6145 if (!nondestructive)
6147 *portToRespecialize = NULL;
6148 respecializedTypeName =
"";
6150 return nondestructive;
6158bool VuoEditorComposition::portCanBeUnspecializedNondestructively(
VuoPort *portToUnspecialize)
6160 map<VuoNode *, string> nodesToReplace;
6161 set<VuoCable *> cablesToDelete;
6166 if (cablesToDelete.empty())
6169 else if ((cablesToDelete.size() == 1) && ((*(cablesToDelete.begin()))->getToPort() == portToUnspecialize))
6194bool VuoEditorComposition::canConnectDirectlyWithRespecialization(
VuoRendererPort *fromPort,
6196 bool eventOnlyConnection,
6197 bool forwardConnection,
6199 string &respecializedTypeName)
6203 *portToRespecialize = NULL;
6204 respecializedTypeName =
"";
6214 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6215 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6217 if (!(fromPortIsEnabledOutput && toPortIsEnabledInput))
6223 if (!(currentFromDataType && currentToDataType))
6238 if (fromSpecializedNodeClass)
6250 if (toSpecializedNodeClass)
6259 bool fromPortIsGeneric = currentFromGenericType;
6260 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6261 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6263 bool toPortIsGeneric = currentToGenericType;
6264 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6265 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6268 set<string> compatibleTypes;
6269 string specializedType =
"";
6273 if ((fromPortIsStatic && toPortIsSpecialized) || (fromPortIsSpecialized && toPortIsStatic))
6277 portToTryToRespecialize = (fromPortIsSpecialized? fromPort : toPort);
6281 else if ((fromPortIsSpecialized || toPortIsSpecialized) && !fromPortIsStatic && !toPortIsStatic)
6284 bool dragSourceIsGeneric = (forwardConnection? fromPortIsGeneric : toPortIsGeneric);
6286 VuoRendererPort *dragDestination = (forwardConnection? toPort : fromPort);
6287 bool dragDestinationIsGeneric = (forwardConnection? toPortIsGeneric : fromPortIsGeneric);
6304 if (!dragSourceIsGeneric && !dragDestinationIsGeneric)
6307 portToTryToRespecialize = dragDestination;
6315 if (portToTryToRespecialize)
6316 compatibleTypes = getRespecializationOptionsForPortInNetwork(portToTryToRespecialize);
6318 bool portsAreCompatible = (compatibleTypes.find(specializedType) != compatibleTypes.end());
6320 if (portsAreCompatible)
6322 *portToRespecialize = portToTryToRespecialize;
6323 respecializedTypeName = specializedType;
6326 return portsAreCompatible;
6335void VuoEditorComposition::updateEligibilityHighlightingForNode(
VuoRendererNode *node)
6350 QGraphicsItem::CacheMode normalCacheMode = drawer->cacheMode();
6351 drawer->setCacheMode(QGraphicsItem::NoCache);
6356 drawer->setCacheMode(normalCacheMode);
6365 QGraphicsItem::CacheMode normalCacheMode = hostPort->cacheMode();
6366 hostPort->setCacheMode(QGraphicsItem::NoCache);
6368 hostPort->setCacheMode(normalCacheMode);
6402 bool toPortIsDragDestination,
6404 string &specializedTypeName,
6405 string &typecastToInsert)
6407 *portToSpecialize = NULL;
6408 specializedTypeName =
"";
6410 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6411 map<string, string> specializedTypeNameForTypecast;
6413 vector<string> candidateTypecasts =
findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast);
6414 bool solutionSelected = selectBridgingSolutionFromOptions(candidateTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, typecastToInsert);
6416 if (!solutionSelected)
6419 if (portToSpecializeForTypecast.find(typecastToInsert) != portToSpecializeForTypecast.end())
6420 *portToSpecialize = portToSpecializeForTypecast[typecastToInsert];
6421 if (specializedTypeNameForTypecast.find(typecastToInsert) != specializedTypeNameForTypecast.end())
6422 specializedTypeName = specializedTypeNameForTypecast[typecastToInsert];
6445bool VuoEditorComposition::selectBridgingSolutionFromOptions(vector<string> suitableTypecasts,
6446 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6447 map<string, string> specializedTypeNameForTypecast,
6448 string &selectedTypecast)
6450 if (suitableTypecasts.empty())
6452 selectedTypecast =
"";
6456 else if (suitableTypecasts.size() == 1)
6458 selectedTypecast = suitableTypecasts[0];
6463 return promptForBridgingSelectionFromOptions(suitableTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, selectedTypecast);
6473 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6474 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6476 return (fromPortIsEnabledOutput && toPortIsEnabledInput &&
6487 const string &candidateFromTypeName,
const string &candidateToTypeName)
6489 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6495 string fromTypeName = ! candidateFromTypeName.empty() ?
6496 candidateFromTypeName :
6498 string toTypeName = ! candidateToTypeName.empty() ?
6499 candidateToTypeName :
6503 if (fromTypeName ==
"VuoBoolean" && toTypeName ==
"VuoInteger")
6512 if (toNodeUsesIndex)
6538 bool toPortIsDragDestination)
6540 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6541 map<string, string> specializedTypeNameForTypecast;
6542 return findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast);
6561 bool toPortIsDragDestination,
6562 map<string, VuoRendererPort *> &portToSpecializeForTypecast,
6563 map<string, string> &specializedTypeNameForTypecast)
6565 portToSpecializeForTypecast.clear();
6566 specializedTypeNameForTypecast.clear();
6568 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6579 string specializedTypeName =
"";
6582 portToSpecializeForTypecast[
""] = portToSpecialize;
6583 specializedTypeNameForTypecast[
""] = specializedTypeName;
6591 if (!(currentFromDataType && currentToDataType))
6602 if (fromSpecializedNodeClass)
6614 if (toSpecializedNodeClass)
6625 bool fromPortIsGeneric = currentFromGenericType;
6626 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6627 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6629 bool toPortIsGeneric = currentToGenericType;
6630 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6631 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6634 if (fromPortIsGeneric && toPortIsGeneric)
6638 else if (fromPortIsStatic && toPortIsStatic)
6640 if (! portsPassSanityCheckToTypeconvert(fromPort, toPort))
6649 bool specializeToPort =
true;
6650 if (toPortIsGeneric)
6651 specializeToPort =
true;
6652 else if (fromPortIsGeneric)
6653 specializeToPort =
false;
6654 else if (fromPortIsSpecialized && toPortIsStatic)
6655 specializeToPort =
false;
6656 else if (fromPortIsStatic && toPortIsSpecialized)
6657 specializeToPort =
true;
6658 else if (fromPortIsSpecialized && toPortIsSpecialized)
6659 specializeToPort = toPortIsDragDestination;
6663 set<string> compatibleTypes;
6664 if (specializeToPort && (toPortIsGeneric || (toPortIsSpecialized && portCanBeUnspecializedNondestructively(toPort->
getBase()))))
6665 compatibleTypes = getRespecializationOptionsForPortInNetwork(toPort);
6666 else if (!specializeToPort && (fromPortIsGeneric || (fromPortIsSpecialized && portCanBeUnspecializedNondestructively(fromPort->
getBase()))))
6667 compatibleTypes = getRespecializationOptionsForPortInNetwork(fromPort);
6675 vector<string> limitedSuitableTypecasts;
6678 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6682 foreach (
string typecastName, limitedSuitableTypecasts)
6684 portToSpecializeForTypecast[typecastName] = specializeToPort? toPort : fromPort;
6685 specializedTypeNameForTypecast[typecastName] = specializeToPort? currentToDataType->
getModuleKey() :
6692 if (compatibleTypes.find(fixedDataType) != compatibleTypes.end())
6694 limitedSuitableTypecasts.push_back(
"");
6695 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6696 specializedTypeNameForTypecast[
""] = fixedDataType;
6699 if (limitedSuitableTypecasts.size() >= 1)
6700 return limitedSuitableTypecasts;
6704 vector<string> suitableTypecasts;
6705 for (
const string &compatibleTypeName : compatibleTypes)
6709 string candidateFromTypeName;
6710 string candidateToTypeName;
6713 if (specializeToPort)
6715 candidateFromTypeName = currentFromDataType->
getModuleKey();
6716 candidateFromType = currentFromDataType;
6717 candidateToTypeName = compatibleTypeName;
6718 candidateToType =
nullptr;
6722 candidateFromTypeName = compatibleTypeName;
6723 candidateFromType =
nullptr;
6724 candidateToTypeName = currentToDataType->
getModuleKey();
6725 candidateToType = currentToDataType;
6729 if (candidateFromTypeName == candidateToTypeName)
6731 suitableTypecasts.push_back(
"");
6732 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6733 specializedTypeNameForTypecast[
""] = compatibleTypeName;
6736 if (portsPassSanityCheckToTypeconvert(fromPort, toPort, candidateFromTypeName, candidateToTypeName))
6738 vector<string> suitableTypecastsForCurrentTypes = moduleManager->
getCompatibleTypecastClasses(candidateFromTypeName, candidateFromType,
6739 candidateToTypeName, candidateToType);
6740 foreach (
string typecast, suitableTypecastsForCurrentTypes)
6742 suitableTypecasts.push_back(typecast);
6743 portToSpecializeForTypecast[typecast] = specializeToPort? toPort : fromPort;
6744 specializedTypeNameForTypecast[typecast] = compatibleTypeName;
6749 return suitableTypecasts;
6764bool VuoEditorComposition::promptForBridgingSelectionFromOptions(vector<string> suitableTypecasts,
6765 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6766 map<string, string> specializedTypeNameForTypecast,
6767 string &selectedTypecast)
6769 QMenu typecastMenu(views()[0]->viewport());
6770 typecastMenu.setSeparatorsCollapsible(
false);
6771 QString spacer(
" ");
6774 set <pair<VuoRendererPort *, string> > specializationDetails;
6775 vector<string> typeconversionOptionsRequiringNoSpecialization;
6776 foreach (
string typecastClassName, suitableTypecasts)
6778 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typecastClassName];
6779 string specializedTypeName = specializedTypeNameForTypecast[typecastClassName];
6780 specializationDetails.insert(std::make_pair(portToSpecialize,
6781 specializedTypeName));
6783 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6784 if (portAlreadyHasTargetType)
6785 typeconversionOptionsRequiringNoSpecialization.push_back(typecastClassName);
6791 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()))
6793 QString menuText = getDisplayTextForSpecializationOption(portToSpecializeForTypecast[
""], specializedTypeNameForTypecast[
""]);
6794 QAction *typecastAction = typecastMenu.addAction(menuText);
6795 typecastAction->setData(QVariant(
""));
6798 bool foundSpecializationOptionsRequiringNoTypeconversion = typecastMenu.actions().size() >= 1;
6799 bool foundTypeconversionOptionsRequiringNoSpecialization = typeconversionOptionsRequiringNoSpecialization.size() >= 1;
6802 bool includingTypeconvertWithNoSpecializationHeader = foundSpecializationOptionsRequiringNoTypeconversion;
6803 if (foundTypeconversionOptionsRequiringNoSpecialization)
6805 if (foundSpecializationOptionsRequiringNoTypeconversion)
6806 typecastMenu.addSeparator();
6808 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6809 string specializedTypeName = specializedTypeNameForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6811 if (portToSpecialize && !specializedTypeName.empty())
6813 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6814 QAction *typecastAction = typecastMenu.addAction(menuText);
6815 typecastAction->setEnabled(
false);
6816 includingTypeconvertWithNoSpecializationHeader =
true;
6820 foreach (
string typecastClassName, typeconversionOptionsRequiringNoSpecialization)
6825 QAction *typecastAction = typecastMenu.addAction((includingTypeconvertWithNoSpecializationHeader? spacer :
"") +
VuoRendererTypecastPort::getTypecastTitleForNodeClass(typecastClass->
getBase(), true));
6826 typecastAction->setData(QVariant(typecastClassName.c_str()));
6831 for (set<pair<VuoRendererPort *, string> >::iterator i = specializationDetails.begin(); i != specializationDetails.end(); ++i)
6834 string specializedTypeName = i->second;
6837 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()) &&
6838 (portToSpecializeForTypecast[
""] == portToSpecialize) &&
6839 (specializedTypeNameForTypecast[
""] == specializedTypeName))
6845 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6846 if (portAlreadyHasTargetType)
6851 if (typecastMenu.actions().size() >= 1)
6852 typecastMenu.addSeparator();
6854 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6855 QAction *typecastAction = typecastMenu.addAction(menuText);
6856 typecastAction->setEnabled(
false);
6859 foreach (
string typecastClassName, suitableTypecasts)
6861 if ((portToSpecializeForTypecast[typecastClassName] == portToSpecialize) &&
6862 (specializedTypeNameForTypecast[typecastClassName] == specializedTypeName))
6868 typecastAction->setData(QVariant(typecastClassName.c_str()));
6874 menuSelectionInProgress =
true;
6875 QAction *selectedTypecastAction = typecastMenu.exec(QCursor::pos());
6876 menuSelectionInProgress =
false;
6878 selectedTypecast = (selectedTypecastAction? selectedTypecastAction->data().toString().toUtf8().constData() :
"");
6879 return selectedTypecastAction;
6885QString VuoEditorComposition::getDisplayTextForSpecializationOption(
VuoRendererPort *portToSpecialize,
string specializedTypeName)
6887 if (!portToSpecialize || specializedTypeName.empty())
6890 bool isInput = portToSpecialize && portToSpecialize->
getInput();
6893 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6895 QString displayText;
6896 if (portAlreadyHasTargetType)
6901 displayText = tr(
"Keep Input Port as %1");
6906 displayText = tr(
"Keep Output Port as %1");
6914 displayText = tr(
"Change Input Port to %1");
6919 displayText = tr(
"Change Output Port to %1");
6923 return displayText.arg(typeDisplayName);
6932 __block json_object *portValue = NULL;
6942 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
6943 if (topLevelComposition->isRunningThreadUnsafe())
6945 portValue = isInput ?
6946 topLevelComposition->runner->getInputPortValue(thisCompositionIdentifier, runningPortIdentifier) :
6947 topLevelComposition->runner->getOutputPortValue(thisCompositionIdentifier, runningPortIdentifier);
6951 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, getPortValue);
6959string VuoEditorComposition::getIdentifierForRunningPort(
VuoPort *runningPort)
6978 return dynamic_cast<VuoPublishedPort *
>(staticPort)->getClass()->getName();
6984 string nodeIdentifier =
"";
6994 if (staticPort->
hasCompiler() && !nodeIdentifier.empty())
7038 if (candidateInputPort == port)
7042 if (candidateOutputPort == port)
7058 map<string, VuoPortPopover *>::iterator popover = activePortPopovers.find(portID);
7059 if (popover != activePortPopovers.end())
7060 return popover->second;
7073void VuoEditorComposition::enableInactivePopoverForPort(
VuoRendererPort *rp)
7076 bool popoverJustClosedAtLastEvent = portsWithPopoversClosedAtLastEvent.find(portID) != portsWithPopoversClosedAtLastEvent.end();
7077 if (!popoverJustClosedAtLastEvent)
7086 if (!popoverEventsEnabled)
7092 VUserLog(
"%s: Open popover for %s",
7096 dispatch_sync(runCompositionQueue, ^{
7098 dispatch_sync(activePortPopoversQueue, ^{
7100 if (activePortPopovers.find(portID) == activePortPopovers.end())
7104 VuoPortPopover *popover = new VuoPortPopover(port, this, views()[0]->viewport());
7106 connect(popover, &VuoPortPopover::popoverClosedForPort, this, &VuoEditorComposition::disablePopoverForPortThreadSafe);
7107 connect(popover, &VuoPortPopover::popoverDetachedFromPort, [=]{
7108 VUserLog(
"%s: Detach popover for %s",
7109 window->getWindowTitleWithoutPlaceholder().toUtf8().data(),
7121 const int cutoffMargin = 16;
7122 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
7123 if (portLeftInScene.x() + popover->size().width() + cutoffMargin > viewportRect.right())
7124 portLeftInScene = QPoint(viewportRect.right() - popover->size().width() - cutoffMargin, portLeftInScene.y());
7125 if (portLeftInScene.y() + popover->size().height() + cutoffMargin > viewportRect.bottom())
7126 portLeftInScene = QPoint(portLeftInScene.x(), viewportRect.bottom() - popover->size().height() - cutoffMargin);
7128 QPoint popoverLeftInView = views()[0]->mapFromScene(portLeftInScene);
7130 const QPoint offset = QPoint(12, 6);
7132 QPoint popoverTopLeft = popoverLeftInView + offset;
7133 popover->move(popoverTopLeft);
7136 activePortPopovers[portID] = popover;
7138 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7165 VUserLog(
"%s: Close popover for %s",
7170 map<string, VuoPortPopover *>::iterator i = activePortPopovers.find(portID);
7171 if (i != activePortPopovers.end())
7173 popover = i->second;
7174 activePortPopovers.erase(i);
7180 popover->deleteLater();
7183 bool isInput =
false;
7191 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7192 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7194 dispatch_async(topLevelComposition->runCompositionQueue, ^{
7195 if (topLevelComposition->isRunningThreadUnsafe())
7198 topLevelComposition->runner->unsubscribeFromInputPortTelemetry(thisCompositionIdentifier, portID) :
7199 topLevelComposition->runner->unsubscribeFromOutputPortTelemetry(thisCompositionIdentifier, portID));
7209void VuoEditorComposition::disablePopoverForPortThreadSafe(
string portID)
7211 dispatch_sync(activePortPopoversQueue, ^{
7221 disablePortPopovers();
7232 errorPopover->hide();
7233 errorPopover->deleteLater();
7236 errorPopovers.clear();
7245 dispatch_sync(activePortPopoversQueue, ^{
7246 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7247 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7249 string portID = i->first;
7250 bool shouldDisable =
false;
7253 shouldDisable =
true;
7272 dispatch_sync(activePortPopoversQueue, ^{
7273 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7274 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7276 string portID = i->first;
7291 if (recordWhichPopoversClosed)
7292 portsWithPopoversClosedAtLastEvent.clear();
7294 dispatch_sync(activePortPopoversQueue, ^{
7295 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7296 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7298 string portID = i->first;
7299 bool shouldDisable =
false;
7302 shouldDisable =
true;
7316 portsWithPopoversClosedAtLastEvent.insert(portID);
7328 moveDetachedPortPopoversBy(dx, dy);
7329 moveErrorPopoversBy(dx, dy);
7335void VuoEditorComposition::moveErrorPopoversBy(
int dx,
int dy)
7338 errorPopover->move(errorPopover->pos().x()+dx, errorPopover->pos().y()+dy);
7344void VuoEditorComposition::moveDetachedPortPopoversBy(
int dx,
int dy)
7346 dispatch_sync(activePortPopoversQueue, ^{
7347 map<string, VuoPortPopover *> portPopovers = activePortPopovers;
7348 for (map<string, VuoPortPopover *>::iterator i = portPopovers.begin(); i != portPopovers.end(); ++i)
7352 popover->move(popover->pos().x()+dx, popover->pos().y()+dy);
7360void VuoEditorComposition::setPopoversHideOnDeactivate(
bool shouldHide)
7362 dispatch_sync(activePortPopoversQueue, ^{
7363 auto portPopovers = activePortPopovers;
7364 for (
auto i : portPopovers)
7370 ((void (*)(id, SEL,
BOOL))objc_msgSend)(nsWindow, sel_getUid(
"setHidesOnDeactivate:"), shouldHide);
7382 dispatch_sync(activePortPopoversQueue, ^{
7383 for (map<string, VuoPortPopover *>::iterator i = activePortPopovers.begin(); i != activePortPopovers.end(); ++i)
7385 string portID = i->first;
7387 bool shouldUpdate =
false;
7390 shouldUpdate =
true;
7399 QMetaObject::invokeMethod(popover,
"updateTextAndResize", Qt::QueuedConnection);
7416 string popoverCompositionIdentifier,
7427 string portSummary = (isInput ?
7431 dispatch_async(popoverComposition->activePortPopoversQueue, ^{
7432 VuoPortPopover *popover = popoverComposition->getActivePopoverForPort(portID);
7435 QMetaObject::invokeMethod(popover,
"updateDataValueImmediately", Qt::QueuedConnection, Q_ARG(QString, portSummary.c_str()));
7436 QMetaObject::invokeMethod(popover,
"setCompositionRunning", Qt::QueuedConnection, Q_ARG(bool, true), Q_ARG(bool, false));
7450 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7452 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7453 if (topLevelComposition->isRunningThreadUnsafe())
7454 topLevelComposition->updateDataInPortPopoverFromRunningTopLevelComposition(this, thisCompositionIdentifier, portID);
7464 bool receivedEvent,
bool receivedData,
string dataSummary)
7468 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7469 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7471 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7472 Q_ARG(bool, receivedEvent),
7473 Q_ARG(bool, receivedData),
7474 Q_ARG(QString, dataSummary.c_str()));
7477 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7485 bool sentEvent,
bool sentData,
string dataSummary)
7489 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7490 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7492 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7493 Q_ARG(bool, sentEvent),
7494 Q_ARG(bool, sentData),
7495 Q_ARG(QString, dataSummary.c_str()));
7498 if (matchingComposition->showEventsMode && sentEvent)
7503 port->getRenderer()->setFiredEvent();
7504 matchingComposition->animatePort(port->getRenderer());
7509 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7520 dispatch_async(matchingComposition->runCompositionQueue, ^{
7521 if (matchingComposition->isRunningThreadUnsafe())
7523 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7524 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7526 QMetaObject::invokeMethod(popover,
"incrementDroppedEventCount", Qt::QueuedConnection);
7531 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7542 if (matchingComposition->showEventsMode)
7544 dispatch_async(this->runCompositionQueue, ^{
7545 if (this->isRunningThreadUnsafe())
7554 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7565 if (matchingComposition->showEventsMode)
7567 dispatch_async(this->runCompositionQueue, ^{
7568 if (this->isRunningThreadUnsafe())
7577 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7606 return showEventsMode;
7614 this->showEventsMode = showEventsMode;
7622 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7623 if (topLevelComposition->isRunningThreadUnsafe())
7624 topLevelComposition->runner->subscribeToEventTelemetry(thisCompositionIdentifier);
7627 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, subscribe);
7635 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7636 if (topLevelComposition->isRunningThreadUnsafe())
7637 topLevelComposition->runner->unsubscribeFromEventTelemetry(thisCompositionIdentifier);
7640 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, unsubscribe);
7676QGraphicsItemAnimation * VuoEditorComposition::setUpAnimationForPort(QGraphicsItemAnimation *animation,
VuoRendererPort *port)
7684 animatedPort->setZValue(VuoRendererItem::triggerAnimationZValue);
7687 animation->setItem(animatedPort);
7688 animation->setScaleAt(0.0, 1, 1);
7689 animation->setScaleAt(0.999, 3, 3);
7690 animation->setScaleAt(1.0, 1, 1);
7692 QTimeLine *animationTimeline = animation->timeLine();
7693 animationTimeline->setFrameRange(0, 100);
7694 animationTimeline->setUpdateInterval(showEventsModeUpdateInterval);
7695 animationTimeline->setCurveShape(QTimeLine::LinearCurve);
7697 preparedAnimations.insert(animation);
7698 animationForTimeline[animation->timeLine()] = animation;
7700 connect(animationTimeline, &QTimeLine::valueChanged,
this, &VuoEditorComposition::updatePortAnimation);
7701 connect(animationTimeline, &QTimeLine::finished,
this, &VuoEditorComposition::endPortAnimation);
7711 dispatch_async(dispatch_get_main_queue(), ^{
7712 QGraphicsItemAnimation *animation = getAvailableAnimationForPort(port);
7718 if (animation->timeLine()->state() == QTimeLine::Running)
7719 animation->timeLine()->setCurrentTime(0);
7723 animatedPort->setPos(port->pos());
7724 animatedPort->setVisible(
true);
7725 animation->timeLine()->start();
7734QGraphicsItemAnimation * VuoEditorComposition::getAvailableAnimationForPort(
VuoRendererPort *port)
7736 vector<QGraphicsItemAnimation *> animations = port->
getAnimations();
7738 QGraphicsItemAnimation *mostAdvancedAnimation = NULL;
7739 qreal maxPercentAdvanced = -1;
7741 for (
int i = 0; i < animations.size(); ++i)
7743 QGraphicsItemAnimation *animation = animations[i];
7744 bool animationPrepared = (preparedAnimations.find(animation) != preparedAnimations.end());
7745 bool animationRunning = (animation->timeLine()->state() == QTimeLine::Running);
7747 if (! animationPrepared)
7748 return setUpAnimationForPort(animation, port);
7750 else if (! animationRunning)
7755 qreal percentAdvanced = animation->timeLine()->currentValue();
7756 if (percentAdvanced > maxPercentAdvanced)
7758 mostAdvancedAnimation = animation;
7759 maxPercentAdvanced = percentAdvanced;
7765 return (maxPercentAdvanced >= 0.5? mostAdvancedAnimation : NULL);
7773void VuoEditorComposition::updatePortAnimation(qreal value)
7775 QTimeLine *animationTimeline = (QTimeLine *)sender();
7776 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7778 const qreal multiplier = 1000.;
7786void VuoEditorComposition::endPortAnimation(
void)
7788 QTimeLine *animationTimeline = (QTimeLine *)sender();
7789 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7791 animatedPort->setVisible(
false);
7797void VuoEditorComposition::setDisableDragStickiness(
bool disable)
7799 this->dragStickinessDisabled = disable;
7807 this->ignoreApplicationStateChangeEvents = ignore;
7817 this->popoverEventsEnabled = enable;
7826 refreshComponentAlphaLevelTimer->start();
7834 refreshComponentAlphaLevelTimer->stop();
7857 if (!activeProtocol)
7864 if (!
getBase()->
getCompiler()->getCachedGraph()->mayEventsReachPublishedOutputPorts())
7866 QString errorHeadline = tr(
"<b>This composition doesn't send any images to <code>outputImage</code>.</b>");
7867 QString errorDetails = tr(
"<p>To export, your composition should use the data and events from the published input ports "
7868 "to output a stream of images through the <code>outputImage</code> published output port.</p>");
7870 if (isExportingMovie)
7871 errorDetails.append(
"<p>Alternatively, you can record a realtime movie by running the composition and selecting File > Start Recording.</p>");
7874 QMessageBox messageBox(window);
7875 messageBox.setWindowFlags(Qt::Sheet);
7876 messageBox.setWindowModality(Qt::WindowModal);
7878 messageBox.setTextFormat(Qt::RichText);
7880 messageBox.setStandardButtons(QMessageBox::Help | QMessageBox::Ok);
7881 messageBox.setButtonText(QMessageBox::Help, tr(
"Open an Example"));
7882 messageBox.setButtonText(QMessageBox::Ok, tr(
"OK"));
7883 messageBox.setDefaultButton(QMessageBox::Ok);
7885 messageBox.setText(errorHeadline);
7886 messageBox.setInformativeText(
"<style>p{" + fonts->
getCSS(fonts->
dialogBodyFont()) +
"}</style>" + errorDetails);
7888 if (messageBox.exec() == QMessageBox::Help)
7890 map<QString, QString> examples =
static_cast<VuoEditor *
>(qApp)->getExampleCompositionsForProtocol(activeProtocol);
7891 map<QString, QString>::iterator i = examples.begin();
7892 if (i != examples.end())
7922 string dir, file, ext;
7936 if (! customizedName.empty())
7937 return QString::fromStdString(customizedName);
7955 string fileNameContentPart = (fileNameParts.size() >= 2 && fileNameParts[fileNameParts.size()-1] ==
"vuo"?
7956 fileNameParts[fileNameParts.size()-2] :
7957 (fileNameParts.size() >= 1? fileNameParts[fileNameParts.size()-1] :
""));
7960 if (QRegExp(
"\\s").indexIn(fileNameContentPart.c_str()) != -1)
7962 string formattedName = fileNameContentPart;
7963 if (formattedName.size() >= 1)
7964 formattedName[0] = toupper(formattedName[0]);
7966 return QString(formattedName.c_str());
7980 QStringList wordsInName = nodeSetName.split(QRegularExpression(
"\\."));
7981 if (wordsInName.size() < 2 || wordsInName[0] !=
"vuo")
7987 map<QString, QString> wordsToReformat;
7988 wordsToReformat[
"artnet"] =
"Art-Net";
7989 wordsToReformat[
"bcf2000"] =
"BCF2000";
7990 wordsToReformat[
"hid"] =
"HID";
7991 wordsToReformat[
"midi"] =
"MIDI";
7992 wordsToReformat[
"ndi"] =
"NDI";
7993 wordsToReformat[
"osc"] =
"OSC";
7994 wordsToReformat[
"rss"] =
"RSS";
7995 wordsToReformat[
"ui"] =
"UI";
7996 wordsToReformat[
"url"] =
"URL";
7998 QString nodeSetDisplayName =
"";
7999 for (
int i = 1; i < wordsInName.size(); ++i)
8001 QString currentWord = wordsInName[i];
8002 if (currentWord.size() >= 1)
8004 if (wordsToReformat.find(currentWord.toLower()) != wordsToReformat.end())
8005 currentWord = wordsToReformat.at(currentWord.toLower());
8007 currentWord[0] = currentWord[0].toUpper();
8009 nodeSetDisplayName += currentWord;
8011 if (i < wordsInName.size()-1)
8012 nodeSetDisplayName +=
" ";
8015 return nodeSetDisplayName;
8024 auto foundListType = listTypes.find(typeName);
8025 if (foundListType != listTypes.end())
8028 auto getVuoType = [
this] (
const string &typeName) ->
VuoCompilerType *
8033 return singletonType;
8038 return compoundType;
8043 return QString::fromStdString(defaultTitle);
8060 return QString::fromStdString(defaultTitle);
8079 return "Transform2D";
8081 return "Transform3D";
8090bool VuoEditorComposition::itemHigherOnCanvas(QGraphicsItem *item1, QGraphicsItem *item2)
8092 qreal item1Y = item1->scenePos().y();
8093 qreal item2Y = item2->scenePos().y();
8095 qreal item1X = item1->scenePos().x();
8096 qreal item2X = item2->scenePos().x();
8098 if (item1Y == item2Y)
8099 return (item1X < item2X);
8101 return item1Y < item2Y;
8114 string originalGenericNode1ClassName, originalGenericNode2ClassName;
8130 vector<string> node1Keywords = node1->
getKeywords();
8131 vector<string> node2Keywords = node2->
getKeywords();
8143 node1Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8147 node2Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8149 set<string> node1KeywordSet(node1Keywords.begin(), node1Keywords.end());
8150 set<string> node2KeywordSet(node2Keywords.begin(), node2Keywords.end());
8152 set<string> nodeKeywordsIntersection;
8153 std::set_intersection(node1KeywordSet.begin(), node1KeywordSet.end(),
8154 node2KeywordSet.begin(), node2KeywordSet.end(),
8155 std::inserter(nodeKeywordsIntersection, nodeKeywordsIntersection.end()));
8157 set<string> nodeKeywordsUnion = node1KeywordSet;
8158 nodeKeywordsUnion.insert(node2KeywordSet.begin(), node2KeywordSet.end());
8161 if (nodeKeywordsUnion.size() == 0)
8165 double nodeSimilarity = nodeKeywordsIntersection.size()/(1.0*nodeKeywordsUnion.size());
8167 return nodeSimilarity;
8186VuoEditorComposition::~VuoEditorComposition()
8188 dispatch_sync(runCompositionQueue, ^{});
8189 dispatch_release(runCompositionQueue);
8191 preparedAnimations.clear();
8192 animationForTimeline.clear();
8194 delete identifierCache;
8196 delete moduleManager;
8220 vector<VuoRendererPort *> sortedPortsToPublish;
8221 foreach (
string portID, portsToPublish)
8225 sortedPortsToPublish.push_back(port->
getRenderer());
8227 std::sort(sortedPortsToPublish.begin(), sortedPortsToPublish.end(), itemHigherOnCanvas);
8229 map<string, string> publishedPortNames;
8236 string publishedPortName = (!specializedPublishedPortName.empty()?
8237 specializedPublishedPortName :
8246 return publishedPortNames;
8274void VuoEditorComposition::repositionPopover()
8279 const int cutoffMargin = 16;
8280 if (popover->pos().x()+popover->size().width()+cutoffMargin > views()[0]->viewport()->rect().right())
8281 popover->move(QPoint(views()[0]->viewport()->rect().right()-popover->size().width()-cutoffMargin, popover->pos().y()));
8283 if (popover->pos().y()+popover->size().height()+cutoffMargin > views()[0]->viewport()->rect().bottom())
8284 popover->move(QPoint(popover->pos().x(), views()[0]->viewport()->rect().bottom()-popover->size().height()-cutoffMargin));