55 #include <ApplicationServices/ApplicationServices.h>
56 #include <objc/objc-runtime.h>
59 const qreal VuoEditorComposition::nodeMoveRate = 15;
60 const qreal VuoEditorComposition::nodeMoveRateMultiplier = 4;
62 const qreal VuoEditorComposition::showEventsModeUpdateInterval = 1000/20.;
63 const int VuoEditorComposition::initialChangeNodeSuggestionCount = 10;
74 VuoEditorComposition_Pro();
77 this->window = window;
79 inputEditorManager = NULL;
80 activeProtocol = NULL;
82 runningComposition = NULL;
83 runningCompositionActiveDriver = NULL;
84 runningCompositionLibraries = NULL;
85 stopRequested =
false;
86 duplicateOnNextMouseMove =
false;
87 duplicationDragInProgress =
false;
88 duplicationCancelled =
false;
89 cursorPosBeforeDuplicationDragMove = QPointF(0,0);
90 cableInProgress = NULL;
91 cableInProgressWasNew =
false;
92 cableInProgressShouldBeWireless =
false;
93 portWithDragInitiated = NULL;
94 cableWithYankInitiated = NULL;
95 menuSelectionInProgress =
false;
96 previousNearbyItem = NULL;
97 dragStickinessDisabled =
false;
98 ignoreApplicationStateChangeEvents =
false;
99 popoverEventsEnabled =
true;
100 runCompositionQueue = dispatch_queue_create(
"org.vuo.editor.run", NULL);
101 activePortPopoversQueue = dispatch_queue_create(
"org.vuo.editor.popovers", NULL);
103 errorMarkingUpdatesEnabled =
true;
104 triggerPortToRefire =
"";
106 contextMenuDeleteSelected =
new QAction(NULL);
107 contextMenuHideSelectedCables =
new QAction(NULL);
108 contextMenuRenameSelected =
new QAction(NULL);
109 contextMenuRefactorSelected =
new QAction(NULL);
110 contextMenuPublishPort =
new QAction(NULL);
111 contextMenuDeleteCables =
new QAction(NULL);
112 contextMenuHideCables =
new QAction(NULL);
113 contextMenuUnhideCables =
new QAction(NULL);
114 contextMenuFireEvent =
new QAction(NULL);
115 contextMenuAddInputPort =
new QAction(NULL);
116 contextMenuRemoveInputPort =
new QAction(NULL);
117 contextMenuSetPortConstant =
new QAction(NULL);
118 contextMenuEditSelectedComments =
new QAction(NULL);
120 contextMenuChangeNode = NULL;
122 contextMenuFireEvent->setText(tr(
"Fire Event"));
123 contextMenuHideSelectedCables->setText(tr(
"Hide"));
124 contextMenuRenameSelected->setText(tr(
"Rename…"));
125 contextMenuRefactorSelected->setText(tr(
"Package as Subcomposition"));
126 contextMenuAddInputPort->setText(tr(
"Add Input Port"));
127 contextMenuRemoveInputPort->setText(tr(
"Remove Input Port"));
128 contextMenuSetPortConstant->setText(tr(
"Edit Value…"));
129 contextMenuEditSelectedComments->setText(tr(
"Edit…"));
136 connect(contextMenuDeleteCables, &QAction::triggered,
this, &VuoEditorComposition::deleteConnectedCables);
137 connect(contextMenuHideCables, &QAction::triggered,
this, &VuoEditorComposition::hideConnectedCables);
138 connect(contextMenuUnhideCables, &QAction::triggered,
this, &VuoEditorComposition::unhideConnectedCables);
139 connect(contextMenuFireEvent, &QAction::triggered,
this,
static_cast<void (
VuoEditorComposition::*)()
>(&VuoEditorComposition::fireTriggerPortEvent));
140 connect(contextMenuAddInputPort, &QAction::triggered,
this, &VuoEditorComposition::addInputPort);
141 connect(contextMenuRemoveInputPort, &QAction::triggered,
this, &VuoEditorComposition::removeInputPort);
142 connect(contextMenuEditSelectedComments, &QAction::triggered,
this, &VuoEditorComposition::editSelectedComments);
147 connect(contextMenuSetPortConstant, &QAction::triggered,
this, &VuoEditorComposition::setPortConstant, Qt::QueuedConnection);
152 QSignalMapper *contextMenuThrottlingMapper =
new QSignalMapper(
this);
153 connect(contextMenuThrottlingMapper,
static_cast<void (QSignalMapper::*)(
int)
>(&QSignalMapper::mapped),
this, &VuoEditorComposition::setTriggerThrottling);
155 QList<QPair<QString, enum VuoPortClass::EventThrottling> > throttlingNamesAndIndices;
159 for (QList<QPair<QString, enum VuoPortClass::EventThrottling> >::iterator i = throttlingNamesAndIndices.begin(); i != throttlingNamesAndIndices.end(); ++i)
161 QString name = i->first;
163 QAction *action =
new QAction(name,
this);
165 contextMenuThrottlingActions.append(action);
167 contextMenuThrottlingMapper->setMapping(action, index);
168 connect(action, &QAction::triggered, contextMenuThrottlingMapper,
static_cast<void (QSignalMapper::*)()
>(&QSignalMapper::map));
185 QSignalMapper *contextMenuTintsMapper =
new QSignalMapper(
this);
188 QList<QPair<QString, enum VuoNode::TintColor> > tintNamesAndIndices;
189 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Yellow"), VuoNode::TintYellow));
190 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Tangerine"), VuoNode::TintTangerine));
191 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Orange"), VuoNode::TintOrange));
192 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Magenta"), VuoNode::TintMagenta));
193 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Violet"), VuoNode::TintViolet));
194 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Blue"), VuoNode::TintBlue));
195 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Cyan"), VuoNode::TintCyan));
196 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Green"), VuoNode::TintGreen));
197 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Lime"), VuoNode::TintLime));
198 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"None"), VuoNode::TintNone));
200 for (QList<QPair<QString, enum VuoNode::TintColor> >::iterator i = tintNamesAndIndices.begin(); i != tintNamesAndIndices.end(); ++i)
202 QString name = i->first;
204 QAction *action =
new QAction(name,
this);
208 QColor fill(0,0,0,0);
210 if (index != VuoNode::TintNone)
220 p.drawEllipse(3, 3, 10, 10);
222 action->setIcon(*icon);
226 contextMenuTintActions.append(action);
228 contextMenuTintsMapper->setMapping(action, index);
229 connect(action, &QAction::triggered, contextMenuTintsMapper,
static_cast<void (QSignalMapper::*)()
>(&QSignalMapper::map));
234 this->refreshComponentAlphaLevelTimer =
new QTimer(
this);
235 this->refreshComponentAlphaLevelTimer->setObjectName(
"VuoEditorComposition::refreshComponentAlphaLevelTimer");
236 refreshComponentAlphaLevelTimer->setInterval(showEventsModeUpdateInterval);
238 setShowEventsMode(
false);
254 connect(
static_cast<VuoEditor *
>(qApp), &VuoEditor::focusChanged,
this, &VuoEditorComposition::updatePopoversForActiveWindowChange, Qt::QueuedConnection);
256 setPopoversHideOnDeactivate(
true);
259 setPopoversHideOnDeactivate(
false);
271 this->compiler = compiler;
287 this->moduleManager = moduleManager;
296 return moduleManager;
306 this->inputEditorManager = inputEditorManager;
316 return this->inputEditorManager;
333 setCustomConstantsForNewNode(rn);
350 __block
bool isAllowed =
true;
353 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
355 bool nodeIsThisComposition = (compositionModuleKey == nodeClass->
getBase()->
getClassName());
358 auto iter = std::find_if(dependencies.begin(), dependencies.end(), [=](
const string &d){ return d == compositionModuleKey; });
359 bool nodeContainsThisComposition = (iter != dependencies.end());
361 isAllowed = ! (nodeIsThisComposition || nodeContainsThisComposition);
370 compiler->
createNode(nodeClass, title, x, y));
388 vector<string> inputPortClassNames;
389 vector<string> outputPortClassNames;
394 inputPortClassNames.push_back(portClass->
getName());
397 outputPortClassNames.push_back(portClass->
getName());
401 dummyNodeClass->
newNode(modelNode) :
410 void VuoEditorComposition::setCustomConstantsForNewNode(
VuoRendererNode *newNode)
418 QString currentYear = QString::number(QDateTime::currentDateTime().date().year());
442 disablePortPopovers(rn);
467 map<VuoCable *, VuoPort *> cablesToTransferFromPort;
468 map<VuoCable *, VuoPort *> cablesToTransferToPort;
469 set<VuoCable *> cablesToRemove;
473 vector<VuoRendererInputDrawer *> attachedDrawers;
474 vector<VuoRendererNode *> collapsedTypecasts;
476 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
482 collapsedTypecasts.push_back(typecastNode);
490 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
495 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
500 attachedDrawers.push_back(attachedDrawer);
507 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferFromPort.begin(); i != cablesToTransferFromPort.end(); ++i)
508 i->first->getRenderer()->setFrom(newNode, i->second);
509 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferToPort.begin(); i != cablesToTransferToPort.end(); ++i)
510 i->first->getRenderer()->setTo(newNode, i->second);
511 foreach (
VuoCable *cable, cablesToRemove)
528 if (! (oldDataType == newDataType && oldDataType && !
dynamic_cast<VuoGenericType *
>(oldDataType)) )
532 string oldConstantValue;
534 oldConstantValue = oldInputPort->getRenderer()->getConstantAsString();
536 oldConstantValue = oldInputPort->getRawInitialValue();
568 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
581 disablePortPopovers(oldNode);
609 if (cableHidden && emitHiddenCableNotification)
621 if (cableHidden && emitHiddenCableNotification)
647 set<VuoRendererNode *> &createdNodes,
648 set<VuoRendererCable *> &createdCables)
662 foreach (QGraphicsItem *component, addedComponents)
665 if (rn && !createButDoNotAdd)
669 return addedComponents;
682 set<string> selectedNodeIDs;
689 set<string> selectedCommentIDs;
696 set<string> selectedCableIDs;
706 foreach (QGraphicsItem *item, items())
713 if (!currentNodeID.empty() && (selectedNodeIDs.find(currentNodeID) != selectedNodeIDs.end()))
714 item->setSelected(
true);
722 if (!currentCommentID.empty() && (selectedCommentIDs.find(currentCommentID) != selectedCommentIDs.end()))
723 item->setSelected(
true);
731 if (!currentCableID.empty() && (selectedCableIDs.find(currentCableID) != selectedCableIDs.end()))
732 item->setSelected(
true);
755 if ((portName ==
"expression") &&
775 if (commandDescription.empty())
778 commandDescription =
"Reset";
780 commandDescription =
"Delete";
783 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
792 if (commandDescription.empty())
793 commandDescription =
"Delete";
795 QList<QGraphicsItem *> selectedNodes;
797 selectedNodes.append(node);
837 void VuoEditorComposition::insertNode()
839 QAction *sender = (QAction *)QObject::sender();
840 QPair<QPointF, QString> pair = sender->data().value<QPair<QPointF, QString> >();
842 QList<QGraphicsItem *> newNodes;
849 newNodes.append(newNode);
857 void VuoEditorComposition::insertComment()
859 QAction *sender = (QAction *)QObject::sender();
860 QPointF scenePos = sender->data().value<QPointF>();
868 void VuoEditorComposition::insertSubcomposition()
870 QAction *sender = (QAction *)QObject::sender();
871 QPointF scenePos = sender->data().value<QPointF>();
882 QAction *sender = (QAction *)QObject::sender();
897 void VuoEditorComposition::deleteConnectedCables()
899 QAction *sender = (QAction *)QObject::sender();
902 QList<QGraphicsItem *> cablesToRemove;
933 void VuoEditorComposition::hideConnectedCables()
935 QAction *sender = (QAction *)QObject::sender();
937 set<VuoRendererCable *> cablesToHide;
968 void VuoEditorComposition::unhideConnectedCables()
970 QAction *sender = (QAction *)QObject::sender();
972 set<VuoRendererCable *> cablesToUnhide;
1001 void VuoEditorComposition::fireTriggerPortEvent()
1003 QAction *sender = (QAction *)QObject::sender();
1005 fireTriggerPortEvent(port->
getBase());
1021 if (triggerPortToRefire.empty())
1024 VuoPort *triggerPort =
nullptr;
1037 if (portID != this->triggerPortToRefire)
1039 this->triggerPortToRefire = portID;
1048 void VuoEditorComposition::setPortConstant()
1050 QAction *sender = (QAction *)QObject::sender();
1061 void VuoEditorComposition::setPortConstantToValue(
VuoRendererPort *port,
string value)
1071 void VuoEditorComposition::specializeGenericPortType()
1073 QAction *sender = (QAction *)QObject::sender();
1074 QList<QVariant> portAndSpecializedType= sender->data().toList();
1076 QString specializedTypeName = portAndSpecializedType[1].toString();
1088 emit
specializePort(port, specializedTypeName.toUtf8().constData());
1095 void VuoEditorComposition::unspecializePortType()
1097 QAction *sender = (QAction *)QObject::sender();
1121 portToUnspecialize,
true);
1122 map<VuoNode *, set<VuoPort *> > portsToUnspecializeForNode;
1123 for (
VuoPort *connectedPort : connectedPotentiallyGenericPorts)
1129 if (isPortCurrentlyRevertible(connectedPort->getRenderer()))
1130 portsToUnspecializeForNode[node].insert(connectedPort);
1133 for (map<
VuoNode *, set<VuoPort *> >::iterator i = portsToUnspecializeForNode.begin(); i != portsToUnspecializeForNode.end(); ++i)
1136 set<VuoPort *> ports = i->second;
1138 if (shouldOutputNodesToReplace)
1141 set<VuoPortClass *> portClasses;
1142 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1143 portClasses.insert((*j)->getClass());
1146 nodesToReplace[node] = unspecializedNodeClassName;
1151 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1156 bool areEndsCompatible =
false;
1159 areEndsCompatible =
true;
1164 if (portOnOtherEnd && isPortCurrentlyRevertible(portOnOtherEnd->
getRenderer()))
1169 if (specializedNodeClassOnOtherEnd)
1172 if (! typeOnOtherEnd ||
dynamic_cast<VuoGenericType *
>(typeOnOtherEnd))
1173 areEndsCompatible =
true;
1178 if (! areEndsCompatible && (cable != cableInProgress))
1179 cablesToDelete.insert(cable);
1188 void VuoEditorComposition::fireTriggerPortEvent(
VuoPort *port)
1196 string runningTriggerPortIdentifier =
"";
1197 bool isTriggerPort =
false;
1205 isTriggerPort =
true;
1223 runningTriggerPortIdentifier.c_str());
1226 bool manuallyFirableInputPortChanged = (oldManuallyFirableInputPort != newManuallyFirableInputPort);
1229 auto fireIfRunning = ^void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
1231 dispatch_async(topLevelComposition->runCompositionQueue, ^{
1232 if (topLevelComposition->isRunningThreadUnsafe())
1234 topLevelComposition->runner->fireTriggerPortEvent(thisCompositionIdentifier, runningTriggerPortIdentifier);
1239 if (! (this->showEventsMode && isTriggerPort) )
1240 this->animatePort(port->getRenderer());
1245 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1246 if (
this == topLevelComposition || ! manuallyFirableInputPortChanged)
1250 if (! newSnapshot.empty() && manuallyFirableInputPortChanged)
1251 updateRunningComposition(oldSnapshot, newSnapshot);
1253 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1259 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
1261 moduleManager->doNextTimeNodeClassIsLoaded(nodeClassName, ^{
1262 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1263 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1267 if (! newSnapshot.empty())
1268 updateRunningComposition(oldSnapshot, newSnapshot);
1273 setTriggerPortToRefire(port);
1279 void VuoEditorComposition::setTriggerThrottling(
int eventThrottling)
1281 QSignalMapper *signalMapper = (QSignalMapper *)QObject::sender();
1282 QAction *sender = (QAction *)signalMapper->mapping(eventThrottling);
1291 void VuoEditorComposition::addInputPort()
1293 QAction *sender = (QAction *)QObject::sender();
1302 void VuoEditorComposition::removeInputPort()
1304 QAction *sender = (QAction *)QObject::sender();
1313 void VuoEditorComposition::swapNode()
1315 QAction *sender = (QAction *)QObject::sender();
1316 QList<QVariant> nodeAndReplacementType= sender->data().toList();
1318 QString newNodeClassName = nodeAndReplacementType[1].toString();
1338 for (set<VuoRendererNode *>::iterator i = selectedNodes.begin(); i != selectedNodes.end(); ++i)
1348 void VuoEditorComposition::editSelectedComments()
1352 for (set<VuoRendererComment *>::iterator i = selectedComments.begin(); i != selectedComments.end(); ++i)
1361 set<VuoRendererCable *> internalCables;
1363 for (QList<QGraphicsItem *>::iterator i = subcompositionComponents.begin(); i != subcompositionComponents.end(); ++i)
1365 QGraphicsItem *compositionComponent = *i;
1370 for (set<VuoCable *>::iterator cable = connectedCables.begin(); cable != connectedCables.end(); ++cable)
1372 VuoNode *fromNode = (*cable)->getFromNode();
1373 VuoNode *toNode = (*cable)->getToNode();
1375 if (fromNode && toNode && subcompositionComponents.contains(fromNode->
getRenderer()) && subcompositionComponents.contains(toNode->
getRenderer()))
1376 internalCables.insert((*cable)->getRenderer());
1381 return internalCables;
1389 return cableInProgress;
1398 return cableInProgressWasNew;
1406 return menuSelectionInProgress;
1414 QList<QGraphicsItem *> compositionComponents = items();
1415 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1417 QGraphicsItem *compositionComponent = *i;
1421 compositionComponent->setSelected(
true);
1430 QList<QGraphicsItem *> compositionComponents = items();
1431 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1433 QGraphicsItem *compositionComponent = *i;
1436 rcomment->setSelected(
true);
1445 QList<QGraphicsItem *> compositionComponents = items();
1446 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1448 QGraphicsItem *compositionComponent = *i;
1449 compositionComponent->setSelected(
false);
1456 void VuoEditorComposition::openSelectedEditableNodes()
1461 QString actionText, sourcePath;
1474 moveItemsBy(selectedNodes, selectedComments, dx, dy,
false);
1480 void VuoEditorComposition::moveNodesBy(set<VuoRendererNode *> nodes, qreal dx, qreal dy,
bool movedByDragging)
1482 set<VuoRendererComment *> comments;
1483 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1489 void VuoEditorComposition::moveCommentsBy(set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1491 set<VuoRendererNode *> nodes;
1492 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1498 void VuoEditorComposition::moveItemsBy(set<VuoRendererNode *> nodes, set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1500 emit
itemsMoved(nodes, comments, dx, dy, movedByDragging);
1502 for (set<VuoRendererNode *>::iterator it = nodes.begin(); it != nodes.end(); ++it)
1503 (*it)->updateConnectedCableGeometry();
1509 void VuoEditorComposition::resizeCommentBy(
VuoRendererComment *comment, qreal dx, qreal dy)
1519 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1520 set<VuoRendererNode *> selectedNodes;
1521 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1523 QGraphicsItem *compositionComponent = *i;
1526 selectedNodes.insert(rn);
1529 return selectedNodes;
1537 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1538 set<VuoRendererComment *> selectedComments;
1539 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1541 QGraphicsItem *compositionComponent = *i;
1544 selectedComments.insert(rc);
1547 return selectedComments;
1555 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1556 set<VuoRendererCable *> selectedCables;
1557 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1559 QGraphicsItem *compositionComponent = *i;
1562 selectedCables.insert(rc);
1565 return selectedCables;
1573 const QMimeData *mimeData =
event->mimeData();
1574 bool disablePortHoverHighlighting =
true;
1577 if (mimeData->hasFormat(
"text/uri-list"))
1579 QList<QUrl> urls = mimeData->urls();
1583 if (portAtDropLocation)
1585 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1587 disablePortHoverHighlighting =
false;
1589 event->setDropAction(Qt::IgnoreAction);
1593 bool dragIncludesDroppableFile =
false;
1594 foreach (QUrl url, urls)
1608 if (isSupportedDragNDropFile)
1610 dragIncludesDroppableFile =
true;
1615 if (!dragIncludesDroppableFile)
1616 event->setDropAction(Qt::IgnoreAction);
1623 else if (mimeData->hasFormat(
"text/plain") || mimeData->hasFormat(
"text/scsv"))
1624 event->acceptProposedAction();
1628 event->setDropAction(Qt::IgnoreAction);
1632 updateHoverHighlighting(event->scenePos(), disablePortHoverHighlighting);
1640 event->acceptProposedAction();
1656 const QMimeData *mimeData =
event->mimeData();
1659 if (mimeData->hasFormat(
"text/uri-list"))
1667 if (topCompositionPath.empty())
1669 QDir compositionDir(QDir(topCompositionPath.c_str()).canonicalPath());
1674 QList<QGraphicsItem *> newNodes;
1675 QList<QUrl> urls = mimeData->urls();
1679 if (portAtDropLocation)
1681 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1684 QString filePath = (useAbsoluteFilePaths? urls[0].path() : compositionDir.relativeFilePath(urls[0].path()));
1685 string constantValue =
"\"" + string(filePath.toUtf8().constData()) +
"\"";
1696 const int ySpacingForNewNodes = 11;
1697 int yOffsetForPreviousNewNode = -1 * ySpacingForNewNodes;
1699 foreach (QUrl url, urls)
1701 QStringList targetNodeClassNames;
1704 targetNodeClassNames +=
"vuo.image.fetch";
1708 targetNodeClassNames +=
"vuo.video.play";
1710 targetNodeClassNames +=
"vuo.video.decodeImage";
1713 targetNodeClassNames +=
"vuo.scene.fetch";
1715 targetNodeClassNames +=
"vuo.audio.file.play";
1717 targetNodeClassNames +=
"vuo.image.project.dome";
1719 targetNodeClassNames +=
"vuo.rss.fetch";
1721 targetNodeClassNames +=
"vuo.tree.fetch.json";
1723 targetNodeClassNames +=
"vuo.tree.fetch.xml";
1725 targetNodeClassNames +=
"vuo.table.fetch";
1727 targetNodeClassNames +=
"vuo.data.fetch";
1729 targetNodeClassNames +=
"vuo.app.launch";
1731 targetNodeClassNames +=
"vuo.file.list";
1733 QString selectedNodeClassName =
"";
1734 if (targetNodeClassNames.size() == 1)
1735 selectedNodeClassName = targetNodeClassNames[0];
1736 else if (targetNodeClassNames.size() > 1)
1738 QMenu nodeMenu(views()[0]->viewport());
1739 nodeMenu.setSeparatorsCollapsible(
false);
1741 foreach (QString nodeClassName, targetNodeClassNames)
1744 string nodeTitle = (nodeClass? nodeClass->
getBase()->
getDefaultTitle() : nodeClassName.toUtf8().constData());
1746 QAction *nodeAction = nodeMenu.addAction(tr(
"Insert \"%1\" Node").arg(nodeTitle.c_str()));
1747 nodeAction->setData(nodeClassName);
1750 menuSelectionInProgress =
true;
1751 QAction *selectedNode = nodeMenu.exec(QCursor::pos());
1752 menuSelectionInProgress =
false;
1754 selectedNodeClassName = (selectedNode? selectedNode->data().toString().toUtf8().constData() :
"");
1757 if (!selectedNodeClassName.isEmpty())
1760 event->scenePos().x(),
1761 event->scenePos().y() + yOffsetForPreviousNewNode + ySpacingForNewNodes);
1770 QString filePath = (useAbsoluteFilePaths? url.path() : compositionDir.relativeFilePath(url.path()));
1773 newNodes.append(newNode);
1775 yOffsetForPreviousNewNode += newNode->
boundingRect().height();
1776 yOffsetForPreviousNewNode += ySpacingForNewNodes;
1782 if (newNodes.size() > 0)
1794 else if (mimeData->hasFormat(
"text/scsv"))
1796 event->setDropAction(Qt::CopyAction);
1799 QByteArray scsvData =
event->mimeData()->data(
"text/scsv");
1800 QString scsvText = QString::fromUtf8(scsvData);
1801 QStringList nodeClassNames = scsvText.split(
';');
1806 int snapDelta = nextYPos - startPos.y();
1808 QList<QGraphicsItem *> newNodes;
1809 for (QStringList::iterator i = nodeClassNames.begin(); i != nodeClassNames.end(); ++i)
1817 int prevYPos = nextYPos;
1820 if (nextYPos <= prevYPos+newNode->boundingRect().height())
1823 newNodes.append((QGraphicsItem *)newNode);
1831 else if (mimeData->hasFormat(
"text/plain"))
1833 event->setDropAction(Qt::CopyAction);
1836 QList<QGraphicsItem *> newNodes;
1837 QStringList dropItems =
event->mimeData()->text().split(
'\n');
1838 QString nodeClassName = dropItems[0];
1842 QPoint hotSpot = (dropItems.size() >= 3? QPoint(dropItems[1].toInt(), dropItems[2].toInt()) : QPoint(0,0));
1844 event->scenePos().x()-hotSpot.x()+1,
1847 newNodes.append(newNode);
1864 QGraphicsScene::sendEvent(nearbyItem, event);
1869 QGraphicsScene::mouseDoubleClickEvent(event);
1877 QPointF scenePos =
event->scenePos();
1880 if (event->button() == Qt::LeftButton)
1885 if (duplicationCancelled)
1887 QGraphicsItem *itemClickedDirectly = itemAt(scenePos, views()[0]->transform());
1893 duplicationCancelled =
false;
1894 correctForCancelledDuplication(event);
1905 mousePressEventNonLeftButton(event);
1914 bool eventHandled =
false;
1928 cableYankedDirectly = currentCable;
1936 if ((event->modifiers() & Qt::ControlModifier) && (currentPort->
getInput() || isTriggerPort))
1937 fireTriggerPortEvent(currentPort->
getBase());
1941 portWithDragInitiated = currentPort;
1942 cableWithYankInitiated = cableYankedDirectly;
1946 eventHandled =
true;
1953 duplicateOnNextMouseMove =
true;
1954 duplicationDragInProgress =
true;
1960 QGraphicsScene::mousePressEvent(event);
1961 eventHandled =
true;
1967 if (event->modifiers() & Qt::ControlModifier)
1968 nearbyItem->setSelected(! nearbyItem->isSelected());
1973 nearbyItem->setSelected(
true);
1977 eventHandled =
true;
1982 QGraphicsScene::mousePressEvent(event);
1989 void VuoEditorComposition::mousePressEventNonLeftButton(QGraphicsSceneMouseEvent *event)
1998 if (event->button() == Qt::RightButton)
2000 QGraphicsSceneContextMenuEvent
contextMenuEvent(QEvent::GraphicsSceneContextMenu);
2009 QGraphicsScene::mousePressEvent(event);
2020 void VuoEditorComposition::correctForCancelledDuplication(QGraphicsSceneMouseEvent *event)
2024 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
2025 pressEvent.setScenePos(event->scenePos());
2026 pressEvent.setButton(Qt::LeftButton);
2027 QApplication::sendEvent(
this, &pressEvent);
2029 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
2030 releaseEvent.setScenePos(event->scenePos());
2031 releaseEvent.setButton(Qt::LeftButton);
2032 QApplication::sendEvent(
this, &releaseEvent);
2044 Qt::KeyboardModifiers modifiers =
event->modifiers();
2045 bool optionKeyPressed = (modifiers & Qt::AltModifier);
2046 bool shiftKeyPressed = (modifiers & Qt::ShiftModifier);
2057 bool creatingNewCable =
false;
2058 bool disconnectingExistingCable =
false;
2059 bool duplicatingExistingCable =
false;
2073 fromPort = fixedPort;
2074 fromNode = fixedNode;
2075 creatingNewCable =
true;
2087 creatingNewCable =
true;
2094 if (optionKeyPressed)
2096 duplicatingExistingCable =
true;
2100 disconnectingExistingCable =
true;
2108 if (creatingNewCable)
2121 cableInProgressWasNew =
true;
2122 cableInProgressShouldBeWireless =
false;
2129 highlightEligibleEndpointsForCable(cableInProgress);
2134 else if (disconnectingExistingCable)
2137 if (cableYankedDirectly)
2138 cableInProgress = cableYankedDirectly->
getBase();
2144 cableInProgressWasNew =
false;
2155 highlightEligibleEndpointsForCable(cableInProgress);
2157 cableInProgress->
getRenderer()->setSelected(
true);
2160 else if (duplicatingExistingCable)
2164 if (cableYankedDirectly)
2165 cableToDuplicate = cableYankedDirectly->
getBase();
2182 cableInProgressWasNew =
true;
2183 cableInProgressShouldBeWireless = cableToDuplicate->
hasCompiler() &&
2191 highlightEligibleEndpointsForCable(cableInProgress);
2193 cableInProgress->
getRenderer()->setSelected(
true);
2198 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2211 if (event->button() == Qt::LeftButton)
2213 portWithDragInitiated = NULL;
2214 cableWithYankInitiated = NULL;
2215 duplicateOnNextMouseMove =
false;
2216 duplicationDragInProgress =
false;
2217 dragStickinessDisabled =
false;
2224 bool cableDragEnding = cableInProgress;
2225 if (cableDragEnding)
2226 concludeCableDrag(event);
2238 if ((event->modifiers() == Qt::NoModifier) &&
2239 (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2246 enablePopoverForNode(typecastNode);
2251 enableInactivePopoverForPort(port);
2257 if (!cableDragEnding && !node)
2261 if (!cableDragEnding)
2262 QGraphicsScene::mouseReleaseEvent(event);
2268 QGraphicsScene::mouseReleaseEvent(event);
2278 void VuoEditorComposition::concludeCableDrag(QGraphicsSceneMouseEvent *event)
2286 concludePublishedCableDrag(event);
2290 if (hasFeedbackErrors())
2308 bool completedCableConnection =
false;
2311 VuoCable *dataCableToDisplace = NULL;
2315 string typecastToInsert =
"";
2319 string specializedTypeName =
"";
2336 bool draggingPreviouslyPublishedCable = (!cableInProgressWasNew &&
2338 ((cableInProgress->
getToPort() == NULL) &&
2341 if (fixedPort && targetPort)
2349 if (recreatingSameConnection)
2359 bool preexistingCableWithMatchingDataCarryingStatus = (preexistingCable?
2361 cableInProgressExpectedToCarryData) :
2367 if (preexistingCable && !preexistingCableWithMatchingDataCarryingStatus &&
2378 if (!preexistingCableWithMatchingDataCarryingStatus &&
2380 selectBridgingSolution(fixedPort, targetPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert)))
2391 targetPort = adjustedTargetPort;
2395 if (cableInProgressExpectedToCarryData &&
2397 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2398 typecastNodeToDelete = uncollapsedTypecast;
2403 if (preexistingCable)
2404 cableToReplace = preexistingCable;
2408 if (cableInProgressExpectedToCarryData)
2412 for (vector<VuoCable *>::iterator cable = previousConnectedCables.begin(); (! dataCableToDisplace) && (cable != previousConnectedCables.end()); ++cable)
2413 if ((((*cable)->getRenderer()->effectivelyCarriesData()) && (*cable) != cableToReplace))
2414 dataCableToDisplace = *cable;
2421 if (publishedDataConnections.size() > 0)
2422 portToUnpublish = targetPort;
2426 completedCableConnection =
true;
2430 else if (!preexistingCableWithMatchingDataCarryingStatus &&
2432 selectBridgingSolution(targetPort, fixedPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert)))
2435 completedCableConnection =
true;
2440 if (completedCableConnection)
2444 if (draggingPreviouslyPublishedCable)
2463 cableInProgressCopy->
setFrom(cableInProgressFromNode, cableInProgressFromPort);
2464 cableInProgressCopy->
setTo(cableInProgressToNode, cableInProgressToPort);
2471 cableInProgress = cableInProgressCopy;
2472 cableInProgressWasNew =
true;
2476 pair<VuoRendererCable *, VuoRendererCable *> cableArgs = std::make_pair((dataCableToDisplace? dataCableToDisplace->
getRenderer() : NULL),
2477 (cableToReplace? cableToReplace->
getRenderer() : NULL));
2478 pair<string, string> typeArgs = std::make_pair(typecastToInsert, specializedTypeName);
2479 pair<VuoRendererPort *, VuoRendererPort *> portArgs = std::make_pair(portToUnpublish, portToSpecialize);
2484 typecastNodeToDelete,
2491 if (cableInProgress)
2495 cableInProgress = NULL;
2509 void VuoEditorComposition::concludePublishedCableDrag(QGraphicsSceneMouseEvent *event)
2519 string typecastToInsert =
"";
2523 string specializedTypeName =
"";
2540 if (internalInputPort)
2543 forceEventOnlyPublication =
true;
2549 if (internalInputPort &&
2555 internalInputPort->
getBase()) &&
2558 if (recreatingSameConnection)
2566 ||
selectBridgingSolution(publishedInputPort, internalInputPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert))
2571 bool cableToReplaceHasMatchingDataCarryingStatus = (cableToReplace?
2573 cableInProgressExpectedToCarryData) :
2578 if (cableToReplace && cableToReplaceHasMatchingDataCarryingStatus)
2584 else if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus &&
2600 if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus)
2602 QList<QGraphicsItem *> removedComponents;
2603 removedComponents.append(cableToReplace->
getRenderer());
2612 if (typecastPort && typecastPort->scene())
2620 if (cableInProgressExpectedToCarryData &&
2622 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2624 QList<QGraphicsItem *> removedComponents;
2625 removedComponents.append(uncollapsedTypecast);
2632 forceEventOnlyPublication,
2633 (portToSpecialize? portToSpecialize->
getBase() : NULL),
2634 specializedTypeName,
2660 if (internalOutputPort)
2663 forceEventOnlyPublication =
true;
2669 if (internalOutputPort &&
2670 publishedOutputPort)
2676 ||
selectBridgingSolution(internalOutputPort, publishedOutputPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert))
2680 forceEventOnlyPublication,
2681 portToSpecialize? portToSpecialize->
getBase() : NULL,
2682 specializedTypeName,
2701 if (! cableInProgress)
2704 if (cableInProgressWasNew)
2708 QList<QGraphicsItem *> removedComponents;
2709 removedComponents.append(cableInProgress->
getRenderer());
2713 cableInProgress = NULL;
2726 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2731 cableInProgress = NULL;
2733 else if (cableInProgress)
2748 bool leftMouseButtonPressed = (
event->buttons() & Qt::LeftButton);
2764 if (cableInProgress || (! leftMouseButtonPressed))
2765 updateHoverHighlighting(event->scenePos());
2769 if (leftMouseButtonPressed)
2772 if ((! dragStickinessDisabled) && (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2776 dragStickinessDisabled =
true;
2780 if (portWithDragInitiated || cableWithYankInitiated)
2782 initiateCableDrag(portWithDragInitiated, cableWithYankInitiated, event);
2783 portWithDragInitiated = NULL;
2784 cableWithYankInitiated = NULL;
2789 if (cableInProgress)
2799 else if (duplicationDragInProgress)
2801 if (duplicateOnNextMouseMove)
2804 duplicateOnNextMouseMove =
false;
2811 QPointF delta = newPos - cursorPosBeforeDuplicationDragMove;
2813 cursorPosBeforeDuplicationDragMove = newPos;
2817 QGraphicsScene::mouseMoveEvent(event);
2822 QGraphicsScene::mouseMoveEvent(event);
2830 void VuoEditorComposition::updateHoverHighlighting(QPointF scenePos,
bool disablePortHoverHighlighting)
2844 bool hoveringOverNodeHeader =
false;
2845 if (cableInProgress)
2854 hoveringOverNodeHeader =
true;
2860 if (! hoveringOverNodeHeader)
2871 if (item != previousNearbyItem)
2879 if (previousNearbyItem)
2887 if (! cableInProgress)
2893 else if (typecastPort && !disablePortHoverHighlighting)
2895 else if (port && !disablePortHoverHighlighting)
2898 if (!cableInProgress)
2909 if (cableInProgress)
2912 cableInProgress->getFromPort()->getRenderer() :
2913 cableInProgress->getToPort()->getRenderer());
2915 QList< QPair<VuoRendererPort *, bool> > updatedPorts;
2916 if (hoveringOverNodeHeader)
2917 updatedPorts.append( QPair<VuoRendererPort *, bool>(port,
true) );
2918 if (previousNode && previousPort)
2919 updatedPorts.append( QPair<VuoRendererPort *, bool>(previousPort, !cableInProgress->getRenderer()->effectivelyCarriesData()) );
2921 QPair<VuoRendererPort *, bool> p;
2922 foreach (p, updatedPorts)
2927 if (typecastParentPort)
2928 updatedPort = typecastParentPort;
2930 updateEligibilityHighlightingForPort(updatedPort, fixedPort, p.second, types);
2933 updateEligibilityHighlightingForNode(potentialDrawer);
2940 if (targetPort || previousTargetPort)
2942 if (cableInProgress)
2943 cableInProgress->getRenderer()->setFloatingEndpointAboveEventPort(targetPort && (!targetPort->
getDataType() || hoveringOverNodeHeader));
2948 previousNearbyItem = item;
2956 else if (port && !disablePortHoverHighlighting)
2959 if (!cableInProgress)
2965 else if (makeListDrawer)
2975 if (previousNearbyItem)
2993 else if (typecastPort)
3002 previousNearbyItem = NULL;
3011 if ((event->key() != Qt::Key_Alt) && (event->key() != Qt::Key_Shift) && (event->key() != Qt::Key_Escape))
3014 Qt::KeyboardModifiers modifiers =
event->modifiers();
3015 qreal adjustedNodeMoveRate = nodeMoveRate;
3016 if (modifiers & Qt::ShiftModifier)
3018 adjustedNodeMoveRate *= nodeMoveRateMultiplier;
3021 switch (event->key()) {
3022 case Qt::Key_Backspace:
3027 case Qt::Key_Delete:
3039 if (modifiers & Qt::ControlModifier)
3040 openSelectedEditableNodes();
3057 if (cableInProgress)
3059 cableInProgress->getRenderer()->updateGeometry();
3060 cableInProgress->getCompiler()->setAlwaysEventOnly(
true);
3061 highlightEligibleEndpointsForCable(cableInProgress);
3071 case Qt::Key_Return:
3074 QGraphicsScene::keyPressEvent(event);
3076 if (!event->isAccepted())
3083 if (selectedComments.empty() && !selectedNodes.empty())
3090 case Qt::Key_Escape:
3092 if (duplicateOnNextMouseMove || duplicationDragInProgress)
3096 duplicateOnNextMouseMove =
false;
3097 duplicationDragInProgress =
false;
3098 duplicationCancelled =
true;
3100 else if (cableInProgress)
3109 QGraphicsScene::keyPressEvent(event);
3120 switch (event->key()) {
3123 if (cableInProgress)
3125 cableInProgress->getRenderer()->updateGeometry();
3126 cableInProgress->getCompiler()->setAlwaysEventOnly(
false);
3127 highlightEligibleEndpointsForCable(cableInProgress);
3139 QGraphicsScene::keyReleaseEvent(event);
3148 void VuoEditorComposition::addActionToMenuAndMapper(QMenu *menu, QSignalMapper *mapper, QString name,
int index)
3150 QAction *action =
new QAction(name,
this);
3151 menu->addAction(action);
3152 mapper->setMapping(action, index);
3153 connect(action, &QAction::triggered, mapper,
static_cast<void (QSignalMapper::*)()
>(&QSignalMapper::map));
3165 contextMenu.setSeparatorsCollapsible(
false);
3170 QAction *insertNodeSection =
new QAction(tr(
"Insert Node"), NULL);
3171 insertNodeSection->setEnabled(
false);
3172 contextMenu.addAction(insertNodeSection);
3174 QString spacer(
" ");
3178 QMenu *shareMenu =
new QMenu(&contextMenu);
3179 shareMenu->setSeparatorsCollapsible(
false);
3180 shareMenu->setTitle(spacer + tr(
"Share"));
3181 contextMenu.addMenu(shareMenu);
3184 QAction *action =
new QAction(tr(
"Share Value"), NULL);
3185 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share"))));
3186 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3187 shareMenu->addAction(action);
3191 QAction *action =
new QAction(tr(
"Share List"), NULL);
3192 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share.list"))));
3193 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3194 shareMenu->addAction(action);
3198 QAction *action =
new QAction(tr(
"Share Event"), NULL);
3199 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.share"))));
3200 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3201 shareMenu->addAction(action);
3207 QMenu *holdMenu =
new QMenu(&contextMenu);
3208 holdMenu->setSeparatorsCollapsible(
false);
3209 holdMenu->setTitle(spacer + tr(
"Hold"));
3210 contextMenu.addMenu(holdMenu);
3213 QAction *action =
new QAction(tr(
"Hold Value"), NULL);
3214 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold2"))));
3215 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3216 holdMenu->addAction(action);
3220 QAction *action =
new QAction(tr(
"Hold List"), NULL);
3221 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold.list2"))));
3222 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3223 holdMenu->addAction(action);
3229 QMenu *allowMenu =
new QMenu(&contextMenu);
3230 allowMenu->setSeparatorsCollapsible(
false);
3231 allowMenu->setTitle(spacer + tr(
"Allow"));
3232 contextMenu.addMenu(allowMenu);
3235 QAction *action =
new QAction(tr(
"Allow First Event"), NULL);
3236 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirst"))));
3237 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3238 allowMenu->addAction(action);
3242 QAction *action =
new QAction(tr(
"Allow First Value"), NULL);
3243 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirstValue"))));
3244 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3245 allowMenu->addAction(action);
3249 QAction *action =
new QAction(tr(
"Allow Periodic Events"), NULL);
3250 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.time.allowPeriodic"))));
3251 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3252 allowMenu->addAction(action);
3256 QAction *action =
new QAction(tr(
"Allow Changes"), NULL);
3257 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowChanges2"))));
3258 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3259 allowMenu->addAction(action);
3263 contextMenu.addSeparator();
3265 QAction *contextMenuInsertComment =
new QAction(NULL);
3266 contextMenuInsertComment->setText(tr(
"Insert Comment"));
3267 contextMenuInsertComment->setData(qVariantFromValue(event->scenePos()));
3268 connect(contextMenuInsertComment, &QAction::triggered,
this, &VuoEditorComposition::insertComment);
3269 contextMenu.addAction(contextMenuInsertComment);
3271 QAction *contextMenuInsertSubcomposition =
new QAction(NULL);
3272 contextMenuInsertSubcomposition->setText(tr(
"Insert Subcomposition"));
3273 contextMenuInsertSubcomposition->setData(qVariantFromValue(event->scenePos()));
3274 connect(contextMenuInsertSubcomposition, &QAction::triggered,
this, &VuoEditorComposition::insertSubcomposition);
3275 contextMenu.addAction(contextMenuInsertSubcomposition);
3280 QAction *contextMenuDeleteSelectedSnapshot =
new QAction(NULL);
3287 if (port->
isConstant() && inputEditorManager)
3291 if (inputEditorLoadedForPortDataType)
3293 contextMenuSetPortConstant->setData(qVariantFromValue((
void *)port));
3294 contextMenu.addAction(contextMenuSetPortConstant);
3296 inputEditorLoadedForPortDataType->deleteLater();
3302 contextMenuPublishPort->setText(tr(
"Publish Port"));
3303 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3304 contextMenu.addAction(contextMenuPublishPort);
3309 vector<VuoRendererPublishedPort *> externalPublishedPorts = port->
getPublishedPorts();
3310 bool hasExternalPublishedPortWithMultipleInternalPorts =
false;
3311 bool hasExternalPublishedPortBelongingToActiveProtocol =
false;
3315 hasExternalPublishedPortWithMultipleInternalPorts =
true;
3318 hasExternalPublishedPortBelongingToActiveProtocol =
true;
3325 if (!hasExternalPublishedPortWithMultipleInternalPorts &&!hasExternalPublishedPortBelongingToActiveProtocol)
3327 contextMenuPublishPort->setText(tr(
"Delete Published Port"));
3328 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3329 contextMenu.addAction(contextMenuPublishPort);
3334 if (isTriggerPort || port->
getInput())
3336 __block
bool isTopLevelCompositionRunning =
false;
3337 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
3339 isTopLevelCompositionRunning = topLevelComposition->
isRunning();
3342 if (isTopLevelCompositionRunning)
3344 contextMenuFireEvent->setData(qVariantFromValue((
void *)port));
3345 contextMenu.addAction(contextMenuFireEvent);
3351 QMenu *contextMenuThrottling =
new QMenu(&contextMenu);
3352 contextMenuThrottling->setSeparatorsCollapsible(
false);
3353 contextMenuThrottling->setTitle(tr(
"Set Event Throttling"));
3355 foreach (QAction *action, contextMenuThrottlingActions)
3357 contextMenuThrottling->addAction(action);
3358 action->setData(qVariantFromValue((
void *)port));
3359 action->setCheckable(
true);
3360 action->setChecked( i++ == port->getBase()->getEventThrottling() );
3362 contextMenu.addMenu(contextMenuThrottling);
3367 if (genericDataType || isPortCurrentlyRevertible(port))
3369 QMenu *contextMenuSpecializeGenericType =
new QMenu(&contextMenu);
3370 contextMenuSpecializeGenericType->setSeparatorsCollapsible(
false);
3371 contextMenuSpecializeGenericType->setTitle(tr(
"Set Data Type"));
3372 contextMenuSpecializeGenericType->setToolTipsVisible(
true);
3374 QAction *unspecializeAction = contextMenuSpecializeGenericType->addAction(tr(
"Generic"));
3379 contextMenuSpecializeGenericType->addSeparator();
3381 unspecializeAction->setData(qVariantFromValue((
void *)port));
3382 unspecializeAction->setCheckable(
true);
3383 unspecializeAction->setChecked(genericDataType);
3385 contextMenu.addMenu(contextMenuSpecializeGenericType);
3388 set<string> compatibleTypes;
3389 set<string> compatibleTypesInIsolation;
3392 if (genericDataType)
3400 compatibleTypes = set<string>(compatibleTypesVector.begin(), compatibleTypesVector.end());
3406 compatibleTypes.insert(compatibleTypeNames.begin(), compatibleTypeNames.end());
3411 else if (isPortCurrentlyRevertible(port))
3413 map<VuoNode *, string> nodesToReplace;
3414 set<VuoCable *> cablesToDelete;
3416 if (cablesToDelete.size() >= 1)
3422 port->getUnderlyingParentNode()->getBase()->getNodeClass()->getCompiler());
3424 compatibleTypes = getRespecializationOptionsForPortInNetwork(port);
3428 if (genericTypeFromPortClass)
3440 if (genericHostPortDataType)
3442 else if (isPortCurrentlyRevertible(hostPort->
getRenderer()))
3452 vector<string> compatibleTypesInIsolationVector;
3453 if (genericTypeFromPortClass)
3460 foreach (
string type, compatibleTypesInIsolationVector)
3467 const int maxTypeCountForFlatMenuDisplay = 10;
3468 if (compatibleTypesInIsolation.size() <= maxTypeCountForFlatMenuDisplay)
3470 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
false,
"", contextMenuSpecializeGenericType);
3478 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true,
"", contextMenuSpecializeGenericType);
3483 QList<QAction *> allNodeSetActionsToAdd;
3484 for (map<
string, set<VuoCompilerType *> >::iterator i = loadedTypesForNodeSet.begin(); i != loadedTypesForNodeSet.end(); ++i)
3486 string nodeSetName = i->first;
3487 if (!nodeSetName.empty())
3488 allNodeSetActionsToAdd +=
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true, nodeSetName, contextMenuSpecializeGenericType);
3491 if ((contextMenuSpecializeGenericType->actions().size() > 0) && (allNodeSetActionsToAdd.size() > 0))
3492 contextMenuSpecializeGenericType->addSeparator();
3497 foreach (QAction *action, contextMenuSpecializeGenericType->actions())
3499 QMenu *specializeSubmenu = action->menu();
3500 if (specializeSubmenu)
3502 foreach (QAction *specializeSubaction, specializeSubmenu->actions())
3503 connect(specializeSubaction, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3505 else if (action == unspecializeAction)
3506 connect(action, &QAction::triggered,
this, &VuoEditorComposition::unspecializePortType);
3508 connect(action, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3515 int numVisibleDirectlyConnectedCables = 0;
3516 int numHidableDirectlyConnectedCables = 0;
3517 int numUnhidableDirectlyConnectedCables = 0;
3523 numVisibleDirectlyConnectedCables++;
3525 numHidableDirectlyConnectedCables++;
3529 numUnhidableDirectlyConnectedCables++;
3532 int numVisibleChildPortConnectedCables = 0;
3533 int numHidableChildPortConnectedCables = 0;
3534 int numUnhidableChildPortConnectedCables = 0;
3544 numVisibleChildPortConnectedCables++;
3547 numHidableChildPortConnectedCables++;
3550 numUnhidableChildPortConnectedCables++;
3555 int numVisibleConnectedCables = numVisibleDirectlyConnectedCables + numVisibleChildPortConnectedCables;
3558 int numHidableConnectedCables = numHidableDirectlyConnectedCables + numHidableChildPortConnectedCables;
3561 int numUnhidableConnectedCables = numUnhidableDirectlyConnectedCables + numUnhidableChildPortConnectedCables;
3563 if ((!
renderHiddenCables && ((numHidableConnectedCables >= 1) || (numUnhidableConnectedCables >= 1)))
3564 || (numVisibleConnectedCables >= 1))
3566 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3567 contextMenu.addSeparator();
3572 if (numHidableConnectedCables >= 1)
3577 int numApparentlyHidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3578 contextMenuHideCables->setText(numApparentlyHidableConnectedCables > 1?
"Hide Cables" :
"Hide Cable");
3579 contextMenuHideCables->setData(qVariantFromValue((
void *)port));
3580 contextMenu.addAction(contextMenuHideCables);
3583 if (numUnhidableConnectedCables >= 1)
3585 int numApparentlyUnhidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3586 contextMenuUnhideCables->setText(numApparentlyUnhidableConnectedCables > 1?
"Unhide Cables" :
"Unhide Cable");
3587 contextMenuUnhideCables->setData(qVariantFromValue((
void *)port));
3588 contextMenu.addAction(contextMenuUnhideCables);
3592 if (numVisibleConnectedCables >= 1)
3594 contextMenuDeleteCables->setText(numVisibleConnectedCables > 1?
"Delete Cables" :
"Delete Cable");
3595 contextMenuDeleteCables->setData(qVariantFromValue((
void *)port));
3596 contextMenu.addAction(contextMenuDeleteCables);
3603 if (! item->isSelected())
3606 item->setSelected(
true);
3609 QList<QGraphicsItem *> selectedComponents = selectedItems();
3610 bool onlyCommentsSelected =
true;
3611 bool onlyCablesSelected =
true;
3612 bool selectionContainsMissingNode =
false;
3613 foreach (QGraphicsItem *item, selectedComponents)
3616 onlyCommentsSelected =
false;
3619 onlyCablesSelected =
false;
3622 selectionContainsMissingNode =
true;
3625 contextMenuDeleteSelectedSnapshot->setText(contextMenuDeleteSelected->text());
3633 if (onlyCommentsSelected)
3634 contextMenu.addAction(contextMenuEditSelectedComments);
3639 contextMenu.addSeparator();
3642 contextMenu.addAction(contextMenuRefactorSelected);
3644 contextMenu.addSeparator();
3647 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3657 contextMenu.addAction(contextMenuHideSelectedCables);
3660 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3675 contextMenuAddInputPort->setData(qVariantFromValue((
void *)node));
3676 contextMenuRemoveInputPort->setData(qVariantFromValue((
void *)node));
3679 contextMenuRemoveInputPort->setEnabled(listItemCount >= 1);
3681 contextMenu.addAction(contextMenuAddInputPort);
3682 contextMenu.addAction(contextMenuRemoveInputPort);
3684 contextMenu.addSeparator();
3688 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3699 if (originalGenericNodeClass)
3700 nodeClass = originalGenericNodeClass->
getBase();
3703 QString actionText, sourcePath;
3707 int numSelectedNonAttachmentNodes = 0;
3708 QList<QGraphicsItem *> selectedComponents = selectedItems();
3709 foreach (QGraphicsItem *item, selectedComponents)
3712 numSelectedNonAttachmentNodes++;
3715 if ((numSelectedNonAttachmentNodes == 1) && nodeClassIsEditable)
3718 QAction *editAction =
new QAction(NULL);
3719 editAction->setText(actionText);
3720 editAction->setData(qVariantFromValue(node));
3723 contextMenu.addAction(editAction);
3724 contextMenu.addSeparator();
3729 contextMenu.addAction(contextMenuRenameSelected);
3734 contextMenu.addSeparator();
3737 if (numSelectedNonAttachmentNodes == 1)
3739 if (contextMenuChangeNode)
3740 contextMenuChangeNode->deleteLater();
3743 contextMenuChangeNode->setSeparatorsCollapsible(
false);
3744 contextMenuChangeNode->setTitle(tr(
"Change To"));
3747 if (!contextMenuChangeNode->actions().isEmpty())
3748 contextMenu.addMenu(contextMenuChangeNode);
3752 if (!selectionContainsMissingNode)
3753 contextMenu.addAction(contextMenuRefactorSelected);
3755 if ((numSelectedNonAttachmentNodes == 1) && (nodeClassIsEditable || nodeClassIs3rdParty))
3759 if (!modulePath.isEmpty())
3761 QString enclosingDirUrl =
"file://" + QFileInfo(modulePath).dir().absolutePath();
3762 QAction *openEnclosingFolderAction =
new QAction(NULL);
3763 openEnclosingFolderAction->setText(
"Show in Finder");
3764 openEnclosingFolderAction->setData(qVariantFromValue(enclosingDirUrl));
3766 contextMenu.addAction(openEnclosingFolderAction);
3770 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3771 contextMenu.addSeparator();
3774 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3780 if (!contextMenu.actions().isEmpty())
3785 menuSelectionInProgress =
true;
3786 contextMenu.exec(event->screenPos());
3787 menuSelectionInProgress =
false;
3790 delete contextMenuDeleteSelectedSnapshot;
3798 QAction *sender = (QAction *)QObject::sender();
3810 vector<string> typeOptions;
3812 map<string, VuoCompilerType *> loadedTypes = compiler->
getTypes();
3813 for (map<string, VuoCompilerType *>::iterator i = loadedTypes.begin(); i != loadedTypes.end(); ++i)
3818 (i->first !=
"VuoMathExpressionList"))
3820 typeOptions.push_back(i->first);
3832 set<string> VuoEditorComposition::getRespecializationOptionsForPortInNetwork(
VuoRendererPort *port)
3835 return set<string>();
3842 vector<string> compatibleInnerTypeNames;
3843 vector<string> compatibleTypeNames;
3844 for (
VuoPort *connectedPort : connectedGenericPorts)
3849 if (specializedNodeClass)
3857 vector<string> innermostCompatibleTypeNamesForPort;
3858 for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
3861 if (! innermostCompatibleTypeNamesForPort.empty())
3863 if (compatibleInnerTypeNames.empty())
3864 compatibleInnerTypeNames = innermostCompatibleTypeNamesForPort;
3867 for (
int k = compatibleInnerTypeNames.size() - 1; k >= 0; --k)
3868 if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleInnerTypeNames[k]) ==
3869 innermostCompatibleTypeNamesForPort.end())
3870 compatibleInnerTypeNames.erase(compatibleInnerTypeNames.begin() + k);
3879 string typeNameForPort = genericTypeFromPortClass->
getModuleKey();
3883 for (vector<string>::iterator k = compatibleInnerTypeNames.begin(); k != compatibleInnerTypeNames.end(); ++k)
3884 compatibleTypeNames.push_back(prefix + *k);
3886 if (compatibleTypeNames.empty())
3896 return set<string>(compatibleTypeNames.begin(), compatibleTypeNames.end());
3919 set<string> compatibleTypesInIsolation,
3920 set<string> compatibleTypesInContext,
3921 bool limitToNodeSet,
3925 QList<QAction *> actionsToAddToMenu;
3927 map<string, VuoCompilerType *> allTypes = compiler->
getTypes();
3929 QList<VuoCompilerType *> compatibleTypesForNodeSetDisplay;
3930 foreach (
string typeName, compatibleTypesInIsolation)
3933 if ((!limitToNodeSet || (loadedTypesForNodeSet[nodeSetName].find(type) != loadedTypesForNodeSet[nodeSetName].end())) &&
3936 (typeName !=
"VuoUrl" && typeName !=
"VuoList_VuoUrl"
3938 && typeName !=
"VuoInteraction" && typeName !=
"VuoList_VuoInteraction"
3939 && typeName !=
"VuoInteractionType" && typeName !=
"VuoList_VuoInteractionType"
3940 && typeName !=
"VuoUuid" && typeName !=
"VuoList_VuoUuid"
3942 && typeName !=
"VuoIconPosition" && typeName !=
"VuoList_VuoIconPosition"
3943 && typeName !=
"VuoMesh" && typeName !=
"VuoList_VuoMesh"
3944 && typeName !=
"VuoWindowProperty" && typeName !=
"VuoList_VuoWindowProperty"
3945 && typeName !=
"VuoWindowReference" && typeName !=
"VuoList_VuoWindowReference"))
3946 compatibleTypesForNodeSetDisplay.append(type);
3949 if (!compatibleTypesForNodeSetDisplay.isEmpty())
3951 QMenu *contextMenuNodeSetTypes = NULL;
3952 QList<QAction *> actionsToAddToNodeSetSubmenu;
3953 bool enabledContentAdded =
false;
3956 if (!nodeSetName.empty())
3958 contextMenuNodeSetTypes =
new QMenu(menu);
3959 contextMenuNodeSetTypes->setSeparatorsCollapsible(
false);
3961 contextMenuNodeSetTypes->setToolTipsVisible(
true);
3962 actionsToAddToMenu.append(contextMenuNodeSetTypes->menuAction());
3968 QList<QVariant> portAndSpecializedType;
3969 portAndSpecializedType.append(qVariantFromValue((
void *)genericPort));
3970 portAndSpecializedType.append(typeName.c_str());
3972 QAction *specializeAction;
3976 if (!nodeSetName.empty())
3978 specializeAction =
new QAction(typeTitle, contextMenuNodeSetTypes);
3979 actionsToAddToNodeSetSubmenu.append(specializeAction);
3985 specializeAction =
new QAction(typeTitle, menu);
3986 actionsToAddToMenu.append(specializeAction);
3989 specializeAction->setData(QVariant(portAndSpecializedType));
3990 specializeAction->setCheckable(
true);
3991 specializeAction->setChecked(genericPort && (genericPort->getDataType()->getModuleKey() == type->
getBase()->
getModuleKey()));
3993 if (compatibleTypesInContext.find(typeName) == compatibleTypesInContext.end())
3995 specializeAction->setEnabled(
false);
3997 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."));
4000 enabledContentAdded =
true;
4003 if (contextMenuNodeSetTypes)
4007 if (!enabledContentAdded)
4008 contextMenuNodeSetTypes->setEnabled(
false);
4012 QList<QAction *> actionListWithPromotions = promoteSingletonsFromSubmenus(actionsToAddToMenu);
4013 return actionListWithPromotions;
4020 QList<QAction *> VuoEditorComposition::promoteSingletonsFromSubmenus(QList<QAction *> actionList)
4022 QList<QAction *> modifiedActionList;
4023 foreach (QAction *action, actionList)
4025 if (action->menu() && (action->menu()->actions().size() == 1))
4027 QAction *singleSubaction = action->menu()->actions().first();
4028 action->menu()->removeAction(singleSubaction);
4029 modifiedActionList.append(singleSubaction);
4032 modifiedActionList.append(action);
4035 return modifiedActionList;
4043 std::sort(actionList.begin(), actionList.end(), nodeSetMenuActionLessThan);
4044 foreach (QAction *action, actionList)
4045 menu->addAction(action);
4051 void VuoEditorComposition::updatePopoversForActiveWindowChange(QWidget *old, QWidget *now)
4064 void VuoEditorComposition::updatePopoversForApplicationStateChange(
bool active)
4066 if (ignoreApplicationStateChangeEvents)
4070 if (activeWindow && (activeWindow->
getComposition() ==
this) && (!activeWindow->isMinimized()))
4084 return findNearbyComponent(scenePos, VuoEditorComposition::targetTypePort, limitPortCollisionRange);
4092 QGraphicsItem *item =
findNearbyComponent(scenePos, VuoEditorComposition::targetTypeNodeHeader);
4110 bool limitPortCollisionRange)
4116 bool ignoreComments;
4120 case VuoEditorComposition::targetTypePort:
4122 ignoreCables =
true;
4124 ignorePorts =
false;
4125 ignoreComments =
true;
4128 case VuoEditorComposition::targetTypeNodeHeader:
4130 ignoreCables =
true;
4133 ignoreComments =
true;
4138 ignoreCables =
false;
4139 ignoreNodes =
false;
4140 ignorePorts =
false;
4141 ignoreComments =
false;
4149 QGraphicsItem *topmostItemUnderCursor = itemAt(scenePos, views()[0]->transform());
4150 if (topmostItemUnderCursor && (!topmostItemUnderCursor->isEnabled()))
4151 topmostItemUnderCursor = NULL;
4155 QRectF searchRect(scenePos.x()-0.5*rectLength, scenePos.y()-0.5*rectLength, rectLength, rectLength);
4156 QList<QGraphicsItem *> itemsInRange = items(searchRect);
4157 for (QList<QGraphicsItem *>::iterator i = itemsInRange.begin(); i != itemsInRange.end(); ++i)
4159 if (!(*i)->isEnabled())
4166 bool makeListDragHandle =
4169 if (makeListDragHandle &&
4170 ((! topmostItemUnderCursor) ||
4171 (topmostItemUnderCursor == makeListDrawer) ||
4172 (topmostItemUnderCursor->zValue() < makeListDrawer->zValue())))
4174 return makeListDrawer;
4185 ((! topmostItemUnderCursor) ||
4186 (topmostItemUnderCursor == port) ||
4188 (topmostItemUnderCursor->zValue() < port->zValue()))
4190 ((! limitPortCollisionRange) ||
4204 ((! topmostItemUnderCursor) ||
4205 (topmostItemUnderCursor == cable) ||
4206 (topmostItemUnderCursor->zValue() < cable->zValue())))
4213 if (! ignoreComments)
4220 ((! topmostItemUnderCursor) ||
4221 (topmostItemUnderCursor == (*i)) ||
4222 (topmostItemUnderCursor->zValue() < (*i)->zValue())))
4229 if (targetType == VuoEditorComposition::targetTypeNodeHeader)
4235 headerRect = node->mapToScene(headerRect).
boundingRect();
4236 if (headerRect.intersects(searchRect) && scenePos.y() <= headerRect.bottom())
4248 return topmostItemUnderCursor;
4253 return ((
VuoRendererPort *)(topmostItemUnderCursor))->getRenderedParentNode();
4270 if (! cableInProgress)
4273 VuoPort *fromPort = cableInProgress->getFromPort();
4274 VuoPort *toPort = cableInProgress->getToPort();
4275 VuoPort *fixedPort = (fromPort? fromPort: toPort);
4290 vector<VuoRendererPort *> portList;
4305 if (portList.size() > firstPortIndex)
4307 targetPort = portList[firstPortIndex];
4313 for (
int i = firstPortIndex; i < portList.size(); ++i)
4317 firstPortWithoutWall = portList[i];
4321 if (firstPortWithoutWall)
4322 targetPort = firstPortWithoutWall;
4331 if (portList.size() > firstPortIndex)
4332 targetPort = portList[firstPortIndex];
4353 QRectF boundingRect;
4354 foreach (QGraphicsItem *item, items())
4358 boundingRect |= item->sceneBoundingRect();
4361 return boundingRect;
4370 QRectF boundingRect;
4372 foreach (QGraphicsItem *item, selectedItems())
4376 boundingRect |= item->sceneBoundingRect();
4379 return boundingRect;
4388 QRectF boundingRect;
4390 foreach (QGraphicsItem *item, selectedItems())
4394 boundingRect |= item->mapToScene(item->childrenBoundingRect()).boundingRect();
4404 boundingRect |= drawer->mapToScene(drawer->childrenBoundingRect()).boundingRect();
4409 return boundingRect;
4449 if (updateInRunningComposition)
4458 if (updateInRunningComposition)
4484 if (!errorMarkingUpdatesEnabled)
4487 errorMarkingUpdatesEnabled =
false;
4504 set<VuoCompilerCable *> potentialCables;
4506 if (targetPort && cableInProgress)
4512 if (cableInProgress->getFromNode())
4514 fromNode = cableInProgress->getFromNode();
4515 fromPort = cableInProgress->getFromPort();
4517 toPort = targetPort->
getBase();
4522 fromPort = targetPort->
getBase();
4523 toNode = cableInProgress->getToNode();
4524 toPort = cableInProgress->getToPort();
4529 potentialCable->
setAlwaysEventOnly(! cableInProgress->getRenderer()->effectivelyCarriesData() ||
4530 cableInProgress->getRenderer()->isFloatingEndpointAboveEventPort());
4534 potentialCables.insert(potentialCable);
4545 VUserLog(
"%s: Showing error popover: %s",
4553 set<VuoRendererNode *> nodesToMark;
4554 set<VuoRendererCable *> cablesToMark;
4556 set<VuoNode *> problemNodes = issue.
getNodes();
4557 foreach (
VuoNode *node, problemNodes)
4561 set<VuoCable *> problemCables = issue.
getCables();
4562 foreach (
VuoCable *cable, problemCables)
4564 VuoCable *cableToMark = (cable->
getCompiler() == potentialCable ? cableInProgress : cable);
4573 errorPopovers.insert(errorPopover);
4577 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
4581 if (targetPort && cableInProgress && nodesToMark.find(targetPort->
getRenderedParentNode()) != nodesToMark.end())
4585 else if (! nodesToMark.empty())
4588 qreal topY = viewportRect.bottom();
4594 QPointF scenePos = node->scenePos();
4595 if (viewportRect.contains(scenePos) && (scenePos.y() < topY))
4597 topmostVisibleNode = node;
4598 topY = scenePos.y();
4602 if (topmostVisibleNode)
4603 nearbyNode = topmostVisibleNode;
4612 VUserLog(
"Warning: no nearby node (no marked nodes).");
4617 const QPoint offsetFromNode(0,10);
4618 QPoint popoverTopLeftInScene = (nearbyNode?
4619 (nearbyNode->scenePos().toPoint() +
4622 QPoint(viewportRect.center().x() - 0.5*errorPopover->sizeHint().width(),
4623 viewportRect.center().y() - 0.5*errorPopover->sizeHint().height()));
4627 const int margin = 5;
4628 popoverTopLeftInScene = (QPoint(fmin(popoverTopLeftInScene.x(), viewportRect.bottomRight().x() - errorPopover->sizeHint().width() - margin),
4629 fmin(popoverTopLeftInScene.y(), viewportRect.bottomRight().y() - errorPopover->sizeHint().height() - margin)));
4631 popoverTopLeftInScene = (QPoint(fmax(popoverTopLeftInScene.x(), viewportRect.topLeft().x() + margin),
4632 fmax(popoverTopLeftInScene.y(), viewportRect.topLeft().y() + margin)));
4634 QPoint popoverTopLeftInView = views()[0]->mapFromScene(popoverTopLeftInScene);
4635 QPoint popoverTopLeftGlobal = views()[0]->mapToGlobal(popoverTopLeftInView);
4637 errorPopover->move(popoverTopLeftGlobal);
4638 errorPopover->show();
4646 delete potentialCable;
4648 errorMarkingUpdatesEnabled =
true;
4654 bool VuoEditorComposition::hasFeedbackErrors(
void)
4656 return this->errorMark;
4664 if (hasFeedbackErrors())
4673 void VuoEditorComposition::buildComposition(
string compositionSnapshot,
const set<string> &dependenciesUninstalled)
4679 if (! dependenciesUninstalled.empty())
4681 vector<string> dependenciesRemovedVec(dependenciesUninstalled.begin(), dependenciesUninstalled.end());
4683 throw VuoException(
"Some modules that the composition needs were uninstalled: " + dependenciesStr);
4686 delete runningComposition;
4687 runningComposition = NULL;
4691 if (runningCompositionActiveDriver)
4695 string dir, file, ext;
4697 linkedCompositionPath = dir + file +
".dylib";
4702 compiler->
compileComposition(runningComposition, compiledCompositionPath,
true, issues);
4706 remove(compiledCompositionPath.c_str());
4712 delete runningComposition;
4713 runningComposition = NULL;
4725 bool VuoEditorComposition::isRunningThreadUnsafe(
void)
4727 return runner != NULL && ! stopRequested && ! runner->
isStopped();
4737 __block
bool running;
4738 dispatch_sync(runCompositionQueue, ^{
4739 running = isRunningThreadUnsafe();
4765 if (matchingComposition->showEventsMode)
4769 stopRequested =
false;
4770 dispatch_async(runCompositionQueue, ^{
4773 runningCompositionLibraries = std::make_shared<VuoRunningCompositionLibraries>();
4775 buildComposition(compositionSnapshot);
4789 if (matchingComposition->showEventsMode)
4790 this->runner->subscribeToEventTelemetry(matchingCompositionIdentifier);
4792 dispatch_sync(activePortPopoversQueue, ^{
4793 for (
auto i : matchingComposition->activePortPopovers)
4795 string portID = i.first;
4796 updateDataInPortPopoverFromRunningTopLevelComposition(matchingComposition, matchingCompositionIdentifier, portID);
4817 stopRequested =
true;
4818 dispatch_async(runCompositionQueue, ^{
4822 runner->waitUntilStopped();
4827 linkedCompositionPath =
"";
4829 runningCompositionLibraries =
nullptr;
4831 delete runningComposition;
4832 runningComposition = NULL;
4840 subcompositionRouter->applyToAllLinkedCompositions(
this, ^
void (
VuoEditorComposition *matchingComposition,
string matchingCompositionIdentifier)
4842 if (matchingComposition->showEventsMode)
4845 dispatch_sync(activePortPopoversQueue, ^{
4846 for (
auto i : matchingComposition->activePortPopovers)
4848 VuoPortPopover *popover = i.second;
4849 popover->setCompositionRunning(false);
4872 dispatch_async(runCompositionQueue, ^{
4873 if (isRunningThreadUnsafe())
4879 runningCompositionLibraries->enqueueLibraryContainingDependencyToUnload(moduleKey);
4882 string oldBuiltCompositionSnapshot = oldCompositionSnapshot;
4884 if (previouslyActiveDriver)
4887 previouslyActiveDriver->applyToComposition(oldBuiltComposition, compiler);
4891 buildComposition(newCompositionSnapshot, dependenciesUninstalled);
4893 string compositionDiff = diffInfo->
diff(oldBuiltCompositionSnapshot, runningComposition, compiler);
4896 catch (exception &e)
4898 VUserLog(
"Composition stopped itself: %s", e.what());
4903 VUserLog(
"Composition stopped itself.");
4909 dispatch_async(dispatch_get_main_queue(), ^{
4928 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, reloadSubcompositionIfUnsaved);
4946 if (
this == topLevelComposition)
4948 dispatch_async(runCompositionQueue, ^{
4949 if (isRunningThreadUnsafe())
4951 json_object *constantObject = json_tokener_parse(constant.c_str());
4952 runner->
setInputPortValue(thisCompositionIdentifier, runningPortID, constantObject);
4960 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
4962 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
4989 if (runningPortIdentifier.empty())
4995 if (
this == topLevelComposition)
4997 dispatch_async(runCompositionQueue, ^{
4998 if (isRunningThreadUnsafe())
5000 json_object *constantObject = json_tokener_parse(constant.c_str());
5001 runner->
setInputPortValue(thisCompositionIdentifier, runningPortIdentifier, constantObject);
5009 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
5011 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
5024 dispatch_async(runCompositionQueue, ^{
5025 if (isRunningThreadUnsafe())
5027 json_object *constantObject = json_tokener_parse(constant.c_str());
5029 foreach (
string subcompositionIdentifier, subcompositionIdentifiers)
5031 runner->
setInputPortValue(subcompositionIdentifier, portIdentifier, constantObject);
5042 dispatch_async(runCompositionQueue, ^{
5043 if (isRunningThreadUnsafe())
5048 json_object *constantObject = json_tokener_parse(constant.c_str());
5049 map<VuoRunner::Port *, json_object *> m;
5050 m[publishedPort] = constantObject;
5063 return contextMenuDeleteSelected;
5073 QMenu *contextMenuTints =
new QMenu(parent);
5074 contextMenuTints->setSeparatorsCollapsible(
false);
5075 contextMenuTints->setTitle(tr(
"Tint"));
5076 foreach (QAction *tintAction, contextMenuTintActions)
5077 contextMenuTints->addAction(tintAction);
5078 contextMenuTints->insertSeparator(contextMenuTintActions.last());
5080 return contextMenuTints;
5086 void VuoEditorComposition::expandChangeNodeMenu()
5088 QAction *sender = (QAction *)QObject::sender();
5093 int currentMatchesListed = contextMenuChangeNode->actions().size()-1;
5094 if (currentMatchesListed <= initialChangeNodeSuggestionCount)
5097 int verticalSpacePerItem = 21;
5101 int targetMatches = availableVerticalSpace/verticalSpacePerItem - 2;
5110 contextMenuChangeNode->exec();
5125 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
5126 vector<VuoCompilerNodeClass *> loadedNodeClasses;
5127 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
5128 loadedNodeClasses.push_back(i->second);
5131 vector<string> bestMatches;
5132 map<string, double> matchScores;
5133 matchScores[
""] = 0;
5135 int targetMatchCount = (matchLimit > 0? matchLimit : loadedNodeClasses.size());
5136 for (
int i = 0; i < targetMatchCount; ++i)
5137 bestMatches.push_back(
"");
5140 bool overflow =
false;
5143 string originalGenericNodeClassName;
5147 originalGenericNodeClassName = nodeClass->
getClassName();
5152 if (loadedNodeClassName == originalGenericNodeClassName)
5155 bool canSwapNondestructively = canSwapWithoutBreakingCables(node, loadedNodeClass->
getBase());
5156 double matchScore = (canSwapNondestructively? calculateNodeSimilarity(nodeClass, loadedNodeClass->
getBase()) : 0);
5157 int highestIndexWithCompetitiveScore = -1;
5158 for (
int i = targetMatchCount-1; (i >= 0) && (highestIndexWithCompetitiveScore == -1); --i)
5159 if (matchScore <= matchScores[bestMatches[i] ])
5160 highestIndexWithCompetitiveScore = i;
5162 if (highestIndexWithCompetitiveScore < targetMatchCount-1)
5164 if (matchScores[bestMatches[targetMatchCount-1] ] > 0)
5167 for (
int j = targetMatchCount-2; j > highestIndexWithCompetitiveScore; --j)
5168 bestMatches[j+1] = bestMatches[j];
5170 bestMatches[highestIndexWithCompetitiveScore+1] = loadedNodeClassName;
5171 matchScores[loadedNodeClassName] = matchScore;
5175 for (
int i = 0; i < targetMatchCount; ++i)
5177 if (matchScores[bestMatches[i] ] > 0)
5182 matchDisplayText += QString(
" (%1)").arg(bestMatches[i].c_str());
5184 QAction *changeAction = menu->addAction(matchDisplayText);
5186 QList<QVariant> currentNodeAndNewClass;
5187 currentNodeAndNewClass.append(qVariantFromValue((
void *)node));
5188 currentNodeAndNewClass.append(bestMatches[i].c_str());
5189 changeAction->setData(QVariant(currentNodeAndNewClass));
5190 connect(changeAction, &QAction::triggered,
this, &VuoEditorComposition::swapNode);
5197 QAction *showMoreAction = menu->addAction(tr(
"More…"));
5198 showMoreAction->setData(qVariantFromValue(
static_cast<void *
>(node)));
5199 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandChangeNodeMenu);
5210 map<string, int> requiredInputs;
5211 bool inputEventSourceRequired =
false;
5214 bool hasDrawerWithNoIncomingCables =
false;
5215 bool hasDrawerWithNoIncomingDataCables =
false;
5222 hasDrawerWithNoIncomingCables =
true;
5223 hasDrawerWithNoIncomingDataCables =
true;
5224 vector<VuoRendererPort *> childPorts = inputDrawer->
getDrawerPorts();
5228 hasDrawerWithNoIncomingCables =
false;
5230 hasDrawerWithNoIncomingDataCables =
false;
5235 if (!hasDrawerWithNoIncomingDataCables)
5242 requiredInputs[typeKey] = ((requiredInputs.find(typeKey) == requiredInputs.end())? 1 : requiredInputs[typeKey]+1);
5247 if (hasIncomingCables && !hasDrawerWithNoIncomingCables)
5248 inputEventSourceRequired =
true;
5252 map<string, int> requiredOutputs;
5253 bool outputEventSourceRequired =
false;
5265 requiredOutputs[typeKey] = ((requiredOutputs.find(typeKey) == requiredOutputs.end())? 1 : requiredOutputs[typeKey]+1);
5269 outputEventSourceRequired =
true;
5273 bool inputEventSourceAvailable =
false;
5274 map<string, int> availableInputs;
5282 availableInputs[typeKey] = ((availableInputs.find(typeKey) == availableInputs.end())? 1 : availableInputs[typeKey]+1);
5287 inputEventSourceAvailable =
true;
5290 bool outputEventSourceAvailable =
false;
5291 map<string, int> availableOutputs;
5299 availableOutputs[typeKey] = ((availableOutputs.find(typeKey) == availableOutputs.end())? 1 : availableOutputs[typeKey]+1);
5304 outputEventSourceAvailable =
true;
5307 for (std::map<string,int>::iterator it=requiredInputs.begin(); it!=requiredInputs.end(); ++it)
5309 string typeKey = it->first;
5310 int typeRequiredCount = it->second;
5311 if (availableInputs[typeKey] < typeRequiredCount)
5316 for (std::map<string,int>::iterator it=requiredOutputs.begin(); it!=requiredOutputs.end(); ++it)
5318 string typeKey = it->first;
5319 int typeRequiredCount = it->second;
5320 if (availableOutputs[typeKey] < typeRequiredCount)
5324 if (inputEventSourceRequired && !inputEventSourceAvailable)
5327 if (outputEventSourceRequired && !outputEventSourceAvailable)
5337 bool VuoEditorComposition::isPortCurrentlyRevertible(
VuoRendererPort *port)
5343 if (!specializedNodeClass)
5348 if (!originalGenericType)
5357 if (hostPort && (!isPortCurrentlyRevertible(hostPort->
getRenderer())))
5384 string publishedPortName = ((! name.empty())?
5395 bool performedMerge =
false;
5398 publishedPort = (isPublishedInput ?
5404 if (isPublishedInput && portType && type && !forceEventOnlyPublication)
5412 performedMerge =
true;
5419 if (! performedMerge)
5422 if (isPublishedInput && type)
5437 if (! existingPublishedCable)
5443 if (mergePerformed != NULL)
5444 *mergePerformed = performedMerge;
5446 return rendererPublishedPort;
5458 if (creatingPublishedInputCable)
5461 VuoPort *fromPort = externalPort;
5464 VuoPort *toPort = internalPort;
5471 publishedCable->
setFrom(fromNode, fromPort);
5478 VuoPort *fromPort = internalPort;
5481 VuoPort *toPort = externalPort;
5488 publishedCable->
setTo(toNode, toPort);
5491 if (forceEventOnlyPublication)
5494 return publishedCable;
5510 vector<VuoPublishedPort *> publishedPortsToAdd;
5511 map<VuoPublishedPort *, string> publishedPortsToRename;
5514 VuoProtocol *previousActiveProtocol = this->activeProtocol;
5515 bool removingPreviousProtocol = previousActiveProtocol && (previousActiveProtocol != protocol);
5517 bool portChangesMadeDuringProtocolRemoval =
false;
5518 if (removingPreviousProtocol)
5521 if (portChangesMadeDuringProtocolRemoval && !useUndoStack)
5523 VUserLog(
"Warning: Unexpected combination: Removing protocol ports, but useUndoStack=false");
5524 useUndoStack =
true;
5528 this->activeProtocol = protocol;
5533 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5535 string portName = i->first;
5536 string portType = i->second;
5538 bool compositionHadCompatiblePort =
false;
5540 if (preexistingPublishedPort)
5544 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5545 (!preexistingType && (portType ==
"")));
5548 compositionHadCompatiblePort =
true;
5553 compositionHadCompatiblePort =
false;
5558 if (!compositionHadCompatiblePort)
5567 publishedPortsToAdd.push_back(publishedPort);
5574 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5576 string portName = i->first;
5577 string portType = i->second;
5579 bool compositionHadCompatiblePort =
false;
5581 if (preexistingPublishedPort)
5584 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5585 (!preexistingType && (portType ==
"")));
5588 compositionHadCompatiblePort =
true;
5593 compositionHadCompatiblePort =
false;
5598 if (!compositionHadCompatiblePort)
5607 publishedPortsToAdd.push_back(publishedPort);
5615 bool undoStackMacroBegunAlready = (removingPreviousProtocol && portChangesMadeDuringProtocolRemoval);
5616 if (!publishedPortsToRename.empty() || !publishedPortsToAdd.empty() || undoStackMacroBegunAlready)
5618 set<VuoPublishedPort *> publishedPortsToRemove;
5619 bool beginUndoStackMacro = !undoStackMacroBegunAlready;
5620 bool endUndoStackMacro =
true;
5621 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5635 string VuoEditorComposition::getNonProtocolVariantForPortName(
string portName)
5637 string modifiedPortName = portName;
5638 if (modifiedPortName.length() > 0)
5639 modifiedPortName[0] = toupper(modifiedPortName[0]);
5640 modifiedPortName =
"some" + modifiedPortName;
5642 return modifiedPortName;
5666 set<VuoPublishedPort *> publishedPortsToRemove;
5667 map<VuoPublishedPort *, string> publishedPortsToRename;
5670 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5672 string portName = i->first;
5673 string portType = i->second;
5676 if (preexistingPublishedPort)
5678 bool portCompatibleAcrossProtocolTransition =
false;
5679 if (replacementProtocol)
5682 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5684 string replacementPortName = i->first;
5685 string replacementPortType = i->second;
5687 if ((portName == replacementPortName) && (portType == replacementPortType))
5688 portCompatibleAcrossProtocolTransition =
true;
5693 publishedPortsToRemove.insert(preexistingPublishedPort);
5694 else if (!portCompatibleAcrossProtocolTransition)
5700 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5702 string portName = i->first;
5703 string portType = i->second;
5706 if (preexistingPublishedPort)
5708 bool portCompatibleAcrossProtocolTransition =
false;
5709 if (replacementProtocol)
5712 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5714 string replacementPortName = i->first;
5715 string replacementPortType = i->second;
5717 if ((portName == replacementPortName) && (portType == replacementPortType))
5718 portCompatibleAcrossProtocolTransition =
true;
5723 publishedPortsToRemove.insert(preexistingPublishedPort);
5724 else if (!portCompatibleAcrossProtocolTransition)
5731 if (!publishedPortsToRemove.empty())
5732 publishedPortsToRename.clear();
5734 bool portChangesRequired = (!publishedPortsToRename.empty() || !publishedPortsToRemove.empty());
5735 if (portChangesRequired)
5737 vector<VuoPublishedPort *> publishedPortsToAdd;
5738 bool beginUndoStackMacro =
true;
5739 bool endUndoStackMacro = !replacementProtocol;
5740 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5745 return portChangesRequired;
5756 if ((activeProtocol != protocol) || !activeProtocol)
5759 activeProtocol = NULL;
5762 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5764 string portName = i->first;
5765 string portType = i->second;
5768 if (preexistingPublishedPort)
5773 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5775 string portName = i->first;
5776 string portType = i->second;
5779 if (preexistingPublishedPort)
5792 return activeProtocol;
5801 if (!activeProtocol)
5804 return static_cast<VuoEditor *
>(qApp)->getBuiltInDriverForProtocol(activeProtocol);
5837 return removalResult;
5864 void VuoEditorComposition::highlightEligibleEndpointsForCable(
VuoCable *cable)
5877 highlightInternalPortsConnectableToPort(fixedPort, cable->
getRenderer());
5891 QList<QGraphicsItem *> compositionComponents = items();
5892 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5894 QGraphicsItem *compositionComponent = *i;
5900 for(vector<VuoPort *>::iterator inputPort = inputPorts.begin(); inputPort != inputPorts.end(); ++inputPort)
5901 updateEligibilityHighlightingForPort((*inputPort)->getRenderer(), port, !cable->
effectivelyCarriesData(), types);
5905 for(vector<VuoPort *>::iterator outputPort = outputPorts.begin(); outputPort != outputPorts.end(); ++outputPort)
5906 updateEligibilityHighlightingForPort((*outputPort)->getRenderer(), port, !cable->
effectivelyCarriesData(), types);
5911 if (rc && rc != cable)
5913 QGraphicsItem::CacheMode normalCacheMode = rc->cacheMode();
5914 rc->setCacheMode(QGraphicsItem::NoCache);
5932 rc->setCacheMode(normalCacheMode);
5937 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5938 updateEligibilityHighlightingForNode(
dynamic_cast<VuoRendererNode *
>(*i));
5945 void VuoEditorComposition::updateEligibilityHighlightingForPort(
VuoRendererPort *portToHighlight,
5947 bool eventOnlyConnection,
5948 map<string, VuoCompilerType *> &types)
5950 QGraphicsItem::CacheMode normalCacheMode = portToHighlight->cacheMode();
5951 portToHighlight->setCacheMode(QGraphicsItem::NoCache);
5959 if (typecastPortToHighlight)
5962 portToHighlight->setCacheMode(normalCacheMode);
5964 if (typecastPortToHighlight)
5965 updateEligibilityHighlightingForPort(typecastPortToHighlight->
getChildPort(), fixedPort, eventOnlyConnection, types);
5986 bool forwardConnection;
5989 fromPort = fixedPort;
5990 toPort = portToHighlight;
5991 forwardConnection =
true;
5995 fromPort = portToHighlight;
5997 forwardConnection =
false;
6000 bool directConnectionPossible;
6004 if (fixedExternalPublishedPort && externalPublishedPortToHighlight)
6005 directConnectionPossible =
false;
6006 else if (fixedExternalPublishedPort && !externalPublishedPortToHighlight)
6008 else if (!fixedExternalPublishedPort && externalPublishedPortToHighlight)
6014 if (directConnectionPossible)
6018 else if (fixedPort == portToHighlight)
6041 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6043 bool eventOnlyConnection,
6044 bool forwardConnection)
6047 string respecializedTypeName =
"";
6049 return canConnectDirectlyWithRespecializationNondestructively(fromPort,
6051 eventOnlyConnection,
6053 &portToRespecialize,
6054 respecializedTypeName);
6067 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6069 bool eventOnlyConnection,
6070 bool forwardConnection,
6072 string &respecializedTypeName)
6074 *portToRespecialize = NULL;
6075 respecializedTypeName =
"";
6077 bool canConnectWithRespecialization = canConnectDirectlyWithRespecialization(fromPort,
6079 eventOnlyConnection,
6082 respecializedTypeName);
6083 if (!canConnectWithRespecialization)
6086 if (canConnectWithRespecialization && !portToRespecialize)
6089 bool nondestructive = portCanBeUnspecializedNondestructively((*portToRespecialize)->getBase());
6090 if (!nondestructive)
6092 *portToRespecialize = NULL;
6093 respecializedTypeName =
"";
6095 return nondestructive;
6103 bool VuoEditorComposition::portCanBeUnspecializedNondestructively(
VuoPort *portToUnspecialize)
6105 map<VuoNode *, string> nodesToReplace;
6106 set<VuoCable *> cablesToDelete;
6111 if (cablesToDelete.empty())
6114 else if ((cablesToDelete.size() == 1) && ((*(cablesToDelete.begin()))->getToPort() == portToUnspecialize))
6139 bool VuoEditorComposition::canConnectDirectlyWithRespecialization(
VuoRendererPort *fromPort,
6141 bool eventOnlyConnection,
6142 bool forwardConnection,
6144 string &respecializedTypeName)
6148 *portToRespecialize = NULL;
6149 respecializedTypeName =
"";
6159 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6160 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6162 if (!(fromPortIsEnabledOutput && toPortIsEnabledInput))
6168 if (!(currentFromDataType && currentToDataType))
6183 if (fromSpecializedNodeClass)
6195 if (toSpecializedNodeClass)
6204 bool fromPortIsGeneric = currentFromGenericType;
6205 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6206 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6208 bool toPortIsGeneric = currentToGenericType;
6209 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6210 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6213 set<string> compatibleTypes;
6214 string specializedType =
"";
6218 if ((fromPortIsStatic && toPortIsSpecialized) || (fromPortIsSpecialized && toPortIsStatic))
6222 portToTryToRespecialize = (fromPortIsSpecialized? fromPort : toPort);
6226 else if ((fromPortIsSpecialized || toPortIsSpecialized) && !fromPortIsStatic && !toPortIsStatic)
6229 bool dragSourceIsGeneric = (forwardConnection? fromPortIsGeneric : toPortIsGeneric);
6231 VuoRendererPort *dragDestination = (forwardConnection? toPort : fromPort);
6232 bool dragDestinationIsGeneric = (forwardConnection? toPortIsGeneric : fromPortIsGeneric);
6249 if (!dragSourceIsGeneric && !dragDestinationIsGeneric)
6252 portToTryToRespecialize = dragDestination;
6260 if (portToTryToRespecialize)
6261 compatibleTypes = getRespecializationOptionsForPortInNetwork(portToTryToRespecialize);
6263 bool portsAreCompatible = (compatibleTypes.find(specializedType) != compatibleTypes.end());
6265 if (portsAreCompatible)
6267 *portToRespecialize = portToTryToRespecialize;
6268 respecializedTypeName = specializedType;
6271 return portsAreCompatible;
6280 void VuoEditorComposition::updateEligibilityHighlightingForNode(
VuoRendererNode *node)
6295 QGraphicsItem::CacheMode normalCacheMode = drawer->cacheMode();
6296 drawer->setCacheMode(QGraphicsItem::NoCache);
6301 drawer->setCacheMode(normalCacheMode);
6310 QGraphicsItem::CacheMode normalCacheMode = hostPort->cacheMode();
6311 hostPort->setCacheMode(QGraphicsItem::NoCache);
6313 hostPort->setCacheMode(normalCacheMode);
6347 bool toPortIsDragDestination,
6349 string &specializedTypeName,
6350 string &typecastToInsert)
6352 *portToSpecialize = NULL;
6353 specializedTypeName =
"";
6355 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6356 map<string, string> specializedTypeNameForTypecast;
6359 vector<string> candidateTypecasts =
findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast, types);
6360 bool solutionSelected = selectBridgingSolutionFromOptions(candidateTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, typecastToInsert);
6362 if (!solutionSelected)
6365 if (portToSpecializeForTypecast.find(typecastToInsert) != portToSpecializeForTypecast.end())
6366 *portToSpecialize = portToSpecializeForTypecast[typecastToInsert];
6367 if (specializedTypeNameForTypecast.find(typecastToInsert) != specializedTypeNameForTypecast.end())
6368 specializedTypeName = specializedTypeNameForTypecast[typecastToInsert];
6391 bool VuoEditorComposition::selectBridgingSolutionFromOptions(vector<string> suitableTypecasts,
6392 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6393 map<string, string> specializedTypeNameForTypecast,
6394 string &selectedTypecast)
6396 if (suitableTypecasts.empty())
6398 selectedTypecast =
"";
6402 else if (suitableTypecasts.size() == 1)
6404 selectedTypecast = suitableTypecasts[0];
6409 return promptForBridgingSelectionFromOptions(suitableTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, selectedTypecast);
6419 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6420 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6422 return (fromPortIsEnabledOutput && toPortIsEnabledInput &&
6434 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6450 if (toNodeUsesIndex)
6477 bool toPortIsDragDestination,
6478 map<string, VuoCompilerType *> &types)
6480 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6481 map<string, string> specializedTypeNameForTypecast;
6482 return findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast, types);
6496 bool toPortIsDragDestination,
6497 map<string, VuoRendererPort *> &portToSpecializeForTypecast,
6498 map<string, string> &specializedTypeNameForTypecast,
6499 map<string, VuoCompilerType *> &types)
6506 const bool limitCombinations =
true;
6508 portToSpecializeForTypecast.clear();
6509 specializedTypeNameForTypecast.clear();
6510 vector<string> suitableTypecasts;
6512 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6513 return suitableTypecasts;
6518 return suitableTypecasts;
6523 string specializedTypeName =
"";
6526 suitableTypecasts.push_back(
"");
6527 portToSpecializeForTypecast[
""] = portToSpecialize;
6528 specializedTypeNameForTypecast[
""] = specializedTypeName;
6530 return suitableTypecasts;
6537 if (!(currentFromDataType && currentToDataType))
6538 return suitableTypecasts;
6548 if (fromSpecializedNodeClass)
6560 if (toSpecializedNodeClass)
6571 bool fromPortIsGeneric = currentFromGenericType;
6572 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6573 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6575 bool toPortIsGeneric = currentToGenericType;
6576 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6577 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6580 if (fromPortIsGeneric && toPortIsGeneric)
6581 return suitableTypecasts;
6584 else if (fromPortIsStatic && toPortIsStatic)
6586 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6588 return suitableTypecasts;
6593 bool specializeToPort =
true;
6594 if (toPortIsGeneric)
6595 specializeToPort =
true;
6596 else if (fromPortIsGeneric)
6597 specializeToPort =
false;
6598 else if (fromPortIsSpecialized && toPortIsStatic)
6599 specializeToPort =
false;
6600 else if (fromPortIsStatic && toPortIsSpecialized)
6601 specializeToPort =
true;
6602 else if (fromPortIsSpecialized && toPortIsSpecialized)
6603 specializeToPort = toPortIsDragDestination;
6607 set<string> compatibleTypes;
6608 if (specializeToPort && (toPortIsGeneric || (toPortIsSpecialized && portCanBeUnspecializedNondestructively(toPort->
getBase()))))
6609 compatibleTypes = getRespecializationOptionsForPortInNetwork(toPort);
6610 else if (!specializeToPort && (fromPortIsGeneric || (fromPortIsSpecialized && portCanBeUnspecializedNondestructively(fromPort->
getBase()))))
6611 compatibleTypes = getRespecializationOptionsForPortInNetwork(fromPort);
6617 if (limitCombinations)
6619 vector<string> limitedSuitableTypecasts;
6622 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6625 foreach (
string typecastName, limitedSuitableTypecasts)
6627 portToSpecializeForTypecast[typecastName] = specializeToPort? toPort : fromPort;
6628 specializedTypeNameForTypecast[typecastName] = specializeToPort? currentToDataType->
getModuleKey() :
6635 if (compatibleTypes.find(fixedDataType) != compatibleTypes.end())
6637 limitedSuitableTypecasts.push_back(
"");
6638 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6639 specializedTypeNameForTypecast[
""] = fixedDataType;
6642 if (limitedSuitableTypecasts.size() >= 1)
6643 return limitedSuitableTypecasts;
6646 foreach (
string compatibleTypeName, compatibleTypes)
6648 VuoCompilerType *compatibleSpecializedType = types[compatibleTypeName];
6649 if (!compatibleSpecializedType)
6650 compatibleSpecializedType = compiler->
getType(compatibleTypeName);
6651 VuoType *candidateFromType = specializeToPort? currentFromDataType : compatibleSpecializedType->
getBase();
6652 VuoType *candidateToType = specializeToPort? compatibleSpecializedType->
getBase() : currentToDataType;
6654 if (compatibleSpecializedType)
6657 if (candidateFromType == candidateToType)
6659 suitableTypecasts.push_back(
"");
6660 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6661 specializedTypeNameForTypecast[
""] = compatibleSpecializedType->
getBase()->
getModuleKey();
6664 if (portsPassSanityCheckToTypeconvert(fromPort,
6670 foreach (
string typecast, suitableTypecastsForCurrentTypes)
6672 suitableTypecasts.push_back(typecast);
6673 portToSpecializeForTypecast[typecast] = specializeToPort? toPort : fromPort;
6674 specializedTypeNameForTypecast[typecast] = compatibleSpecializedType->
getBase()->
getModuleKey();
6680 return suitableTypecasts;
6695 bool VuoEditorComposition::promptForBridgingSelectionFromOptions(vector<string> suitableTypecasts,
6696 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6697 map<string, string> specializedTypeNameForTypecast,
6698 string &selectedTypecast)
6700 QMenu typecastMenu(views()[0]->viewport());
6701 typecastMenu.setSeparatorsCollapsible(
false);
6702 QString spacer(
" ");
6705 set <pair<VuoRendererPort *, string> > specializationDetails;
6706 vector<string> typeconversionOptionsRequiringNoSpecialization;
6707 foreach (
string typecastClassName, suitableTypecasts)
6709 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typecastClassName];
6710 string specializedTypeName = specializedTypeNameForTypecast[typecastClassName];
6711 specializationDetails.insert(std::make_pair(portToSpecialize,
6712 specializedTypeName));
6714 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6715 if (portAlreadyHasTargetType)
6716 typeconversionOptionsRequiringNoSpecialization.push_back(typecastClassName);
6722 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()))
6724 QString menuText = getDisplayTextForSpecializationOption(portToSpecializeForTypecast[
""], specializedTypeNameForTypecast[
""]);
6725 QAction *typecastAction = typecastMenu.addAction(menuText);
6726 typecastAction->setData(QVariant(
""));
6729 bool foundSpecializationOptionsRequiringNoTypeconversion = typecastMenu.actions().size() >= 1;
6730 bool foundTypeconversionOptionsRequiringNoSpecialization = typeconversionOptionsRequiringNoSpecialization.size() >= 1;
6733 bool includingTypeconvertWithNoSpecializationHeader = foundSpecializationOptionsRequiringNoTypeconversion;
6734 if (foundTypeconversionOptionsRequiringNoSpecialization)
6736 if (foundSpecializationOptionsRequiringNoTypeconversion)
6737 typecastMenu.addSeparator();
6739 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6740 string specializedTypeName = specializedTypeNameForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6742 if (portToSpecialize && !specializedTypeName.empty())
6744 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6745 QAction *typecastAction = typecastMenu.addAction(menuText);
6746 typecastAction->setEnabled(
false);
6747 includingTypeconvertWithNoSpecializationHeader =
true;
6751 foreach (
string typecastClassName, typeconversionOptionsRequiringNoSpecialization)
6757 typecastAction->setData(QVariant(typecastClassName.c_str()));
6762 for (set<pair<VuoRendererPort *, string> >::iterator i = specializationDetails.begin(); i != specializationDetails.end(); ++i)
6765 string specializedTypeName = i->second;
6768 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()) &&
6769 (portToSpecializeForTypecast[
""] == portToSpecialize) &&
6770 (specializedTypeNameForTypecast[
""] == specializedTypeName))
6776 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6777 if (portAlreadyHasTargetType)
6782 if (typecastMenu.actions().size() >= 1)
6783 typecastMenu.addSeparator();
6785 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6786 QAction *typecastAction = typecastMenu.addAction(menuText);
6787 typecastAction->setEnabled(
false);
6790 foreach (
string typecastClassName, suitableTypecasts)
6792 if ((portToSpecializeForTypecast[typecastClassName] == portToSpecialize) &&
6793 (specializedTypeNameForTypecast[typecastClassName] == specializedTypeName))
6799 typecastAction->setData(QVariant(typecastClassName.c_str()));
6805 menuSelectionInProgress =
true;
6806 QAction *selectedTypecastAction = typecastMenu.exec(QCursor::pos());
6807 menuSelectionInProgress =
false;
6809 selectedTypecast = (selectedTypecastAction? selectedTypecastAction->data().toString().toUtf8().constData() :
"");
6810 return selectedTypecastAction;
6816 QString VuoEditorComposition::getDisplayTextForSpecializationOption(
VuoRendererPort *portToSpecialize,
string specializedTypeName)
6818 if (!portToSpecialize || specializedTypeName.empty())
6821 bool isInput = portToSpecialize && portToSpecialize->
getInput();
6822 QString typeDisplayName = compiler->
getType(specializedTypeName)?
6824 specializedTypeName.c_str();
6826 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6828 QString displayText;
6829 if (portAlreadyHasTargetType)
6834 displayText = tr(
"Keep Input Port as %1");
6839 displayText = tr(
"Keep Output Port as %1");
6847 displayText = tr(
"Change Input Port to %1");
6852 displayText = tr(
"Change Output Port to %1");
6856 return displayText.arg(typeDisplayName);
6875 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
6876 if (topLevelComposition->isRunningThreadUnsafe())
6878 portValue = isInput ?
6879 topLevelComposition->runner->getInputPortValue(thisCompositionIdentifier, runningPortIdentifier) :
6880 topLevelComposition->runner->getOutputPortValue(thisCompositionIdentifier, runningPortIdentifier);
6884 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, getPortValue);
6892 string VuoEditorComposition::getIdentifierForRunningPort(
VuoPort *runningPort)
6909 return dynamic_cast<VuoPublishedPort *
>(staticPort)->getClass()->getName();
6915 string nodeIdentifier =
"";
6925 if (staticPort->
hasCompiler() && !nodeIdentifier.empty())
6969 if (candidateInputPort == port)
6973 if (candidateOutputPort == port)
6989 map<string, VuoPortPopover *>::iterator popover = activePortPopovers.find(portID);
6990 if (popover != activePortPopovers.end())
6991 return popover->second;
7004 void VuoEditorComposition::enableInactivePopoverForPort(
VuoRendererPort *rp)
7007 bool popoverJustClosedAtLastEvent = portsWithPopoversClosedAtLastEvent.find(portID) != portsWithPopoversClosedAtLastEvent.end();
7008 if (!popoverJustClosedAtLastEvent)
7017 if (!popoverEventsEnabled)
7023 VUserLog(
"%s: Open popover for %s",
7027 dispatch_sync(runCompositionQueue, ^{
7029 dispatch_sync(activePortPopoversQueue, ^{
7031 if (activePortPopovers.find(portID) == activePortPopovers.end())
7035 VuoPortPopover *popover = new VuoPortPopover(port, this, views()[0]->viewport());
7037 connect(popover, &VuoPortPopover::popoverClosedForPort, this, &VuoEditorComposition::disablePopoverForPortThreadSafe);
7038 connect(popover, &VuoPortPopover::popoverDetachedFromPort, [=]{
7039 VUserLog(
"%s: Detach popover for %s",
7040 window->getWindowTitleWithoutPlaceholder().toUtf8().data(),
7052 const int cutoffMargin = 16;
7053 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
7054 if (portLeftInScene.x() + popover->size().width() + cutoffMargin > viewportRect.right())
7055 portLeftInScene = QPoint(viewportRect.right() - popover->size().width() - cutoffMargin, portLeftInScene.y());
7056 if (portLeftInScene.y() + popover->size().height() + cutoffMargin > viewportRect.bottom())
7057 portLeftInScene = QPoint(portLeftInScene.x(), viewportRect.bottom() - popover->size().height() - cutoffMargin);
7059 QPoint popoverLeftInView = views()[0]->mapFromScene(portLeftInScene);
7061 const QPoint offset = QPoint(12, 6);
7063 QPoint popoverTopLeft = popoverLeftInView + offset;
7064 popover->move(popoverTopLeft);
7067 activePortPopovers[portID] = popover;
7069 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7096 VUserLog(
"%s: Close popover for %s",
7101 map<string, VuoPortPopover *>::iterator i = activePortPopovers.find(portID);
7102 if (i != activePortPopovers.end())
7104 popover = i->second;
7105 activePortPopovers.erase(i);
7114 bool isInput =
false;
7122 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7123 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7125 dispatch_async(topLevelComposition->runCompositionQueue, ^{
7126 if (topLevelComposition->isRunningThreadUnsafe())
7129 topLevelComposition->runner->unsubscribeFromInputPortTelemetry(thisCompositionIdentifier, portID) :
7130 topLevelComposition->runner->unsubscribeFromOutputPortTelemetry(thisCompositionIdentifier, portID));
7140 void VuoEditorComposition::disablePopoverForPortThreadSafe(
string portID)
7142 dispatch_sync(activePortPopoversQueue, ^{
7152 disablePortPopovers();
7163 errorPopover->hide();
7164 errorPopover->deleteLater();
7167 errorPopovers.clear();
7176 dispatch_sync(activePortPopoversQueue, ^{
7177 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7178 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7180 string portID = i->first;
7181 bool shouldDisable = false;
7184 shouldDisable = true;
7187 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7188 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7203 dispatch_sync(activePortPopoversQueue, ^{
7204 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7205 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7207 string portID = i->first;
7209 bool foundPort = identifierCache->doForPortWithIdentifier(portID, [&foundPort](VuoPort *port) {});
7222 if (recordWhichPopoversClosed)
7223 portsWithPopoversClosedAtLastEvent.clear();
7225 dispatch_sync(activePortPopoversQueue, ^{
7226 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7227 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7229 string portID = i->first;
7230 bool shouldDisable = false;
7233 shouldDisable = true;
7236 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7237 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7247 portsWithPopoversClosedAtLastEvent.insert(portID);
7259 moveDetachedPortPopoversBy(dx, dy);
7260 moveErrorPopoversBy(dx, dy);
7266 void VuoEditorComposition::moveErrorPopoversBy(
int dx,
int dy)
7269 errorPopover->move(errorPopover->pos().x()+dx, errorPopover->pos().y()+dy);
7275 void VuoEditorComposition::moveDetachedPortPopoversBy(
int dx,
int dy)
7277 dispatch_sync(activePortPopoversQueue, ^{
7278 map<string, VuoPortPopover *> portPopovers = activePortPopovers;
7279 for (map<string, VuoPortPopover *>::iterator i = portPopovers.begin(); i != portPopovers.end(); ++i)
7281 VuoPortPopover *popover = i->second;
7282 if (popover && popover->getDetached())
7283 popover->move(popover->pos().x()+dx, popover->pos().y()+dy);
7291 void VuoEditorComposition::setPopoversHideOnDeactivate(
bool shouldHide)
7293 dispatch_sync(activePortPopoversQueue, ^{
7294 auto portPopovers = activePortPopovers;
7295 for (
auto i : portPopovers)
7300 id nsWindow = (id)VuoPopover::getWindowForPopover(popover);
7301 objc_msgSend(nsWindow, sel_getUid(
"setHidesOnDeactivate:"), shouldHide);
7313 dispatch_sync(activePortPopoversQueue, ^{
7314 for (map<string, VuoPortPopover *>::iterator i = activePortPopovers.begin(); i != activePortPopovers.end(); ++i)
7316 string portID = i->first;
7317 VuoPortPopover *popover = i->second;
7318 bool shouldUpdate = false;
7321 shouldUpdate = true;
7324 identifierCache->doForPortWithIdentifier(portID, [&shouldUpdate, node](VuoPort *port) {
7325 shouldUpdate = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7330 QMetaObject::invokeMethod(popover,
"updateTextAndResize", Qt::QueuedConnection);
7347 string popoverCompositionIdentifier,
7358 string portSummary = (isInput ?
7362 dispatch_async(popoverComposition->activePortPopoversQueue, ^{
7363 VuoPortPopover *popover = popoverComposition->getActivePopoverForPort(portID);
7366 QMetaObject::invokeMethod(popover,
"updateDataValueImmediately", Qt::QueuedConnection, Q_ARG(QString, portSummary.c_str()));
7367 QMetaObject::invokeMethod(popover,
"setCompositionRunning", Qt::QueuedConnection, Q_ARG(bool, true), Q_ARG(bool, false));
7381 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7383 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7384 if (topLevelComposition->isRunningThreadUnsafe())
7385 topLevelComposition->updateDataInPortPopoverFromRunningTopLevelComposition(this, thisCompositionIdentifier, portID);
7395 bool receivedEvent,
bool receivedData,
string dataSummary)
7399 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7400 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7402 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7403 Q_ARG(bool, receivedEvent),
7404 Q_ARG(bool, receivedData),
7405 Q_ARG(QString, dataSummary.c_str()));
7408 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7416 bool sentEvent,
bool sentData,
string dataSummary)
7420 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7421 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7423 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7424 Q_ARG(bool, sentEvent),
7425 Q_ARG(bool, sentData),
7426 Q_ARG(QString, dataSummary.c_str()));
7429 if (matchingComposition->showEventsMode && sentEvent)
7434 port->getRenderer()->setFiredEvent();
7435 matchingComposition->animatePort(port->getRenderer());
7440 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7451 dispatch_async(matchingComposition->runCompositionQueue, ^{
7452 if (matchingComposition->isRunningThreadUnsafe())
7454 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7455 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7457 QMetaObject::invokeMethod(popover,
"incrementDroppedEventCount", Qt::QueuedConnection);
7462 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7473 if (matchingComposition->showEventsMode)
7475 dispatch_async(this->runCompositionQueue, ^{
7476 if (this->isRunningThreadUnsafe())
7485 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7496 if (matchingComposition->showEventsMode)
7498 dispatch_async(this->runCompositionQueue, ^{
7499 if (this->isRunningThreadUnsafe())
7508 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7537 return showEventsMode;
7545 this->showEventsMode = showEventsMode;
7553 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7554 if (topLevelComposition->isRunningThreadUnsafe())
7555 topLevelComposition->runner->subscribeToEventTelemetry(thisCompositionIdentifier);
7558 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, subscribe);
7566 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7567 if (topLevelComposition->isRunningThreadUnsafe())
7568 topLevelComposition->runner->unsubscribeFromEventTelemetry(thisCompositionIdentifier);
7571 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, unsubscribe);
7607 QGraphicsItemAnimation * VuoEditorComposition::setUpAnimationForPort(QGraphicsItemAnimation *animation,
VuoRendererPort *port)
7615 animatedPort->setZValue(VuoRendererItem::triggerAnimationZValue);
7618 animation->setItem(animatedPort);
7619 animation->setScaleAt(0.0, 1, 1);
7620 animation->setScaleAt(0.999, 3, 3);
7621 animation->setScaleAt(1.0, 1, 1);
7623 QTimeLine *animationTimeline = animation->timeLine();
7624 animationTimeline->setFrameRange(0, 100);
7625 animationTimeline->setUpdateInterval(showEventsModeUpdateInterval);
7626 animationTimeline->setCurveShape(QTimeLine::LinearCurve);
7628 preparedAnimations.insert(animation);
7629 animationForTimeline[animation->timeLine()] = animation;
7631 connect(animationTimeline, &QTimeLine::valueChanged,
this, &VuoEditorComposition::updatePortAnimation);
7632 connect(animationTimeline, &QTimeLine::finished,
this, &VuoEditorComposition::endPortAnimation);
7642 dispatch_async(dispatch_get_main_queue(), ^{
7643 QGraphicsItemAnimation *animation = getAvailableAnimationForPort(port);
7649 if (animation->timeLine()->state() == QTimeLine::Running)
7650 animation->timeLine()->setCurrentTime(0);
7654 animatedPort->setPos(port->pos());
7655 animatedPort->setVisible(
true);
7656 animation->timeLine()->start();
7665 QGraphicsItemAnimation * VuoEditorComposition::getAvailableAnimationForPort(
VuoRendererPort *port)
7667 vector<QGraphicsItemAnimation *> animations = port->
getAnimations();
7669 QGraphicsItemAnimation *mostAdvancedAnimation = NULL;
7670 qreal maxPercentAdvanced = -1;
7672 for (
int i = 0; i < animations.size(); ++i)
7674 QGraphicsItemAnimation *animation = animations[i];
7675 bool animationPrepared = (preparedAnimations.find(animation) != preparedAnimations.end());
7676 bool animationRunning = (animation->timeLine()->state() == QTimeLine::Running);
7678 if (! animationPrepared)
7679 return setUpAnimationForPort(animation, port);
7681 else if (! animationRunning)
7686 qreal percentAdvanced = animation->timeLine()->currentValue();
7687 if (percentAdvanced > maxPercentAdvanced)
7689 mostAdvancedAnimation = animation;
7690 maxPercentAdvanced = percentAdvanced;
7696 return (maxPercentAdvanced >= 0.5? mostAdvancedAnimation : NULL);
7704 void VuoEditorComposition::updatePortAnimation(qreal value)
7706 QTimeLine *animationTimeline = (QTimeLine *)sender();
7707 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7709 const qreal multiplier = 1000.;
7717 void VuoEditorComposition::endPortAnimation(
void)
7719 QTimeLine *animationTimeline = (QTimeLine *)sender();
7720 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7722 animatedPort->setVisible(
false);
7728 void VuoEditorComposition::setDisableDragStickiness(
bool disable)
7730 this->dragStickinessDisabled = disable;
7738 this->ignoreApplicationStateChangeEvents = ignore;
7748 this->popoverEventsEnabled = enable;
7757 refreshComponentAlphaLevelTimer->start();
7765 refreshComponentAlphaLevelTimer->stop();
7788 if (!activeProtocol)
7795 if (!
getBase()->
getCompiler()->getCachedGraph()->mayEventsReachPublishedOutputPorts())
7797 QString errorHeadline = tr(
"<b>This composition doesn't send any images to <code>outputImage</code>.</b>");
7798 QString errorDetails = tr(
"<p>To export, your composition should use the data and events from the published input ports "
7799 "to output a stream of images through the <code>outputImage</code> published output port.</p>");
7801 if (isExportingMovie)
7802 errorDetails.append(
"<p>Alternatively, you can record a realtime movie by running the composition and selecting File > Start Recording.</p>");
7805 QMessageBox messageBox(window);
7806 messageBox.setWindowFlags(Qt::Sheet);
7807 messageBox.setWindowModality(Qt::WindowModal);
7809 messageBox.setTextFormat(Qt::RichText);
7811 messageBox.setStandardButtons(QMessageBox::Help | QMessageBox::Ok);
7812 messageBox.setButtonText(QMessageBox::Help, tr(
"Open an Example"));
7813 messageBox.setButtonText(QMessageBox::Ok, tr(
"OK"));
7814 messageBox.setDefaultButton(QMessageBox::Ok);
7816 messageBox.setText(errorHeadline);
7817 messageBox.setInformativeText(
"<style>p{" + fonts->
getCSS(fonts->
dialogBodyFont()) +
"}</style>" + errorDetails);
7819 if (messageBox.exec() == QMessageBox::Help)
7821 map<QString, QString> examples =
static_cast<VuoEditor *
>(qApp)->getExampleCompositionsForProtocol(activeProtocol);
7822 map<QString, QString>::iterator i = examples.begin();
7823 if (i != examples.end())
7853 string dir, file, ext;
7867 if (! customizedName.empty())
7868 return QString::fromStdString(customizedName);
7886 string fileNameContentPart = (fileNameParts.size() >= 2 && fileNameParts[fileNameParts.size()-1] ==
"vuo"?
7887 fileNameParts[fileNameParts.size()-2] :
7888 (fileNameParts.size() >= 1? fileNameParts[fileNameParts.size()-1] :
""));
7891 if (QRegExp(
"\\s").indexIn(fileNameContentPart.c_str()) != -1)
7893 string formattedName = fileNameContentPart;
7894 if (formattedName.size() >= 1)
7895 formattedName[0] = toupper(formattedName[0]);
7897 return QString(formattedName.c_str());
7911 QStringList wordsInName = nodeSetName.split(QRegularExpression(
"\\."));
7912 if (wordsInName.size() < 2 || wordsInName[0] !=
"vuo")
7918 map<QString, QString> wordsToReformat;
7919 wordsToReformat[
"artnet"] =
"Art-Net";
7920 wordsToReformat[
"bcf2000"] =
"BCF2000";
7921 wordsToReformat[
"hid"] =
"HID";
7922 wordsToReformat[
"midi"] =
"MIDI";
7923 wordsToReformat[
"ndi"] =
"NDI";
7924 wordsToReformat[
"osc"] =
"OSC";
7925 wordsToReformat[
"rss"] =
"RSS";
7926 wordsToReformat[
"ui"] =
"UI";
7927 wordsToReformat[
"url"] =
"URL";
7929 QString nodeSetDisplayName =
"";
7930 for (
int i = 1; i < wordsInName.size(); ++i)
7932 QString currentWord = wordsInName[i];
7933 if (currentWord.size() >= 1)
7935 if (wordsToReformat.find(currentWord.toLower()) != wordsToReformat.end())
7936 currentWord = wordsToReformat.at(currentWord.toLower());
7938 currentWord[0] = currentWord[0].toUpper();
7940 nodeSetDisplayName += currentWord;
7942 if (i < wordsInName.size()-1)
7943 nodeSetDisplayName +=
" ";
7946 return nodeSetDisplayName;
7959 string formattedTypeName =
"";
7967 formattedTypeName =
"List of " + formattedInnerTypeName +
" elements";
7973 return formattedTypeName.c_str();
7992 return "Transform2D";
7994 return "Transform3D";
8002 bool VuoEditorComposition::nodeSetMenuActionLessThan(QAction *action1, QAction *action2)
8004 QString item1Text = action1->text();
8005 QString item2Text = action2->text();
8008 const QString listPrefix =
"List of ";
8009 const QString builtInTypePrefix =
"Vuo";
8011 if (item1Text.startsWith(listPrefix))
8013 item1Text.remove(0, listPrefix.length());
8014 if (item1Text.startsWith(builtInTypePrefix))
8015 item1Text.remove(0, builtInTypePrefix.length());
8018 if (item2Text.startsWith(listPrefix))
8020 item2Text.remove(0, listPrefix.length());
8021 if (item2Text.startsWith(builtInTypePrefix))
8022 item2Text.remove(0, builtInTypePrefix.length());
8026 return (item1Text.compare(item2Text, Qt::CaseInsensitive) < 0);
8033 bool VuoEditorComposition::itemHigherOnCanvas(QGraphicsItem *item1, QGraphicsItem *item2)
8035 qreal item1Y = item1->scenePos().y();
8036 qreal item2Y = item2->scenePos().y();
8038 qreal item1X = item1->scenePos().x();
8039 qreal item2X = item2->scenePos().x();
8041 if (item1Y == item2Y)
8042 return (item1X < item2X);
8044 return item1Y < item2Y;
8057 string originalGenericNode1ClassName, originalGenericNode2ClassName;
8073 vector<string> node1Keywords = node1->
getKeywords();
8074 vector<string> node2Keywords = node2->
getKeywords();
8086 node1Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8090 node2Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8092 set<string> node1KeywordSet(node1Keywords.begin(), node1Keywords.end());
8093 set<string> node2KeywordSet(node2Keywords.begin(), node2Keywords.end());
8095 set<string> nodeKeywordsIntersection;
8096 std::set_intersection(node1KeywordSet.begin(), node1KeywordSet.end(),
8097 node2KeywordSet.begin(), node2KeywordSet.end(),
8098 std::inserter(nodeKeywordsIntersection, nodeKeywordsIntersection.end()));
8100 set<string> nodeKeywordsUnion = node1KeywordSet;
8101 nodeKeywordsUnion.insert(node2KeywordSet.begin(), node2KeywordSet.end());
8104 if (nodeKeywordsUnion.size() == 0)
8108 double nodeSimilarity = nodeKeywordsIntersection.size()/(1.0*nodeKeywordsUnion.size());
8110 return nodeSimilarity;
8129 VuoEditorComposition::~VuoEditorComposition()
8131 dispatch_sync(runCompositionQueue, ^{});
8132 dispatch_release(runCompositionQueue);
8134 preparedAnimations.clear();
8135 animationForTimeline.clear();
8137 delete identifierCache;
8163 vector<VuoRendererPort *> sortedPortsToPublish;
8164 foreach (
string portID, portsToPublish)
8168 sortedPortsToPublish.push_back(port->
getRenderer());
8170 std::sort(sortedPortsToPublish.begin(), sortedPortsToPublish.end(), itemHigherOnCanvas);
8172 map<string, string> publishedPortNames;
8179 string publishedPortName = (!specializedPublishedPortName.empty()?
8180 specializedPublishedPortName :
8189 return publishedPortNames;
8217 void VuoEditorComposition::repositionPopover()
8222 const int cutoffMargin = 16;
8223 if (popover->pos().x()+popover->size().width()+cutoffMargin > views()[0]->viewport()->rect().right())
8224 popover->move(QPoint(views()[0]->viewport()->rect().right()-popover->size().width()-cutoffMargin, popover->pos().y()));
8226 if (popover->pos().y()+popover->size().height()+cutoffMargin > views()[0]->viewport()->rect().bottom())
8227 popover->move(QPoint(popover->pos().x(), views()[0]->viewport()->rect().bottom()-popover->size().height()-cutoffMargin));