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 targetPort = adjustedTargetPort;
2358 if (cableInProgressExpectedToCarryData &&
2360 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2361 typecastNodeToDelete = uncollapsedTypecast;
2366 if (preexistingCable)
2367 cableToReplace = preexistingCable;
2371 if (cableInProgressExpectedToCarryData)
2375 for (vector<VuoCable *>::iterator cable = previousConnectedCables.begin(); (! dataCableToDisplace) && (cable != previousConnectedCables.end()); ++cable)
2376 if ((((*cable)->getRenderer()->effectivelyCarriesData()) && (*cable) != cableToReplace))
2377 dataCableToDisplace = *cable;
2384 if (publishedDataConnections.size() > 0)
2385 portToUnpublish = targetPort;
2389 completedCableConnection =
true;
2393 else if (!preexistingCableWithMatchingDataCarryingStatus &&
2395 selectBridgingSolution(targetPort, fixedPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert)))
2398 completedCableConnection =
true;
2403 if (completedCableConnection)
2407 if (draggingPreviouslyPublishedCable)
2426 cableInProgressCopy->
setFrom(cableInProgressFromNode, cableInProgressFromPort);
2427 cableInProgressCopy->
setTo(cableInProgressToNode, cableInProgressToPort);
2434 cableInProgress = cableInProgressCopy;
2435 cableInProgressWasNew =
true;
2439 pair<VuoRendererCable *, VuoRendererCable *> cableArgs = std::make_pair((dataCableToDisplace? dataCableToDisplace->
getRenderer() : NULL),
2440 (cableToReplace? cableToReplace->
getRenderer() : NULL));
2441 pair<string, string> typeArgs = std::make_pair(typecastToInsert, specializedTypeName);
2442 pair<VuoRendererPort *, VuoRendererPort *> portArgs = std::make_pair(portToUnpublish, portToSpecialize);
2447 typecastNodeToDelete,
2454 if (cableInProgress)
2458 cableInProgress = NULL;
2472 void VuoEditorComposition::concludePublishedCableDrag(QGraphicsSceneMouseEvent *event)
2482 string typecastToInsert =
"";
2486 string specializedTypeName =
"";
2503 if (internalInputPort)
2506 forceEventOnlyPublication =
true;
2512 if (internalInputPort &&
2518 internalInputPort->
getBase()) &&
2521 if (recreatingSameConnection)
2529 ||
selectBridgingSolution(publishedInputPort, internalInputPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert))
2534 bool cableToReplaceHasMatchingDataCarryingStatus = (cableToReplace?
2536 cableInProgressExpectedToCarryData) :
2541 if (cableToReplace && cableToReplaceHasMatchingDataCarryingStatus)
2547 else if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus &&
2563 if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus)
2565 QList<QGraphicsItem *> removedComponents;
2566 removedComponents.append(cableToReplace->
getRenderer());
2575 if (typecastPort && typecastPort->scene())
2583 if (cableInProgressExpectedToCarryData &&
2585 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2587 QList<QGraphicsItem *> removedComponents;
2588 removedComponents.append(uncollapsedTypecast);
2595 forceEventOnlyPublication,
2596 (portToSpecialize? portToSpecialize->
getBase() : NULL),
2597 specializedTypeName,
2623 if (internalOutputPort)
2626 forceEventOnlyPublication =
true;
2632 if (internalOutputPort &&
2633 publishedOutputPort)
2639 ||
selectBridgingSolution(internalOutputPort, publishedOutputPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert))
2643 forceEventOnlyPublication,
2644 portToSpecialize? portToSpecialize->
getBase() : NULL,
2645 specializedTypeName,
2664 if (! cableInProgress)
2667 if (cableInProgressWasNew)
2671 QList<QGraphicsItem *> removedComponents;
2672 removedComponents.append(cableInProgress->
getRenderer());
2676 cableInProgress = NULL;
2689 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2694 cableInProgress = NULL;
2696 else if (cableInProgress)
2711 bool leftMouseButtonPressed = (
event->buttons() & Qt::LeftButton);
2727 if (cableInProgress || (! leftMouseButtonPressed))
2728 updateHoverHighlighting(event->scenePos());
2732 if (leftMouseButtonPressed)
2735 if ((! dragStickinessDisabled) && (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2739 dragStickinessDisabled =
true;
2743 if (portWithDragInitiated || cableWithYankInitiated)
2745 initiateCableDrag(portWithDragInitiated, cableWithYankInitiated, event);
2746 portWithDragInitiated = NULL;
2747 cableWithYankInitiated = NULL;
2752 if (cableInProgress)
2762 else if (duplicationDragInProgress)
2764 if (duplicateOnNextMouseMove)
2767 duplicateOnNextMouseMove =
false;
2774 QPointF delta = newPos - cursorPosBeforeDuplicationDragMove;
2776 cursorPosBeforeDuplicationDragMove = newPos;
2780 QGraphicsScene::mouseMoveEvent(event);
2785 QGraphicsScene::mouseMoveEvent(event);
2793 void VuoEditorComposition::updateHoverHighlighting(QPointF scenePos,
bool disablePortHoverHighlighting)
2807 bool hoveringOverNodeHeader =
false;
2808 if (cableInProgress)
2817 hoveringOverNodeHeader =
true;
2823 if (! hoveringOverNodeHeader)
2834 if (item != previousNearbyItem)
2842 if (previousNearbyItem)
2850 if (! cableInProgress)
2856 else if (typecastPort && !disablePortHoverHighlighting)
2858 else if (port && !disablePortHoverHighlighting)
2861 if (!cableInProgress)
2872 if (cableInProgress)
2875 cableInProgress->getFromPort()->getRenderer() :
2876 cableInProgress->getToPort()->getRenderer());
2878 QList< QPair<VuoRendererPort *, bool> > updatedPorts;
2879 if (hoveringOverNodeHeader)
2880 updatedPorts.append( QPair<VuoRendererPort *, bool>(port,
true) );
2881 if (previousNode && previousPort)
2882 updatedPorts.append( QPair<VuoRendererPort *, bool>(previousPort, !cableInProgress->getRenderer()->effectivelyCarriesData()) );
2884 QPair<VuoRendererPort *, bool> p;
2885 foreach (p, updatedPorts)
2890 if (typecastParentPort)
2891 updatedPort = typecastParentPort;
2893 updateEligibilityHighlightingForPort(updatedPort, fixedPort, p.second, types);
2896 updateEligibilityHighlightingForNode(potentialDrawer);
2903 if (targetPort || previousTargetPort)
2905 if (cableInProgress)
2906 cableInProgress->getRenderer()->setFloatingEndpointAboveEventPort(targetPort && (!targetPort->
getDataType() || hoveringOverNodeHeader));
2911 previousNearbyItem = item;
2919 else if (port && !disablePortHoverHighlighting)
2922 if (!cableInProgress)
2928 else if (makeListDrawer)
2938 if (previousNearbyItem)
2956 else if (typecastPort)
2965 previousNearbyItem = NULL;
2974 if ((event->key() != Qt::Key_Alt) && (event->key() != Qt::Key_Shift) && (event->key() != Qt::Key_Escape))
2977 Qt::KeyboardModifiers modifiers =
event->modifiers();
2978 qreal adjustedNodeMoveRate = nodeMoveRate;
2979 if (modifiers & Qt::ShiftModifier)
2981 adjustedNodeMoveRate *= nodeMoveRateMultiplier;
2984 switch (event->key()) {
2985 case Qt::Key_Backspace:
2990 case Qt::Key_Delete:
3002 if (modifiers & Qt::ControlModifier)
3003 openSelectedEditableNodes();
3020 if (cableInProgress)
3022 cableInProgress->getRenderer()->updateGeometry();
3023 cableInProgress->getCompiler()->setAlwaysEventOnly(
true);
3024 highlightEligibleEndpointsForCable(cableInProgress);
3034 case Qt::Key_Return:
3037 QGraphicsScene::keyPressEvent(event);
3039 if (!event->isAccepted())
3046 if (selectedComments.empty() && !selectedNodes.empty())
3053 case Qt::Key_Escape:
3055 if (duplicateOnNextMouseMove || duplicationDragInProgress)
3059 duplicateOnNextMouseMove =
false;
3060 duplicationDragInProgress =
false;
3061 duplicationCancelled =
true;
3063 else if (cableInProgress)
3072 QGraphicsScene::keyPressEvent(event);
3083 switch (event->key()) {
3086 if (cableInProgress)
3088 cableInProgress->getRenderer()->updateGeometry();
3089 cableInProgress->getCompiler()->setAlwaysEventOnly(
false);
3090 highlightEligibleEndpointsForCable(cableInProgress);
3102 QGraphicsScene::keyReleaseEvent(event);
3117 contextMenu.setSeparatorsCollapsible(
false);
3122 QAction *insertNodeSection =
new QAction(tr(
"Insert Node"), NULL);
3123 insertNodeSection->setEnabled(
false);
3124 contextMenu.addAction(insertNodeSection);
3126 QString spacer(
" ");
3130 QMenu *shareMenu =
new QMenu(&contextMenu);
3131 shareMenu->setSeparatorsCollapsible(
false);
3132 shareMenu->setTitle(spacer + tr(
"Share"));
3133 contextMenu.addMenu(shareMenu);
3136 QAction *action =
new QAction(tr(
"Share Value"), NULL);
3137 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share"))));
3138 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3139 shareMenu->addAction(action);
3143 QAction *action =
new QAction(tr(
"Share List"), NULL);
3144 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share.list"))));
3145 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3146 shareMenu->addAction(action);
3150 QAction *action =
new QAction(tr(
"Share Event"), NULL);
3151 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.share"))));
3152 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3153 shareMenu->addAction(action);
3159 QMenu *holdMenu =
new QMenu(&contextMenu);
3160 holdMenu->setSeparatorsCollapsible(
false);
3161 holdMenu->setTitle(spacer + tr(
"Hold"));
3162 contextMenu.addMenu(holdMenu);
3165 QAction *action =
new QAction(tr(
"Hold Value"), NULL);
3166 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold2"))));
3167 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3168 holdMenu->addAction(action);
3172 QAction *action =
new QAction(tr(
"Hold List"), NULL);
3173 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold.list2"))));
3174 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3175 holdMenu->addAction(action);
3181 QMenu *allowMenu =
new QMenu(&contextMenu);
3182 allowMenu->setSeparatorsCollapsible(
false);
3183 allowMenu->setTitle(spacer + tr(
"Allow"));
3184 contextMenu.addMenu(allowMenu);
3187 QAction *action =
new QAction(tr(
"Allow First Event"), NULL);
3188 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirst"))));
3189 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3190 allowMenu->addAction(action);
3194 QAction *action =
new QAction(tr(
"Allow First Value"), NULL);
3195 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirstValue"))));
3196 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3197 allowMenu->addAction(action);
3201 QAction *action =
new QAction(tr(
"Allow Periodic Events"), NULL);
3202 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.time.allowPeriodic"))));
3203 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3204 allowMenu->addAction(action);
3208 QAction *action =
new QAction(tr(
"Allow Changes"), NULL);
3209 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowChanges2"))));
3210 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3211 allowMenu->addAction(action);
3215 contextMenu.addSeparator();
3217 QAction *contextMenuInsertComment =
new QAction(NULL);
3218 contextMenuInsertComment->setText(tr(
"Insert Comment"));
3219 contextMenuInsertComment->setData(qVariantFromValue(event->scenePos()));
3220 connect(contextMenuInsertComment, &QAction::triggered,
this, &VuoEditorComposition::insertComment);
3221 contextMenu.addAction(contextMenuInsertComment);
3223 QAction *contextMenuInsertSubcomposition =
new QAction(NULL);
3224 contextMenuInsertSubcomposition->setText(tr(
"Insert Subcomposition"));
3225 contextMenuInsertSubcomposition->setData(qVariantFromValue(event->scenePos()));
3226 connect(contextMenuInsertSubcomposition, &QAction::triggered,
this, &VuoEditorComposition::insertSubcomposition);
3227 contextMenu.addAction(contextMenuInsertSubcomposition);
3232 QAction *contextMenuDeleteSelectedSnapshot =
new QAction(NULL);
3239 if (port->
isConstant() && inputEditorManager)
3243 if (inputEditorLoadedForPortDataType)
3245 contextMenuSetPortConstant->setData(qVariantFromValue((
void *)port));
3246 contextMenu.addAction(contextMenuSetPortConstant);
3248 inputEditorLoadedForPortDataType->deleteLater();
3254 contextMenuPublishPort->setText(tr(
"Publish Port"));
3255 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3256 contextMenu.addAction(contextMenuPublishPort);
3261 vector<VuoRendererPublishedPort *> externalPublishedPorts = port->
getPublishedPorts();
3262 bool hasExternalPublishedPortWithMultipleInternalPorts =
false;
3263 bool hasExternalPublishedPortBelongingToActiveProtocol =
false;
3267 hasExternalPublishedPortWithMultipleInternalPorts =
true;
3270 hasExternalPublishedPortBelongingToActiveProtocol =
true;
3277 if (!hasExternalPublishedPortWithMultipleInternalPorts &&!hasExternalPublishedPortBelongingToActiveProtocol)
3279 contextMenuPublishPort->setText(tr(
"Delete Published Port"));
3280 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3281 contextMenu.addAction(contextMenuPublishPort);
3286 if (isTriggerPort || port->
getInput())
3288 __block
bool isTopLevelCompositionRunning =
false;
3289 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
3291 isTopLevelCompositionRunning = topLevelComposition->
isRunning();
3294 if (isTopLevelCompositionRunning)
3296 contextMenuFireEvent->setData(qVariantFromValue((
void *)port));
3297 contextMenu.addAction(contextMenuFireEvent);
3303 QMenu *contextMenuThrottling =
new QMenu(&contextMenu);
3304 contextMenuThrottling->setSeparatorsCollapsible(
false);
3305 contextMenuThrottling->setTitle(tr(
"Set Event Throttling"));
3307 foreach (QAction *action, contextMenuThrottlingActions)
3309 contextMenuThrottling->addAction(action);
3310 action->setData(qVariantFromValue(port));
3311 action->setCheckable(
true);
3314 contextMenu.addMenu(contextMenuThrottling);
3319 if (genericDataType || isPortCurrentlyRevertible(port))
3321 QMenu *contextMenuSpecializeGenericType =
new QMenu(&contextMenu);
3322 contextMenuSpecializeGenericType->setSeparatorsCollapsible(
false);
3323 contextMenuSpecializeGenericType->setTitle(tr(
"Set Data Type"));
3324 contextMenuSpecializeGenericType->setToolTipsVisible(
true);
3326 QAction *unspecializeAction = contextMenuSpecializeGenericType->addAction(tr(
"Generic"));
3331 contextMenuSpecializeGenericType->addSeparator();
3333 unspecializeAction->setData(qVariantFromValue((
void *)port));
3334 unspecializeAction->setCheckable(
true);
3335 unspecializeAction->setChecked(genericDataType);
3337 contextMenu.addMenu(contextMenuSpecializeGenericType);
3340 set<string> compatibleTypes;
3341 set<string> compatibleTypesInIsolation;
3344 if (genericDataType)
3352 compatibleTypes = set<string>(compatibleTypesVector.begin(), compatibleTypesVector.end());
3358 compatibleTypes.insert(compatibleTypeNames.begin(), compatibleTypeNames.end());
3363 else if (isPortCurrentlyRevertible(port))
3365 map<VuoNode *, string> nodesToReplace;
3366 set<VuoCable *> cablesToDelete;
3368 if (cablesToDelete.size() >= 1)
3374 port->getUnderlyingParentNode()->getBase()->getNodeClass()->getCompiler());
3376 compatibleTypes = getRespecializationOptionsForPortInNetwork(port);
3380 if (genericTypeFromPortClass)
3392 if (genericHostPortDataType)
3394 else if (isPortCurrentlyRevertible(hostPort->
getRenderer()))
3404 vector<string> compatibleTypesInIsolationVector;
3405 if (genericTypeFromPortClass)
3412 foreach (
string type, compatibleTypesInIsolationVector)
3419 const int maxTypeCountForFlatMenuDisplay = 10;
3420 if (compatibleTypesInIsolation.size() <= maxTypeCountForFlatMenuDisplay)
3422 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
false,
"", contextMenuSpecializeGenericType);
3430 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true,
"", contextMenuSpecializeGenericType);
3435 QList<QAction *> allNodeSetActionsToAdd;
3436 for (map<
string, set<VuoCompilerType *> >::iterator i = loadedTypesForNodeSet.begin(); i != loadedTypesForNodeSet.end(); ++i)
3438 string nodeSetName = i->first;
3439 if (!nodeSetName.empty())
3440 allNodeSetActionsToAdd +=
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true, nodeSetName, contextMenuSpecializeGenericType);
3443 if ((contextMenuSpecializeGenericType->actions().size() > 0) && (allNodeSetActionsToAdd.size() > 0))
3444 contextMenuSpecializeGenericType->addSeparator();
3449 foreach (QAction *action, contextMenuSpecializeGenericType->actions())
3451 QMenu *specializeSubmenu = action->menu();
3452 if (specializeSubmenu)
3454 foreach (QAction *specializeSubaction, specializeSubmenu->actions())
3455 connect(specializeSubaction, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3457 else if (action == unspecializeAction)
3458 connect(action, &QAction::triggered,
this, &VuoEditorComposition::unspecializePortType);
3460 connect(action, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3467 int numVisibleDirectlyConnectedCables = 0;
3468 int numHidableDirectlyConnectedCables = 0;
3469 int numUnhidableDirectlyConnectedCables = 0;
3475 numVisibleDirectlyConnectedCables++;
3477 numHidableDirectlyConnectedCables++;
3481 numUnhidableDirectlyConnectedCables++;
3484 int numVisibleChildPortConnectedCables = 0;
3485 int numHidableChildPortConnectedCables = 0;
3486 int numUnhidableChildPortConnectedCables = 0;
3496 numVisibleChildPortConnectedCables++;
3499 numHidableChildPortConnectedCables++;
3502 numUnhidableChildPortConnectedCables++;
3507 int numVisibleConnectedCables = numVisibleDirectlyConnectedCables + numVisibleChildPortConnectedCables;
3510 int numHidableConnectedCables = numHidableDirectlyConnectedCables + numHidableChildPortConnectedCables;
3513 int numUnhidableConnectedCables = numUnhidableDirectlyConnectedCables + numUnhidableChildPortConnectedCables;
3515 if ((!
renderHiddenCables && ((numHidableConnectedCables >= 1) || (numUnhidableConnectedCables >= 1)))
3516 || (numVisibleConnectedCables >= 1))
3518 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3519 contextMenu.addSeparator();
3524 if (numHidableConnectedCables >= 1)
3529 int numApparentlyHidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3530 contextMenuHideCables->setText(numApparentlyHidableConnectedCables > 1?
"Hide Cables" :
"Hide Cable");
3531 contextMenuHideCables->setData(qVariantFromValue((
void *)port));
3532 contextMenu.addAction(contextMenuHideCables);
3535 if (numUnhidableConnectedCables >= 1)
3537 int numApparentlyUnhidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3538 contextMenuUnhideCables->setText(numApparentlyUnhidableConnectedCables > 1?
"Unhide Cables" :
"Unhide Cable");
3539 contextMenuUnhideCables->setData(qVariantFromValue((
void *)port));
3540 contextMenu.addAction(contextMenuUnhideCables);
3544 if (numVisibleConnectedCables >= 1)
3546 contextMenuDeleteCables->setText(numVisibleConnectedCables > 1?
"Delete Cables" :
"Delete Cable");
3547 contextMenuDeleteCables->setData(qVariantFromValue((
void *)port));
3548 contextMenu.addAction(contextMenuDeleteCables);
3555 if (! item->isSelected())
3558 item->setSelected(
true);
3561 QList<QGraphicsItem *> selectedComponents = selectedItems();
3562 bool onlyCommentsSelected =
true;
3563 bool onlyCablesSelected =
true;
3564 bool selectionContainsMissingNode =
false;
3565 foreach (QGraphicsItem *item, selectedComponents)
3568 onlyCommentsSelected =
false;
3571 onlyCablesSelected =
false;
3574 selectionContainsMissingNode =
true;
3577 contextMenuDeleteSelectedSnapshot->setText(contextMenuDeleteSelected->text());
3585 if (onlyCommentsSelected)
3586 contextMenu.addAction(contextMenuEditSelectedComments);
3591 contextMenu.addSeparator();
3594 contextMenu.addAction(contextMenuRefactorSelected);
3596 contextMenu.addSeparator();
3599 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3609 contextMenu.addAction(contextMenuHideSelectedCables);
3612 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3627 contextMenuAddInputPort->setData(qVariantFromValue((
void *)node));
3628 contextMenuRemoveInputPort->setData(qVariantFromValue((
void *)node));
3631 contextMenuRemoveInputPort->setEnabled(listItemCount >= 1);
3633 contextMenu.addAction(contextMenuAddInputPort);
3634 contextMenu.addAction(contextMenuRemoveInputPort);
3636 contextMenu.addSeparator();
3640 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3651 if (originalGenericNodeClass)
3652 nodeClass = originalGenericNodeClass->
getBase();
3655 QString actionText, sourcePath;
3659 int numSelectedNonAttachmentNodes = 0;
3660 QList<QGraphicsItem *> selectedComponents = selectedItems();
3661 foreach (QGraphicsItem *item, selectedComponents)
3664 numSelectedNonAttachmentNodes++;
3667 if ((numSelectedNonAttachmentNodes == 1) && nodeClassIsEditable)
3670 QAction *editAction =
new QAction(NULL);
3671 editAction->setText(actionText);
3672 editAction->setData(qVariantFromValue(node));
3675 contextMenu.addAction(editAction);
3676 contextMenu.addSeparator();
3681 contextMenu.addAction(contextMenuRenameSelected);
3686 contextMenu.addSeparator();
3689 if (numSelectedNonAttachmentNodes == 1)
3691 if (contextMenuChangeNode)
3692 contextMenuChangeNode->deleteLater();
3695 contextMenuChangeNode->setSeparatorsCollapsible(
false);
3696 contextMenuChangeNode->setTitle(tr(
"Change To"));
3699 if (!contextMenuChangeNode->actions().isEmpty())
3700 contextMenu.addMenu(contextMenuChangeNode);
3704 if (!selectionContainsMissingNode)
3705 contextMenu.addAction(contextMenuRefactorSelected);
3707 if ((numSelectedNonAttachmentNodes == 1) && (nodeClassIsEditable || nodeClassIs3rdParty))
3711 if (!modulePath.isEmpty())
3713 QString enclosingDirUrl =
"file://" + QFileInfo(modulePath).dir().absolutePath();
3714 QAction *openEnclosingFolderAction =
new QAction(NULL);
3715 openEnclosingFolderAction->setText(
"Show in Finder");
3716 openEnclosingFolderAction->setData(qVariantFromValue(enclosingDirUrl));
3718 contextMenu.addAction(openEnclosingFolderAction);
3722 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3723 contextMenu.addSeparator();
3726 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3732 if (!contextMenu.actions().isEmpty())
3737 menuSelectionInProgress =
true;
3738 contextMenu.exec(event->screenPos());
3739 menuSelectionInProgress =
false;
3742 delete contextMenuDeleteSelectedSnapshot;
3750 QAction *sender = (QAction *)QObject::sender();
3762 vector<string> typeOptions;
3764 map<string, VuoCompilerType *> loadedTypes = compiler->
getTypes();
3765 for (map<string, VuoCompilerType *>::iterator i = loadedTypes.begin(); i != loadedTypes.end(); ++i)
3770 (i->first !=
"VuoMathExpressionList"))
3772 typeOptions.push_back(i->first);
3784 set<string> VuoEditorComposition::getRespecializationOptionsForPortInNetwork(
VuoRendererPort *port)
3787 return set<string>();
3794 vector<string> compatibleInnerTypeNames;
3795 vector<string> compatibleTypeNames;
3796 for (
VuoPort *connectedPort : connectedGenericPorts)
3801 if (specializedNodeClass)
3809 vector<string> innermostCompatibleTypeNamesForPort;
3810 for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
3813 if (! innermostCompatibleTypeNamesForPort.empty())
3815 if (compatibleInnerTypeNames.empty())
3816 compatibleInnerTypeNames = innermostCompatibleTypeNamesForPort;
3819 for (
int k = compatibleInnerTypeNames.size() - 1; k >= 0; --k)
3820 if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleInnerTypeNames[k]) ==
3821 innermostCompatibleTypeNamesForPort.end())
3822 compatibleInnerTypeNames.erase(compatibleInnerTypeNames.begin() + k);
3831 string typeNameForPort = genericTypeFromPortClass->
getModuleKey();
3835 for (vector<string>::iterator k = compatibleInnerTypeNames.begin(); k != compatibleInnerTypeNames.end(); ++k)
3836 compatibleTypeNames.push_back(prefix + *k);
3838 if (compatibleTypeNames.empty())
3848 return set<string>(compatibleTypeNames.begin(), compatibleTypeNames.end());
3871 set<string> compatibleTypesInIsolation,
3872 set<string> compatibleTypesInContext,
3873 bool limitToNodeSet,
3877 QList<QAction *> actionsToAddToMenu;
3879 map<string, VuoCompilerType *> allTypes = compiler->
getTypes();
3881 QList<VuoCompilerType *> compatibleTypesForNodeSetDisplay;
3882 foreach (
string typeName, compatibleTypesInIsolation)
3885 if ((!limitToNodeSet || (loadedTypesForNodeSet[nodeSetName].find(type) != loadedTypesForNodeSet[nodeSetName].end())) &&
3888 (typeName !=
"VuoUrl" && typeName !=
"VuoList_VuoUrl"
3890 && typeName !=
"VuoInteraction" && typeName !=
"VuoList_VuoInteraction"
3891 && typeName !=
"VuoInteractionType" && typeName !=
"VuoList_VuoInteractionType"
3892 && typeName !=
"VuoUuid" && typeName !=
"VuoList_VuoUuid"
3894 && typeName !=
"VuoIconPosition" && typeName !=
"VuoList_VuoIconPosition"
3895 && typeName !=
"VuoMesh" && typeName !=
"VuoList_VuoMesh"
3896 && typeName !=
"VuoWindowProperty" && typeName !=
"VuoList_VuoWindowProperty"
3897 && typeName !=
"VuoWindowReference" && typeName !=
"VuoList_VuoWindowReference"))
3898 compatibleTypesForNodeSetDisplay.append(type);
3901 if (!compatibleTypesForNodeSetDisplay.isEmpty())
3903 QMenu *contextMenuNodeSetTypes = NULL;
3904 QList<QAction *> actionsToAddToNodeSetSubmenu;
3905 bool enabledContentAdded =
false;
3908 if (!nodeSetName.empty())
3910 contextMenuNodeSetTypes =
new QMenu(menu);
3911 contextMenuNodeSetTypes->setSeparatorsCollapsible(
false);
3913 contextMenuNodeSetTypes->setToolTipsVisible(
true);
3914 actionsToAddToMenu.append(contextMenuNodeSetTypes->menuAction());
3920 QList<QVariant> portAndSpecializedType;
3921 portAndSpecializedType.append(qVariantFromValue((
void *)genericPort));
3922 portAndSpecializedType.append(typeName.c_str());
3924 QAction *specializeAction;
3928 if (!nodeSetName.empty())
3930 specializeAction =
new QAction(typeTitle, contextMenuNodeSetTypes);
3931 actionsToAddToNodeSetSubmenu.append(specializeAction);
3937 specializeAction =
new QAction(typeTitle, menu);
3938 actionsToAddToMenu.append(specializeAction);
3941 specializeAction->setData(QVariant(portAndSpecializedType));
3942 specializeAction->setCheckable(
true);
3943 specializeAction->setChecked(genericPort && (genericPort->getDataType()->getModuleKey() == type->
getBase()->
getModuleKey()));
3945 if (compatibleTypesInContext.find(typeName) == compatibleTypesInContext.end())
3947 specializeAction->setEnabled(
false);
3949 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."));
3952 enabledContentAdded =
true;
3955 if (contextMenuNodeSetTypes)
3959 if (!enabledContentAdded)
3960 contextMenuNodeSetTypes->setEnabled(
false);
3964 QList<QAction *> actionListWithPromotions = promoteSingletonsFromSubmenus(actionsToAddToMenu);
3965 return actionListWithPromotions;
3972 QList<QAction *> VuoEditorComposition::promoteSingletonsFromSubmenus(QList<QAction *> actionList)
3974 QList<QAction *> modifiedActionList;
3975 foreach (QAction *action, actionList)
3977 if (action->menu() && (action->menu()->actions().size() == 1))
3979 QAction *singleSubaction = action->menu()->actions().first();
3980 action->menu()->removeAction(singleSubaction);
3981 modifiedActionList.append(singleSubaction);
3984 modifiedActionList.append(action);
3987 return modifiedActionList;
3995 std::sort(actionList.begin(), actionList.end(), nodeSetMenuActionLessThan);
3996 foreach (QAction *action, actionList)
3997 menu->addAction(action);
4003 void VuoEditorComposition::updatePopoversForActiveWindowChange(QWidget *old, QWidget *now)
4016 void VuoEditorComposition::updatePopoversForApplicationStateChange(
bool active)
4018 if (ignoreApplicationStateChangeEvents)
4022 if (activeWindow && (activeWindow->
getComposition() ==
this) && (!activeWindow->isMinimized()))
4036 return findNearbyComponent(scenePos, VuoEditorComposition::targetTypePort, limitPortCollisionRange);
4044 QGraphicsItem *item =
findNearbyComponent(scenePos, VuoEditorComposition::targetTypeNodeHeader);
4062 bool limitPortCollisionRange)
4068 bool ignoreComments;
4072 case VuoEditorComposition::targetTypePort:
4074 ignoreCables =
true;
4076 ignorePorts =
false;
4077 ignoreComments =
true;
4080 case VuoEditorComposition::targetTypeNodeHeader:
4082 ignoreCables =
true;
4085 ignoreComments =
true;
4090 ignoreCables =
false;
4091 ignoreNodes =
false;
4092 ignorePorts =
false;
4093 ignoreComments =
false;
4101 QGraphicsItem *topmostItemUnderCursor = itemAt(scenePos, views()[0]->transform());
4102 if (topmostItemUnderCursor && (!topmostItemUnderCursor->isEnabled()))
4103 topmostItemUnderCursor = NULL;
4107 QRectF searchRect(scenePos.x()-0.5*rectLength, scenePos.y()-0.5*rectLength, rectLength, rectLength);
4108 QList<QGraphicsItem *> itemsInRange = items(searchRect);
4109 for (QList<QGraphicsItem *>::iterator i = itemsInRange.begin(); i != itemsInRange.end(); ++i)
4111 if (!(*i)->isEnabled())
4118 bool makeListDragHandle =
4121 if (makeListDragHandle &&
4122 ((! topmostItemUnderCursor) ||
4123 (topmostItemUnderCursor == makeListDrawer) ||
4124 (topmostItemUnderCursor->zValue() < makeListDrawer->zValue())))
4126 return makeListDrawer;
4137 ((! topmostItemUnderCursor) ||
4138 (topmostItemUnderCursor == port) ||
4140 (topmostItemUnderCursor->zValue() < port->zValue()))
4142 ((! limitPortCollisionRange) ||
4156 ((! topmostItemUnderCursor) ||
4157 (topmostItemUnderCursor == cable) ||
4158 (topmostItemUnderCursor->zValue() < cable->zValue())))
4165 if (! ignoreComments)
4172 ((! topmostItemUnderCursor) ||
4173 (topmostItemUnderCursor == (*i)) ||
4174 (topmostItemUnderCursor->zValue() < (*i)->zValue())))
4181 if (targetType == VuoEditorComposition::targetTypeNodeHeader)
4187 headerRect = node->mapToScene(headerRect).
boundingRect();
4188 if (headerRect.intersects(searchRect) && scenePos.y() <= headerRect.bottom())
4200 return topmostItemUnderCursor;
4205 return ((
VuoRendererPort *)(topmostItemUnderCursor))->getRenderedParentNode();
4222 if (! cableInProgress)
4225 VuoPort *fromPort = cableInProgress->getFromPort();
4226 VuoPort *toPort = cableInProgress->getToPort();
4227 VuoPort *fixedPort = (fromPort? fromPort: toPort);
4242 vector<VuoRendererPort *> portList;
4257 if (portList.size() > firstPortIndex)
4259 targetPort = portList[firstPortIndex];
4265 for (
int i = firstPortIndex; i < portList.size(); ++i)
4269 firstPortWithoutWall = portList[i];
4273 if (firstPortWithoutWall)
4274 targetPort = firstPortWithoutWall;
4283 if (portList.size() > firstPortIndex)
4284 targetPort = portList[firstPortIndex];
4305 QRectF boundingRect;
4306 foreach (QGraphicsItem *item, items())
4310 boundingRect |= item->sceneBoundingRect();
4313 return boundingRect;
4322 QRectF boundingRect;
4324 foreach (QGraphicsItem *item, selectedItems())
4328 boundingRect |= item->sceneBoundingRect();
4331 return boundingRect;
4340 QRectF boundingRect;
4342 foreach (QGraphicsItem *item, selectedItems())
4346 boundingRect |= item->mapToScene(item->childrenBoundingRect()).boundingRect();
4356 boundingRect |= drawer->mapToScene(drawer->childrenBoundingRect()).boundingRect();
4361 return boundingRect;
4401 if (updateInRunningComposition)
4410 if (updateInRunningComposition)
4436 if (!errorMarkingUpdatesEnabled)
4439 errorMarkingUpdatesEnabled =
false;
4456 set<VuoCompilerCable *> potentialCables;
4458 if (targetPort && cableInProgress)
4464 if (cableInProgress->getFromNode())
4466 fromNode = cableInProgress->getFromNode();
4467 fromPort = cableInProgress->getFromPort();
4469 toPort = targetPort->
getBase();
4474 fromPort = targetPort->
getBase();
4475 toNode = cableInProgress->getToNode();
4476 toPort = cableInProgress->getToPort();
4481 potentialCable->
setAlwaysEventOnly(! cableInProgress->getRenderer()->effectivelyCarriesData() ||
4482 cableInProgress->getRenderer()->isFloatingEndpointAboveEventPort());
4486 potentialCables.insert(potentialCable);
4497 VUserLog(
"%s: Showing error popover: %s",
4505 set<VuoRendererNode *> nodesToMark;
4506 set<VuoRendererCable *> cablesToMark;
4508 set<VuoNode *> problemNodes = issue.
getNodes();
4509 foreach (
VuoNode *node, problemNodes)
4513 set<VuoCable *> problemCables = issue.
getCables();
4514 foreach (
VuoCable *cable, problemCables)
4516 VuoCable *cableToMark = (cable->
getCompiler() == potentialCable ? cableInProgress : cable);
4525 errorPopovers.insert(errorPopover);
4529 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
4533 if (targetPort && cableInProgress && nodesToMark.find(targetPort->
getRenderedParentNode()) != nodesToMark.end())
4537 else if (! nodesToMark.empty())
4540 qreal topY = viewportRect.bottom();
4546 QPointF scenePos = node->scenePos();
4547 if (viewportRect.contains(scenePos) && (scenePos.y() < topY))
4549 topmostVisibleNode = node;
4550 topY = scenePos.y();
4554 if (topmostVisibleNode)
4555 nearbyNode = topmostVisibleNode;
4564 VUserLog(
"Warning: no nearby node (no marked nodes).");
4569 const QPoint offsetFromNode(0,10);
4570 QPoint popoverTopLeftInScene = (nearbyNode?
4571 (nearbyNode->scenePos().toPoint() +
4574 QPoint(viewportRect.center().x() - 0.5*errorPopover->sizeHint().width(),
4575 viewportRect.center().y() - 0.5*errorPopover->sizeHint().height()));
4579 const int margin = 5;
4580 popoverTopLeftInScene = (QPoint(fmin(popoverTopLeftInScene.x(), viewportRect.bottomRight().x() - errorPopover->sizeHint().width() - margin),
4581 fmin(popoverTopLeftInScene.y(), viewportRect.bottomRight().y() - errorPopover->sizeHint().height() - margin)));
4583 popoverTopLeftInScene = (QPoint(fmax(popoverTopLeftInScene.x(), viewportRect.topLeft().x() + margin),
4584 fmax(popoverTopLeftInScene.y(), viewportRect.topLeft().y() + margin)));
4586 QPoint popoverTopLeftInView = views()[0]->mapFromScene(popoverTopLeftInScene);
4587 QPoint popoverTopLeftGlobal = views()[0]->mapToGlobal(popoverTopLeftInView);
4589 errorPopover->move(popoverTopLeftGlobal);
4590 errorPopover->show();
4598 delete potentialCable;
4600 errorMarkingUpdatesEnabled =
true;
4606 bool VuoEditorComposition::hasFeedbackErrors(
void)
4608 return this->errorMark;
4616 if (hasFeedbackErrors())
4625 void VuoEditorComposition::buildComposition(
string compositionSnapshot,
const set<string> &dependenciesUninstalled)
4631 if (! dependenciesUninstalled.empty())
4633 vector<string> dependenciesRemovedVec(dependenciesUninstalled.begin(), dependenciesUninstalled.end());
4635 throw VuoException(
"Some modules that the composition needs were uninstalled: " + dependenciesStr);
4638 delete runningComposition;
4639 runningComposition = NULL;
4643 if (runningCompositionActiveDriver)
4647 string dir, file, ext;
4649 linkedCompositionPath = dir + file +
".dylib";
4654 compiler->
compileComposition(runningComposition, compiledCompositionPath,
true, issues);
4658 remove(compiledCompositionPath.c_str());
4664 delete runningComposition;
4665 runningComposition = NULL;
4677 bool VuoEditorComposition::isRunningThreadUnsafe(
void)
4679 return runner != NULL && ! stopRequested && ! runner->
isStopped();
4689 __block
bool running;
4690 dispatch_sync(runCompositionQueue, ^{
4691 running = isRunningThreadUnsafe();
4717 if (matchingComposition->showEventsMode)
4721 stopRequested =
false;
4722 dispatch_async(runCompositionQueue, ^{
4725 runningCompositionLibraries = std::make_shared<VuoRunningCompositionLibraries>();
4727 buildComposition(compositionSnapshot);
4741 if (matchingComposition->showEventsMode)
4742 this->runner->subscribeToEventTelemetry(matchingCompositionIdentifier);
4744 dispatch_sync(activePortPopoversQueue, ^{
4745 for (
auto i : matchingComposition->activePortPopovers)
4747 string portID = i.first;
4748 updateDataInPortPopoverFromRunningTopLevelComposition(matchingComposition, matchingCompositionIdentifier, portID);
4769 stopRequested =
true;
4770 dispatch_async(runCompositionQueue, ^{
4774 runner->waitUntilStopped();
4779 linkedCompositionPath =
"";
4781 runningCompositionLibraries =
nullptr;
4783 delete runningComposition;
4784 runningComposition = NULL;
4792 subcompositionRouter->applyToAllLinkedCompositions(
this, ^
void (
VuoEditorComposition *matchingComposition,
string matchingCompositionIdentifier)
4794 if (matchingComposition->showEventsMode)
4797 dispatch_sync(activePortPopoversQueue, ^{
4798 for (
auto i : matchingComposition->activePortPopovers)
4800 VuoPortPopover *popover = i.second;
4801 popover->setCompositionRunning(false);
4824 dispatch_async(runCompositionQueue, ^{
4825 if (isRunningThreadUnsafe())
4831 runningCompositionLibraries->enqueueLibraryContainingDependencyToUnload(moduleKey);
4834 string oldBuiltCompositionSnapshot = oldCompositionSnapshot;
4836 if (previouslyActiveDriver)
4839 previouslyActiveDriver->applyToComposition(oldBuiltComposition, compiler);
4843 buildComposition(newCompositionSnapshot, dependenciesUninstalled);
4845 string compositionDiff = diffInfo->
diff(oldBuiltCompositionSnapshot, runningComposition, compiler);
4848 catch (exception &e)
4850 VUserLog(
"Composition stopped itself: %s", e.what());
4855 VUserLog(
"Composition stopped itself.");
4861 dispatch_async(dispatch_get_main_queue(), ^{
4880 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, reloadSubcompositionIfUnsaved);
4898 if (
this == topLevelComposition)
4900 dispatch_async(runCompositionQueue, ^{
4901 if (isRunningThreadUnsafe())
4903 json_object *constantObject = json_tokener_parse(constant.c_str());
4904 runner->
setInputPortValue(thisCompositionIdentifier, runningPortID, constantObject);
4912 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
4914 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
4941 if (runningPortIdentifier.empty())
4947 if (
this == topLevelComposition)
4949 dispatch_async(runCompositionQueue, ^{
4950 if (isRunningThreadUnsafe())
4952 json_object *constantObject = json_tokener_parse(constant.c_str());
4953 runner->
setInputPortValue(thisCompositionIdentifier, runningPortIdentifier, constantObject);
4961 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
4963 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
4976 dispatch_async(runCompositionQueue, ^{
4977 if (isRunningThreadUnsafe())
4979 json_object *constantObject = json_tokener_parse(constant.c_str());
4981 foreach (
string subcompositionIdentifier, subcompositionIdentifiers)
4983 runner->
setInputPortValue(subcompositionIdentifier, portIdentifier, constantObject);
4994 dispatch_async(runCompositionQueue, ^{
4995 if (isRunningThreadUnsafe())
5000 json_object *constantObject = json_tokener_parse(constant.c_str());
5001 map<VuoRunner::Port *, json_object *> m;
5002 m[publishedPort] = constantObject;
5015 return contextMenuDeleteSelected;
5025 QMenu *contextMenuTints =
new QMenu(parent);
5026 contextMenuTints->setSeparatorsCollapsible(
false);
5027 contextMenuTints->setTitle(tr(
"Tint"));
5028 foreach (QAction *tintAction, contextMenuTintActions)
5029 contextMenuTints->addAction(tintAction);
5030 contextMenuTints->insertSeparator(contextMenuTintActions.last());
5032 return contextMenuTints;
5038 void VuoEditorComposition::expandChangeNodeMenu()
5040 QAction *sender = (QAction *)QObject::sender();
5045 int currentMatchesListed = contextMenuChangeNode->actions().size()-1;
5046 if (currentMatchesListed <= initialChangeNodeSuggestionCount)
5049 int verticalSpacePerItem = 21;
5053 int targetMatches = availableVerticalSpace/verticalSpacePerItem - 2;
5062 contextMenuChangeNode->exec();
5077 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
5078 vector<VuoCompilerNodeClass *> loadedNodeClasses;
5079 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
5080 loadedNodeClasses.push_back(i->second);
5083 vector<string> bestMatches;
5084 map<string, double> matchScores;
5085 matchScores[
""] = 0;
5087 int targetMatchCount = (matchLimit > 0? matchLimit : loadedNodeClasses.size());
5088 for (
int i = 0; i < targetMatchCount; ++i)
5089 bestMatches.push_back(
"");
5092 bool overflow =
false;
5095 string originalGenericNodeClassName;
5099 originalGenericNodeClassName = nodeClass->
getClassName();
5104 if (loadedNodeClassName == originalGenericNodeClassName)
5107 bool canSwapNondestructively = canSwapWithoutBreakingCables(node, loadedNodeClass->
getBase());
5108 double matchScore = (canSwapNondestructively? calculateNodeSimilarity(nodeClass, loadedNodeClass->
getBase()) : 0);
5109 int highestIndexWithCompetitiveScore = -1;
5110 for (
int i = targetMatchCount-1; (i >= 0) && (highestIndexWithCompetitiveScore == -1); --i)
5111 if (matchScore <= matchScores[bestMatches[i] ])
5112 highestIndexWithCompetitiveScore = i;
5114 if (highestIndexWithCompetitiveScore < targetMatchCount-1)
5116 if (matchScores[bestMatches[targetMatchCount-1] ] > 0)
5119 for (
int j = targetMatchCount-2; j > highestIndexWithCompetitiveScore; --j)
5120 bestMatches[j+1] = bestMatches[j];
5122 bestMatches[highestIndexWithCompetitiveScore+1] = loadedNodeClassName;
5123 matchScores[loadedNodeClassName] = matchScore;
5127 for (
int i = 0; i < targetMatchCount; ++i)
5129 if (matchScores[bestMatches[i] ] > 0)
5134 matchDisplayText += QString(
" (%1)").arg(bestMatches[i].c_str());
5136 QAction *changeAction = menu->addAction(matchDisplayText);
5138 QList<QVariant> currentNodeAndNewClass;
5139 currentNodeAndNewClass.append(qVariantFromValue((
void *)node));
5140 currentNodeAndNewClass.append(bestMatches[i].c_str());
5141 changeAction->setData(QVariant(currentNodeAndNewClass));
5142 connect(changeAction, &QAction::triggered,
this, &VuoEditorComposition::swapNode);
5149 QAction *showMoreAction = menu->addAction(tr(
"More…"));
5150 showMoreAction->setData(qVariantFromValue(
static_cast<void *
>(node)));
5151 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandChangeNodeMenu);
5162 map<string, int> requiredInputs;
5163 bool inputEventSourceRequired =
false;
5166 bool hasDrawerWithNoIncomingCables =
false;
5167 bool hasDrawerWithNoIncomingDataCables =
false;
5174 hasDrawerWithNoIncomingCables =
true;
5175 hasDrawerWithNoIncomingDataCables =
true;
5176 vector<VuoRendererPort *> childPorts = inputDrawer->
getDrawerPorts();
5180 hasDrawerWithNoIncomingCables =
false;
5182 hasDrawerWithNoIncomingDataCables =
false;
5187 if (!hasDrawerWithNoIncomingDataCables)
5194 requiredInputs[typeKey] = ((requiredInputs.find(typeKey) == requiredInputs.end())? 1 : requiredInputs[typeKey]+1);
5199 if (hasIncomingCables && !hasDrawerWithNoIncomingCables)
5200 inputEventSourceRequired =
true;
5204 map<string, int> requiredOutputs;
5205 bool outputEventSourceRequired =
false;
5217 requiredOutputs[typeKey] = ((requiredOutputs.find(typeKey) == requiredOutputs.end())? 1 : requiredOutputs[typeKey]+1);
5221 outputEventSourceRequired =
true;
5225 bool inputEventSourceAvailable =
false;
5226 map<string, int> availableInputs;
5234 availableInputs[typeKey] = ((availableInputs.find(typeKey) == availableInputs.end())? 1 : availableInputs[typeKey]+1);
5239 inputEventSourceAvailable =
true;
5242 bool outputEventSourceAvailable =
false;
5243 map<string, int> availableOutputs;
5251 availableOutputs[typeKey] = ((availableOutputs.find(typeKey) == availableOutputs.end())? 1 : availableOutputs[typeKey]+1);
5256 outputEventSourceAvailable =
true;
5259 for (std::map<string,int>::iterator it=requiredInputs.begin(); it!=requiredInputs.end(); ++it)
5261 string typeKey = it->first;
5262 int typeRequiredCount = it->second;
5263 if (availableInputs[typeKey] < typeRequiredCount)
5268 for (std::map<string,int>::iterator it=requiredOutputs.begin(); it!=requiredOutputs.end(); ++it)
5270 string typeKey = it->first;
5271 int typeRequiredCount = it->second;
5272 if (availableOutputs[typeKey] < typeRequiredCount)
5276 if (inputEventSourceRequired && !inputEventSourceAvailable)
5279 if (outputEventSourceRequired && !outputEventSourceAvailable)
5289 bool VuoEditorComposition::isPortCurrentlyRevertible(
VuoRendererPort *port)
5295 if (!specializedNodeClass)
5300 if (!originalGenericType)
5309 if (hostPort && (!isPortCurrentlyRevertible(hostPort->
getRenderer())))
5336 string publishedPortName = ((! name.empty())?
5347 bool performedMerge =
false;
5350 publishedPort = (isPublishedInput ?
5356 if (isPublishedInput && portType && type && !forceEventOnlyPublication)
5364 performedMerge =
true;
5371 if (! performedMerge)
5374 if (isPublishedInput && type)
5389 if (! existingPublishedCable)
5395 if (mergePerformed != NULL)
5396 *mergePerformed = performedMerge;
5398 return rendererPublishedPort;
5410 if (creatingPublishedInputCable)
5413 VuoPort *fromPort = externalPort;
5416 VuoPort *toPort = internalPort;
5423 publishedCable->
setFrom(fromNode, fromPort);
5430 VuoPort *fromPort = internalPort;
5433 VuoPort *toPort = externalPort;
5440 publishedCable->
setTo(toNode, toPort);
5443 if (forceEventOnlyPublication)
5446 return publishedCable;
5462 vector<VuoPublishedPort *> publishedPortsToAdd;
5463 map<VuoPublishedPort *, string> publishedPortsToRename;
5466 VuoProtocol *previousActiveProtocol = this->activeProtocol;
5467 bool removingPreviousProtocol = previousActiveProtocol && (previousActiveProtocol != protocol);
5469 bool portChangesMadeDuringProtocolRemoval =
false;
5470 if (removingPreviousProtocol)
5473 if (portChangesMadeDuringProtocolRemoval && !useUndoStack)
5475 VUserLog(
"Warning: Unexpected combination: Removing protocol ports, but useUndoStack=false");
5476 useUndoStack =
true;
5480 this->activeProtocol = protocol;
5485 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5487 string portName = i->first;
5488 string portType = i->second;
5490 bool compositionHadCompatiblePort =
false;
5492 if (preexistingPublishedPort)
5496 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5497 (!preexistingType && (portType ==
"")));
5500 compositionHadCompatiblePort =
true;
5505 compositionHadCompatiblePort =
false;
5510 if (!compositionHadCompatiblePort)
5519 publishedPortsToAdd.push_back(publishedPort);
5526 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5528 string portName = i->first;
5529 string portType = i->second;
5531 bool compositionHadCompatiblePort =
false;
5533 if (preexistingPublishedPort)
5536 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5537 (!preexistingType && (portType ==
"")));
5540 compositionHadCompatiblePort =
true;
5545 compositionHadCompatiblePort =
false;
5550 if (!compositionHadCompatiblePort)
5559 publishedPortsToAdd.push_back(publishedPort);
5567 bool undoStackMacroBegunAlready = (removingPreviousProtocol && portChangesMadeDuringProtocolRemoval);
5568 if (!publishedPortsToRename.empty() || !publishedPortsToAdd.empty() || undoStackMacroBegunAlready)
5570 set<VuoPublishedPort *> publishedPortsToRemove;
5571 bool beginUndoStackMacro = !undoStackMacroBegunAlready;
5572 bool endUndoStackMacro =
true;
5573 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5587 string VuoEditorComposition::getNonProtocolVariantForPortName(
string portName)
5589 string modifiedPortName = portName;
5590 if (modifiedPortName.length() > 0)
5591 modifiedPortName[0] = toupper(modifiedPortName[0]);
5592 modifiedPortName =
"some" + modifiedPortName;
5594 return modifiedPortName;
5618 set<VuoPublishedPort *> publishedPortsToRemove;
5619 map<VuoPublishedPort *, string> publishedPortsToRename;
5622 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5624 string portName = i->first;
5625 string portType = i->second;
5628 if (preexistingPublishedPort)
5630 bool portCompatibleAcrossProtocolTransition =
false;
5631 if (replacementProtocol)
5634 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5636 string replacementPortName = i->first;
5637 string replacementPortType = i->second;
5639 if ((portName == replacementPortName) && (portType == replacementPortType))
5640 portCompatibleAcrossProtocolTransition =
true;
5645 publishedPortsToRemove.insert(preexistingPublishedPort);
5646 else if (!portCompatibleAcrossProtocolTransition)
5652 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5654 string portName = i->first;
5655 string portType = i->second;
5658 if (preexistingPublishedPort)
5660 bool portCompatibleAcrossProtocolTransition =
false;
5661 if (replacementProtocol)
5664 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5666 string replacementPortName = i->first;
5667 string replacementPortType = i->second;
5669 if ((portName == replacementPortName) && (portType == replacementPortType))
5670 portCompatibleAcrossProtocolTransition =
true;
5675 publishedPortsToRemove.insert(preexistingPublishedPort);
5676 else if (!portCompatibleAcrossProtocolTransition)
5683 if (!publishedPortsToRemove.empty())
5684 publishedPortsToRename.clear();
5686 bool portChangesRequired = (!publishedPortsToRename.empty() || !publishedPortsToRemove.empty());
5687 if (portChangesRequired)
5689 vector<VuoPublishedPort *> publishedPortsToAdd;
5690 bool beginUndoStackMacro =
true;
5691 bool endUndoStackMacro = !replacementProtocol;
5692 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5697 return portChangesRequired;
5708 if ((activeProtocol != protocol) || !activeProtocol)
5711 activeProtocol = NULL;
5714 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5716 string portName = i->first;
5717 string portType = i->second;
5720 if (preexistingPublishedPort)
5725 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5727 string portName = i->first;
5728 string portType = i->second;
5731 if (preexistingPublishedPort)
5744 return activeProtocol;
5753 if (!activeProtocol)
5756 return static_cast<VuoEditor *
>(qApp)->getBuiltInDriverForProtocol(activeProtocol);
5789 return removalResult;
5816 void VuoEditorComposition::highlightEligibleEndpointsForCable(
VuoCable *cable)
5829 highlightInternalPortsConnectableToPort(fixedPort, cable->
getRenderer());
5843 QList<QGraphicsItem *> compositionComponents = items();
5844 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5846 QGraphicsItem *compositionComponent = *i;
5852 for(vector<VuoPort *>::iterator inputPort = inputPorts.begin(); inputPort != inputPorts.end(); ++inputPort)
5853 updateEligibilityHighlightingForPort((*inputPort)->getRenderer(), port, !cable->
effectivelyCarriesData(), types);
5857 for(vector<VuoPort *>::iterator outputPort = outputPorts.begin(); outputPort != outputPorts.end(); ++outputPort)
5858 updateEligibilityHighlightingForPort((*outputPort)->getRenderer(), port, !cable->
effectivelyCarriesData(), types);
5863 if (rc && rc != cable)
5865 QGraphicsItem::CacheMode normalCacheMode = rc->cacheMode();
5866 rc->setCacheMode(QGraphicsItem::NoCache);
5884 rc->setCacheMode(normalCacheMode);
5889 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5890 updateEligibilityHighlightingForNode(
dynamic_cast<VuoRendererNode *
>(*i));
5897 void VuoEditorComposition::updateEligibilityHighlightingForPort(
VuoRendererPort *portToHighlight,
5899 bool eventOnlyConnection,
5900 map<string, VuoCompilerType *> &types)
5902 QGraphicsItem::CacheMode normalCacheMode = portToHighlight->cacheMode();
5903 portToHighlight->setCacheMode(QGraphicsItem::NoCache);
5911 if (typecastPortToHighlight)
5914 portToHighlight->setCacheMode(normalCacheMode);
5916 if (typecastPortToHighlight)
5917 updateEligibilityHighlightingForPort(typecastPortToHighlight->
getChildPort(), fixedPort, eventOnlyConnection, types);
5938 bool forwardConnection;
5941 fromPort = fixedPort;
5942 toPort = portToHighlight;
5943 forwardConnection =
true;
5947 fromPort = portToHighlight;
5949 forwardConnection =
false;
5952 bool directConnectionPossible;
5956 if (fixedExternalPublishedPort && externalPublishedPortToHighlight)
5957 directConnectionPossible =
false;
5958 else if (fixedExternalPublishedPort && !externalPublishedPortToHighlight)
5960 else if (!fixedExternalPublishedPort && externalPublishedPortToHighlight)
5966 if (directConnectionPossible)
5970 else if (fixedPort == portToHighlight)
5993 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
5995 bool eventOnlyConnection,
5996 bool forwardConnection)
5999 string respecializedTypeName =
"";
6001 return canConnectDirectlyWithRespecializationNondestructively(fromPort,
6003 eventOnlyConnection,
6005 &portToRespecialize,
6006 respecializedTypeName);
6019 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6021 bool eventOnlyConnection,
6022 bool forwardConnection,
6024 string &respecializedTypeName)
6026 *portToRespecialize = NULL;
6027 respecializedTypeName =
"";
6029 bool canConnectWithRespecialization = canConnectDirectlyWithRespecialization(fromPort,
6031 eventOnlyConnection,
6034 respecializedTypeName);
6035 if (!canConnectWithRespecialization)
6038 if (canConnectWithRespecialization && !portToRespecialize)
6041 bool nondestructive = portCanBeUnspecializedNondestructively((*portToRespecialize)->getBase());
6042 if (!nondestructive)
6044 *portToRespecialize = NULL;
6045 respecializedTypeName =
"";
6047 return nondestructive;
6055 bool VuoEditorComposition::portCanBeUnspecializedNondestructively(
VuoPort *portToUnspecialize)
6057 map<VuoNode *, string> nodesToReplace;
6058 set<VuoCable *> cablesToDelete;
6063 if (cablesToDelete.empty())
6066 else if ((cablesToDelete.size() == 1) && ((*(cablesToDelete.begin()))->getToPort() == portToUnspecialize))
6091 bool VuoEditorComposition::canConnectDirectlyWithRespecialization(
VuoRendererPort *fromPort,
6093 bool eventOnlyConnection,
6094 bool forwardConnection,
6096 string &respecializedTypeName)
6100 *portToRespecialize = NULL;
6101 respecializedTypeName =
"";
6111 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6112 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6114 if (!(fromPortIsEnabledOutput && toPortIsEnabledInput))
6120 if (!(currentFromDataType && currentToDataType))
6135 if (fromSpecializedNodeClass)
6147 if (toSpecializedNodeClass)
6156 bool fromPortIsGeneric = currentFromGenericType;
6157 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6158 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6160 bool toPortIsGeneric = currentToGenericType;
6161 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6162 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6165 set<string> compatibleTypes;
6166 string specializedType =
"";
6170 if ((fromPortIsStatic && toPortIsSpecialized) || (fromPortIsSpecialized && toPortIsStatic))
6174 portToTryToRespecialize = (fromPortIsSpecialized? fromPort : toPort);
6178 else if ((fromPortIsSpecialized || toPortIsSpecialized) && !fromPortIsStatic && !toPortIsStatic)
6181 bool dragSourceIsGeneric = (forwardConnection? fromPortIsGeneric : toPortIsGeneric);
6183 VuoRendererPort *dragDestination = (forwardConnection? toPort : fromPort);
6184 bool dragDestinationIsGeneric = (forwardConnection? toPortIsGeneric : fromPortIsGeneric);
6201 if (!dragSourceIsGeneric && !dragDestinationIsGeneric)
6204 portToTryToRespecialize = dragDestination;
6212 if (portToTryToRespecialize)
6213 compatibleTypes = getRespecializationOptionsForPortInNetwork(portToTryToRespecialize);
6215 bool portsAreCompatible = (compatibleTypes.find(specializedType) != compatibleTypes.end());
6217 if (portsAreCompatible)
6219 *portToRespecialize = portToTryToRespecialize;
6220 respecializedTypeName = specializedType;
6223 return portsAreCompatible;
6232 void VuoEditorComposition::updateEligibilityHighlightingForNode(
VuoRendererNode *node)
6247 QGraphicsItem::CacheMode normalCacheMode = drawer->cacheMode();
6248 drawer->setCacheMode(QGraphicsItem::NoCache);
6253 drawer->setCacheMode(normalCacheMode);
6262 QGraphicsItem::CacheMode normalCacheMode = hostPort->cacheMode();
6263 hostPort->setCacheMode(QGraphicsItem::NoCache);
6265 hostPort->setCacheMode(normalCacheMode);
6299 bool toPortIsDragDestination,
6301 string &specializedTypeName,
6302 string &typecastToInsert)
6304 *portToSpecialize = NULL;
6305 specializedTypeName =
"";
6307 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6308 map<string, string> specializedTypeNameForTypecast;
6311 vector<string> candidateTypecasts =
findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast, types);
6312 bool solutionSelected = selectBridgingSolutionFromOptions(candidateTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, typecastToInsert);
6314 if (!solutionSelected)
6317 if (portToSpecializeForTypecast.find(typecastToInsert) != portToSpecializeForTypecast.end())
6318 *portToSpecialize = portToSpecializeForTypecast[typecastToInsert];
6319 if (specializedTypeNameForTypecast.find(typecastToInsert) != specializedTypeNameForTypecast.end())
6320 specializedTypeName = specializedTypeNameForTypecast[typecastToInsert];
6343 bool VuoEditorComposition::selectBridgingSolutionFromOptions(vector<string> suitableTypecasts,
6344 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6345 map<string, string> specializedTypeNameForTypecast,
6346 string &selectedTypecast)
6348 if (suitableTypecasts.empty())
6350 selectedTypecast =
"";
6354 else if (suitableTypecasts.size() == 1)
6356 selectedTypecast = suitableTypecasts[0];
6361 return promptForBridgingSelectionFromOptions(suitableTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, selectedTypecast);
6371 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6372 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6374 return (fromPortIsEnabledOutput && toPortIsEnabledInput &&
6386 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6402 if (toNodeUsesIndex)
6429 bool toPortIsDragDestination,
6430 map<string, VuoCompilerType *> &types)
6432 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6433 map<string, string> specializedTypeNameForTypecast;
6434 return findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast, types);
6448 bool toPortIsDragDestination,
6449 map<string, VuoRendererPort *> &portToSpecializeForTypecast,
6450 map<string, string> &specializedTypeNameForTypecast,
6451 map<string, VuoCompilerType *> &types)
6458 const bool limitCombinations =
true;
6460 portToSpecializeForTypecast.clear();
6461 specializedTypeNameForTypecast.clear();
6462 vector<string> suitableTypecasts;
6464 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6465 return suitableTypecasts;
6470 return suitableTypecasts;
6475 string specializedTypeName =
"";
6478 suitableTypecasts.push_back(
"");
6479 portToSpecializeForTypecast[
""] = portToSpecialize;
6480 specializedTypeNameForTypecast[
""] = specializedTypeName;
6482 return suitableTypecasts;
6489 if (!(currentFromDataType && currentToDataType))
6490 return suitableTypecasts;
6500 if (fromSpecializedNodeClass)
6512 if (toSpecializedNodeClass)
6523 bool fromPortIsGeneric = currentFromGenericType;
6524 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6525 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6527 bool toPortIsGeneric = currentToGenericType;
6528 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6529 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6532 if (fromPortIsGeneric && toPortIsGeneric)
6533 return suitableTypecasts;
6536 else if (fromPortIsStatic && toPortIsStatic)
6538 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6540 return suitableTypecasts;
6545 bool specializeToPort =
true;
6546 if (toPortIsGeneric)
6547 specializeToPort =
true;
6548 else if (fromPortIsGeneric)
6549 specializeToPort =
false;
6550 else if (fromPortIsSpecialized && toPortIsStatic)
6551 specializeToPort =
false;
6552 else if (fromPortIsStatic && toPortIsSpecialized)
6553 specializeToPort =
true;
6554 else if (fromPortIsSpecialized && toPortIsSpecialized)
6555 specializeToPort = toPortIsDragDestination;
6559 set<string> compatibleTypes;
6560 if (specializeToPort && (toPortIsGeneric || (toPortIsSpecialized && portCanBeUnspecializedNondestructively(toPort->
getBase()))))
6561 compatibleTypes = getRespecializationOptionsForPortInNetwork(toPort);
6562 else if (!specializeToPort && (fromPortIsGeneric || (fromPortIsSpecialized && portCanBeUnspecializedNondestructively(fromPort->
getBase()))))
6563 compatibleTypes = getRespecializationOptionsForPortInNetwork(fromPort);
6569 if (limitCombinations)
6571 vector<string> limitedSuitableTypecasts;
6574 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6577 foreach (
string typecastName, limitedSuitableTypecasts)
6579 portToSpecializeForTypecast[typecastName] = specializeToPort? toPort : fromPort;
6580 specializedTypeNameForTypecast[typecastName] = specializeToPort? currentToDataType->
getModuleKey() :
6587 if (compatibleTypes.find(fixedDataType) != compatibleTypes.end())
6589 limitedSuitableTypecasts.push_back(
"");
6590 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6591 specializedTypeNameForTypecast[
""] = fixedDataType;
6594 if (limitedSuitableTypecasts.size() >= 1)
6595 return limitedSuitableTypecasts;
6598 foreach (
string compatibleTypeName, compatibleTypes)
6600 VuoCompilerType *compatibleSpecializedType = types[compatibleTypeName];
6601 if (!compatibleSpecializedType)
6602 compatibleSpecializedType = compiler->
getType(compatibleTypeName);
6603 VuoType *candidateFromType = specializeToPort? currentFromDataType : compatibleSpecializedType->
getBase();
6604 VuoType *candidateToType = specializeToPort? compatibleSpecializedType->
getBase() : currentToDataType;
6606 if (compatibleSpecializedType)
6609 if (candidateFromType == candidateToType)
6611 suitableTypecasts.push_back(
"");
6612 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6613 specializedTypeNameForTypecast[
""] = compatibleSpecializedType->
getBase()->
getModuleKey();
6616 if (portsPassSanityCheckToTypeconvert(fromPort,
6622 foreach (
string typecast, suitableTypecastsForCurrentTypes)
6624 suitableTypecasts.push_back(typecast);
6625 portToSpecializeForTypecast[typecast] = specializeToPort? toPort : fromPort;
6626 specializedTypeNameForTypecast[typecast] = compatibleSpecializedType->
getBase()->
getModuleKey();
6632 return suitableTypecasts;
6647 bool VuoEditorComposition::promptForBridgingSelectionFromOptions(vector<string> suitableTypecasts,
6648 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6649 map<string, string> specializedTypeNameForTypecast,
6650 string &selectedTypecast)
6652 QMenu typecastMenu(views()[0]->viewport());
6653 typecastMenu.setSeparatorsCollapsible(
false);
6654 QString spacer(
" ");
6657 set <pair<VuoRendererPort *, string> > specializationDetails;
6658 vector<string> typeconversionOptionsRequiringNoSpecialization;
6659 foreach (
string typecastClassName, suitableTypecasts)
6661 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typecastClassName];
6662 string specializedTypeName = specializedTypeNameForTypecast[typecastClassName];
6663 specializationDetails.insert(std::make_pair(portToSpecialize,
6664 specializedTypeName));
6666 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6667 if (portAlreadyHasTargetType)
6668 typeconversionOptionsRequiringNoSpecialization.push_back(typecastClassName);
6674 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()))
6676 QString menuText = getDisplayTextForSpecializationOption(portToSpecializeForTypecast[
""], specializedTypeNameForTypecast[
""]);
6677 QAction *typecastAction = typecastMenu.addAction(menuText);
6678 typecastAction->setData(QVariant(
""));
6681 bool foundSpecializationOptionsRequiringNoTypeconversion = typecastMenu.actions().size() >= 1;
6682 bool foundTypeconversionOptionsRequiringNoSpecialization = typeconversionOptionsRequiringNoSpecialization.size() >= 1;
6685 bool includingTypeconvertWithNoSpecializationHeader = foundSpecializationOptionsRequiringNoTypeconversion;
6686 if (foundTypeconversionOptionsRequiringNoSpecialization)
6688 if (foundSpecializationOptionsRequiringNoTypeconversion)
6689 typecastMenu.addSeparator();
6691 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6692 string specializedTypeName = specializedTypeNameForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6694 if (portToSpecialize && !specializedTypeName.empty())
6696 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6697 QAction *typecastAction = typecastMenu.addAction(menuText);
6698 typecastAction->setEnabled(
false);
6699 includingTypeconvertWithNoSpecializationHeader =
true;
6703 foreach (
string typecastClassName, typeconversionOptionsRequiringNoSpecialization)
6709 typecastAction->setData(QVariant(typecastClassName.c_str()));
6714 for (set<pair<VuoRendererPort *, string> >::iterator i = specializationDetails.begin(); i != specializationDetails.end(); ++i)
6717 string specializedTypeName = i->second;
6720 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()) &&
6721 (portToSpecializeForTypecast[
""] == portToSpecialize) &&
6722 (specializedTypeNameForTypecast[
""] == specializedTypeName))
6728 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6729 if (portAlreadyHasTargetType)
6734 if (typecastMenu.actions().size() >= 1)
6735 typecastMenu.addSeparator();
6737 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6738 QAction *typecastAction = typecastMenu.addAction(menuText);
6739 typecastAction->setEnabled(
false);
6742 foreach (
string typecastClassName, suitableTypecasts)
6744 if ((portToSpecializeForTypecast[typecastClassName] == portToSpecialize) &&
6745 (specializedTypeNameForTypecast[typecastClassName] == specializedTypeName))
6751 typecastAction->setData(QVariant(typecastClassName.c_str()));
6757 menuSelectionInProgress =
true;
6758 QAction *selectedTypecastAction = typecastMenu.exec(QCursor::pos());
6759 menuSelectionInProgress =
false;
6761 selectedTypecast = (selectedTypecastAction? selectedTypecastAction->data().toString().toUtf8().constData() :
"");
6762 return selectedTypecastAction;
6768 QString VuoEditorComposition::getDisplayTextForSpecializationOption(
VuoRendererPort *portToSpecialize,
string specializedTypeName)
6770 if (!portToSpecialize || specializedTypeName.empty())
6773 bool isInput = portToSpecialize && portToSpecialize->
getInput();
6774 QString typeDisplayName = compiler->
getType(specializedTypeName)?
6776 specializedTypeName.c_str();
6778 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6780 QString displayText;
6781 if (portAlreadyHasTargetType)
6786 displayText = tr(
"Keep Input Port as %1");
6791 displayText = tr(
"Keep Output Port as %1");
6799 displayText = tr(
"Change Input Port to %1");
6804 displayText = tr(
"Change Output Port to %1");
6808 return displayText.arg(typeDisplayName);
6827 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
6828 if (topLevelComposition->isRunningThreadUnsafe())
6830 portValue = isInput ?
6831 topLevelComposition->runner->getInputPortValue(thisCompositionIdentifier, runningPortIdentifier) :
6832 topLevelComposition->runner->getOutputPortValue(thisCompositionIdentifier, runningPortIdentifier);
6836 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, getPortValue);
6844 string VuoEditorComposition::getIdentifierForRunningPort(
VuoPort *runningPort)
6861 return dynamic_cast<VuoPublishedPort *
>(staticPort)->getClass()->getName();
6867 string nodeIdentifier =
"";
6877 if (staticPort->
hasCompiler() && !nodeIdentifier.empty())
6921 if (candidateInputPort == port)
6925 if (candidateOutputPort == port)
6941 map<string, VuoPortPopover *>::iterator popover = activePortPopovers.find(portID);
6942 if (popover != activePortPopovers.end())
6943 return popover->second;
6956 void VuoEditorComposition::enableInactivePopoverForPort(
VuoRendererPort *rp)
6959 bool popoverJustClosedAtLastEvent = portsWithPopoversClosedAtLastEvent.find(portID) != portsWithPopoversClosedAtLastEvent.end();
6960 if (!popoverJustClosedAtLastEvent)
6969 if (!popoverEventsEnabled)
6975 VUserLog(
"%s: Open popover for %s",
6979 dispatch_sync(runCompositionQueue, ^{
6981 dispatch_sync(activePortPopoversQueue, ^{
6983 if (activePortPopovers.find(portID) == activePortPopovers.end())
6987 VuoPortPopover *popover = new VuoPortPopover(port, this, views()[0]->viewport());
6989 connect(popover, &VuoPortPopover::popoverClosedForPort, this, &VuoEditorComposition::disablePopoverForPortThreadSafe);
6990 connect(popover, &VuoPortPopover::popoverDetachedFromPort, [=]{
6991 VUserLog(
"%s: Detach popover for %s",
6992 window->getWindowTitleWithoutPlaceholder().toUtf8().data(),
7004 const int cutoffMargin = 16;
7005 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
7006 if (portLeftInScene.x() + popover->size().width() + cutoffMargin > viewportRect.right())
7007 portLeftInScene = QPoint(viewportRect.right() - popover->size().width() - cutoffMargin, portLeftInScene.y());
7008 if (portLeftInScene.y() + popover->size().height() + cutoffMargin > viewportRect.bottom())
7009 portLeftInScene = QPoint(portLeftInScene.x(), viewportRect.bottom() - popover->size().height() - cutoffMargin);
7011 QPoint popoverLeftInView = views()[0]->mapFromScene(portLeftInScene);
7013 const QPoint offset = QPoint(12, 6);
7015 QPoint popoverTopLeft = popoverLeftInView + offset;
7016 popover->move(popoverTopLeft);
7019 activePortPopovers[portID] = popover;
7021 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7048 VUserLog(
"%s: Close popover for %s",
7053 map<string, VuoPortPopover *>::iterator i = activePortPopovers.find(portID);
7054 if (i != activePortPopovers.end())
7056 popover = i->second;
7057 activePortPopovers.erase(i);
7063 popover->deleteLater();
7066 bool isInput =
false;
7074 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7075 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7077 dispatch_async(topLevelComposition->runCompositionQueue, ^{
7078 if (topLevelComposition->isRunningThreadUnsafe())
7081 topLevelComposition->runner->unsubscribeFromInputPortTelemetry(thisCompositionIdentifier, portID) :
7082 topLevelComposition->runner->unsubscribeFromOutputPortTelemetry(thisCompositionIdentifier, portID));
7092 void VuoEditorComposition::disablePopoverForPortThreadSafe(
string portID)
7094 dispatch_sync(activePortPopoversQueue, ^{
7104 disablePortPopovers();
7115 errorPopover->hide();
7116 errorPopover->deleteLater();
7119 errorPopovers.clear();
7128 dispatch_sync(activePortPopoversQueue, ^{
7129 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7130 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7132 string portID = i->first;
7133 bool shouldDisable = false;
7136 shouldDisable = true;
7139 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7140 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7155 dispatch_sync(activePortPopoversQueue, ^{
7156 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7157 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7159 string portID = i->first;
7161 bool foundPort = identifierCache->doForPortWithIdentifier(portID, [&foundPort](VuoPort *port) {});
7174 if (recordWhichPopoversClosed)
7175 portsWithPopoversClosedAtLastEvent.clear();
7177 dispatch_sync(activePortPopoversQueue, ^{
7178 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7179 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7181 string portID = i->first;
7182 bool shouldDisable = false;
7185 shouldDisable = true;
7188 identifierCache->doForPortWithIdentifier(portID, [&shouldDisable, node](VuoPort *port) {
7189 shouldDisable = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7199 portsWithPopoversClosedAtLastEvent.insert(portID);
7211 moveDetachedPortPopoversBy(dx, dy);
7212 moveErrorPopoversBy(dx, dy);
7218 void VuoEditorComposition::moveErrorPopoversBy(
int dx,
int dy)
7221 errorPopover->move(errorPopover->pos().x()+dx, errorPopover->pos().y()+dy);
7227 void VuoEditorComposition::moveDetachedPortPopoversBy(
int dx,
int dy)
7229 dispatch_sync(activePortPopoversQueue, ^{
7230 map<string, VuoPortPopover *> portPopovers = activePortPopovers;
7231 for (map<string, VuoPortPopover *>::iterator i = portPopovers.begin(); i != portPopovers.end(); ++i)
7233 VuoPortPopover *popover = i->second;
7234 if (popover && popover->getDetached())
7235 popover->move(popover->pos().x()+dx, popover->pos().y()+dy);
7243 void VuoEditorComposition::setPopoversHideOnDeactivate(
bool shouldHide)
7245 dispatch_sync(activePortPopoversQueue, ^{
7246 auto portPopovers = activePortPopovers;
7247 for (
auto i : portPopovers)
7252 id nsWindow = (id)VuoPopover::getWindowForPopover(popover);
7253 objc_msgSend(nsWindow, sel_getUid(
"setHidesOnDeactivate:"), shouldHide);
7265 dispatch_sync(activePortPopoversQueue, ^{
7266 for (map<string, VuoPortPopover *>::iterator i = activePortPopovers.begin(); i != activePortPopovers.end(); ++i)
7268 string portID = i->first;
7269 VuoPortPopover *popover = i->second;
7270 bool shouldUpdate = false;
7273 shouldUpdate = true;
7276 identifierCache->doForPortWithIdentifier(portID, [&shouldUpdate, node](VuoPort *port) {
7277 shouldUpdate = port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node);
7282 QMetaObject::invokeMethod(popover,
"updateTextAndResize", Qt::QueuedConnection);
7299 string popoverCompositionIdentifier,
7310 string portSummary = (isInput ?
7314 dispatch_async(popoverComposition->activePortPopoversQueue, ^{
7315 VuoPortPopover *popover = popoverComposition->getActivePopoverForPort(portID);
7318 QMetaObject::invokeMethod(popover,
"updateDataValueImmediately", Qt::QueuedConnection, Q_ARG(QString, portSummary.c_str()));
7319 QMetaObject::invokeMethod(popover,
"setCompositionRunning", Qt::QueuedConnection, Q_ARG(bool, true), Q_ARG(bool, false));
7333 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7335 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7336 if (topLevelComposition->isRunningThreadUnsafe())
7337 topLevelComposition->updateDataInPortPopoverFromRunningTopLevelComposition(this, thisCompositionIdentifier, portID);
7347 bool receivedEvent,
bool receivedData,
string dataSummary)
7351 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7352 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7354 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7355 Q_ARG(bool, receivedEvent),
7356 Q_ARG(bool, receivedData),
7357 Q_ARG(QString, dataSummary.c_str()));
7360 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7368 bool sentEvent,
bool sentData,
string dataSummary)
7372 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7373 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7375 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndDataValue", Qt::QueuedConnection,
7376 Q_ARG(bool, sentEvent),
7377 Q_ARG(bool, sentData),
7378 Q_ARG(QString, dataSummary.c_str()));
7381 if (matchingComposition->showEventsMode && sentEvent)
7386 port->getRenderer()->setFiredEvent();
7387 matchingComposition->animatePort(port->getRenderer());
7392 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7403 dispatch_async(matchingComposition->runCompositionQueue, ^{
7404 if (matchingComposition->isRunningThreadUnsafe())
7406 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7407 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7409 QMetaObject::invokeMethod(popover,
"incrementDroppedEventCount", Qt::QueuedConnection);
7414 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7425 if (matchingComposition->showEventsMode)
7427 dispatch_async(this->runCompositionQueue, ^{
7428 if (this->isRunningThreadUnsafe())
7437 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7448 if (matchingComposition->showEventsMode)
7450 dispatch_async(this->runCompositionQueue, ^{
7451 if (this->isRunningThreadUnsafe())
7460 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7489 return showEventsMode;
7497 this->showEventsMode = showEventsMode;
7505 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7506 if (topLevelComposition->isRunningThreadUnsafe())
7507 topLevelComposition->runner->subscribeToEventTelemetry(thisCompositionIdentifier);
7510 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, subscribe);
7518 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7519 if (topLevelComposition->isRunningThreadUnsafe())
7520 topLevelComposition->runner->unsubscribeFromEventTelemetry(thisCompositionIdentifier);
7523 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, unsubscribe);
7559 QGraphicsItemAnimation * VuoEditorComposition::setUpAnimationForPort(QGraphicsItemAnimation *animation,
VuoRendererPort *port)
7567 animatedPort->setZValue(VuoRendererItem::triggerAnimationZValue);
7570 animation->setItem(animatedPort);
7571 animation->setScaleAt(0.0, 1, 1);
7572 animation->setScaleAt(0.999, 3, 3);
7573 animation->setScaleAt(1.0, 1, 1);
7575 QTimeLine *animationTimeline = animation->timeLine();
7576 animationTimeline->setFrameRange(0, 100);
7577 animationTimeline->setUpdateInterval(showEventsModeUpdateInterval);
7578 animationTimeline->setCurveShape(QTimeLine::LinearCurve);
7580 preparedAnimations.insert(animation);
7581 animationForTimeline[animation->timeLine()] = animation;
7583 connect(animationTimeline, &QTimeLine::valueChanged,
this, &VuoEditorComposition::updatePortAnimation);
7584 connect(animationTimeline, &QTimeLine::finished,
this, &VuoEditorComposition::endPortAnimation);
7594 dispatch_async(dispatch_get_main_queue(), ^{
7595 QGraphicsItemAnimation *animation = getAvailableAnimationForPort(port);
7601 if (animation->timeLine()->state() == QTimeLine::Running)
7602 animation->timeLine()->setCurrentTime(0);
7606 animatedPort->setPos(port->pos());
7607 animatedPort->setVisible(
true);
7608 animation->timeLine()->start();
7617 QGraphicsItemAnimation * VuoEditorComposition::getAvailableAnimationForPort(
VuoRendererPort *port)
7619 vector<QGraphicsItemAnimation *> animations = port->
getAnimations();
7621 QGraphicsItemAnimation *mostAdvancedAnimation = NULL;
7622 qreal maxPercentAdvanced = -1;
7624 for (
int i = 0; i < animations.size(); ++i)
7626 QGraphicsItemAnimation *animation = animations[i];
7627 bool animationPrepared = (preparedAnimations.find(animation) != preparedAnimations.end());
7628 bool animationRunning = (animation->timeLine()->state() == QTimeLine::Running);
7630 if (! animationPrepared)
7631 return setUpAnimationForPort(animation, port);
7633 else if (! animationRunning)
7638 qreal percentAdvanced = animation->timeLine()->currentValue();
7639 if (percentAdvanced > maxPercentAdvanced)
7641 mostAdvancedAnimation = animation;
7642 maxPercentAdvanced = percentAdvanced;
7648 return (maxPercentAdvanced >= 0.5? mostAdvancedAnimation : NULL);
7656 void VuoEditorComposition::updatePortAnimation(qreal value)
7658 QTimeLine *animationTimeline = (QTimeLine *)sender();
7659 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7661 const qreal multiplier = 1000.;
7669 void VuoEditorComposition::endPortAnimation(
void)
7671 QTimeLine *animationTimeline = (QTimeLine *)sender();
7672 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7674 animatedPort->setVisible(
false);
7680 void VuoEditorComposition::setDisableDragStickiness(
bool disable)
7682 this->dragStickinessDisabled = disable;
7690 this->ignoreApplicationStateChangeEvents = ignore;
7700 this->popoverEventsEnabled = enable;
7709 refreshComponentAlphaLevelTimer->start();
7717 refreshComponentAlphaLevelTimer->stop();
7740 if (!activeProtocol)
7747 if (!
getBase()->
getCompiler()->getCachedGraph()->mayEventsReachPublishedOutputPorts())
7749 QString errorHeadline = tr(
"<b>This composition doesn't send any images to <code>outputImage</code>.</b>");
7750 QString errorDetails = tr(
"<p>To export, your composition should use the data and events from the published input ports "
7751 "to output a stream of images through the <code>outputImage</code> published output port.</p>");
7753 if (isExportingMovie)
7754 errorDetails.append(
"<p>Alternatively, you can record a realtime movie by running the composition and selecting File > Start Recording.</p>");
7757 QMessageBox messageBox(window);
7758 messageBox.setWindowFlags(Qt::Sheet);
7759 messageBox.setWindowModality(Qt::WindowModal);
7761 messageBox.setTextFormat(Qt::RichText);
7763 messageBox.setStandardButtons(QMessageBox::Help | QMessageBox::Ok);
7764 messageBox.setButtonText(QMessageBox::Help, tr(
"Open an Example"));
7765 messageBox.setButtonText(QMessageBox::Ok, tr(
"OK"));
7766 messageBox.setDefaultButton(QMessageBox::Ok);
7768 messageBox.setText(errorHeadline);
7769 messageBox.setInformativeText(
"<style>p{" + fonts->
getCSS(fonts->
dialogBodyFont()) +
"}</style>" + errorDetails);
7771 if (messageBox.exec() == QMessageBox::Help)
7773 map<QString, QString> examples =
static_cast<VuoEditor *
>(qApp)->getExampleCompositionsForProtocol(activeProtocol);
7774 map<QString, QString>::iterator i = examples.begin();
7775 if (i != examples.end())
7805 string dir, file, ext;
7819 if (! customizedName.empty())
7820 return QString::fromStdString(customizedName);
7838 string fileNameContentPart = (fileNameParts.size() >= 2 && fileNameParts[fileNameParts.size()-1] ==
"vuo"?
7839 fileNameParts[fileNameParts.size()-2] :
7840 (fileNameParts.size() >= 1? fileNameParts[fileNameParts.size()-1] :
""));
7843 if (QRegExp(
"\\s").indexIn(fileNameContentPart.c_str()) != -1)
7845 string formattedName = fileNameContentPart;
7846 if (formattedName.size() >= 1)
7847 formattedName[0] = toupper(formattedName[0]);
7849 return QString(formattedName.c_str());
7863 QStringList wordsInName = nodeSetName.split(QRegularExpression(
"\\."));
7864 if (wordsInName.size() < 2 || wordsInName[0] !=
"vuo")
7870 map<QString, QString> wordsToReformat;
7871 wordsToReformat[
"artnet"] =
"Art-Net";
7872 wordsToReformat[
"bcf2000"] =
"BCF2000";
7873 wordsToReformat[
"hid"] =
"HID";
7874 wordsToReformat[
"midi"] =
"MIDI";
7875 wordsToReformat[
"ndi"] =
"NDI";
7876 wordsToReformat[
"osc"] =
"OSC";
7877 wordsToReformat[
"rss"] =
"RSS";
7878 wordsToReformat[
"ui"] =
"UI";
7879 wordsToReformat[
"url"] =
"URL";
7881 QString nodeSetDisplayName =
"";
7882 for (
int i = 1; i < wordsInName.size(); ++i)
7884 QString currentWord = wordsInName[i];
7885 if (currentWord.size() >= 1)
7887 if (wordsToReformat.find(currentWord.toLower()) != wordsToReformat.end())
7888 currentWord = wordsToReformat.at(currentWord.toLower());
7890 currentWord[0] = currentWord[0].toUpper();
7892 nodeSetDisplayName += currentWord;
7894 if (i < wordsInName.size()-1)
7895 nodeSetDisplayName +=
" ";
7898 return nodeSetDisplayName;
7911 string formattedTypeName =
"";
7919 formattedTypeName =
"List of " + formattedInnerTypeName +
" elements";
7925 return formattedTypeName.c_str();
7944 return "Transform2D";
7946 return "Transform3D";
7954 bool VuoEditorComposition::nodeSetMenuActionLessThan(QAction *action1, QAction *action2)
7956 QString item1Text = action1->text();
7957 QString item2Text = action2->text();
7960 const QString listPrefix =
"List of ";
7961 const QString builtInTypePrefix =
"Vuo";
7963 if (item1Text.startsWith(listPrefix))
7965 item1Text.remove(0, listPrefix.length());
7966 if (item1Text.startsWith(builtInTypePrefix))
7967 item1Text.remove(0, builtInTypePrefix.length());
7970 if (item2Text.startsWith(listPrefix))
7972 item2Text.remove(0, listPrefix.length());
7973 if (item2Text.startsWith(builtInTypePrefix))
7974 item2Text.remove(0, builtInTypePrefix.length());
7978 return (item1Text.compare(item2Text, Qt::CaseInsensitive) < 0);
7985 bool VuoEditorComposition::itemHigherOnCanvas(QGraphicsItem *item1, QGraphicsItem *item2)
7987 qreal item1Y = item1->scenePos().y();
7988 qreal item2Y = item2->scenePos().y();
7990 qreal item1X = item1->scenePos().x();
7991 qreal item2X = item2->scenePos().x();
7993 if (item1Y == item2Y)
7994 return (item1X < item2X);
7996 return item1Y < item2Y;
8009 string originalGenericNode1ClassName, originalGenericNode2ClassName;
8025 vector<string> node1Keywords = node1->
getKeywords();
8026 vector<string> node2Keywords = node2->
getKeywords();
8038 node1Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8042 node2Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8044 set<string> node1KeywordSet(node1Keywords.begin(), node1Keywords.end());
8045 set<string> node2KeywordSet(node2Keywords.begin(), node2Keywords.end());
8047 set<string> nodeKeywordsIntersection;
8048 std::set_intersection(node1KeywordSet.begin(), node1KeywordSet.end(),
8049 node2KeywordSet.begin(), node2KeywordSet.end(),
8050 std::inserter(nodeKeywordsIntersection, nodeKeywordsIntersection.end()));
8052 set<string> nodeKeywordsUnion = node1KeywordSet;
8053 nodeKeywordsUnion.insert(node2KeywordSet.begin(), node2KeywordSet.end());
8056 if (nodeKeywordsUnion.size() == 0)
8060 double nodeSimilarity = nodeKeywordsIntersection.size()/(1.0*nodeKeywordsUnion.size());
8062 return nodeSimilarity;
8081 VuoEditorComposition::~VuoEditorComposition()
8083 dispatch_sync(runCompositionQueue, ^{});
8084 dispatch_release(runCompositionQueue);
8086 preparedAnimations.clear();
8087 animationForTimeline.clear();
8089 delete identifierCache;
8115 vector<VuoRendererPort *> sortedPortsToPublish;
8116 foreach (
string portID, portsToPublish)
8120 sortedPortsToPublish.push_back(port->
getRenderer());
8122 std::sort(sortedPortsToPublish.begin(), sortedPortsToPublish.end(), itemHigherOnCanvas);
8124 map<string, string> publishedPortNames;
8131 string publishedPortName = (!specializedPublishedPortName.empty()?
8132 specializedPublishedPortName :
8141 return publishedPortNames;
8169 void VuoEditorComposition::repositionPopover()
8174 const int cutoffMargin = 16;
8175 if (popover->pos().x()+popover->size().width()+cutoffMargin > views()[0]->viewport()->rect().right())
8176 popover->move(QPoint(views()[0]->viewport()->rect().right()-popover->size().width()-cutoffMargin, popover->pos().y()));
8178 if (popover->pos().y()+popover->size().height()+cutoffMargin > views()[0]->viewport()->rect().bottom())
8179 popover->move(QPoint(popover->pos().x(), views()[0]->viewport()->rect().bottom()-popover->size().height()-cutoffMargin));