56 #include <ApplicationServices/ApplicationServices.h>
57 #include <objc/objc-runtime.h>
60 const qreal VuoEditorComposition::nodeMoveRate = 15;
61 const qreal VuoEditorComposition::nodeMoveRateMultiplier = 4;
63 const qreal VuoEditorComposition::showEventsModeUpdateInterval = 1000/20.;
64 const int VuoEditorComposition::initialChangeNodeSuggestionCount = 10;
76 VuoEditorComposition_Pro();
79 this->window = window;
81 inputEditorManager = NULL;
82 activeProtocol = NULL;
84 runningComposition = NULL;
85 runningCompositionActiveDriver = NULL;
86 runningCompositionLibraries = NULL;
87 stopRequested =
false;
88 duplicateOnNextMouseMove =
false;
89 duplicationDragInProgress =
false;
90 duplicationCancelled =
false;
91 cursorPosBeforeDuplicationDragMove = QPointF(0,0);
92 cableInProgress = NULL;
93 cableInProgressWasNew =
false;
94 cableInProgressShouldBeWireless =
false;
95 portWithDragInitiated = NULL;
96 cableWithYankInitiated = NULL;
97 menuSelectionInProgress =
false;
98 previousNearbyItem = NULL;
99 dragStickinessDisabled =
false;
100 ignoreApplicationStateChangeEvents =
false;
101 popoverEventsEnabled =
true;
102 runCompositionQueue = dispatch_queue_create(
"org.vuo.editor.run", NULL);
103 activePortPopoversQueue = dispatch_queue_create(
"org.vuo.editor.popovers", NULL);
105 errorMarkingUpdatesEnabled =
true;
106 triggerPortToRefire =
"";
108 contextMenuDeleteSelected =
new QAction(NULL);
109 contextMenuHideSelectedCables =
new QAction(NULL);
110 contextMenuRenameSelected =
new QAction(NULL);
111 contextMenuRefactorSelected =
new QAction(NULL);
112 contextMenuPublishPort =
new QAction(NULL);
113 contextMenuDeleteCables =
new QAction(NULL);
114 contextMenuHideCables =
new QAction(NULL);
115 contextMenuUnhideCables =
new QAction(NULL);
116 contextMenuFireEvent =
new QAction(NULL);
117 contextMenuAddInputPort =
new QAction(NULL);
118 contextMenuRemoveInputPort =
new QAction(NULL);
119 contextMenuSetPortConstant =
new QAction(NULL);
120 contextMenuEditSelectedComments =
new QAction(NULL);
122 contextMenuChangeNode = NULL;
124 contextMenuFireEvent->setText(tr(
"Fire Event"));
125 contextMenuHideSelectedCables->setText(tr(
"Hide"));
126 contextMenuRenameSelected->setText(tr(
"Rename…"));
127 contextMenuRefactorSelected->setText(tr(
"Package as Subcomposition"));
128 contextMenuAddInputPort->setText(tr(
"Add Input Port"));
129 contextMenuRemoveInputPort->setText(tr(
"Remove Input Port"));
130 contextMenuSetPortConstant->setText(tr(
"Edit Value…"));
131 contextMenuEditSelectedComments->setText(tr(
"Edit…"));
138 connect(contextMenuDeleteCables, &QAction::triggered,
this, &VuoEditorComposition::deleteConnectedCables);
139 connect(contextMenuHideCables, &QAction::triggered,
this, &VuoEditorComposition::hideConnectedCables);
140 connect(contextMenuUnhideCables, &QAction::triggered,
this, &VuoEditorComposition::unhideConnectedCables);
141 connect(contextMenuFireEvent, &QAction::triggered,
this,
static_cast<void (
VuoEditorComposition::*)()
>(&VuoEditorComposition::fireTriggerPortEvent));
142 connect(contextMenuAddInputPort, &QAction::triggered,
this, &VuoEditorComposition::addInputPort);
143 connect(contextMenuRemoveInputPort, &QAction::triggered,
this, &VuoEditorComposition::removeInputPort);
144 connect(contextMenuEditSelectedComments, &QAction::triggered,
this, &VuoEditorComposition::editSelectedComments);
149 connect(contextMenuSetPortConstant, &QAction::triggered,
this, &VuoEditorComposition::setPortConstant, Qt::QueuedConnection);
156 QAction *action =
new QAction(label,
this);
157 connect(action, &QAction::triggered, [=](){
160 contextMenuThrottlingActions.append(action);
171 QAction *action =
new QAction(label,
this);
172 connect(action, &QAction::triggered, [=](){
178 QColor fill(0,0,0,0);
180 if (tint != VuoNode::TintNone)
190 p.drawEllipse(3, 3, 10, 10);
192 action->setIcon(*icon);
196 contextMenuTintActions.append(action);
198 addTintAction(tr(
"Yellow"), VuoNode::TintYellow);
199 addTintAction(tr(
"Tangerine"), VuoNode::TintTangerine);
200 addTintAction(tr(
"Orange"), VuoNode::TintOrange);
201 addTintAction(tr(
"Magenta"), VuoNode::TintMagenta);
202 addTintAction(tr(
"Violet"), VuoNode::TintViolet);
203 addTintAction(tr(
"Blue"), VuoNode::TintBlue);
204 addTintAction(tr(
"Cyan"), VuoNode::TintCyan);
205 addTintAction(tr(
"Green"), VuoNode::TintGreen);
206 addTintAction(tr(
"Lime"), VuoNode::TintLime);
207 addTintAction(tr(
"None"), VuoNode::TintNone);
211 this->refreshComponentAlphaLevelTimer =
new QTimer(
this);
212 this->refreshComponentAlphaLevelTimer->setObjectName(
"VuoEditorComposition::refreshComponentAlphaLevelTimer");
213 refreshComponentAlphaLevelTimer->setInterval(showEventsModeUpdateInterval);
215 setShowEventsMode(
false);
231 connect(
static_cast<VuoEditor *
>(qApp), &VuoEditor::focusChanged,
this, &VuoEditorComposition::updatePopoversForActiveWindowChange, Qt::QueuedConnection);
233 setPopoversHideOnDeactivate(
true);
236 setPopoversHideOnDeactivate(
false);
248 this->compiler = compiler;
264 this->moduleManager = moduleManager;
273 return moduleManager;
283 this->inputEditorManager = inputEditorManager;
293 return this->inputEditorManager;
310 setCustomConstantsForNewNode(rn);
327 __block
bool isAllowed =
true;
330 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
332 bool nodeIsThisComposition = (compositionModuleKey == nodeClass->
getBase()->
getClassName());
335 auto iter = std::find_if(dependencies.begin(), dependencies.end(), [=](
const string &d){ return d == compositionModuleKey; });
336 bool nodeContainsThisComposition = (iter != dependencies.end());
338 isAllowed = ! (nodeIsThisComposition || nodeContainsThisComposition);
347 compiler->
createNode(nodeClass, title, x, y));
365 vector<string> inputPortClassNames;
366 vector<string> outputPortClassNames;
371 inputPortClassNames.push_back(portClass->
getName());
374 outputPortClassNames.push_back(portClass->
getName());
378 dummyNodeClass->
newNode(modelNode) :
387 void VuoEditorComposition::setCustomConstantsForNewNode(
VuoRendererNode *newNode)
395 QString currentYear = QString::number(QDateTime::currentDateTime().date().year());
419 disablePortPopovers(rn);
444 map<VuoCable *, VuoPort *> cablesToTransferFromPort;
445 map<VuoCable *, VuoPort *> cablesToTransferToPort;
446 set<VuoCable *> cablesToRemove;
450 vector<VuoRendererInputDrawer *> attachedDrawers;
451 vector<VuoRendererNode *> collapsedTypecasts;
453 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
459 collapsedTypecasts.push_back(typecastNode);
467 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
472 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
477 attachedDrawers.push_back(attachedDrawer);
484 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferFromPort.begin(); i != cablesToTransferFromPort.end(); ++i)
485 i->first->getRenderer()->setFrom(newNode, i->second);
486 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferToPort.begin(); i != cablesToTransferToPort.end(); ++i)
487 i->first->getRenderer()->setTo(newNode, i->second);
488 foreach (
VuoCable *cable, cablesToRemove)
505 if (! (oldDataType == newDataType && oldDataType && !
dynamic_cast<VuoGenericType *
>(oldDataType)) )
509 string oldConstantValue;
511 oldConstantValue = oldInputPort->getRenderer()->getConstantAsString();
513 oldConstantValue = oldInputPort->getRawInitialValue();
545 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
558 disablePortPopovers(oldNode);
586 if (cableHidden && emitHiddenCableNotification)
598 if (cableHidden && emitHiddenCableNotification)
624 set<VuoRendererNode *> &createdNodes,
625 set<VuoRendererCable *> &createdCables)
639 foreach (QGraphicsItem *component, addedComponents)
642 if (rn && !createButDoNotAdd)
646 return addedComponents;
659 set<string> selectedNodeIDs;
666 set<string> selectedCommentIDs;
673 set<string> selectedCableIDs;
683 foreach (QGraphicsItem *item, items())
690 if (!currentNodeID.empty() && (selectedNodeIDs.find(currentNodeID) != selectedNodeIDs.end()))
691 item->setSelected(
true);
699 if (!currentCommentID.empty() && (selectedCommentIDs.find(currentCommentID) != selectedCommentIDs.end()))
700 item->setSelected(
true);
708 if (!currentCableID.empty() && (selectedCableIDs.find(currentCableID) != selectedCableIDs.end()))
709 item->setSelected(
true);
732 if ((portName ==
"expression") &&
752 if (commandDescription.empty())
755 commandDescription =
"Reset";
757 commandDescription =
"Delete";
760 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
769 if (commandDescription.empty())
770 commandDescription =
"Delete";
772 QList<QGraphicsItem *> selectedNodes;
774 selectedNodes.append(node);
814 void VuoEditorComposition::insertNode()
816 QAction *sender = (QAction *)QObject::sender();
817 QPair<QPointF, QString> pair = sender->data().value<QPair<QPointF, QString> >();
819 QList<QGraphicsItem *> newNodes;
826 newNodes.append(newNode);
834 void VuoEditorComposition::insertComment()
836 QAction *sender = (QAction *)QObject::sender();
837 QPointF scenePos = sender->data().value<QPointF>();
845 void VuoEditorComposition::insertSubcomposition()
847 QAction *sender = (QAction *)QObject::sender();
848 QPointF scenePos = sender->data().value<QPointF>();
859 QAction *sender = (QAction *)QObject::sender();
874 void VuoEditorComposition::deleteConnectedCables()
876 QAction *sender = (QAction *)QObject::sender();
879 QList<QGraphicsItem *> cablesToRemove;
910 void VuoEditorComposition::hideConnectedCables()
912 QAction *sender = (QAction *)QObject::sender();
914 set<VuoRendererCable *> cablesToHide;
945 void VuoEditorComposition::unhideConnectedCables()
947 QAction *sender = (QAction *)QObject::sender();
949 set<VuoRendererCable *> cablesToUnhide;
978 void VuoEditorComposition::fireTriggerPortEvent()
980 QAction *sender = (QAction *)QObject::sender();
982 fireTriggerPortEvent(port->
getBase());
998 if (triggerPortToRefire.empty())
1001 VuoPort *triggerPort =
nullptr;
1014 if (portID != this->triggerPortToRefire)
1016 this->triggerPortToRefire = portID;
1025 void VuoEditorComposition::setPortConstant()
1027 QAction *sender = (QAction *)QObject::sender();
1038 void VuoEditorComposition::setPortConstantToValue(
VuoRendererPort *port,
string value)
1048 void VuoEditorComposition::specializeGenericPortType()
1050 QAction *sender = (QAction *)QObject::sender();
1051 QList<QVariant> portAndSpecializedType= sender->data().toList();
1053 QString specializedTypeName = portAndSpecializedType[1].toString();
1065 emit
specializePort(port, specializedTypeName.toUtf8().constData());
1072 void VuoEditorComposition::unspecializePortType()
1074 QAction *sender = (QAction *)QObject::sender();
1098 portToUnspecialize,
true);
1099 map<VuoNode *, set<VuoPort *> > portsToUnspecializeForNode;
1100 for (
VuoPort *connectedPort : connectedPotentiallyGenericPorts)
1106 if (isPortCurrentlyRevertible(connectedPort->getRenderer()))
1107 portsToUnspecializeForNode[node].insert(connectedPort);
1110 for (map<
VuoNode *, set<VuoPort *> >::iterator i = portsToUnspecializeForNode.begin(); i != portsToUnspecializeForNode.end(); ++i)
1113 set<VuoPort *> ports = i->second;
1115 if (shouldOutputNodesToReplace)
1118 set<VuoPortClass *> portClasses;
1119 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1120 portClasses.insert((*j)->getClass());
1123 nodesToReplace[node] = unspecializedNodeClassName;
1128 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1133 bool areEndsCompatible =
false;
1136 areEndsCompatible =
true;
1141 if (portOnOtherEnd && isPortCurrentlyRevertible(portOnOtherEnd->
getRenderer()))
1146 if (specializedNodeClassOnOtherEnd)
1149 if (! typeOnOtherEnd ||
dynamic_cast<VuoGenericType *
>(typeOnOtherEnd))
1150 areEndsCompatible =
true;
1155 if (! areEndsCompatible && (cable != cableInProgress))
1156 cablesToDelete.insert(cable);
1165 void VuoEditorComposition::fireTriggerPortEvent(
VuoPort *port)
1173 string runningTriggerPortIdentifier =
"";
1174 bool isTriggerPort =
false;
1182 isTriggerPort =
true;
1200 runningTriggerPortIdentifier.c_str());
1203 bool manuallyFirableInputPortChanged = (oldManuallyFirableInputPort != newManuallyFirableInputPort);
1206 auto fireIfRunning = ^void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
1208 dispatch_async(topLevelComposition->runCompositionQueue, ^{
1209 if (topLevelComposition->isRunningThreadUnsafe())
1211 topLevelComposition->runner->fireTriggerPortEvent(thisCompositionIdentifier, runningTriggerPortIdentifier);
1216 if (! (this->showEventsMode && isTriggerPort) )
1217 this->animatePort(port->getRenderer());
1222 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1223 if (
this == topLevelComposition || ! manuallyFirableInputPortChanged)
1227 if (! newSnapshot.empty() && manuallyFirableInputPortChanged)
1228 updateRunningComposition(oldSnapshot, newSnapshot);
1230 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1236 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
1238 moduleManager->doNextTimeNodeClassIsLoaded(nodeClassName, ^{
1239 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1240 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1244 if (! newSnapshot.empty())
1245 updateRunningComposition(oldSnapshot, newSnapshot);
1250 setTriggerPortToRefire(port);
1257 void VuoEditorComposition::addInputPort()
1259 QAction *sender = (QAction *)QObject::sender();
1268 void VuoEditorComposition::removeInputPort()
1270 QAction *sender = (QAction *)QObject::sender();
1279 void VuoEditorComposition::swapNode()
1281 QAction *sender = (QAction *)QObject::sender();
1282 QList<QVariant> nodeAndReplacementType= sender->data().toList();
1284 QString newNodeClassName = nodeAndReplacementType[1].toString();
1295 for (set<VuoRendererNode *>::iterator i = selectedNodes.begin(); i != selectedNodes.end(); ++i)
1305 void VuoEditorComposition::editSelectedComments()
1309 for (set<VuoRendererComment *>::iterator i = selectedComments.begin(); i != selectedComments.end(); ++i)
1318 set<VuoRendererCable *> internalCables;
1320 for (QList<QGraphicsItem *>::iterator i = subcompositionComponents.begin(); i != subcompositionComponents.end(); ++i)
1322 QGraphicsItem *compositionComponent = *i;
1327 for (set<VuoCable *>::iterator cable = connectedCables.begin(); cable != connectedCables.end(); ++cable)
1329 VuoNode *fromNode = (*cable)->getFromNode();
1330 VuoNode *toNode = (*cable)->getToNode();
1332 if (fromNode && toNode && subcompositionComponents.contains(fromNode->
getRenderer()) && subcompositionComponents.contains(toNode->
getRenderer()))
1333 internalCables.insert((*cable)->getRenderer());
1338 return internalCables;
1346 return cableInProgress;
1355 return cableInProgressWasNew;
1363 return menuSelectionInProgress;
1371 QList<QGraphicsItem *> compositionComponents = items();
1372 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1374 QGraphicsItem *compositionComponent = *i;
1378 compositionComponent->setSelected(
true);
1387 QList<QGraphicsItem *> compositionComponents = items();
1388 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1390 QGraphicsItem *compositionComponent = *i;
1393 rcomment->setSelected(
true);
1402 QList<QGraphicsItem *> compositionComponents = items();
1403 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1405 QGraphicsItem *compositionComponent = *i;
1406 compositionComponent->setSelected(
false);
1413 void VuoEditorComposition::openSelectedEditableNodes()
1418 QString actionText, sourcePath;
1431 moveItemsBy(selectedNodes, selectedComments, dx, dy,
false);
1437 void VuoEditorComposition::moveNodesBy(set<VuoRendererNode *> nodes, qreal dx, qreal dy,
bool movedByDragging)
1439 set<VuoRendererComment *> comments;
1440 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1446 void VuoEditorComposition::moveCommentsBy(set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1448 set<VuoRendererNode *> nodes;
1449 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1455 void VuoEditorComposition::moveItemsBy(set<VuoRendererNode *> nodes, set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1457 emit
itemsMoved(nodes, comments, dx, dy, movedByDragging);
1459 for (set<VuoRendererNode *>::iterator it = nodes.begin(); it != nodes.end(); ++it)
1460 (*it)->updateConnectedCableGeometry();
1466 void VuoEditorComposition::resizeCommentBy(
VuoRendererComment *comment, qreal dx, qreal dy)
1476 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1477 set<VuoRendererNode *> selectedNodes;
1478 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1480 QGraphicsItem *compositionComponent = *i;
1483 selectedNodes.insert(rn);
1486 return selectedNodes;
1494 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1495 set<VuoRendererComment *> selectedComments;
1496 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1498 QGraphicsItem *compositionComponent = *i;
1501 selectedComments.insert(rc);
1504 return selectedComments;
1512 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1513 set<VuoRendererCable *> selectedCables;
1514 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1516 QGraphicsItem *compositionComponent = *i;
1519 selectedCables.insert(rc);
1522 return selectedCables;
1530 const QMimeData *mimeData =
event->mimeData();
1531 bool disablePortHoverHighlighting =
true;
1534 if (mimeData->hasFormat(
"text/uri-list"))
1536 QList<QUrl> urls = mimeData->urls();
1540 if (portAtDropLocation)
1542 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1544 disablePortHoverHighlighting =
false;
1546 event->setDropAction(Qt::IgnoreAction);
1550 bool dragIncludesDroppableFile =
false;
1551 foreach (QUrl url, urls)
1553 char *urlZ = strdup(url.path().toUtf8().constData());
1554 bool isSupportedDragNDropFile =
1568 if (isSupportedDragNDropFile)
1570 dragIncludesDroppableFile =
true;
1575 if (!dragIncludesDroppableFile)
1576 event->setDropAction(Qt::IgnoreAction);
1583 else if (mimeData->hasFormat(
"text/plain") || mimeData->hasFormat(
"text/scsv"))
1584 event->acceptProposedAction();
1588 event->setDropAction(Qt::IgnoreAction);
1592 updateHoverHighlighting(event->scenePos(), disablePortHoverHighlighting);
1600 event->acceptProposedAction();
1616 const QMimeData *mimeData =
event->mimeData();
1619 if (mimeData->hasFormat(
"text/uri-list"))
1627 if (topCompositionPath.empty())
1629 QDir compositionDir(QDir(topCompositionPath.c_str()).canonicalPath());
1634 QList<QGraphicsItem *> newNodes;
1635 QList<QUrl> urls = mimeData->urls();
1639 if (portAtDropLocation)
1641 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1644 QString filePath = (useAbsoluteFilePaths? urls[0].path() : compositionDir.relativeFilePath(urls[0].path()));
1645 string constantValue =
"\"" + string(filePath.toUtf8().constData()) +
"\"";
1656 const int ySpacingForNewNodes = 11;
1657 int yOffsetForPreviousNewNode = -1 * ySpacingForNewNodes;
1659 foreach (QUrl url, urls)
1661 QStringList targetNodeClassNames;
1662 char *urlZ = strdup(url.path().toUtf8().constData());
1665 targetNodeClassNames +=
"vuo.image.fetch";
1669 targetNodeClassNames +=
"vuo.video.play";
1671 targetNodeClassNames +=
"vuo.video.decodeImage";
1674 targetNodeClassNames +=
"vuo.scene.fetch";
1676 targetNodeClassNames +=
"vuo.audio.file.play";
1678 targetNodeClassNames +=
"vuo.image.project.dome";
1680 targetNodeClassNames +=
"vuo.rss.fetch";
1682 targetNodeClassNames +=
"vuo.tree.fetch.json";
1684 targetNodeClassNames +=
"vuo.tree.fetch.xml";
1686 targetNodeClassNames +=
"vuo.table.fetch";
1688 targetNodeClassNames +=
"vuo.data.fetch";
1690 targetNodeClassNames +=
"vuo.app.launch";
1692 targetNodeClassNames +=
"vuo.file.list";
1696 QString selectedNodeClassName =
"";
1697 if (targetNodeClassNames.size() == 1)
1698 selectedNodeClassName = targetNodeClassNames[0];
1699 else if (targetNodeClassNames.size() > 1)
1701 QMenu nodeMenu(views()[0]->viewport());
1702 nodeMenu.setSeparatorsCollapsible(
false);
1704 foreach (QString nodeClassName, targetNodeClassNames)
1707 string nodeTitle = (nodeClass? nodeClass->
getBase()->
getDefaultTitle() : nodeClassName.toUtf8().constData());
1709 QAction *nodeAction = nodeMenu.addAction(tr(
"Insert \"%1\" Node").arg(nodeTitle.c_str()));
1710 nodeAction->setData(nodeClassName);
1713 menuSelectionInProgress =
true;
1714 QAction *selectedNode = nodeMenu.exec(QCursor::pos());
1715 menuSelectionInProgress =
false;
1717 selectedNodeClassName = (selectedNode? selectedNode->data().toString().toUtf8().constData() :
"");
1720 if (!selectedNodeClassName.isEmpty())
1723 event->scenePos().x(),
1724 event->scenePos().y() + yOffsetForPreviousNewNode + ySpacingForNewNodes);
1733 QString filePath = (useAbsoluteFilePaths? url.path() : compositionDir.relativeFilePath(url.path()));
1736 newNodes.append(newNode);
1738 yOffsetForPreviousNewNode += newNode->
boundingRect().height();
1739 yOffsetForPreviousNewNode += ySpacingForNewNodes;
1745 if (newNodes.size() > 0)
1757 else if (mimeData->hasFormat(
"text/scsv"))
1759 event->setDropAction(Qt::CopyAction);
1762 QByteArray scsvData =
event->mimeData()->data(
"text/scsv");
1763 QString scsvText = QString::fromUtf8(scsvData);
1764 QStringList nodeClassNames = scsvText.split(
';');
1769 int snapDelta = nextYPos - startPos.y();
1771 QList<QGraphicsItem *> newNodes;
1772 for (QStringList::iterator i = nodeClassNames.begin(); i != nodeClassNames.end(); ++i)
1780 int prevYPos = nextYPos;
1783 if (nextYPos <= prevYPos+newNode->boundingRect().height())
1786 newNodes.append((QGraphicsItem *)newNode);
1794 else if (mimeData->hasFormat(
"text/plain"))
1796 event->setDropAction(Qt::CopyAction);
1799 QList<QGraphicsItem *> newNodes;
1800 QStringList dropItems =
event->mimeData()->text().split(
'\n');
1801 QString nodeClassName = dropItems[0];
1805 QPoint hotSpot = (dropItems.size() >= 3? QPoint(dropItems[1].toInt(), dropItems[2].toInt()) : QPoint(0,0));
1807 event->scenePos().x()-hotSpot.x()+1,
1810 newNodes.append(newNode);
1827 QGraphicsScene::sendEvent(nearbyItem, event);
1832 QGraphicsScene::mouseDoubleClickEvent(event);
1840 QPointF scenePos =
event->scenePos();
1843 if (event->button() == Qt::LeftButton)
1848 if (duplicationCancelled)
1850 QGraphicsItem *itemClickedDirectly = itemAt(scenePos, views()[0]->transform());
1856 duplicationCancelled =
false;
1857 correctForCancelledDuplication(event);
1868 mousePressEventNonLeftButton(event);
1877 bool eventHandled =
false;
1891 cableYankedDirectly = currentCable;
1899 if ((event->modifiers() & Qt::ControlModifier) && (currentPort->
getInput() || isTriggerPort))
1900 fireTriggerPortEvent(currentPort->
getBase());
1904 portWithDragInitiated = currentPort;
1905 cableWithYankInitiated = cableYankedDirectly;
1909 eventHandled =
true;
1916 duplicateOnNextMouseMove =
true;
1917 duplicationDragInProgress =
true;
1923 QGraphicsScene::mousePressEvent(event);
1924 eventHandled =
true;
1930 if (event->modifiers() & Qt::ControlModifier)
1931 nearbyItem->setSelected(! nearbyItem->isSelected());
1936 nearbyItem->setSelected(
true);
1940 eventHandled =
true;
1945 QGraphicsScene::mousePressEvent(event);
1952 void VuoEditorComposition::mousePressEventNonLeftButton(QGraphicsSceneMouseEvent *event)
1961 if (event->button() == Qt::RightButton)
1963 QGraphicsSceneContextMenuEvent
contextMenuEvent(QEvent::GraphicsSceneContextMenu);
1972 QGraphicsScene::mousePressEvent(event);
1983 void VuoEditorComposition::correctForCancelledDuplication(QGraphicsSceneMouseEvent *event)
1987 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
1988 pressEvent.setScenePos(event->scenePos());
1989 pressEvent.setButton(Qt::LeftButton);
1990 QApplication::sendEvent(
this, &pressEvent);
1992 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
1993 releaseEvent.setScenePos(event->scenePos());
1994 releaseEvent.setButton(Qt::LeftButton);
1995 QApplication::sendEvent(
this, &releaseEvent);
2007 Qt::KeyboardModifiers modifiers =
event->modifiers();
2008 bool optionKeyPressed = (modifiers & Qt::AltModifier);
2009 bool shiftKeyPressed = (modifiers & Qt::ShiftModifier);
2020 bool creatingNewCable =
false;
2021 bool disconnectingExistingCable =
false;
2022 bool duplicatingExistingCable =
false;
2036 fromPort = fixedPort;
2037 fromNode = fixedNode;
2038 creatingNewCable =
true;
2050 creatingNewCable =
true;
2057 if (optionKeyPressed)
2059 duplicatingExistingCable =
true;
2063 disconnectingExistingCable =
true;
2071 if (creatingNewCable)
2084 cableInProgressWasNew =
true;
2085 cableInProgressShouldBeWireless =
false;
2092 highlightEligibleEndpointsForCable(cableInProgress);
2097 else if (disconnectingExistingCable)
2100 if (cableYankedDirectly)
2101 cableInProgress = cableYankedDirectly->
getBase();
2107 cableInProgressWasNew =
false;
2118 highlightEligibleEndpointsForCable(cableInProgress);
2120 cableInProgress->
getRenderer()->setSelected(
true);
2123 else if (duplicatingExistingCable)
2127 if (cableYankedDirectly)
2128 cableToDuplicate = cableYankedDirectly->
getBase();
2145 cableInProgressWasNew =
true;
2146 cableInProgressShouldBeWireless = cableToDuplicate->
hasCompiler() &&
2154 highlightEligibleEndpointsForCable(cableInProgress);
2156 cableInProgress->
getRenderer()->setSelected(
true);
2161 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2174 if (event->button() == Qt::LeftButton)
2176 portWithDragInitiated = NULL;
2177 cableWithYankInitiated = NULL;
2178 duplicateOnNextMouseMove =
false;
2179 duplicationDragInProgress =
false;
2180 dragStickinessDisabled =
false;
2187 bool cableDragEnding = cableInProgress;
2188 if (cableDragEnding)
2189 concludeCableDrag(event);
2201 if ((event->modifiers() == Qt::NoModifier) &&
2202 (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2209 enablePopoverForNode(typecastNode);
2214 enableInactivePopoverForPort(port);
2220 if (!cableDragEnding && !node)
2224 if (!cableDragEnding)
2225 QGraphicsScene::mouseReleaseEvent(event);
2231 QGraphicsScene::mouseReleaseEvent(event);
2241 void VuoEditorComposition::concludeCableDrag(QGraphicsSceneMouseEvent *event)
2249 concludePublishedCableDrag(event);
2253 if (hasFeedbackErrors())
2271 bool completedCableConnection =
false;
2274 VuoCable *dataCableToDisplace = NULL;
2278 string typecastToInsert =
"";
2282 string specializedTypeName =
"";
2299 bool draggingPreviouslyPublishedCable = (!cableInProgressWasNew &&
2301 ((cableInProgress->
getToPort() == NULL) &&
2304 if (fixedPort && targetPort)
2312 if (recreatingSameConnection)
2322 bool preexistingCableWithMatchingDataCarryingStatus = (preexistingCable?
2324 cableInProgressExpectedToCarryData) :
2330 if (preexistingCable && !preexistingCableWithMatchingDataCarryingStatus &&
2341 if (!preexistingCableWithMatchingDataCarryingStatus &&
2343 selectBridgingSolution(fixedPort, targetPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert)))
2354 if (portToSpecialize == targetPort)
2355 portToSpecialize = adjustedTargetPort;
2357 targetPort = adjustedTargetPort;
2361 if (cableInProgressExpectedToCarryData &&
2363 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2364 typecastNodeToDelete = uncollapsedTypecast;
2369 if (preexistingCable)
2370 cableToReplace = preexistingCable;
2374 if (cableInProgressExpectedToCarryData)
2378 for (vector<VuoCable *>::iterator cable = previousConnectedCables.begin(); (! dataCableToDisplace) && (cable != previousConnectedCables.end()); ++cable)
2379 if ((((*cable)->getRenderer()->effectivelyCarriesData()) && (*cable) != cableToReplace))
2380 dataCableToDisplace = *cable;
2387 if (publishedDataConnections.size() > 0)
2388 portToUnpublish = targetPort;
2392 completedCableConnection =
true;
2396 else if (!preexistingCableWithMatchingDataCarryingStatus &&
2398 selectBridgingSolution(targetPort, fixedPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert)))
2401 completedCableConnection =
true;
2406 if (completedCableConnection)
2410 if (draggingPreviouslyPublishedCable)
2429 cableInProgressCopy->
setFrom(cableInProgressFromNode, cableInProgressFromPort);
2430 cableInProgressCopy->
setTo(cableInProgressToNode, cableInProgressToPort);
2437 cableInProgress = cableInProgressCopy;
2438 cableInProgressWasNew =
true;
2442 pair<VuoRendererCable *, VuoRendererCable *> cableArgs = std::make_pair((dataCableToDisplace? dataCableToDisplace->
getRenderer() : NULL),
2443 (cableToReplace? cableToReplace->
getRenderer() : NULL));
2444 pair<string, string> typeArgs = std::make_pair(typecastToInsert, specializedTypeName);
2445 pair<VuoRendererPort *, VuoRendererPort *> portArgs = std::make_pair(portToUnpublish, portToSpecialize);
2450 typecastNodeToDelete,
2457 if (cableInProgress)
2461 cableInProgress = NULL;
2475 void VuoEditorComposition::concludePublishedCableDrag(QGraphicsSceneMouseEvent *event)
2485 string typecastToInsert =
"";
2489 string specializedTypeName =
"";
2506 if (internalInputPort)
2509 forceEventOnlyPublication =
true;
2515 if (internalInputPort &&
2521 internalInputPort->
getBase()) &&
2524 if (recreatingSameConnection)
2532 ||
selectBridgingSolution(publishedInputPort, internalInputPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert))
2537 bool cableToReplaceHasMatchingDataCarryingStatus = (cableToReplace?
2539 cableInProgressExpectedToCarryData) :
2544 if (cableToReplace && cableToReplaceHasMatchingDataCarryingStatus)
2550 else if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus &&
2566 if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus)
2568 QList<QGraphicsItem *> removedComponents;
2569 removedComponents.append(cableToReplace->
getRenderer());
2578 if (typecastPort && typecastPort->scene())
2586 if (cableInProgressExpectedToCarryData &&
2588 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2590 QList<QGraphicsItem *> removedComponents;
2591 removedComponents.append(uncollapsedTypecast);
2598 forceEventOnlyPublication,
2599 (portToSpecialize? portToSpecialize->
getBase() : NULL),
2600 specializedTypeName,
2626 if (internalOutputPort)
2629 forceEventOnlyPublication =
true;
2635 if (internalOutputPort &&
2636 publishedOutputPort)
2642 ||
selectBridgingSolution(internalOutputPort, publishedOutputPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert))
2646 forceEventOnlyPublication,
2647 portToSpecialize? portToSpecialize->
getBase() : NULL,
2648 specializedTypeName,
2667 if (! cableInProgress)
2670 if (cableInProgressWasNew)
2674 QList<QGraphicsItem *> removedComponents;
2675 removedComponents.append(cableInProgress->
getRenderer());
2679 cableInProgress = NULL;
2692 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2697 cableInProgress = NULL;
2699 else if (cableInProgress)
2714 bool leftMouseButtonPressed = (
event->buttons() & Qt::LeftButton);
2730 if (cableInProgress || (! leftMouseButtonPressed))
2731 updateHoverHighlighting(event->scenePos());
2735 if (leftMouseButtonPressed)
2738 if ((! dragStickinessDisabled) && (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2742 dragStickinessDisabled =
true;
2746 if (portWithDragInitiated || cableWithYankInitiated)
2748 initiateCableDrag(portWithDragInitiated, cableWithYankInitiated, event);
2749 portWithDragInitiated = NULL;
2750 cableWithYankInitiated = NULL;
2755 if (cableInProgress)
2765 else if (duplicationDragInProgress)
2767 if (duplicateOnNextMouseMove)
2770 duplicateOnNextMouseMove =
false;
2777 QPointF delta = newPos - cursorPosBeforeDuplicationDragMove;
2779 cursorPosBeforeDuplicationDragMove = newPos;
2783 QGraphicsScene::mouseMoveEvent(event);
2788 QGraphicsScene::mouseMoveEvent(event);
2796 void VuoEditorComposition::updateHoverHighlighting(QPointF scenePos,
bool disablePortHoverHighlighting)
2810 bool hoveringOverNodeHeader =
false;
2811 if (cableInProgress)
2820 hoveringOverNodeHeader =
true;
2826 if (! hoveringOverNodeHeader)
2837 if (item != previousNearbyItem)
2845 if (previousNearbyItem)
2853 if (! cableInProgress)
2859 else if (typecastPort && !disablePortHoverHighlighting)
2861 else if (port && !disablePortHoverHighlighting)
2864 if (!cableInProgress)
2875 if (cableInProgress)
2878 cableInProgress->getFromPort()->getRenderer() :
2879 cableInProgress->getToPort()->getRenderer());
2881 QList< QPair<VuoRendererPort *, bool> > updatedPorts;
2882 if (hoveringOverNodeHeader)
2883 updatedPorts.append( QPair<VuoRendererPort *, bool>(port,
true) );
2884 if (previousNode && previousPort)
2885 updatedPorts.append( QPair<VuoRendererPort *, bool>(previousPort, !cableInProgress->getRenderer()->effectivelyCarriesData()) );
2887 QPair<VuoRendererPort *, bool> p;
2888 foreach (p, updatedPorts)
2893 if (typecastParentPort)
2894 updatedPort = typecastParentPort;
2896 updateEligibilityHighlightingForPort(updatedPort, fixedPort, p.second, types);
2899 updateEligibilityHighlightingForNode(potentialDrawer);
2906 if (targetPort || previousTargetPort)
2908 if (cableInProgress)
2909 cableInProgress->getRenderer()->setFloatingEndpointAboveEventPort(targetPort && (!targetPort->
getDataType() || hoveringOverNodeHeader));
2914 previousNearbyItem = item;
2922 else if (port && !disablePortHoverHighlighting)
2925 if (!cableInProgress)
2931 else if (makeListDrawer)
2941 if (previousNearbyItem)
2959 else if (typecastPort)
2968 previousNearbyItem = NULL;
2977 if ((event->key() != Qt::Key_Alt) && (event->key() != Qt::Key_Shift) && (event->key() != Qt::Key_Escape))
2980 Qt::KeyboardModifiers modifiers =
event->modifiers();
2981 qreal adjustedNodeMoveRate = nodeMoveRate;
2982 if (modifiers & Qt::ShiftModifier)
2984 adjustedNodeMoveRate *= nodeMoveRateMultiplier;
2987 switch (event->key()) {
2988 case Qt::Key_Backspace:
2993 case Qt::Key_Delete:
3005 if (modifiers & Qt::ControlModifier)
3006 openSelectedEditableNodes();
3023 if (cableInProgress)
3025 cableInProgress->getRenderer()->updateGeometry();
3026 cableInProgress->getCompiler()->setAlwaysEventOnly(
true);
3027 highlightEligibleEndpointsForCable(cableInProgress);
3037 case Qt::Key_Return:
3040 QGraphicsScene::keyPressEvent(event);
3042 if (!event->isAccepted())
3049 if (selectedComments.empty() && !selectedNodes.empty())
3056 case Qt::Key_Escape:
3058 if (duplicateOnNextMouseMove || duplicationDragInProgress)
3062 duplicateOnNextMouseMove =
false;
3063 duplicationDragInProgress =
false;
3064 duplicationCancelled =
true;
3066 else if (cableInProgress)
3075 QGraphicsScene::keyPressEvent(event);
3086 switch (event->key()) {
3089 if (cableInProgress)
3091 cableInProgress->getRenderer()->updateGeometry();
3092 cableInProgress->getCompiler()->setAlwaysEventOnly(
false);
3093 highlightEligibleEndpointsForCable(cableInProgress);
3105 QGraphicsScene::keyReleaseEvent(event);
3120 contextMenu.setSeparatorsCollapsible(
false);
3125 QAction *insertNodeSection =
new QAction(tr(
"Insert Node"), NULL);
3126 insertNodeSection->setEnabled(
false);
3127 contextMenu.addAction(insertNodeSection);
3129 QString spacer(
" ");
3133 QMenu *shareMenu =
new QMenu(&contextMenu);
3134 shareMenu->setSeparatorsCollapsible(
false);
3135 shareMenu->setTitle(spacer + tr(
"Share"));
3136 contextMenu.addMenu(shareMenu);
3139 QAction *action =
new QAction(tr(
"Share Value"), NULL);
3140 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share"))));
3141 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3142 shareMenu->addAction(action);
3146 QAction *action =
new QAction(tr(
"Share List"), NULL);
3147 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share.list"))));
3148 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3149 shareMenu->addAction(action);
3153 QAction *action =
new QAction(tr(
"Share Event"), NULL);
3154 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.share"))));
3155 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3156 shareMenu->addAction(action);
3162 QMenu *holdMenu =
new QMenu(&contextMenu);
3163 holdMenu->setSeparatorsCollapsible(
false);
3164 holdMenu->setTitle(spacer + tr(
"Hold"));
3165 contextMenu.addMenu(holdMenu);
3168 QAction *action =
new QAction(tr(
"Hold Value"), NULL);
3169 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold2"))));
3170 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3171 holdMenu->addAction(action);
3175 QAction *action =
new QAction(tr(
"Hold List"), NULL);
3176 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold.list2"))));
3177 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3178 holdMenu->addAction(action);
3184 QMenu *allowMenu =
new QMenu(&contextMenu);
3185 allowMenu->setSeparatorsCollapsible(
false);
3186 allowMenu->setTitle(spacer + tr(
"Allow"));
3187 contextMenu.addMenu(allowMenu);
3190 QAction *action =
new QAction(tr(
"Allow First Event"), NULL);
3191 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirst"))));
3192 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3193 allowMenu->addAction(action);
3197 QAction *action =
new QAction(tr(
"Allow First Value"), NULL);
3198 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirstValue"))));
3199 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3200 allowMenu->addAction(action);
3204 QAction *action =
new QAction(tr(
"Allow Periodic Events"), NULL);
3205 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.time.allowPeriodic"))));
3206 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3207 allowMenu->addAction(action);
3211 QAction *action =
new QAction(tr(
"Allow Changes"), NULL);
3212 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowChanges2"))));
3213 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3214 allowMenu->addAction(action);
3218 contextMenu.addSeparator();
3220 QAction *contextMenuInsertComment =
new QAction(NULL);
3221 contextMenuInsertComment->setText(tr(
"Insert Comment"));
3222 contextMenuInsertComment->setData(qVariantFromValue(event->scenePos()));
3223 connect(contextMenuInsertComment, &QAction::triggered,
this, &VuoEditorComposition::insertComment);
3224 contextMenu.addAction(contextMenuInsertComment);
3226 QAction *contextMenuInsertSubcomposition =
new QAction(NULL);
3227 contextMenuInsertSubcomposition->setText(tr(
"Insert Subcomposition"));
3228 contextMenuInsertSubcomposition->setData(qVariantFromValue(event->scenePos()));
3229 connect(contextMenuInsertSubcomposition, &QAction::triggered,
this, &VuoEditorComposition::insertSubcomposition);
3230 contextMenu.addAction(contextMenuInsertSubcomposition);
3235 QAction *contextMenuDeleteSelectedSnapshot =
new QAction(NULL);
3242 if (port->
isConstant() && inputEditorManager)
3246 if (inputEditorLoadedForPortDataType)
3248 contextMenuSetPortConstant->setData(qVariantFromValue((
void *)port));
3249 contextMenu.addAction(contextMenuSetPortConstant);
3251 inputEditorLoadedForPortDataType->deleteLater();
3257 contextMenuPublishPort->setText(tr(
"Publish Port"));
3258 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3259 contextMenu.addAction(contextMenuPublishPort);
3264 vector<VuoRendererPublishedPort *> externalPublishedPorts = port->
getPublishedPorts();
3265 bool hasExternalPublishedPortWithMultipleInternalPorts =
false;
3266 bool hasExternalPublishedPortBelongingToActiveProtocol =
false;
3270 hasExternalPublishedPortWithMultipleInternalPorts =
true;
3273 hasExternalPublishedPortBelongingToActiveProtocol =
true;
3280 if (!hasExternalPublishedPortWithMultipleInternalPorts &&!hasExternalPublishedPortBelongingToActiveProtocol)
3282 contextMenuPublishPort->setText(tr(
"Delete Published Port"));
3283 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3284 contextMenu.addAction(contextMenuPublishPort);
3289 if (isTriggerPort || port->
getInput())
3291 __block
bool isTopLevelCompositionRunning =
false;
3292 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
3294 isTopLevelCompositionRunning = topLevelComposition->
isRunning();
3297 if (isTopLevelCompositionRunning)
3299 contextMenuFireEvent->setData(qVariantFromValue((
void *)port));
3300 contextMenu.addAction(contextMenuFireEvent);
3306 QMenu *contextMenuThrottling =
new QMenu(&contextMenu);
3307 contextMenuThrottling->setSeparatorsCollapsible(
false);
3308 contextMenuThrottling->setTitle(tr(
"Set Event Throttling"));
3310 foreach (QAction *action, contextMenuThrottlingActions)
3312 contextMenuThrottling->addAction(action);
3313 action->setData(qVariantFromValue(port));
3314 action->setCheckable(
true);
3317 contextMenu.addMenu(contextMenuThrottling);
3322 if (genericDataType || isPortCurrentlyRevertible(port))
3324 QMenu *contextMenuSpecializeGenericType =
new QMenu(&contextMenu);
3325 contextMenuSpecializeGenericType->setSeparatorsCollapsible(
false);
3326 contextMenuSpecializeGenericType->setTitle(tr(
"Set Data Type"));
3327 contextMenuSpecializeGenericType->setToolTipsVisible(
true);
3329 QAction *unspecializeAction = contextMenuSpecializeGenericType->addAction(tr(
"Generic"));
3334 contextMenuSpecializeGenericType->addSeparator();
3336 unspecializeAction->setData(qVariantFromValue((
void *)port));
3337 unspecializeAction->setCheckable(
true);
3338 unspecializeAction->setChecked(genericDataType);
3340 contextMenu.addMenu(contextMenuSpecializeGenericType);
3343 set<string> compatibleTypes;
3344 set<string> compatibleTypesInIsolation;
3347 if (genericDataType)
3355 compatibleTypes = set<string>(compatibleTypesVector.begin(), compatibleTypesVector.end());
3361 compatibleTypes.insert(compatibleTypeNames.begin(), compatibleTypeNames.end());
3366 else if (isPortCurrentlyRevertible(port))
3368 map<VuoNode *, string> nodesToReplace;
3369 set<VuoCable *> cablesToDelete;
3371 if (cablesToDelete.size() >= 1)
3377 port->getUnderlyingParentNode()->getBase()->getNodeClass()->getCompiler());
3379 compatibleTypes = getRespecializationOptionsForPortInNetwork(port);
3383 if (genericTypeFromPortClass)
3395 if (genericHostPortDataType)
3397 else if (isPortCurrentlyRevertible(hostPort->
getRenderer()))
3407 vector<string> compatibleTypesInIsolationVector;
3408 if (genericTypeFromPortClass)
3415 foreach (
string type, compatibleTypesInIsolationVector)
3422 const int maxTypeCountForFlatMenuDisplay = 10;
3423 if (compatibleTypesInIsolation.size() <= maxTypeCountForFlatMenuDisplay)
3425 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
false,
"", contextMenuSpecializeGenericType);
3433 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true,
"", contextMenuSpecializeGenericType);
3438 QList<QAction *> allNodeSetActionsToAdd;
3439 for (map<
string, set<VuoCompilerType *> >::iterator i = loadedTypesForNodeSet.begin(); i != loadedTypesForNodeSet.end(); ++i)
3441 string nodeSetName = i->first;
3442 if (!nodeSetName.empty())
3443 allNodeSetActionsToAdd +=
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true, nodeSetName, contextMenuSpecializeGenericType);
3446 if ((contextMenuSpecializeGenericType->actions().size() > 0) && (allNodeSetActionsToAdd.size() > 0))
3447 contextMenuSpecializeGenericType->addSeparator();
3452 foreach (QAction *action, contextMenuSpecializeGenericType->actions())
3454 QMenu *specializeSubmenu = action->menu();
3455 if (specializeSubmenu)
3457 foreach (QAction *specializeSubaction, specializeSubmenu->actions())
3458 connect(specializeSubaction, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3460 else if (action == unspecializeAction)
3461 connect(action, &QAction::triggered,
this, &VuoEditorComposition::unspecializePortType);
3463 connect(action, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3470 int numVisibleDirectlyConnectedCables = 0;
3471 int numHidableDirectlyConnectedCables = 0;
3472 int numUnhidableDirectlyConnectedCables = 0;
3478 numVisibleDirectlyConnectedCables++;
3480 numHidableDirectlyConnectedCables++;
3484 numUnhidableDirectlyConnectedCables++;
3487 int numVisibleChildPortConnectedCables = 0;
3488 int numHidableChildPortConnectedCables = 0;
3489 int numUnhidableChildPortConnectedCables = 0;
3499 numVisibleChildPortConnectedCables++;
3502 numHidableChildPortConnectedCables++;
3505 numUnhidableChildPortConnectedCables++;
3510 int numVisibleConnectedCables = numVisibleDirectlyConnectedCables + numVisibleChildPortConnectedCables;
3513 int numHidableConnectedCables = numHidableDirectlyConnectedCables + numHidableChildPortConnectedCables;
3516 int numUnhidableConnectedCables = numUnhidableDirectlyConnectedCables + numUnhidableChildPortConnectedCables;
3518 if ((!
renderHiddenCables && ((numHidableConnectedCables >= 1) || (numUnhidableConnectedCables >= 1)))
3519 || (numVisibleConnectedCables >= 1))
3521 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3522 contextMenu.addSeparator();
3527 if (numHidableConnectedCables >= 1)
3532 int numApparentlyHidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3533 contextMenuHideCables->setText(numApparentlyHidableConnectedCables > 1?
"Hide Cables" :
"Hide Cable");
3534 contextMenuHideCables->setData(qVariantFromValue((
void *)port));
3535 contextMenu.addAction(contextMenuHideCables);
3538 if (numUnhidableConnectedCables >= 1)
3540 int numApparentlyUnhidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3541 contextMenuUnhideCables->setText(numApparentlyUnhidableConnectedCables > 1?
"Unhide Cables" :
"Unhide Cable");
3542 contextMenuUnhideCables->setData(qVariantFromValue((
void *)port));
3543 contextMenu.addAction(contextMenuUnhideCables);
3547 if (numVisibleConnectedCables >= 1)
3549 contextMenuDeleteCables->setText(numVisibleConnectedCables > 1?
"Delete Cables" :
"Delete Cable");
3550 contextMenuDeleteCables->setData(qVariantFromValue((
void *)port));
3551 contextMenu.addAction(contextMenuDeleteCables);
3558 if (! item->isSelected())
3561 item->setSelected(
true);
3564 QList<QGraphicsItem *> selectedComponents = selectedItems();
3565 bool onlyCommentsSelected =
true;
3566 bool onlyCablesSelected =
true;
3567 bool selectionContainsMissingNode =
false;
3568 foreach (QGraphicsItem *item, selectedComponents)
3571 onlyCommentsSelected =
false;
3574 onlyCablesSelected =
false;
3577 selectionContainsMissingNode =
true;
3580 contextMenuDeleteSelectedSnapshot->setText(contextMenuDeleteSelected->text());
3588 if (onlyCommentsSelected)
3589 contextMenu.addAction(contextMenuEditSelectedComments);
3594 contextMenu.addSeparator();
3597 contextMenu.addAction(contextMenuRefactorSelected);
3599 contextMenu.addSeparator();
3602 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3612 contextMenu.addAction(contextMenuHideSelectedCables);
3615 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3630 contextMenuAddInputPort->setData(qVariantFromValue((
void *)node));
3631 contextMenuRemoveInputPort->setData(qVariantFromValue((
void *)node));
3634 contextMenuRemoveInputPort->setEnabled(listItemCount >= 1);
3636 contextMenu.addAction(contextMenuAddInputPort);
3637 contextMenu.addAction(contextMenuRemoveInputPort);
3639 contextMenu.addSeparator();
3643 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3654 if (originalGenericNodeClass)
3655 nodeClass = originalGenericNodeClass->
getBase();
3658 QString actionText, sourcePath;
3662 int numSelectedNonAttachmentNodes = 0;
3663 QList<QGraphicsItem *> selectedComponents = selectedItems();
3664 foreach (QGraphicsItem *item, selectedComponents)
3667 numSelectedNonAttachmentNodes++;
3670 if ((numSelectedNonAttachmentNodes == 1) && nodeClassIsEditable)
3673 QAction *editAction =
new QAction(NULL);
3674 editAction->setText(actionText);
3675 editAction->setData(qVariantFromValue(node));
3678 contextMenu.addAction(editAction);
3679 contextMenu.addSeparator();
3684 contextMenu.addAction(contextMenuRenameSelected);
3689 contextMenu.addSeparator();
3692 if (numSelectedNonAttachmentNodes == 1)
3694 if (contextMenuChangeNode)
3695 contextMenuChangeNode->deleteLater();
3698 contextMenuChangeNode->setSeparatorsCollapsible(
false);
3699 contextMenuChangeNode->setTitle(tr(
"Change To"));
3702 if (!contextMenuChangeNode->actions().isEmpty())
3703 contextMenu.addMenu(contextMenuChangeNode);
3707 if (!selectionContainsMissingNode)
3708 contextMenu.addAction(contextMenuRefactorSelected);
3710 if ((numSelectedNonAttachmentNodes == 1) && (nodeClassIsEditable || nodeClassIs3rdParty))
3714 if (!modulePath.isEmpty())
3716 QString enclosingDirUrl =
"file://" + QFileInfo(modulePath).dir().absolutePath();
3717 QAction *openEnclosingFolderAction =
new QAction(NULL);
3718 openEnclosingFolderAction->setText(
"Show in Finder");
3719 openEnclosingFolderAction->setData(qVariantFromValue(enclosingDirUrl));
3721 contextMenu.addAction(openEnclosingFolderAction);
3725 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3726 contextMenu.addSeparator();
3729 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3735 if (!contextMenu.actions().isEmpty())
3740 menuSelectionInProgress =
true;
3741 contextMenu.exec(event->screenPos());
3742 menuSelectionInProgress =
false;
3745 delete contextMenuDeleteSelectedSnapshot;
3753 QAction *sender = (QAction *)QObject::sender();
3765 vector<string> typeOptions;
3767 map<string, VuoCompilerType *> loadedTypes = compiler->
getTypes();
3768 for (map<string, VuoCompilerType *>::iterator i = loadedTypes.begin(); i != loadedTypes.end(); ++i)
3773 (i->first !=
"VuoMathExpressionList"))
3775 typeOptions.push_back(i->first);
3787 set<string> VuoEditorComposition::getRespecializationOptionsForPortInNetwork(
VuoRendererPort *port)
3790 return set<string>();
3797 vector<string> compatibleInnerTypeNames;
3798 vector<string> compatibleTypeNames;
3799 for (
VuoPort *connectedPort : connectedGenericPorts)
3804 if (specializedNodeClass)
3812 vector<string> innermostCompatibleTypeNamesForPort;
3813 for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
3816 if (! innermostCompatibleTypeNamesForPort.empty())
3818 if (compatibleInnerTypeNames.empty())
3819 compatibleInnerTypeNames = innermostCompatibleTypeNamesForPort;
3822 for (
int k = compatibleInnerTypeNames.size() - 1; k >= 0; --k)
3823 if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleInnerTypeNames[k]) ==
3824 innermostCompatibleTypeNamesForPort.end())
3825 compatibleInnerTypeNames.erase(compatibleInnerTypeNames.begin() + k);
3834 string typeNameForPort = genericTypeFromPortClass->
getModuleKey();
3838 for (vector<string>::iterator k = compatibleInnerTypeNames.begin(); k != compatibleInnerTypeNames.end(); ++k)
3839 compatibleTypeNames.push_back(prefix + *k);
3841 if (compatibleTypeNames.empty())
3851 return set<string>(compatibleTypeNames.begin(), compatibleTypeNames.end());
3874 set<string> compatibleTypesInIsolation,
3875 set<string> compatibleTypesInContext,
3876 bool limitToNodeSet,
3880 QList<QAction *> actionsToAddToMenu;
3882 map<string, VuoCompilerType *> allTypes = compiler->
getTypes();
3884 QList<VuoCompilerType *> compatibleTypesForNodeSetDisplay;
3885 foreach (
string typeName, compatibleTypesInIsolation)
3888 if ((!limitToNodeSet || (loadedTypesForNodeSet[nodeSetName].find(type) != loadedTypesForNodeSet[nodeSetName].end())) &&
3891 (typeName !=
"VuoUrl" && typeName !=
"VuoList_VuoUrl"
3893 && typeName !=
"VuoInteraction" && typeName !=
"VuoList_VuoInteraction"
3894 && typeName !=
"VuoInteractionType" && typeName !=
"VuoList_VuoInteractionType"
3895 && typeName !=
"VuoUuid" && typeName !=
"VuoList_VuoUuid"
3897 && typeName !=
"VuoIconPosition" && typeName !=
"VuoList_VuoIconPosition"
3898 && typeName !=
"VuoMesh" && typeName !=
"VuoList_VuoMesh"
3899 && typeName !=
"VuoWindowProperty" && typeName !=
"VuoList_VuoWindowProperty"
3900 && typeName !=
"VuoWindowReference" && typeName !=
"VuoList_VuoWindowReference"))
3901 compatibleTypesForNodeSetDisplay.append(type);
3904 if (!compatibleTypesForNodeSetDisplay.isEmpty())
3906 QMenu *contextMenuNodeSetTypes = NULL;
3907 QList<QAction *> actionsToAddToNodeSetSubmenu;
3908 bool enabledContentAdded =
false;
3911 if (!nodeSetName.empty())
3913 contextMenuNodeSetTypes =
new QMenu(menu);
3914 contextMenuNodeSetTypes->setSeparatorsCollapsible(
false);
3916 contextMenuNodeSetTypes->setToolTipsVisible(
true);
3917 actionsToAddToMenu.append(contextMenuNodeSetTypes->menuAction());
3923 QList<QVariant> portAndSpecializedType;
3924 portAndSpecializedType.append(qVariantFromValue((
void *)genericPort));
3925 portAndSpecializedType.append(typeName.c_str());
3927 QAction *specializeAction;
3931 if (!nodeSetName.empty())
3933 specializeAction =
new QAction(typeTitle, contextMenuNodeSetTypes);
3934 actionsToAddToNodeSetSubmenu.append(specializeAction);
3940 specializeAction =
new QAction(typeTitle, menu);
3941 actionsToAddToMenu.append(specializeAction);
3944 specializeAction->setData(QVariant(portAndSpecializedType));
3945 specializeAction->setCheckable(
true);
3946 specializeAction->setChecked(genericPort && (genericPort->getDataType()->getModuleKey() == type->
getBase()->
getModuleKey()));
3948 if (compatibleTypesInContext.find(typeName) == compatibleTypesInContext.end())
3950 specializeAction->setEnabled(
false);
3952 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."));
3955 enabledContentAdded =
true;
3958 if (contextMenuNodeSetTypes)
3962 if (!enabledContentAdded)
3963 contextMenuNodeSetTypes->setEnabled(
false);
3967 QList<QAction *> actionListWithPromotions = promoteSingletonsFromSubmenus(actionsToAddToMenu);
3968 return actionListWithPromotions;
3975 QList<QAction *> VuoEditorComposition::promoteSingletonsFromSubmenus(QList<QAction *> actionList)
3977 QList<QAction *> modifiedActionList;
3978 foreach (QAction *action, actionList)
3980 if (action->menu() && (action->menu()->actions().size() == 1))
3982 QAction *singleSubaction = action->menu()->actions().first();
3983 action->menu()->removeAction(singleSubaction);
3984 modifiedActionList.append(singleSubaction);
3987 modifiedActionList.append(action);
3990 return modifiedActionList;
3998 std::sort(actionList.begin(), actionList.end(), nodeSetMenuActionLessThan);
3999 foreach (QAction *action, actionList)
4000 menu->addAction(action);
4006 void VuoEditorComposition::updatePopoversForActiveWindowChange(QWidget *old, QWidget *now)
4019 void VuoEditorComposition::updatePopoversForApplicationStateChange(
bool active)
4021 if (ignoreApplicationStateChangeEvents)
4025 if (activeWindow && (activeWindow->
getComposition() ==
this) && (!activeWindow->isMinimized()))
4039 return findNearbyComponent(scenePos, VuoEditorComposition::targetTypePort, limitPortCollisionRange);
4047 QGraphicsItem *item =
findNearbyComponent(scenePos, VuoEditorComposition::targetTypeNodeHeader);
4065 bool limitPortCollisionRange)
4071 bool ignoreComments;
4075 case VuoEditorComposition::targetTypePort:
4077 ignoreCables =
true;
4079 ignorePorts =
false;
4080 ignoreComments =
true;
4083 case VuoEditorComposition::targetTypeNodeHeader:
4085 ignoreCables =
true;
4088 ignoreComments =
true;
4093 ignoreCables =
false;
4094 ignoreNodes =
false;
4095 ignorePorts =
false;
4096 ignoreComments =
false;
4104 QGraphicsItem *topmostItemUnderCursor = itemAt(scenePos, views()[0]->transform());
4105 if (topmostItemUnderCursor && (!topmostItemUnderCursor->isEnabled()))
4106 topmostItemUnderCursor = NULL;
4110 QRectF searchRect(scenePos.x()-0.5*rectLength, scenePos.y()-0.5*rectLength, rectLength, rectLength);
4111 QList<QGraphicsItem *> itemsInRange = items(searchRect);
4112 for (QList<QGraphicsItem *>::iterator i = itemsInRange.begin(); i != itemsInRange.end(); ++i)
4114 if (!(*i)->isEnabled())
4121 bool makeListDragHandle =
4124 if (makeListDragHandle &&
4125 ((! topmostItemUnderCursor) ||
4126 (topmostItemUnderCursor == makeListDrawer) ||
4127 (topmostItemUnderCursor->zValue() < makeListDrawer->zValue())))
4129 return makeListDrawer;
4140 ((! topmostItemUnderCursor) ||
4141 (topmostItemUnderCursor == port) ||
4143 (topmostItemUnderCursor->zValue() < port->zValue()))
4145 ((! limitPortCollisionRange) ||
4159 ((! topmostItemUnderCursor) ||
4160 (topmostItemUnderCursor == cable) ||
4161 (topmostItemUnderCursor->zValue() < cable->zValue())))
4168 if (! ignoreComments)
4175 ((! topmostItemUnderCursor) ||
4176 (topmostItemUnderCursor == (*i)) ||
4177 (topmostItemUnderCursor->zValue() < (*i)->zValue())))
4184 if (targetType == VuoEditorComposition::targetTypeNodeHeader)
4190 headerRect = node->mapToScene(headerRect).
boundingRect();
4191 if (headerRect.intersects(searchRect) && scenePos.y() <= headerRect.bottom())
4203 return topmostItemUnderCursor;
4208 return ((
VuoRendererPort *)(topmostItemUnderCursor))->getRenderedParentNode();
4225 if (! cableInProgress)
4228 VuoPort *fromPort = cableInProgress->getFromPort();
4229 VuoPort *toPort = cableInProgress->getToPort();
4230 VuoPort *fixedPort = (fromPort? fromPort: toPort);
4245 vector<VuoRendererPort *> portList;
4260 if (portList.size() > firstPortIndex)
4262 targetPort = portList[firstPortIndex];
4268 for (
int i = firstPortIndex; i < portList.size(); ++i)
4272 firstPortWithoutWall = portList[i];
4276 if (firstPortWithoutWall)
4277 targetPort = firstPortWithoutWall;
4286 if (portList.size() > firstPortIndex)
4287 targetPort = portList[firstPortIndex];
4308 QRectF boundingRect;
4309 foreach (QGraphicsItem *item, items())
4313 boundingRect |= item->sceneBoundingRect();
4316 return boundingRect;
4325 QRectF boundingRect;
4327 foreach (QGraphicsItem *item, selectedItems())
4331 boundingRect |= item->sceneBoundingRect();
4334 return boundingRect;
4343 QRectF boundingRect;
4345 foreach (QGraphicsItem *item, selectedItems())
4349 boundingRect |= item->mapToScene(item->childrenBoundingRect()).boundingRect();
4359 boundingRect |= drawer->mapToScene(drawer->childrenBoundingRect()).boundingRect();
4364 return boundingRect;
4404 if (updateInRunningComposition)
4413 if (updateInRunningComposition)
4439 if (!errorMarkingUpdatesEnabled)
4442 errorMarkingUpdatesEnabled =
false;
4459 set<VuoCompilerCable *> potentialCables;
4461 if (targetPort && cableInProgress)
4467 if (cableInProgress->getFromNode())
4469 fromNode = cableInProgress->getFromNode();
4470 fromPort = cableInProgress->getFromPort();
4472 toPort = targetPort->
getBase();
4477 fromPort = targetPort->
getBase();
4478 toNode = cableInProgress->getToNode();
4479 toPort = cableInProgress->getToPort();
4484 potentialCable->
setAlwaysEventOnly(! cableInProgress->getRenderer()->effectivelyCarriesData() ||
4485 cableInProgress->getRenderer()->isFloatingEndpointAboveEventPort());
4489 potentialCables.insert(potentialCable);
4500 VUserLog(
"%s: Showing error popover: %s",
4508 set<VuoRendererNode *> nodesToMark;
4509 set<VuoRendererCable *> cablesToMark;
4511 set<VuoNode *> problemNodes = issue.
getNodes();
4512 foreach (
VuoNode *node, problemNodes)
4516 set<VuoCable *> problemCables = issue.
getCables();
4517 foreach (
VuoCable *cable, problemCables)
4519 VuoCable *cableToMark = (cable->
getCompiler() == potentialCable ? cableInProgress : cable);
4528 errorPopovers.insert(errorPopover);
4532 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
4536 if (targetPort && cableInProgress && nodesToMark.find(targetPort->
getRenderedParentNode()) != nodesToMark.end())
4540 else if (! nodesToMark.empty())
4543 qreal topY = viewportRect.bottom();
4549 QPointF scenePos = node->scenePos();
4550 if (viewportRect.contains(scenePos) && (scenePos.y() < topY))
4552 topmostVisibleNode = node;
4553 topY = scenePos.y();
4557 if (topmostVisibleNode)
4558 nearbyNode = topmostVisibleNode;
4567 VUserLog(
"Warning: no nearby node (no marked nodes).");
4572 const QPoint offsetFromNode(0,10);
4573 QPoint popoverTopLeftInScene = (nearbyNode?
4574 (nearbyNode->scenePos().toPoint() +
4577 QPoint(viewportRect.center().x() - 0.5*errorPopover->sizeHint().width(),
4578 viewportRect.center().y() - 0.5*errorPopover->sizeHint().height()));
4582 const int margin = 5;
4583 popoverTopLeftInScene = (QPoint(fmin(popoverTopLeftInScene.x(), viewportRect.bottomRight().x() - errorPopover->sizeHint().width() - margin),
4584 fmin(popoverTopLeftInScene.y(), viewportRect.bottomRight().y() - errorPopover->sizeHint().height() - margin)));
4586 popoverTopLeftInScene = (QPoint(fmax(popoverTopLeftInScene.x(), viewportRect.topLeft().x() + margin),
4587 fmax(popoverTopLeftInScene.y(), viewportRect.topLeft().y() + margin)));
4589 QPoint popoverTopLeftInView = views()[0]->mapFromScene(popoverTopLeftInScene);
4590 QPoint popoverTopLeftGlobal = views()[0]->mapToGlobal(popoverTopLeftInView);
4592 errorPopover->move(popoverTopLeftGlobal);
4593 errorPopover->show();
4601 delete potentialCable;
4603 errorMarkingUpdatesEnabled =
true;
4609 bool VuoEditorComposition::hasFeedbackErrors(
void)
4611 return this->errorMark;
4619 if (hasFeedbackErrors())
4628 void VuoEditorComposition::buildComposition(
string compositionSnapshot,
const set<string> &dependenciesUninstalled)
4634 if (! dependenciesUninstalled.empty())
4636 vector<string> dependenciesRemovedVec(dependenciesUninstalled.begin(), dependenciesUninstalled.end());
4638 throw VuoException(
"Some modules that the composition needs were uninstalled: " + dependenciesStr);
4641 delete runningComposition;
4642 runningComposition = NULL;
4646 if (runningCompositionActiveDriver)
4650 string dir, file, ext;
4652 linkedCompositionPath = dir + file +
".dylib";
4657 compiler->
compileComposition(runningComposition, compiledCompositionPath,
true, issues);
4661 remove(compiledCompositionPath.c_str());
4667 delete runningComposition;
4668 runningComposition = NULL;
4680 bool VuoEditorComposition::isRunningThreadUnsafe(
void)
4682 return runner != NULL && ! stopRequested && ! runner->
isStopped();
4692 __block
bool running;
4693 dispatch_sync(runCompositionQueue, ^{
4694 running = isRunningThreadUnsafe();
4720 if (matchingComposition->showEventsMode)
4724 stopRequested =
false;
4725 dispatch_async(runCompositionQueue, ^{
4728 runningCompositionLibraries = std::make_shared<VuoRunningCompositionLibraries>();
4730 buildComposition(compositionSnapshot);
4744 if (matchingComposition->showEventsMode)
4745 this->runner->subscribeToEventTelemetry(matchingCompositionIdentifier);
4747 dispatch_sync(activePortPopoversQueue, ^{
4748 for (
auto i : matchingComposition->activePortPopovers)
4750 string portID = i.first;
4751 updateDataInPortPopoverFromRunningTopLevelComposition(matchingComposition, matchingCompositionIdentifier, portID);
4772 stopRequested =
true;
4773 dispatch_async(runCompositionQueue, ^{
4777 runner->waitUntilStopped();
4782 linkedCompositionPath =
"";
4784 runningCompositionLibraries =
nullptr;
4786 delete runningComposition;
4787 runningComposition = NULL;
4795 subcompositionRouter->applyToAllLinkedCompositions(
this, ^
void (
VuoEditorComposition *matchingComposition,
string matchingCompositionIdentifier)
4797 if (matchingComposition->showEventsMode)
4800 dispatch_sync(activePortPopoversQueue, ^{
4801 for (
auto i : matchingComposition->activePortPopovers)
4803 VuoPortPopover *popover = i.second;
4804 popover->setCompositionRunning(false);
4827 dispatch_async(runCompositionQueue, ^{
4828 if (isRunningThreadUnsafe())
4834 runningCompositionLibraries->enqueueLibraryContainingDependencyToUnload(moduleKey);
4837 string oldBuiltCompositionSnapshot = oldCompositionSnapshot;
4839 if (previouslyActiveDriver)
4842 previouslyActiveDriver->applyToComposition(oldBuiltComposition, compiler);
4846 buildComposition(newCompositionSnapshot, dependenciesUninstalled);
4848 string compositionDiff = diffInfo->
diff(oldBuiltCompositionSnapshot, runningComposition, compiler);
4851 catch (exception &e)
4853 VUserLog(
"Composition stopped itself: %s", e.what());
4858 VUserLog(
"Composition stopped itself.");
4864 dispatch_async(dispatch_get_main_queue(), ^{
4883 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, reloadSubcompositionIfUnsaved);
4901 if (
this == topLevelComposition)
4903 dispatch_async(runCompositionQueue, ^{
4904 if (isRunningThreadUnsafe())
4906 json_object *constantObject = json_tokener_parse(constant.c_str());
4907 runner->
setInputPortValue(thisCompositionIdentifier, runningPortID, constantObject);
4915 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
4917 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
4944 if (runningPortIdentifier.empty())
4950 if (
this == topLevelComposition)
4952 dispatch_async(runCompositionQueue, ^{
4953 if (isRunningThreadUnsafe())
4955 json_object *constantObject = json_tokener_parse(constant.c_str());
4956 runner->
setInputPortValue(thisCompositionIdentifier, runningPortIdentifier, constantObject);
4964 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
4966 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
4979 dispatch_async(runCompositionQueue, ^{
4980 if (isRunningThreadUnsafe())
4982 json_object *constantObject = json_tokener_parse(constant.c_str());
4984 foreach (
string subcompositionIdentifier, subcompositionIdentifiers)
4986 runner->
setInputPortValue(subcompositionIdentifier, portIdentifier, constantObject);
4997 dispatch_async(runCompositionQueue, ^{
4998 if (isRunningThreadUnsafe())
5003 json_object *constantObject = json_tokener_parse(constant.c_str());
5004 map<VuoRunner::Port *, json_object *> m;
5005 m[publishedPort] = constantObject;
5018 return contextMenuDeleteSelected;
5028 QMenu *contextMenuTints =
new QMenu(parent);
5029 contextMenuTints->setSeparatorsCollapsible(
false);
5030 contextMenuTints->setTitle(tr(
"Tint"));
5031 foreach (QAction *tintAction, contextMenuTintActions)
5032 contextMenuTints->addAction(tintAction);
5033 contextMenuTints->insertSeparator(contextMenuTintActions.last());
5035 return contextMenuTints;
5041 void VuoEditorComposition::expandChangeNodeMenu()
5043 QAction *sender = (QAction *)QObject::sender();
5048 int currentMatchesListed = contextMenuChangeNode->actions().size()-1;
5049 if (currentMatchesListed <= initialChangeNodeSuggestionCount)
5052 int verticalSpacePerItem = 21;
5056 int targetMatches = availableVerticalSpace/verticalSpacePerItem - 2;
5065 contextMenuChangeNode->exec();
5080 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
5081 vector<VuoCompilerNodeClass *> loadedNodeClasses;
5082 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
5083 loadedNodeClasses.push_back(i->second);
5086 vector<string> bestMatches;
5087 map<string, double> matchScores;
5088 matchScores[
""] = 0;
5090 int targetMatchCount = (matchLimit > 0? matchLimit : loadedNodeClasses.size());
5091 for (
int i = 0; i < targetMatchCount; ++i)
5092 bestMatches.push_back(
"");
5095 bool overflow =
false;
5098 string originalGenericNodeClassName;
5102 originalGenericNodeClassName = nodeClass->
getClassName();
5107 if (loadedNodeClassName == originalGenericNodeClassName)
5110 bool canSwapNondestructively = canSwapWithoutBreakingCables(node, loadedNodeClass->
getBase());
5111 double matchScore = (canSwapNondestructively? calculateNodeSimilarity(nodeClass, loadedNodeClass->
getBase()) : 0);
5112 int highestIndexWithCompetitiveScore = -1;
5113 for (
int i = targetMatchCount-1; (i >= 0) && (highestIndexWithCompetitiveScore == -1); --i)
5114 if (matchScore <= matchScores[bestMatches[i] ])
5115 highestIndexWithCompetitiveScore = i;
5117 if (highestIndexWithCompetitiveScore < targetMatchCount-1)
5119 if (matchScores[bestMatches[targetMatchCount-1] ] > 0)
5122 for (
int j = targetMatchCount-2; j > highestIndexWithCompetitiveScore; --j)
5123 bestMatches[j+1] = bestMatches[j];
5125 bestMatches[highestIndexWithCompetitiveScore+1] = loadedNodeClassName;
5126 matchScores[loadedNodeClassName] = matchScore;
5130 for (
int i = 0; i < targetMatchCount; ++i)
5132 if (matchScores[bestMatches[i] ] > 0)
5137 matchDisplayText += QString(
" (%1)").arg(bestMatches[i].c_str());
5139 QAction *changeAction = menu->addAction(matchDisplayText);
5141 QList<QVariant> currentNodeAndNewClass;
5142 currentNodeAndNewClass.append(qVariantFromValue((
void *)node));
5143 currentNodeAndNewClass.append(bestMatches[i].c_str());
5144 changeAction->setData(QVariant(currentNodeAndNewClass));
5145 connect(changeAction, &QAction::triggered,
this, &VuoEditorComposition::swapNode);
5152 QAction *showMoreAction = menu->addAction(tr(
"More…"));
5153 showMoreAction->setData(qVariantFromValue(
static_cast<void *
>(node)));
5154 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandChangeNodeMenu);
5165 map<string, int> requiredInputs;
5166 bool inputEventSourceRequired =
false;
5169 bool hasDrawerWithNoIncomingCables =
false;
5170 bool hasDrawerWithNoIncomingDataCables =
false;
5177 hasDrawerWithNoIncomingCables =
true;
5178 hasDrawerWithNoIncomingDataCables =
true;
5179 vector<VuoRendererPort *> childPorts = inputDrawer->
getDrawerPorts();
5183 hasDrawerWithNoIncomingCables =
false;
5185 hasDrawerWithNoIncomingDataCables =
false;
5190 if (!hasDrawerWithNoIncomingDataCables)
5197 requiredInputs[typeKey] = ((requiredInputs.find(typeKey) == requiredInputs.end())? 1 : requiredInputs[typeKey]+1);
5202 if (hasIncomingCables && !hasDrawerWithNoIncomingCables)
5203 inputEventSourceRequired =
true;
5207 map<string, int> requiredOutputs;
5208 bool outputEventSourceRequired =
false;
5220 requiredOutputs[typeKey] = ((requiredOutputs.find(typeKey) == requiredOutputs.end())? 1 : requiredOutputs[typeKey]+1);
5224 outputEventSourceRequired =
true;
5228 bool inputEventSourceAvailable =
false;
5229 map<string, int> availableInputs;
5237 availableInputs[typeKey] = ((availableInputs.find(typeKey) == availableInputs.end())? 1 : availableInputs[typeKey]+1);
5242 inputEventSourceAvailable =
true;
5245 bool outputEventSourceAvailable =
false;
5246 map<string, int> availableOutputs;
5254 availableOutputs[typeKey] = ((availableOutputs.find(typeKey) == availableOutputs.end())? 1 : availableOutputs[typeKey]+1);
5259 outputEventSourceAvailable =
true;
5262 for (std::map<string,int>::iterator it=requiredInputs.begin(); it!=requiredInputs.end(); ++it)
5264 string typeKey = it->first;
5265 int typeRequiredCount = it->second;
5266 if (availableInputs[typeKey] < typeRequiredCount)
5271 for (std::map<string,int>::iterator it=requiredOutputs.begin(); it!=requiredOutputs.end(); ++it)
5273 string typeKey = it->first;
5274 int typeRequiredCount = it->second;
5275 if (availableOutputs[typeKey] < typeRequiredCount)
5279 if (inputEventSourceRequired && !inputEventSourceAvailable)
5282 if (outputEventSourceRequired && !outputEventSourceAvailable)
5292 bool VuoEditorComposition::isPortCurrentlyRevertible(
VuoRendererPort *port)
5298 if (!specializedNodeClass)
5303 if (!originalGenericType)
5312 if (hostPort && (!isPortCurrentlyRevertible(hostPort->
getRenderer())))
5339 string publishedPortName = ((! name.empty())?
5350 bool performedMerge =
false;
5353 publishedPort = (isPublishedInput ?
5359 if (isPublishedInput && portType && type && !forceEventOnlyPublication)
5367 performedMerge =
true;
5374 if (! performedMerge)
5377 if (isPublishedInput && type)
5392 if (! existingPublishedCable)
5398 if (mergePerformed != NULL)
5399 *mergePerformed = performedMerge;
5401 return rendererPublishedPort;
5413 if (creatingPublishedInputCable)
5416 VuoPort *fromPort = externalPort;
5419 VuoPort *toPort = internalPort;
5426 publishedCable->
setFrom(fromNode, fromPort);
5433 VuoPort *fromPort = internalPort;
5436 VuoPort *toPort = externalPort;
5443 publishedCable->
setTo(toNode, toPort);
5446 if (forceEventOnlyPublication)
5449 return publishedCable;
5465 vector<VuoPublishedPort *> publishedPortsToAdd;
5466 map<VuoPublishedPort *, string> publishedPortsToRename;
5469 VuoProtocol *previousActiveProtocol = this->activeProtocol;
5470 bool removingPreviousProtocol = previousActiveProtocol && (previousActiveProtocol != protocol);
5472 bool portChangesMadeDuringProtocolRemoval =
false;
5473 if (removingPreviousProtocol)
5476 if (portChangesMadeDuringProtocolRemoval && !useUndoStack)
5478 VUserLog(
"Warning: Unexpected combination: Removing protocol ports, but useUndoStack=false");
5479 useUndoStack =
true;
5483 this->activeProtocol = protocol;
5488 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5490 string portName = i->first;
5491 string portType = i->second;
5493 bool compositionHadCompatiblePort =
false;
5495 if (preexistingPublishedPort)
5499 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5500 (!preexistingType && (portType ==
"")));
5503 compositionHadCompatiblePort =
true;
5508 compositionHadCompatiblePort =
false;
5513 if (!compositionHadCompatiblePort)
5522 publishedPortsToAdd.push_back(publishedPort);
5529 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5531 string portName = i->first;
5532 string portType = i->second;
5534 bool compositionHadCompatiblePort =
false;
5536 if (preexistingPublishedPort)
5539 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5540 (!preexistingType && (portType ==
"")));
5543 compositionHadCompatiblePort =
true;
5548 compositionHadCompatiblePort =
false;
5553 if (!compositionHadCompatiblePort)
5562 publishedPortsToAdd.push_back(publishedPort);
5570 bool undoStackMacroBegunAlready = (removingPreviousProtocol && portChangesMadeDuringProtocolRemoval);
5571 if (!publishedPortsToRename.empty() || !publishedPortsToAdd.empty() || undoStackMacroBegunAlready)
5573 set<VuoPublishedPort *> publishedPortsToRemove;
5574 bool beginUndoStackMacro = !undoStackMacroBegunAlready;
5575 bool endUndoStackMacro =
true;
5576 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5590 string VuoEditorComposition::getNonProtocolVariantForPortName(
string portName)
5592 string modifiedPortName = portName;
5593 if (modifiedPortName.length() > 0)
5594 modifiedPortName[0] = toupper(modifiedPortName[0]);
5595 modifiedPortName =
"some" + modifiedPortName;
5597 return modifiedPortName;
5621 set<VuoPublishedPort *> publishedPortsToRemove;
5622 map<VuoPublishedPort *, string> publishedPortsToRename;
5625 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5627 string portName = i->first;
5628 string portType = i->second;
5631 if (preexistingPublishedPort)
5633 bool portCompatibleAcrossProtocolTransition =
false;
5634 if (replacementProtocol)
5637 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5639 string replacementPortName = i->first;
5640 string replacementPortType = i->second;
5642 if ((portName == replacementPortName) && (portType == replacementPortType))
5643 portCompatibleAcrossProtocolTransition =
true;
5648 publishedPortsToRemove.insert(preexistingPublishedPort);
5649 else if (!portCompatibleAcrossProtocolTransition)
5655 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5657 string portName = i->first;
5658 string portType = i->second;
5661 if (preexistingPublishedPort)
5663 bool portCompatibleAcrossProtocolTransition =
false;
5664 if (replacementProtocol)
5667 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5669 string replacementPortName = i->first;
5670 string replacementPortType = i->second;
5672 if ((portName == replacementPortName) && (portType == replacementPortType))
5673 portCompatibleAcrossProtocolTransition =
true;
5678 publishedPortsToRemove.insert(preexistingPublishedPort);
5679 else if (!portCompatibleAcrossProtocolTransition)
5686 if (!publishedPortsToRemove.empty())
5687 publishedPortsToRename.clear();
5689 bool portChangesRequired = (!publishedPortsToRename.empty() || !publishedPortsToRemove.empty());
5690 if (portChangesRequired)
5692 vector<VuoPublishedPort *> publishedPortsToAdd;
5693 bool beginUndoStackMacro =
true;
5694 bool endUndoStackMacro = !replacementProtocol;
5695 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5700 return portChangesRequired;
5711 if ((activeProtocol != protocol) || !activeProtocol)
5714 activeProtocol = NULL;
5717 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5719 string portName = i->first;
5720 string portType = i->second;
5723 if (preexistingPublishedPort)
5728 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5730 string portName = i->first;
5731 string portType = i->second;
5734 if (preexistingPublishedPort)
5747 return activeProtocol;
5756 if (!activeProtocol)
5759 return static_cast<VuoEditor *
>(qApp)->getBuiltInDriverForProtocol(activeProtocol);
5792 return removalResult;
5819 void VuoEditorComposition::highlightEligibleEndpointsForCable(
VuoCable *cable)
5832 highlightInternalPortsConnectableToPort(fixedPort, cable->
getRenderer());
5846 QList<QGraphicsItem *> compositionComponents = items();
5847 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5849 QGraphicsItem *compositionComponent = *i;
5855 for(vector<VuoPort *>::iterator inputPort = inputPorts.begin(); inputPort != inputPorts.end(); ++inputPort)
5856 updateEligibilityHighlightingForPort((*inputPort)->getRenderer(), port, !cable->
effectivelyCarriesData(), types);
5860 for(vector<VuoPort *>::iterator outputPort = outputPorts.begin(); outputPort != outputPorts.end(); ++outputPort)
5861 updateEligibilityHighlightingForPort((*outputPort)->getRenderer(), port, !cable->
effectivelyCarriesData(), types);
5866 if (rc && rc != cable)
5868 QGraphicsItem::CacheMode normalCacheMode = rc->cacheMode();
5869 rc->setCacheMode(QGraphicsItem::NoCache);
5887 rc->setCacheMode(normalCacheMode);
5892 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5893 updateEligibilityHighlightingForNode(
dynamic_cast<VuoRendererNode *
>(*i));
5900 void VuoEditorComposition::updateEligibilityHighlightingForPort(
VuoRendererPort *portToHighlight,
5902 bool eventOnlyConnection,
5903 map<string, VuoCompilerType *> &types)
5905 QGraphicsItem::CacheMode normalCacheMode = portToHighlight->cacheMode();
5906 portToHighlight->setCacheMode(QGraphicsItem::NoCache);
5914 if (typecastPortToHighlight)
5917 portToHighlight->setCacheMode(normalCacheMode);
5919 if (typecastPortToHighlight)
5920 updateEligibilityHighlightingForPort(typecastPortToHighlight->
getChildPort(), fixedPort, eventOnlyConnection, types);
5941 bool forwardConnection;
5944 fromPort = fixedPort;
5945 toPort = portToHighlight;
5946 forwardConnection =
true;
5950 fromPort = portToHighlight;
5952 forwardConnection =
false;
5955 bool directConnectionPossible;
5959 if (fixedExternalPublishedPort && externalPublishedPortToHighlight)
5960 directConnectionPossible =
false;
5961 else if (fixedExternalPublishedPort && !externalPublishedPortToHighlight)
5963 else if (!fixedExternalPublishedPort && externalPublishedPortToHighlight)
5969 if (directConnectionPossible)
5973 else if (fixedPort == portToHighlight)
5996 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
5998 bool eventOnlyConnection,
5999 bool forwardConnection)
6002 string respecializedTypeName =
"";
6004 return canConnectDirectlyWithRespecializationNondestructively(fromPort,
6006 eventOnlyConnection,
6008 &portToRespecialize,
6009 respecializedTypeName);
6022 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6024 bool eventOnlyConnection,
6025 bool forwardConnection,
6027 string &respecializedTypeName)
6029 *portToRespecialize = NULL;
6030 respecializedTypeName =
"";
6032 bool canConnectWithRespecialization = canConnectDirectlyWithRespecialization(fromPort,
6034 eventOnlyConnection,
6037 respecializedTypeName);
6038 if (!canConnectWithRespecialization)
6041 if (canConnectWithRespecialization && !portToRespecialize)
6044 bool nondestructive = portCanBeUnspecializedNondestructively((*portToRespecialize)->getBase());
6045 if (!nondestructive)
6047 *portToRespecialize = NULL;
6048 respecializedTypeName =
"";
6050 return nondestructive;
6058 bool VuoEditorComposition::portCanBeUnspecializedNondestructively(
VuoPort *portToUnspecialize)
6060 map<VuoNode *, string> nodesToReplace;
6061 set<VuoCable *> cablesToDelete;
6066 if (cablesToDelete.empty())
6069 else if ((cablesToDelete.size() == 1) && ((*(cablesToDelete.begin()))->getToPort() == portToUnspecialize))
6094 bool VuoEditorComposition::canConnectDirectlyWithRespecialization(
VuoRendererPort *fromPort,
6096 bool eventOnlyConnection,
6097 bool forwardConnection,
6099 string &respecializedTypeName)
6103 *portToRespecialize = NULL;
6104 respecializedTypeName =
"";
6114 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6115 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6117 if (!(fromPortIsEnabledOutput && toPortIsEnabledInput))
6123 if (!(currentFromDataType && currentToDataType))
6138 if (fromSpecializedNodeClass)
6150 if (toSpecializedNodeClass)
6159 bool fromPortIsGeneric = currentFromGenericType;
6160 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6161 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6163 bool toPortIsGeneric = currentToGenericType;
6164 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6165 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6168 set<string> compatibleTypes;
6169 string specializedType =
"";
6173 if ((fromPortIsStatic && toPortIsSpecialized) || (fromPortIsSpecialized && toPortIsStatic))
6177 portToTryToRespecialize = (fromPortIsSpecialized? fromPort : toPort);
6181 else if ((fromPortIsSpecialized || toPortIsSpecialized) && !fromPortIsStatic && !toPortIsStatic)
6184 bool dragSourceIsGeneric = (forwardConnection? fromPortIsGeneric : toPortIsGeneric);
6186 VuoRendererPort *dragDestination = (forwardConnection? toPort : fromPort);
6187 bool dragDestinationIsGeneric = (forwardConnection? toPortIsGeneric : fromPortIsGeneric);
6204 if (!dragSourceIsGeneric && !dragDestinationIsGeneric)
6207 portToTryToRespecialize = dragDestination;
6215 if (portToTryToRespecialize)
6216 compatibleTypes = getRespecializationOptionsForPortInNetwork(portToTryToRespecialize);
6218 bool portsAreCompatible = (compatibleTypes.find(specializedType) != compatibleTypes.end());
6220 if (portsAreCompatible)
6222 *portToRespecialize = portToTryToRespecialize;
6223 respecializedTypeName = specializedType;
6226 return portsAreCompatible;
6235 void VuoEditorComposition::updateEligibilityHighlightingForNode(
VuoRendererNode *node)
6250 QGraphicsItem::CacheMode normalCacheMode = drawer->cacheMode();
6251 drawer->setCacheMode(QGraphicsItem::NoCache);
6256 drawer->setCacheMode(normalCacheMode);
6265 QGraphicsItem::CacheMode normalCacheMode = hostPort->cacheMode();
6266 hostPort->setCacheMode(QGraphicsItem::NoCache);
6268 hostPort->setCacheMode(normalCacheMode);
6302 bool toPortIsDragDestination,
6304 string &specializedTypeName,
6305 string &typecastToInsert)
6307 *portToSpecialize = NULL;
6308 specializedTypeName =
"";
6310 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6311 map<string, string> specializedTypeNameForTypecast;
6314 vector<string> candidateTypecasts =
findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast, types);
6315 bool solutionSelected = selectBridgingSolutionFromOptions(candidateTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, typecastToInsert);
6317 if (!solutionSelected)
6320 if (portToSpecializeForTypecast.find(typecastToInsert) != portToSpecializeForTypecast.end())
6321 *portToSpecialize = portToSpecializeForTypecast[typecastToInsert];
6322 if (specializedTypeNameForTypecast.find(typecastToInsert) != specializedTypeNameForTypecast.end())
6323 specializedTypeName = specializedTypeNameForTypecast[typecastToInsert];
6346 bool VuoEditorComposition::selectBridgingSolutionFromOptions(vector<string> suitableTypecasts,
6347 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6348 map<string, string> specializedTypeNameForTypecast,
6349 string &selectedTypecast)
6351 if (suitableTypecasts.empty())
6353 selectedTypecast =
"";
6357 else if (suitableTypecasts.size() == 1)
6359 selectedTypecast = suitableTypecasts[0];
6364 return promptForBridgingSelectionFromOptions(suitableTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, selectedTypecast);
6374 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6375 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6377 return (fromPortIsEnabledOutput && toPortIsEnabledInput &&
6389 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6405 if (toNodeUsesIndex)
6432 bool toPortIsDragDestination,
6433 map<string, VuoCompilerType *> &types)
6435 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6436 map<string, string> specializedTypeNameForTypecast;
6437 return findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast, types);
6451 bool toPortIsDragDestination,
6452 map<string, VuoRendererPort *> &portToSpecializeForTypecast,
6453 map<string, string> &specializedTypeNameForTypecast,
6454 map<string, VuoCompilerType *> &types)
6461 const bool limitCombinations =
true;
6463 portToSpecializeForTypecast.clear();
6464 specializedTypeNameForTypecast.clear();
6465 vector<string> suitableTypecasts;
6467 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6468 return suitableTypecasts;
6473 return suitableTypecasts;
6478 string specializedTypeName =
"";
6481 suitableTypecasts.push_back(
"");
6482 portToSpecializeForTypecast[
""] = portToSpecialize;
6483 specializedTypeNameForTypecast[
""] = specializedTypeName;
6485 return suitableTypecasts;
6492 if (!(currentFromDataType && currentToDataType))
6493 return suitableTypecasts;
6503 if (fromSpecializedNodeClass)
6515 if (toSpecializedNodeClass)
6526 bool fromPortIsGeneric = currentFromGenericType;
6527 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6528 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6530 bool toPortIsGeneric = currentToGenericType;
6531 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6532 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6535 if (fromPortIsGeneric && toPortIsGeneric)
6536 return suitableTypecasts;
6539 else if (fromPortIsStatic && toPortIsStatic)
6541 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6543 return suitableTypecasts;
6548 bool specializeToPort =
true;
6549 if (toPortIsGeneric)
6550 specializeToPort =
true;
6551 else if (fromPortIsGeneric)
6552 specializeToPort =
false;
6553 else if (fromPortIsSpecialized && toPortIsStatic)
6554 specializeToPort =
false;
6555 else if (fromPortIsStatic && toPortIsSpecialized)
6556 specializeToPort =
true;
6557 else if (fromPortIsSpecialized && toPortIsSpecialized)
6558 specializeToPort = toPortIsDragDestination;
6562 set<string> compatibleTypes;
6563 if (specializeToPort && (toPortIsGeneric || (toPortIsSpecialized && portCanBeUnspecializedNondestructively(toPort->
getBase()))))
6564 compatibleTypes = getRespecializationOptionsForPortInNetwork(toPort);
6565 else if (!specializeToPort && (fromPortIsGeneric || (fromPortIsSpecialized && portCanBeUnspecializedNondestructively(fromPort->
getBase()))))
6566 compatibleTypes = getRespecializationOptionsForPortInNetwork(fromPort);
6572 if (limitCombinations)
6574 vector<string> limitedSuitableTypecasts;
6577 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6580 foreach (
string typecastName, limitedSuitableTypecasts)
6582 portToSpecializeForTypecast[typecastName] = specializeToPort? toPort : fromPort;
6583 specializedTypeNameForTypecast[typecastName] = specializeToPort? currentToDataType->
getModuleKey() :
6590 if (compatibleTypes.find(fixedDataType) != compatibleTypes.end())
6592 limitedSuitableTypecasts.push_back(
"");
6593 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6594 specializedTypeNameForTypecast[
""] = fixedDataType;
6597 if (limitedSuitableTypecasts.size() >= 1)
6598 return limitedSuitableTypecasts;
6601 foreach (
string compatibleTypeName, compatibleTypes)
6603 VuoCompilerType *compatibleSpecializedType = types[compatibleTypeName];
6604 if (!compatibleSpecializedType)
6605 compatibleSpecializedType = compiler->
getType(compatibleTypeName);
6606 VuoType *candidateFromType = specializeToPort? currentFromDataType : compatibleSpecializedType->
getBase();
6607 VuoType *candidateToType = specializeToPort? compatibleSpecializedType->
getBase() : currentToDataType;
6609 if (compatibleSpecializedType)
6612 if (candidateFromType == candidateToType)
6614 suitableTypecasts.push_back(
"");
6615 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6616 specializedTypeNameForTypecast[
""] = compatibleSpecializedType->
getBase()->
getModuleKey();
6619 if (portsPassSanityCheckToTypeconvert(fromPort,
6625 foreach (
string typecast, suitableTypecastsForCurrentTypes)
6627 suitableTypecasts.push_back(typecast);
6628 portToSpecializeForTypecast[typecast] = specializeToPort? toPort : fromPort;
6629 specializedTypeNameForTypecast[typecast] = compatibleSpecializedType->
getBase()->
getModuleKey();
6635 return suitableTypecasts;
6650 bool VuoEditorComposition::promptForBridgingSelectionFromOptions(vector<string> suitableTypecasts,
6651 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6652 map<string, string> specializedTypeNameForTypecast,
6653 string &selectedTypecast)
6655 QMenu typecastMenu(views()[0]->viewport());
6656 typecastMenu.setSeparatorsCollapsible(
false);
6657 QString spacer(
" ");
6660 set <pair<VuoRendererPort *, string> > specializationDetails;
6661 vector<string> typeconversionOptionsRequiringNoSpecialization;
6662 foreach (
string typecastClassName, suitableTypecasts)
6664 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typecastClassName];
6665 string specializedTypeName = specializedTypeNameForTypecast[typecastClassName];
6666 specializationDetails.insert(std::make_pair(portToSpecialize,
6667 specializedTypeName));
6669 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6670 if (portAlreadyHasTargetType)
6671 typeconversionOptionsRequiringNoSpecialization.push_back(typecastClassName);
6677 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()))
6679 QString menuText = getDisplayTextForSpecializationOption(portToSpecializeForTypecast[
""], specializedTypeNameForTypecast[
""]);
6680 QAction *typecastAction = typecastMenu.addAction(menuText);
6681 typecastAction->setData(QVariant(
""));
6684 bool foundSpecializationOptionsRequiringNoTypeconversion = typecastMenu.actions().size() >= 1;
6685 bool foundTypeconversionOptionsRequiringNoSpecialization = typeconversionOptionsRequiringNoSpecialization.size() >= 1;
6688 bool includingTypeconvertWithNoSpecializationHeader = foundSpecializationOptionsRequiringNoTypeconversion;
6689 if (foundTypeconversionOptionsRequiringNoSpecialization)
6691 if (foundSpecializationOptionsRequiringNoTypeconversion)
6692 typecastMenu.addSeparator();
6694 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6695 string specializedTypeName = specializedTypeNameForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6697 if (portToSpecialize && !specializedTypeName.empty())
6699 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6700 QAction *typecastAction = typecastMenu.addAction(menuText);
6701 typecastAction->setEnabled(
false);
6702 includingTypeconvertWithNoSpecializationHeader =
true;
6706 foreach (
string typecastClassName, typeconversionOptionsRequiringNoSpecialization)
6712 typecastAction->setData(QVariant(typecastClassName.c_str()));
6717 for (set<pair<VuoRendererPort *, string> >::iterator i = specializationDetails.begin(); i != specializationDetails.end(); ++i)
6720 string specializedTypeName = i->second;
6723 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()) &&
6724 (portToSpecializeForTypecast[
""] == portToSpecialize) &&
6725 (specializedTypeNameForTypecast[
""] == specializedTypeName))
6731 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6732 if (portAlreadyHasTargetType)
6737 if (typecastMenu.actions().size() >= 1)
6738 typecastMenu.addSeparator();
6740 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6741 QAction *typecastAction = typecastMenu.addAction(menuText);
6742 typecastAction->setEnabled(
false);
6745 foreach (
string typecastClassName, suitableTypecasts)
6747 if ((portToSpecializeForTypecast[typecastClassName] == portToSpecialize) &&
6748 (specializedTypeNameForTypecast[typecastClassName] == specializedTypeName))
6754 typecastAction->setData(QVariant(typecastClassName.c_str()));
6760 menuSelectionInProgress =
true;
6761 QAction *selectedTypecastAction = typecastMenu.exec(QCursor::pos());
6762 menuSelectionInProgress =
false;
6764 selectedTypecast = (selectedTypecastAction? selectedTypecastAction->data().toString().toUtf8().constData() :
"");
6765 return selectedTypecastAction;
6771 QString VuoEditorComposition::getDisplayTextForSpecializationOption(
VuoRendererPort *portToSpecialize,
string specializedTypeName)
6773 if (!portToSpecialize || specializedTypeName.empty())
6776 bool isInput = portToSpecialize && portToSpecialize->
getInput();
6777 QString typeDisplayName = compiler->
getType(specializedTypeName)?
6779 specializedTypeName.c_str();
6781 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6783 QString displayText;
6784 if (portAlreadyHasTargetType)
6789 displayText = tr(
"Keep Input Port as %1");
6794 displayText = tr(
"Keep Output Port as %1");
6802 displayText = tr(
"Change Input Port to %1");
6807 displayText = tr(
"Change Output Port to %1");
6811 return displayText.arg(typeDisplayName);
6830 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
6831 if (topLevelComposition->isRunningThreadUnsafe())
6833 portValue = isInput ?
6834 topLevelComposition->runner->getInputPortValue(thisCompositionIdentifier, runningPortIdentifier) :
6835 topLevelComposition->runner->getOutputPortValue(thisCompositionIdentifier, runningPortIdentifier);
6839 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, getPortValue);
6847 string VuoEditorComposition::getIdentifierForRunningPort(
VuoPort *runningPort)
6864 return dynamic_cast<VuoPublishedPort *
>(staticPort)->getClass()->getName();
6870 string nodeIdentifier =
"";
6880 if (staticPort->
hasCompiler() && !nodeIdentifier.empty())
6924 if (candidateInputPort == port)
6928 if (candidateOutputPort == port)
6944 map<string, VuoPortPopover *>::iterator popover = activePortPopovers.find(portID);
6945 if (popover != activePortPopovers.end())
6946 return popover->second;
6959 void VuoEditorComposition::enableInactivePopoverForPort(
VuoRendererPort *rp)
6962 bool popoverJustClosedAtLastEvent = portsWithPopoversClosedAtLastEvent.find(portID) != portsWithPopoversClosedAtLastEvent.end();
6963 if (!popoverJustClosedAtLastEvent)
6972 if (!popoverEventsEnabled)
6978 VUserLog(
"%s: Open popover for %s",
6982 dispatch_sync(runCompositionQueue, ^{
6984 dispatch_sync(activePortPopoversQueue, ^{
6986 if (activePortPopovers.find(portID) == activePortPopovers.end())
6990 VuoPortPopover *popover = new VuoPortPopover(port, this, views()[0]->viewport());
6992 connect(popover, &VuoPortPopover::popoverClosedForPort, this, &VuoEditorComposition::disablePopoverForPortThreadSafe);
6993 connect(popover, &VuoPortPopover::popoverDetachedFromPort, [=]{
6994 VUserLog(
"%s: Detach popover for %s",
6995 window->getWindowTitleWithoutPlaceholder().toUtf8().data(),
7007 const int cutoffMargin = 16;
7008 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
7009 if (portLeftInScene.x() + popover->size().width() + cutoffMargin > viewportRect.right())
7010 portLeftInScene = QPoint(viewportRect.right() - popover->size().width() - cutoffMargin, portLeftInScene.y());
7011 if (portLeftInScene.y() + popover->size().height() + cutoffMargin > viewportRect.bottom())
7012 portLeftInScene = QPoint(portLeftInScene.x(), viewportRect.bottom() - popover->size().height() - cutoffMargin);
7014 QPoint popoverLeftInView = views()[0]->mapFromScene(portLeftInScene);
7016 const QPoint offset = QPoint(12, 6);
7018 QPoint popoverTopLeft = popoverLeftInView + offset;
7019 popover->move(popoverTopLeft);
7022 activePortPopovers[portID] = popover;
7024 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7051 VUserLog(
"%s: Close popover for %s",
7056 map<string, VuoPortPopover *>::iterator i = activePortPopovers.find(portID);
7057 if (i != activePortPopovers.end())
7059 popover = i->second;
7060 activePortPopovers.erase(i);
7066 popover->deleteLater();
7069 bool isInput =
false;
7077 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7078 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7080 dispatch_async(topLevelComposition->runCompositionQueue, ^{
7081 if (topLevelComposition->isRunningThreadUnsafe())
7084 topLevelComposition->runner->unsubscribeFromInputPortTelemetry(thisCompositionIdentifier, portID) :
7085 topLevelComposition->runner->unsubscribeFromOutputPortTelemetry(thisCompositionIdentifier, portID));
7095 void VuoEditorComposition::disablePopoverForPortThreadSafe(
string portID)
7097 dispatch_sync(activePortPopoversQueue, ^{
7107 disablePortPopovers();
7118 errorPopover->hide();
7119 errorPopover->deleteLater();
7122 errorPopovers.clear();
7131 dispatch_sync(activePortPopoversQueue, ^{
7132 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7133 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7135 string portID = i->first;
7136 bool shouldDisable = false;
7139 shouldDisable = true;
7142 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7143 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7158 dispatch_sync(activePortPopoversQueue, ^{
7159 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7160 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7162 string portID = i->first;
7164 bool foundPort = identifierCache->doForPortWithIdentifier(portID, [&foundPort](VuoPort *port) {});
7177 if (recordWhichPopoversClosed)
7178 portsWithPopoversClosedAtLastEvent.clear();
7180 dispatch_sync(activePortPopoversQueue, ^{
7181 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7182 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7184 string portID = i->first;
7185 bool shouldDisable = false;
7188 shouldDisable = true;
7191 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7192 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7202 portsWithPopoversClosedAtLastEvent.insert(portID);
7214 moveDetachedPortPopoversBy(dx, dy);
7215 moveErrorPopoversBy(dx, dy);
7221 void VuoEditorComposition::moveErrorPopoversBy(
int dx,
int dy)
7224 errorPopover->move(errorPopover->pos().x()+dx, errorPopover->pos().y()+dy);
7230 void VuoEditorComposition::moveDetachedPortPopoversBy(
int dx,
int dy)
7232 dispatch_sync(activePortPopoversQueue, ^{
7233 map<string, VuoPortPopover *> portPopovers = activePortPopovers;
7234 for (map<string, VuoPortPopover *>::iterator i = portPopovers.begin(); i != portPopovers.end(); ++i)
7236 VuoPortPopover *popover = i->second;
7237 if (popover && popover->getDetached())
7238 popover->move(popover->pos().x()+dx, popover->pos().y()+dy);
7246 void VuoEditorComposition::setPopoversHideOnDeactivate(
bool shouldHide)
7248 dispatch_sync(activePortPopoversQueue, ^{
7249 auto portPopovers = activePortPopovers;
7250 for (
auto i : portPopovers)
7255 id nsWindow = (id)VuoPopover::getWindowForPopover(popover);
7256 objc_msgSend(nsWindow, sel_getUid(
"setHidesOnDeactivate:"), shouldHide);
7268 dispatch_sync(activePortPopoversQueue, ^{
7269 for (map<string, VuoPortPopover *>::iterator i = activePortPopovers.begin(); i != activePortPopovers.end(); ++i)
7271 string portID = i->first;
7272 VuoPortPopover *popover = i->second;
7273 bool shouldUpdate = false;
7276 shouldUpdate = true;
7279 identifierCache->doForPortWithIdentifier(portID, [&shouldUpdate, node](VuoPort *port) {
7280 shouldUpdate = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7285 QMetaObject::invokeMethod(popover,
"updateTextAndResize", Qt::QueuedConnection);
7302 string popoverCompositionIdentifier,
7313 string portSummary = (isInput ?
7317 dispatch_async(popoverComposition->activePortPopoversQueue, ^{
7318 VuoPortPopover *popover = popoverComposition->getActivePopoverForPort(portID);
7321 QMetaObject::invokeMethod(popover,
"updateDataValueImmediately", Qt::QueuedConnection, Q_ARG(QString, portSummary.c_str()));
7322 QMetaObject::invokeMethod(popover,
"setCompositionRunning", Qt::QueuedConnection, Q_ARG(bool, true), Q_ARG(bool, false));
7336 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7338 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7339 if (topLevelComposition->isRunningThreadUnsafe())
7340 topLevelComposition->updateDataInPortPopoverFromRunningTopLevelComposition(this, thisCompositionIdentifier, portID);
7350 bool receivedEvent,
bool receivedData,
string dataSummary)
7354 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7355 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7357 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7358 Q_ARG(bool, receivedEvent),
7359 Q_ARG(bool, receivedData),
7360 Q_ARG(QString, dataSummary.c_str()));
7363 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7371 bool sentEvent,
bool sentData,
string dataSummary)
7375 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7376 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7378 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7379 Q_ARG(bool, sentEvent),
7380 Q_ARG(bool, sentData),
7381 Q_ARG(QString, dataSummary.c_str()));
7384 if (matchingComposition->showEventsMode && sentEvent)
7389 port->getRenderer()->setFiredEvent();
7390 matchingComposition->animatePort(port->getRenderer());
7395 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7406 dispatch_async(matchingComposition->runCompositionQueue, ^{
7407 if (matchingComposition->isRunningThreadUnsafe())
7409 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7410 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7412 QMetaObject::invokeMethod(popover,
"incrementDroppedEventCount", Qt::QueuedConnection);
7417 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7428 if (matchingComposition->showEventsMode)
7430 dispatch_async(this->runCompositionQueue, ^{
7431 if (this->isRunningThreadUnsafe())
7440 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7451 if (matchingComposition->showEventsMode)
7453 dispatch_async(this->runCompositionQueue, ^{
7454 if (this->isRunningThreadUnsafe())
7463 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7492 return showEventsMode;
7500 this->showEventsMode = showEventsMode;
7508 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7509 if (topLevelComposition->isRunningThreadUnsafe())
7510 topLevelComposition->runner->subscribeToEventTelemetry(thisCompositionIdentifier);
7513 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, subscribe);
7521 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7522 if (topLevelComposition->isRunningThreadUnsafe())
7523 topLevelComposition->runner->unsubscribeFromEventTelemetry(thisCompositionIdentifier);
7526 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, unsubscribe);
7562 QGraphicsItemAnimation * VuoEditorComposition::setUpAnimationForPort(QGraphicsItemAnimation *animation,
VuoRendererPort *port)
7570 animatedPort->setZValue(VuoRendererItem::triggerAnimationZValue);
7573 animation->setItem(animatedPort);
7574 animation->setScaleAt(0.0, 1, 1);
7575 animation->setScaleAt(0.999, 3, 3);
7576 animation->setScaleAt(1.0, 1, 1);
7578 QTimeLine *animationTimeline = animation->timeLine();
7579 animationTimeline->setFrameRange(0, 100);
7580 animationTimeline->setUpdateInterval(showEventsModeUpdateInterval);
7581 animationTimeline->setCurveShape(QTimeLine::LinearCurve);
7583 preparedAnimations.insert(animation);
7584 animationForTimeline[animation->timeLine()] = animation;
7586 connect(animationTimeline, &QTimeLine::valueChanged,
this, &VuoEditorComposition::updatePortAnimation);
7587 connect(animationTimeline, &QTimeLine::finished,
this, &VuoEditorComposition::endPortAnimation);
7597 dispatch_async(dispatch_get_main_queue(), ^{
7598 QGraphicsItemAnimation *animation = getAvailableAnimationForPort(port);
7604 if (animation->timeLine()->state() == QTimeLine::Running)
7605 animation->timeLine()->setCurrentTime(0);
7609 animatedPort->setPos(port->pos());
7610 animatedPort->setVisible(
true);
7611 animation->timeLine()->start();
7620 QGraphicsItemAnimation * VuoEditorComposition::getAvailableAnimationForPort(
VuoRendererPort *port)
7622 vector<QGraphicsItemAnimation *> animations = port->
getAnimations();
7624 QGraphicsItemAnimation *mostAdvancedAnimation = NULL;
7625 qreal maxPercentAdvanced = -1;
7627 for (
int i = 0; i < animations.size(); ++i)
7629 QGraphicsItemAnimation *animation = animations[i];
7630 bool animationPrepared = (preparedAnimations.find(animation) != preparedAnimations.end());
7631 bool animationRunning = (animation->timeLine()->state() == QTimeLine::Running);
7633 if (! animationPrepared)
7634 return setUpAnimationForPort(animation, port);
7636 else if (! animationRunning)
7641 qreal percentAdvanced = animation->timeLine()->currentValue();
7642 if (percentAdvanced > maxPercentAdvanced)
7644 mostAdvancedAnimation = animation;
7645 maxPercentAdvanced = percentAdvanced;
7651 return (maxPercentAdvanced >= 0.5? mostAdvancedAnimation : NULL);
7659 void VuoEditorComposition::updatePortAnimation(qreal value)
7661 QTimeLine *animationTimeline = (QTimeLine *)sender();
7662 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7664 const qreal multiplier = 1000.;
7672 void VuoEditorComposition::endPortAnimation(
void)
7674 QTimeLine *animationTimeline = (QTimeLine *)sender();
7675 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7677 animatedPort->setVisible(
false);
7683 void VuoEditorComposition::setDisableDragStickiness(
bool disable)
7685 this->dragStickinessDisabled = disable;
7693 this->ignoreApplicationStateChangeEvents = ignore;
7703 this->popoverEventsEnabled = enable;
7712 refreshComponentAlphaLevelTimer->start();
7720 refreshComponentAlphaLevelTimer->stop();
7743 if (!activeProtocol)
7750 if (!
getBase()->
getCompiler()->getCachedGraph()->mayEventsReachPublishedOutputPorts())
7752 QString errorHeadline = tr(
"<b>This composition doesn't send any images to <code>outputImage</code>.</b>");
7753 QString errorDetails = tr(
"<p>To export, your composition should use the data and events from the published input ports "
7754 "to output a stream of images through the <code>outputImage</code> published output port.</p>");
7756 if (isExportingMovie)
7757 errorDetails.append(
"<p>Alternatively, you can record a realtime movie by running the composition and selecting File > Start Recording.</p>");
7760 QMessageBox messageBox(window);
7761 messageBox.setWindowFlags(Qt::Sheet);
7762 messageBox.setWindowModality(Qt::WindowModal);
7764 messageBox.setTextFormat(Qt::RichText);
7766 messageBox.setStandardButtons(QMessageBox::Help | QMessageBox::Ok);
7767 messageBox.setButtonText(QMessageBox::Help, tr(
"Open an Example"));
7768 messageBox.setButtonText(QMessageBox::Ok, tr(
"OK"));
7769 messageBox.setDefaultButton(QMessageBox::Ok);
7771 messageBox.setText(errorHeadline);
7772 messageBox.setInformativeText(
"<style>p{" + fonts->
getCSS(fonts->
dialogBodyFont()) +
"}</style>" + errorDetails);
7774 if (messageBox.exec() == QMessageBox::Help)
7776 map<QString, QString> examples =
static_cast<VuoEditor *
>(qApp)->getExampleCompositionsForProtocol(activeProtocol);
7777 map<QString, QString>::iterator i = examples.begin();
7778 if (i != examples.end())
7808 string dir, file, ext;
7822 if (! customizedName.empty())
7823 return QString::fromStdString(customizedName);
7841 string fileNameContentPart = (fileNameParts.size() >= 2 && fileNameParts[fileNameParts.size()-1] ==
"vuo"?
7842 fileNameParts[fileNameParts.size()-2] :
7843 (fileNameParts.size() >= 1? fileNameParts[fileNameParts.size()-1] :
""));
7846 if (QRegExp(
"\\s").indexIn(fileNameContentPart.c_str()) != -1)
7848 string formattedName = fileNameContentPart;
7849 if (formattedName.size() >= 1)
7850 formattedName[0] = toupper(formattedName[0]);
7852 return QString(formattedName.c_str());
7866 QStringList wordsInName = nodeSetName.split(QRegularExpression(
"\\."));
7867 if (wordsInName.size() < 2 || wordsInName[0] !=
"vuo")
7873 map<QString, QString> wordsToReformat;
7874 wordsToReformat[
"artnet"] =
"Art-Net";
7875 wordsToReformat[
"bcf2000"] =
"BCF2000";
7876 wordsToReformat[
"hid"] =
"HID";
7877 wordsToReformat[
"midi"] =
"MIDI";
7878 wordsToReformat[
"ndi"] =
"NDI";
7879 wordsToReformat[
"osc"] =
"OSC";
7880 wordsToReformat[
"rss"] =
"RSS";
7881 wordsToReformat[
"ui"] =
"UI";
7882 wordsToReformat[
"url"] =
"URL";
7884 QString nodeSetDisplayName =
"";
7885 for (
int i = 1; i < wordsInName.size(); ++i)
7887 QString currentWord = wordsInName[i];
7888 if (currentWord.size() >= 1)
7890 if (wordsToReformat.find(currentWord.toLower()) != wordsToReformat.end())
7891 currentWord = wordsToReformat.at(currentWord.toLower());
7893 currentWord[0] = currentWord[0].toUpper();
7895 nodeSetDisplayName += currentWord;
7897 if (i < wordsInName.size()-1)
7898 nodeSetDisplayName +=
" ";
7901 return nodeSetDisplayName;
7914 string formattedTypeName =
"";
7922 formattedTypeName =
"List of " + formattedInnerTypeName +
" elements";
7928 return formattedTypeName.c_str();
7947 return "Transform2D";
7949 return "Transform3D";
7957 bool VuoEditorComposition::nodeSetMenuActionLessThan(QAction *action1, QAction *action2)
7959 QString item1Text = action1->text();
7960 QString item2Text = action2->text();
7963 const QString listPrefix =
"List of ";
7964 const QString builtInTypePrefix =
"Vuo";
7966 if (item1Text.startsWith(listPrefix))
7968 item1Text.remove(0, listPrefix.length());
7969 if (item1Text.startsWith(builtInTypePrefix))
7970 item1Text.remove(0, builtInTypePrefix.length());
7973 if (item2Text.startsWith(listPrefix))
7975 item2Text.remove(0, listPrefix.length());
7976 if (item2Text.startsWith(builtInTypePrefix))
7977 item2Text.remove(0, builtInTypePrefix.length());
7981 return (item1Text.compare(item2Text, Qt::CaseInsensitive) < 0);
7988 bool VuoEditorComposition::itemHigherOnCanvas(QGraphicsItem *item1, QGraphicsItem *item2)
7990 qreal item1Y = item1->scenePos().y();
7991 qreal item2Y = item2->scenePos().y();
7993 qreal item1X = item1->scenePos().x();
7994 qreal item2X = item2->scenePos().x();
7996 if (item1Y == item2Y)
7997 return (item1X < item2X);
7999 return item1Y < item2Y;
8012 string originalGenericNode1ClassName, originalGenericNode2ClassName;
8028 vector<string> node1Keywords = node1->
getKeywords();
8029 vector<string> node2Keywords = node2->
getKeywords();
8041 node1Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8045 node2Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8047 set<string> node1KeywordSet(node1Keywords.begin(), node1Keywords.end());
8048 set<string> node2KeywordSet(node2Keywords.begin(), node2Keywords.end());
8050 set<string> nodeKeywordsIntersection;
8051 std::set_intersection(node1KeywordSet.begin(), node1KeywordSet.end(),
8052 node2KeywordSet.begin(), node2KeywordSet.end(),
8053 std::inserter(nodeKeywordsIntersection, nodeKeywordsIntersection.end()));
8055 set<string> nodeKeywordsUnion = node1KeywordSet;
8056 nodeKeywordsUnion.insert(node2KeywordSet.begin(), node2KeywordSet.end());
8059 if (nodeKeywordsUnion.size() == 0)
8063 double nodeSimilarity = nodeKeywordsIntersection.size()/(1.0*nodeKeywordsUnion.size());
8065 return nodeSimilarity;
8084 VuoEditorComposition::~VuoEditorComposition()
8086 dispatch_sync(runCompositionQueue, ^{});
8087 dispatch_release(runCompositionQueue);
8089 preparedAnimations.clear();
8090 animationForTimeline.clear();
8092 delete identifierCache;
8118 vector<VuoRendererPort *> sortedPortsToPublish;
8119 foreach (
string portID, portsToPublish)
8123 sortedPortsToPublish.push_back(port->
getRenderer());
8125 std::sort(sortedPortsToPublish.begin(), sortedPortsToPublish.end(), itemHigherOnCanvas);
8127 map<string, string> publishedPortNames;
8134 string publishedPortName = (!specializedPublishedPortName.empty()?
8135 specializedPublishedPortName :
8144 return publishedPortNames;
8172 void VuoEditorComposition::repositionPopover()
8177 const int cutoffMargin = 16;
8178 if (popover->pos().x()+popover->size().width()+cutoffMargin > views()[0]->viewport()->rect().right())
8179 popover->move(QPoint(views()[0]->viewport()->rect().right()-popover->size().width()-cutoffMargin, popover->pos().y()));
8181 if (popover->pos().y()+popover->size().height()+cutoffMargin > views()[0]->viewport()->rect().bottom())
8182 popover->move(QPoint(popover->pos().x(), views()[0]->viewport()->rect().bottom()-popover->size().height()-cutoffMargin));