54 #include <ApplicationServices/ApplicationServices.h>
55 #include <objc/objc-runtime.h>
58 const qreal VuoEditorComposition::nodeMoveRate = 15;
59 const qreal VuoEditorComposition::nodeMoveRateMultiplier = 4;
61 const qreal VuoEditorComposition::showEventsModeUpdateInterval = 1000/20.;
62 const int VuoEditorComposition::initialChangeNodeSuggestionCount = 10;
73 VuoEditorComposition_Pro();
76 this->window = window;
78 inputEditorManager = NULL;
79 activeProtocol = NULL;
81 runningComposition = NULL;
82 runningCompositionActiveDriver = NULL;
83 runningCompositionLibraries = NULL;
84 stopRequested =
false;
85 duplicateOnNextMouseMove =
false;
86 duplicationDragInProgress =
false;
87 duplicationCancelled =
false;
88 cursorPosBeforeDuplicationDragMove = QPointF(0,0);
89 cableInProgress = NULL;
90 cableInProgressWasNew =
false;
91 cableInProgressShouldBeWireless =
false;
92 portWithDragInitiated = NULL;
93 cableWithYankInitiated = NULL;
94 menuSelectionInProgress =
false;
95 previousNearbyItem = NULL;
96 dragStickinessDisabled =
false;
97 ignoreApplicationStateChangeEvents =
false;
98 popoverEventsEnabled =
true;
99 runCompositionQueue = dispatch_queue_create(
"org.vuo.editor.run", NULL);
100 activePortPopoversQueue = dispatch_queue_create(
"org.vuo.editor.popovers", NULL);
102 errorMarkingUpdatesEnabled =
true;
103 triggerPortToRefire =
"";
105 contextMenuDeleteSelected =
new QAction(NULL);
106 contextMenuHideSelectedCables =
new QAction(NULL);
107 contextMenuRenameSelected =
new QAction(NULL);
108 contextMenuRefactorSelected =
new QAction(NULL);
109 contextMenuPublishPort =
new QAction(NULL);
110 contextMenuDeleteCables =
new QAction(NULL);
111 contextMenuHideCables =
new QAction(NULL);
112 contextMenuUnhideCables =
new QAction(NULL);
113 contextMenuFireEvent =
new QAction(NULL);
114 contextMenuAddInputPort =
new QAction(NULL);
115 contextMenuRemoveInputPort =
new QAction(NULL);
116 contextMenuSetPortConstant =
new QAction(NULL);
117 contextMenuEditSelectedComments =
new QAction(NULL);
119 contextMenuChangeNode = NULL;
121 contextMenuFireEvent->setText(tr(
"Fire Event"));
122 contextMenuHideSelectedCables->setText(tr(
"Hide"));
123 contextMenuRenameSelected->setText(tr(
"Rename…"));
124 contextMenuRefactorSelected->setText(tr(
"Package as Subcomposition"));
125 contextMenuAddInputPort->setText(tr(
"Add Input Port"));
126 contextMenuRemoveInputPort->setText(tr(
"Remove Input Port"));
127 contextMenuSetPortConstant->setText(tr(
"Edit Value…"));
128 contextMenuEditSelectedComments->setText(tr(
"Edit…"));
135 connect(contextMenuDeleteCables, &QAction::triggered,
this, &VuoEditorComposition::deleteConnectedCables);
136 connect(contextMenuHideCables, &QAction::triggered,
this, &VuoEditorComposition::hideConnectedCables);
137 connect(contextMenuUnhideCables, &QAction::triggered,
this, &VuoEditorComposition::unhideConnectedCables);
138 connect(contextMenuFireEvent, &QAction::triggered,
this,
static_cast<void (
VuoEditorComposition::*)()
>(&VuoEditorComposition::fireTriggerPortEvent));
139 connect(contextMenuAddInputPort, &QAction::triggered,
this, &VuoEditorComposition::addInputPort);
140 connect(contextMenuRemoveInputPort, &QAction::triggered,
this, &VuoEditorComposition::removeInputPort);
141 connect(contextMenuEditSelectedComments, &QAction::triggered,
this, &VuoEditorComposition::editSelectedComments);
146 connect(contextMenuSetPortConstant, &QAction::triggered,
this, &VuoEditorComposition::setPortConstant, Qt::QueuedConnection);
151 QSignalMapper *contextMenuThrottlingMapper =
new QSignalMapper(
this);
152 connect(contextMenuThrottlingMapper,
static_cast<void (QSignalMapper::*)(
int)
>(&QSignalMapper::mapped),
this, &VuoEditorComposition::setTriggerThrottling);
154 QList<QPair<QString, enum VuoPortClass::EventThrottling> > throttlingNamesAndIndices;
158 for (QList<QPair<QString, enum VuoPortClass::EventThrottling> >::iterator i = throttlingNamesAndIndices.begin(); i != throttlingNamesAndIndices.end(); ++i)
160 QString name = i->first;
162 QAction *action =
new QAction(name,
this);
164 contextMenuThrottlingActions.append(action);
166 contextMenuThrottlingMapper->setMapping(action, index);
167 connect(action, &QAction::triggered, contextMenuThrottlingMapper,
static_cast<void (QSignalMapper::*)()
>(&QSignalMapper::map));
184 QSignalMapper *contextMenuTintsMapper =
new QSignalMapper(
this);
187 QList<QPair<QString, enum VuoNode::TintColor> > tintNamesAndIndices;
188 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Yellow"), VuoNode::TintYellow));
189 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Tangerine"), VuoNode::TintTangerine));
190 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Orange"), VuoNode::TintOrange));
191 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Magenta"), VuoNode::TintMagenta));
192 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Violet"), VuoNode::TintViolet));
193 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Blue"), VuoNode::TintBlue));
194 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Cyan"), VuoNode::TintCyan));
195 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Green"), VuoNode::TintGreen));
196 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"Lime"), VuoNode::TintLime));
197 tintNamesAndIndices.append(QPair<QString, enum VuoNode::TintColor>(tr(
"None"), VuoNode::TintNone));
199 for (QList<QPair<QString, enum VuoNode::TintColor> >::iterator i = tintNamesAndIndices.begin(); i != tintNamesAndIndices.end(); ++i)
201 QString name = i->first;
203 QAction *action =
new QAction(name,
this);
207 QColor fill(0,0,0,0);
209 if (index != VuoNode::TintNone)
219 p.drawEllipse(3, 3, 10, 10);
221 action->setIcon(*icon);
225 contextMenuTintActions.append(action);
227 contextMenuTintsMapper->setMapping(action, index);
228 connect(action, &QAction::triggered, contextMenuTintsMapper,
static_cast<void (QSignalMapper::*)()
>(&QSignalMapper::map));
233 this->refreshComponentAlphaLevelTimer =
new QTimer(
this);
234 this->refreshComponentAlphaLevelTimer->setObjectName(
"VuoEditorComposition::refreshComponentAlphaLevelTimer");
235 refreshComponentAlphaLevelTimer->setInterval(showEventsModeUpdateInterval);
237 setShowEventsMode(
false);
253 connect(
static_cast<VuoEditor *
>(qApp), &VuoEditor::focusChanged,
this, &VuoEditorComposition::updatePopoversForActiveWindowChange, Qt::QueuedConnection);
255 setPopoversHideOnDeactivate(
true);
258 setPopoversHideOnDeactivate(
false);
261 populateNodeAndPortIdentifierMappings();
269 this->compiler = compiler;
277 this->moduleManager = moduleManager;
286 return moduleManager;
296 this->inputEditorManager = inputEditorManager;
306 return this->inputEditorManager;
323 setCustomConstantsForNewNode(rn);
340 __block
bool isAllowed =
true;
343 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
345 bool nodeIsThisComposition = (compositionModuleKey == nodeClass->
getBase()->
getClassName());
348 auto iter = std::find_if(dependencies.begin(), dependencies.end(), [=](
const string &d){ return d == compositionModuleKey; });
349 bool nodeContainsThisComposition = (iter != dependencies.end());
351 isAllowed = ! (nodeIsThisComposition || nodeContainsThisComposition);
360 compiler->
createNode(nodeClass, title, x, y));
378 vector<string> inputPortClassNames;
379 vector<string> outputPortClassNames;
384 inputPortClassNames.push_back(portClass->
getName());
387 outputPortClassNames.push_back(portClass->
getName());
391 dummyNodeClass->
newNode(modelNode) :
400 void VuoEditorComposition::setCustomConstantsForNewNode(
VuoRendererNode *newNode)
408 QString currentYear = QString::number(QDateTime::currentDateTime().date().year());
432 disablePortPopovers(rn);
457 map<VuoCable *, VuoPort *> cablesToTransferFromPort;
458 map<VuoCable *, VuoPort *> cablesToTransferToPort;
459 set<VuoCable *> cablesToRemove;
463 vector<VuoRendererInputDrawer *> attachedDrawers;
464 vector<VuoRendererNode *> collapsedTypecasts;
466 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
472 collapsedTypecasts.push_back(typecastNode);
480 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
485 for (vector<VuoPort *>::iterator inputPort = oldInputPorts.begin(); inputPort != oldInputPorts.end(); ++inputPort)
490 attachedDrawers.push_back(attachedDrawer);
497 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferFromPort.begin(); i != cablesToTransferFromPort.end(); ++i)
498 i->first->getRenderer()->setFrom(newNode, i->second);
499 for (map<VuoCable *, VuoPort *>::iterator i = cablesToTransferToPort.begin(); i != cablesToTransferToPort.end(); ++i)
500 i->first->getRenderer()->setTo(newNode, i->second);
501 foreach (
VuoCable *cable, cablesToRemove)
518 if (! (oldDataType == newDataType && oldDataType && !
dynamic_cast<VuoGenericType *
>(oldDataType)) )
522 string oldConstantValue;
524 oldConstantValue = oldInputPort->getRenderer()->getConstantAsString();
526 oldConstantValue = oldInputPort->getRawInitialValue();
558 for (vector<VuoRendererNode *>::iterator i = collapsedTypecasts.begin(); i != collapsedTypecasts.end(); ++i)
571 disablePortPopovers(oldNode);
588 registerNodeID(newNode);
599 if (cableHidden && emitHiddenCableNotification)
611 if (cableHidden && emitHiddenCableNotification)
637 set<VuoRendererNode *> &createdNodes,
638 set<VuoRendererCable *> &createdCables)
652 foreach (QGraphicsItem *component, addedComponents)
655 if (rn && !createButDoNotAdd)
659 return addedComponents;
670 set<string> selectedNodeIDs;
677 set<string> selectedCommentIDs;
684 set<string> selectedCableIDs;
694 foreach (QGraphicsItem *item, items())
701 if (!currentNodeID.empty() && (selectedNodeIDs.find(currentNodeID) != selectedNodeIDs.end()))
702 item->setSelected(
true);
710 if (!currentCommentID.empty() && (selectedCommentIDs.find(currentCommentID) != selectedCommentIDs.end()))
711 item->setSelected(
true);
719 if (!currentCableID.empty() && (selectedCableIDs.find(currentCableID) != selectedCableIDs.end()))
720 item->setSelected(
true);
743 if ((portName ==
"expression") &&
763 if (commandDescription.empty())
766 commandDescription =
"Reset";
768 commandDescription =
"Delete";
771 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
780 if (commandDescription.empty())
781 commandDescription =
"Delete";
783 QList<QGraphicsItem *> selectedNodes;
785 selectedNodes.append(node);
819 nodeWithGraphvizIdentifier.clear();
820 portWithStaticIdentifier.clear();
821 staticIdentifierForPort.clear();
827 void VuoEditorComposition::insertNode()
829 QAction *sender = (QAction *)QObject::sender();
830 QPair<QPointF, QString> pair = sender->data().value<QPair<QPointF, QString> >();
832 QList<QGraphicsItem *> newNodes;
839 newNodes.append(newNode);
847 void VuoEditorComposition::insertComment()
849 QAction *sender = (QAction *)QObject::sender();
850 QPointF scenePos = sender->data().value<QPointF>();
858 void VuoEditorComposition::insertSubcomposition()
860 QAction *sender = (QAction *)QObject::sender();
861 QPointF scenePos = sender->data().value<QPointF>();
872 QAction *sender = (QAction *)QObject::sender();
887 void VuoEditorComposition::deleteConnectedCables()
889 QAction *sender = (QAction *)QObject::sender();
892 QList<QGraphicsItem *> cablesToRemove;
923 void VuoEditorComposition::hideConnectedCables()
925 QAction *sender = (QAction *)QObject::sender();
927 set<VuoRendererCable *> cablesToHide;
958 void VuoEditorComposition::unhideConnectedCables()
960 QAction *sender = (QAction *)QObject::sender();
962 set<VuoRendererCable *> cablesToUnhide;
991 void VuoEditorComposition::fireTriggerPortEvent()
993 QAction *sender = (QAction *)QObject::sender();
995 fireTriggerPortEvent(port->
getBase());
1011 if (triggerPortToRefire.empty() ||
1012 (portWithStaticIdentifier.find(triggerPortToRefire) == portWithStaticIdentifier.end()))
1015 return portWithStaticIdentifier[triggerPortToRefire];
1024 if (portID != this->triggerPortToRefire)
1026 this->triggerPortToRefire = portID;
1035 void VuoEditorComposition::setPortConstant()
1037 QAction *sender = (QAction *)QObject::sender();
1048 void VuoEditorComposition::setPortConstantToValue(
VuoRendererPort *port,
string value)
1058 void VuoEditorComposition::specializeGenericPortType()
1060 QAction *sender = (QAction *)QObject::sender();
1061 QList<QVariant> portAndSpecializedType= sender->data().toList();
1063 QString specializedTypeName = portAndSpecializedType[1].toString();
1075 emit
specializePort(port, specializedTypeName.toUtf8().constData());
1082 void VuoEditorComposition::unspecializePortType()
1084 QAction *sender = (QAction *)QObject::sender();
1106 map<VuoPort *, VuoNode *> nodeForPort;
1107 map<VuoPort *, set<VuoCable *> > cablesForPort;
1109 for (set<VuoNode *>::iterator i = nodes.begin(); i != nodes.end(); ++i)
1114 vector<VuoPort *> ports;
1115 ports.insert(ports.end(), inputPorts.begin(), inputPorts.end());
1116 ports.insert(ports.end(), outputPorts.begin(), outputPorts.end());
1118 for (vector<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1122 nodeForPort[port] = node;
1124 cablesForPort[port].insert(cables.begin(), cables.end());
1130 portToUnspecialize,
true);
1131 map<VuoNode *, set<VuoPort *> > portsToUnspecializeForNode;
1132 for (
VuoPort *connectedPort : connectedPotentiallyGenericPorts)
1134 VuoNode *node = nodeForPort[connectedPort];
1138 if (isPortCurrentlyRevertible(connectedPort->getRenderer()))
1139 portsToUnspecializeForNode[node].insert(connectedPort);
1142 for (map<
VuoNode *, set<VuoPort *> >::iterator i = portsToUnspecializeForNode.begin(); i != portsToUnspecializeForNode.end(); ++i)
1145 set<VuoPort *> ports = i->second;
1148 set<VuoPortClass *> portClasses;
1149 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1150 portClasses.insert((*j)->getClass());
1153 nodesToReplace[node] = unspecializedNodeClassName;
1157 for (set<VuoPort *>::iterator j = ports.begin(); j != ports.end(); ++j)
1160 set<VuoCable *> connectedCables = cablesForPort[port];
1161 for (set<VuoCable *>::iterator k = connectedCables.begin(); k != connectedCables.end(); ++k)
1165 bool areEndsCompatible =
false;
1168 areEndsCompatible =
true;
1173 if (portOnOtherEnd && isPortCurrentlyRevertible(portOnOtherEnd->
getRenderer()))
1175 VuoNode *nodeOnOtherEnd = nodeForPort[portOnOtherEnd];
1178 if (specializedNodeClassOnOtherEnd)
1181 if (! typeOnOtherEnd ||
dynamic_cast<VuoGenericType *
>(typeOnOtherEnd))
1182 areEndsCompatible =
true;
1187 if (! areEndsCompatible && (cable != cableInProgress))
1188 cablesToDelete.insert(cable);
1197 void VuoEditorComposition::fireTriggerPortEvent(
VuoPort *port)
1205 string runningTriggerPortIdentifier =
"";
1206 bool isTriggerPort =
false;
1213 runningTriggerPortIdentifier = staticIdentifierForPort[port];
1214 isTriggerPort =
true;
1232 runningTriggerPortIdentifier.c_str());
1235 bool manuallyFirableInputPortChanged = (oldManuallyFirableInputPort != newManuallyFirableInputPort);
1238 auto fireIfRunning = ^void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
1240 dispatch_async(topLevelComposition->runCompositionQueue, ^{
1241 if (topLevelComposition->isRunningThreadUnsafe())
1243 topLevelComposition->runner->fireTriggerPortEvent(thisCompositionIdentifier, runningTriggerPortIdentifier);
1248 if (! (this->showEventsMode && isTriggerPort) )
1249 this->animatePort(port->getRenderer());
1254 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1255 if (
this == topLevelComposition || ! manuallyFirableInputPortChanged)
1259 if (! newSnapshot.empty() && manuallyFirableInputPortChanged)
1260 updateRunningComposition(oldSnapshot, newSnapshot);
1262 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1268 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^
void (
VuoEditorComposition *currComposition,
string compositionPath) {
1270 moduleManager->doNextTimeNodeClassIsLoaded(nodeClassName, ^{
1271 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier) {
1272 fireIfRunning(topLevelComposition, thisCompositionIdentifier);
1276 if (! newSnapshot.empty())
1277 updateRunningComposition(oldSnapshot, newSnapshot);
1282 setTriggerPortToRefire(port);
1288 void VuoEditorComposition::setTriggerThrottling(
int eventThrottling)
1290 QSignalMapper *signalMapper = (QSignalMapper *)QObject::sender();
1291 QAction *sender = (QAction *)signalMapper->mapping(eventThrottling);
1300 void VuoEditorComposition::addInputPort()
1302 QAction *sender = (QAction *)QObject::sender();
1311 void VuoEditorComposition::removeInputPort()
1313 QAction *sender = (QAction *)QObject::sender();
1322 void VuoEditorComposition::swapNode()
1324 QAction *sender = (QAction *)QObject::sender();
1325 QList<QVariant> nodeAndReplacementType= sender->data().toList();
1327 QString newNodeClassName = nodeAndReplacementType[1].toString();
1347 for (set<VuoRendererNode *>::iterator i = selectedNodes.begin(); i != selectedNodes.end(); ++i)
1357 void VuoEditorComposition::editSelectedComments()
1361 for (set<VuoRendererComment *>::iterator i = selectedComments.begin(); i != selectedComments.end(); ++i)
1370 set<VuoRendererCable *> internalCables;
1372 for (QList<QGraphicsItem *>::iterator i = subcompositionComponents.begin(); i != subcompositionComponents.end(); ++i)
1374 QGraphicsItem *compositionComponent = *i;
1379 for (set<VuoCable *>::iterator cable = connectedCables.begin(); cable != connectedCables.end(); ++cable)
1381 VuoNode *fromNode = (*cable)->getFromNode();
1382 VuoNode *toNode = (*cable)->getToNode();
1384 if (fromNode && toNode && subcompositionComponents.contains(fromNode->
getRenderer()) && subcompositionComponents.contains(toNode->
getRenderer()))
1385 internalCables.insert((*cable)->getRenderer());
1390 return internalCables;
1398 return cableInProgress;
1407 return cableInProgressWasNew;
1415 return menuSelectionInProgress;
1423 QList<QGraphicsItem *> compositionComponents = items();
1424 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1426 QGraphicsItem *compositionComponent = *i;
1430 compositionComponent->setSelected(
true);
1439 QList<QGraphicsItem *> compositionComponents = items();
1440 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1442 QGraphicsItem *compositionComponent = *i;
1445 rcomment->setSelected(
true);
1454 QList<QGraphicsItem *> compositionComponents = items();
1455 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
1457 QGraphicsItem *compositionComponent = *i;
1458 compositionComponent->setSelected(
false);
1465 void VuoEditorComposition::openSelectedEditableNodes()
1470 QString actionText, sourcePath;
1483 moveItemsBy(selectedNodes, selectedComments, dx, dy,
false);
1489 void VuoEditorComposition::moveNodesBy(set<VuoRendererNode *> nodes, qreal dx, qreal dy,
bool movedByDragging)
1491 set<VuoRendererComment *> comments;
1492 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1498 void VuoEditorComposition::moveCommentsBy(set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1500 set<VuoRendererNode *> nodes;
1501 moveItemsBy(nodes, comments, dx, dy, movedByDragging);
1507 void VuoEditorComposition::moveItemsBy(set<VuoRendererNode *> nodes, set<VuoRendererComment *> comments, qreal dx, qreal dy,
bool movedByDragging)
1509 emit
itemsMoved(nodes, comments, dx, dy, movedByDragging);
1511 for (set<VuoRendererNode *>::iterator it = nodes.begin(); it != nodes.end(); ++it)
1512 (*it)->updateConnectedCableGeometry();
1518 void VuoEditorComposition::resizeCommentBy(
VuoRendererComment *comment, qreal dx, qreal dy)
1528 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1529 set<VuoRendererNode *> selectedNodes;
1530 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1532 QGraphicsItem *compositionComponent = *i;
1535 selectedNodes.insert(rn);
1538 return selectedNodes;
1546 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1547 set<VuoRendererComment *> selectedComments;
1548 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1550 QGraphicsItem *compositionComponent = *i;
1553 selectedComments.insert(rc);
1556 return selectedComments;
1564 QList<QGraphicsItem *> selectedCompositionComponents = selectedItems();
1565 set<VuoRendererCable *> selectedCables;
1566 for (QList<QGraphicsItem *>::iterator i = selectedCompositionComponents.begin(); i != selectedCompositionComponents.end(); ++i)
1568 QGraphicsItem *compositionComponent = *i;
1571 selectedCables.insert(rc);
1574 return selectedCables;
1582 const QMimeData *mimeData =
event->mimeData();
1583 bool disablePortHoverHighlighting =
true;
1586 if (mimeData->hasFormat(
"text/uri-list"))
1588 QList<QUrl> urls = mimeData->urls();
1592 if (portAtDropLocation)
1594 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1596 disablePortHoverHighlighting =
false;
1598 event->setDropAction(Qt::IgnoreAction);
1602 bool dragIncludesDroppableFile =
false;
1603 foreach (QUrl url, urls)
1617 if (isSupportedDragNDropFile)
1619 dragIncludesDroppableFile =
true;
1624 if (!dragIncludesDroppableFile)
1625 event->setDropAction(Qt::IgnoreAction);
1632 else if (mimeData->hasFormat(
"text/plain") || mimeData->hasFormat(
"text/scsv"))
1633 event->acceptProposedAction();
1637 event->setDropAction(Qt::IgnoreAction);
1641 updateHoverHighlighting(event->scenePos(), disablePortHoverHighlighting);
1649 event->acceptProposedAction();
1665 const QMimeData *mimeData =
event->mimeData();
1668 if (mimeData->hasFormat(
"text/uri-list"))
1676 if (topCompositionPath.empty())
1678 QDir compositionDir(QDir(topCompositionPath.c_str()).canonicalPath());
1683 QList<QGraphicsItem *> newNodes;
1684 QList<QUrl> urls = mimeData->urls();
1688 if (portAtDropLocation)
1690 if ((urls.size() == 1) && portAtDropLocation->
isConstant() &&
1693 QString filePath = (useAbsoluteFilePaths? urls[0].path() : compositionDir.relativeFilePath(urls[0].path()));
1694 string constantValue =
"\"" + string(filePath.toUtf8().constData()) +
"\"";
1705 const int ySpacingForNewNodes = 11;
1706 int yOffsetForPreviousNewNode = -1 * ySpacingForNewNodes;
1708 foreach (QUrl url, urls)
1710 QStringList targetNodeClassNames;
1713 targetNodeClassNames +=
"vuo.image.fetch";
1717 targetNodeClassNames +=
"vuo.video.play";
1719 targetNodeClassNames +=
"vuo.video.decodeImage";
1722 targetNodeClassNames +=
"vuo.scene.fetch";
1724 targetNodeClassNames +=
"vuo.audio.file.play";
1726 targetNodeClassNames +=
"vuo.image.project.dome";
1728 targetNodeClassNames +=
"vuo.rss.fetch";
1730 targetNodeClassNames +=
"vuo.tree.fetch.json";
1732 targetNodeClassNames +=
"vuo.tree.fetch.xml";
1734 targetNodeClassNames +=
"vuo.table.fetch";
1736 targetNodeClassNames +=
"vuo.data.fetch";
1738 targetNodeClassNames +=
"vuo.app.launch";
1740 targetNodeClassNames +=
"vuo.file.list";
1742 QString selectedNodeClassName =
"";
1743 if (targetNodeClassNames.size() == 1)
1744 selectedNodeClassName = targetNodeClassNames[0];
1745 else if (targetNodeClassNames.size() > 1)
1747 QMenu nodeMenu(views()[0]->viewport());
1748 nodeMenu.setSeparatorsCollapsible(
false);
1750 foreach (QString nodeClassName, targetNodeClassNames)
1753 string nodeTitle = (nodeClass? nodeClass->
getBase()->
getDefaultTitle() : nodeClassName.toUtf8().constData());
1755 QAction *nodeAction = nodeMenu.addAction(tr(
"Insert \"%1\" Node").arg(nodeTitle.c_str()));
1756 nodeAction->setData(nodeClassName);
1759 menuSelectionInProgress =
true;
1760 QAction *selectedNode = nodeMenu.exec(QCursor::pos());
1761 menuSelectionInProgress =
false;
1763 selectedNodeClassName = (selectedNode? selectedNode->data().toString().toUtf8().constData() :
"");
1766 if (!selectedNodeClassName.isEmpty())
1769 event->scenePos().x(),
1770 event->scenePos().y() + yOffsetForPreviousNewNode + ySpacingForNewNodes);
1779 QString filePath = (useAbsoluteFilePaths? url.path() : compositionDir.relativeFilePath(url.path()));
1782 newNodes.append(newNode);
1784 yOffsetForPreviousNewNode += newNode->
boundingRect().height();
1785 yOffsetForPreviousNewNode += ySpacingForNewNodes;
1791 if (newNodes.size() > 0)
1803 else if (mimeData->hasFormat(
"text/scsv"))
1805 event->setDropAction(Qt::CopyAction);
1808 QByteArray scsvData =
event->mimeData()->data(
"text/scsv");
1809 QString scsvText = QString::fromUtf8(scsvData);
1810 QStringList nodeClassNames = scsvText.split(
';');
1815 int snapDelta = nextYPos - startPos.y();
1817 QList<QGraphicsItem *> newNodes;
1818 for (QStringList::iterator i = nodeClassNames.begin(); i != nodeClassNames.end(); ++i)
1826 int prevYPos = nextYPos;
1829 if (nextYPos <= prevYPos+newNode->boundingRect().height())
1832 newNodes.append((QGraphicsItem *)newNode);
1840 else if (mimeData->hasFormat(
"text/plain"))
1842 event->setDropAction(Qt::CopyAction);
1845 QList<QGraphicsItem *> newNodes;
1846 QStringList dropItems =
event->mimeData()->text().split(
'\n');
1847 QString nodeClassName = dropItems[0];
1851 QPoint hotSpot = (dropItems.size() >= 3? QPoint(dropItems[1].toInt(), dropItems[2].toInt()) : QPoint(0,0));
1853 event->scenePos().x()-hotSpot.x()+1,
1856 newNodes.append(newNode);
1873 QGraphicsScene::sendEvent(nearbyItem, event);
1878 QGraphicsScene::mouseDoubleClickEvent(event);
1886 QPointF scenePos =
event->scenePos();
1889 if (event->button() == Qt::LeftButton)
1894 if (duplicationCancelled)
1896 QGraphicsItem *itemClickedDirectly = itemAt(scenePos, views()[0]->transform());
1902 duplicationCancelled =
false;
1903 correctForCancelledDuplication(event);
1914 mousePressEventNonLeftButton(event);
1923 bool eventHandled =
false;
1937 cableYankedDirectly = currentCable;
1945 if ((event->modifiers() & Qt::ControlModifier) && (currentPort->
getInput() || isTriggerPort))
1946 fireTriggerPortEvent(currentPort->
getBase());
1950 portWithDragInitiated = currentPort;
1951 cableWithYankInitiated = cableYankedDirectly;
1955 eventHandled =
true;
1962 duplicateOnNextMouseMove =
true;
1963 duplicationDragInProgress =
true;
1969 QGraphicsScene::mousePressEvent(event);
1970 eventHandled =
true;
1976 if (event->modifiers() & Qt::ControlModifier)
1977 nearbyItem->setSelected(! nearbyItem->isSelected());
1982 nearbyItem->setSelected(
true);
1986 eventHandled =
true;
1991 QGraphicsScene::mousePressEvent(event);
1998 void VuoEditorComposition::mousePressEventNonLeftButton(QGraphicsSceneMouseEvent *event)
2007 if (event->button() == Qt::RightButton)
2009 QGraphicsSceneContextMenuEvent
contextMenuEvent(QEvent::GraphicsSceneContextMenu);
2018 QGraphicsScene::mousePressEvent(event);
2029 void VuoEditorComposition::correctForCancelledDuplication(QGraphicsSceneMouseEvent *event)
2033 QGraphicsSceneMouseEvent pressEvent(QEvent::GraphicsSceneMousePress);
2034 pressEvent.setScenePos(event->scenePos());
2035 pressEvent.setButton(Qt::LeftButton);
2036 QApplication::sendEvent(
this, &pressEvent);
2038 QGraphicsSceneMouseEvent releaseEvent(QEvent::GraphicsSceneMouseRelease);
2039 releaseEvent.setScenePos(event->scenePos());
2040 releaseEvent.setButton(Qt::LeftButton);
2041 QApplication::sendEvent(
this, &releaseEvent);
2053 Qt::KeyboardModifiers modifiers =
event->modifiers();
2054 bool optionKeyPressed = (modifiers & Qt::AltModifier);
2055 bool shiftKeyPressed = (modifiers & Qt::ShiftModifier);
2066 bool creatingNewCable =
false;
2067 bool disconnectingExistingCable =
false;
2068 bool duplicatingExistingCable =
false;
2082 fromPort = fixedPort;
2083 fromNode = fixedNode;
2084 creatingNewCable =
true;
2096 creatingNewCable =
true;
2103 if (optionKeyPressed)
2105 duplicatingExistingCable =
true;
2109 disconnectingExistingCable =
true;
2117 if (creatingNewCable)
2130 cableInProgressWasNew =
true;
2131 cableInProgressShouldBeWireless =
false;
2138 highlightEligibleEndpointsForCable(cableInProgress);
2143 else if (disconnectingExistingCable)
2146 if (cableYankedDirectly)
2147 cableInProgress = cableYankedDirectly->
getBase();
2153 cableInProgressWasNew =
false;
2164 highlightEligibleEndpointsForCable(cableInProgress);
2166 cableInProgress->
getRenderer()->setSelected(
true);
2169 else if (duplicatingExistingCable)
2173 if (cableYankedDirectly)
2174 cableToDuplicate = cableYankedDirectly->
getBase();
2191 cableInProgressWasNew =
true;
2192 cableInProgressShouldBeWireless = cableToDuplicate->
hasCompiler() &&
2200 highlightEligibleEndpointsForCable(cableInProgress);
2202 cableInProgress->
getRenderer()->setSelected(
true);
2207 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2220 if (event->button() == Qt::LeftButton)
2222 portWithDragInitiated = NULL;
2223 cableWithYankInitiated = NULL;
2224 duplicateOnNextMouseMove =
false;
2225 duplicationDragInProgress =
false;
2226 dragStickinessDisabled =
false;
2233 bool cableDragEnding = cableInProgress;
2234 if (cableDragEnding)
2235 concludeCableDrag(event);
2247 if ((event->modifiers() == Qt::NoModifier) &&
2248 (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2255 enablePopoverForNode(typecastNode);
2260 enableInactivePopoverForPort(port);
2266 if (!cableDragEnding && !node)
2270 if (!cableDragEnding)
2271 QGraphicsScene::mouseReleaseEvent(event);
2277 QGraphicsScene::mouseReleaseEvent(event);
2287 void VuoEditorComposition::concludeCableDrag(QGraphicsSceneMouseEvent *event)
2295 concludePublishedCableDrag(event);
2299 if (hasFeedbackErrors())
2317 bool completedCableConnection =
false;
2320 VuoCable *dataCableToDisplace = NULL;
2324 string typecastToInsert =
"";
2328 string specializedTypeName =
"";
2345 bool draggingPreviouslyPublishedCable = (!cableInProgressWasNew &&
2347 ((cableInProgress->
getToPort() == NULL) &&
2350 if (fixedPort && targetPort)
2358 if (recreatingSameConnection)
2368 bool preexistingCableWithMatchingDataCarryingStatus = (preexistingCable?
2370 cableInProgressExpectedToCarryData) :
2376 if (preexistingCable && !preexistingCableWithMatchingDataCarryingStatus &&
2387 if (!preexistingCableWithMatchingDataCarryingStatus &&
2389 selectBridgingSolution(fixedPort, targetPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert)))
2400 targetPort = adjustedTargetPort;
2404 if (cableInProgressExpectedToCarryData &&
2406 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2407 typecastNodeToDelete = uncollapsedTypecast;
2412 if (preexistingCable)
2413 cableToReplace = preexistingCable;
2417 if (cableInProgressExpectedToCarryData)
2421 for (vector<VuoCable *>::iterator cable = previousConnectedCables.begin(); (! dataCableToDisplace) && (cable != previousConnectedCables.end()); ++cable)
2422 if ((((*cable)->getRenderer()->effectivelyCarriesData()) && (*cable) != cableToReplace))
2423 dataCableToDisplace = *cable;
2430 if (publishedDataConnections.size() > 0)
2431 portToUnpublish = targetPort;
2435 completedCableConnection =
true;
2439 else if (!preexistingCableWithMatchingDataCarryingStatus &&
2441 selectBridgingSolution(targetPort, fixedPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert)))
2444 completedCableConnection =
true;
2449 if (completedCableConnection)
2453 if (draggingPreviouslyPublishedCable)
2472 cableInProgressCopy->
setFrom(cableInProgressFromNode, cableInProgressFromPort);
2473 cableInProgressCopy->
setTo(cableInProgressToNode, cableInProgressToPort);
2480 cableInProgress = cableInProgressCopy;
2481 cableInProgressWasNew =
true;
2485 pair<VuoRendererCable *, VuoRendererCable *> cableArgs = std::make_pair((dataCableToDisplace? dataCableToDisplace->
getRenderer() : NULL),
2486 (cableToReplace? cableToReplace->
getRenderer() : NULL));
2487 pair<string, string> typeArgs = std::make_pair(typecastToInsert, specializedTypeName);
2488 pair<VuoRendererPort *, VuoRendererPort *> portArgs = std::make_pair(portToUnpublish, portToSpecialize);
2493 typecastNodeToDelete,
2500 if (cableInProgress)
2504 cableInProgress = NULL;
2518 void VuoEditorComposition::concludePublishedCableDrag(QGraphicsSceneMouseEvent *event)
2528 string typecastToInsert =
"";
2532 string specializedTypeName =
"";
2549 if (internalInputPort)
2552 forceEventOnlyPublication =
true;
2558 if (internalInputPort &&
2564 internalInputPort->
getBase()) &&
2567 if (recreatingSameConnection)
2575 ||
selectBridgingSolution(publishedInputPort, internalInputPort,
true, &portToSpecialize, specializedTypeName, typecastToInsert))
2580 bool cableToReplaceHasMatchingDataCarryingStatus = (cableToReplace?
2582 cableInProgressExpectedToCarryData) :
2587 if (cableToReplace && cableToReplaceHasMatchingDataCarryingStatus)
2593 else if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus &&
2609 if (cableToReplace && !cableToReplaceHasMatchingDataCarryingStatus)
2611 QList<QGraphicsItem *> removedComponents;
2612 removedComponents.append(cableToReplace->
getRenderer());
2621 if (typecastPort && typecastPort->scene())
2629 if (cableInProgressExpectedToCarryData &&
2631 (*typecastOutPort->
getConnectedCables(
true).begin())->getRenderer()->effectivelyCarriesData())
2633 QList<QGraphicsItem *> removedComponents;
2634 removedComponents.append(uncollapsedTypecast);
2641 forceEventOnlyPublication,
2642 (portToSpecialize? portToSpecialize->
getBase() : NULL),
2643 specializedTypeName,
2669 if (internalOutputPort)
2672 forceEventOnlyPublication =
true;
2678 if (internalOutputPort &&
2679 publishedOutputPort)
2685 ||
selectBridgingSolution(internalOutputPort, publishedOutputPort,
false, &portToSpecialize, specializedTypeName, typecastToInsert))
2689 forceEventOnlyPublication,
2690 portToSpecialize? portToSpecialize->
getBase() : NULL,
2691 specializedTypeName,
2710 if (! cableInProgress)
2713 if (cableInProgressWasNew)
2717 QList<QGraphicsItem *> removedComponents;
2718 removedComponents.append(cableInProgress->
getRenderer());
2722 cableInProgress = NULL;
2735 cableInProgress->
getRenderer()->setCacheMode(QGraphicsItem::NoCache);
2740 cableInProgress = NULL;
2742 else if (cableInProgress)
2757 bool leftMouseButtonPressed = (
event->buttons() & Qt::LeftButton);
2773 if (cableInProgress || (! leftMouseButtonPressed))
2774 updateHoverHighlighting(event->scenePos());
2778 if (leftMouseButtonPressed)
2781 if ((! dragStickinessDisabled) && (QLineF(event->screenPos(), event->buttonDownScreenPos(Qt::LeftButton)).length() < QApplication::startDragDistance()))
2785 dragStickinessDisabled =
true;
2789 if (portWithDragInitiated || cableWithYankInitiated)
2791 initiateCableDrag(portWithDragInitiated, cableWithYankInitiated, event);
2792 portWithDragInitiated = NULL;
2793 cableWithYankInitiated = NULL;
2798 if (cableInProgress)
2808 else if (duplicationDragInProgress)
2810 if (duplicateOnNextMouseMove)
2813 duplicateOnNextMouseMove =
false;
2820 QPointF delta = newPos - cursorPosBeforeDuplicationDragMove;
2822 cursorPosBeforeDuplicationDragMove = newPos;
2826 QGraphicsScene::mouseMoveEvent(event);
2831 QGraphicsScene::mouseMoveEvent(event);
2839 void VuoEditorComposition::updateHoverHighlighting(QPointF scenePos,
bool disablePortHoverHighlighting)
2851 bool hoveringOverNodeHeader =
false;
2852 if (cableInProgress)
2861 hoveringOverNodeHeader =
true;
2867 if (! hoveringOverNodeHeader)
2878 if (item != previousNearbyItem)
2886 if (previousNearbyItem)
2894 if (! cableInProgress)
2900 else if (typecastPort && !disablePortHoverHighlighting)
2902 else if (port && !disablePortHoverHighlighting)
2905 if (!cableInProgress)
2916 if (cableInProgress)
2919 cableInProgress->getFromPort()->getRenderer() :
2920 cableInProgress->getToPort()->getRenderer());
2922 QList< QPair<VuoRendererPort *, bool> > updatedPorts;
2923 if (hoveringOverNodeHeader)
2924 updatedPorts.append( QPair<VuoRendererPort *, bool>(port,
true) );
2925 if (previousNode && previousPort)
2926 updatedPorts.append( QPair<VuoRendererPort *, bool>(previousPort, !cableInProgress->getRenderer()->effectivelyCarriesData()) );
2928 QPair<VuoRendererPort *, bool> p;
2929 foreach (p, updatedPorts)
2934 if (typecastParentPort)
2935 updatedPort = typecastParentPort;
2937 updateEligibilityHighlightingForPort(updatedPort, fixedPort, p.second);
2940 updateEligibilityHighlightingForNode(potentialDrawer);
2947 if (targetPort || previousTargetPort)
2949 if (cableInProgress)
2950 cableInProgress->getRenderer()->setFloatingEndpointAboveEventPort(targetPort && (!targetPort->
getDataType() || hoveringOverNodeHeader));
2955 previousNearbyItem = item;
2963 else if (port && !disablePortHoverHighlighting)
2966 if (!cableInProgress)
2972 else if (makeListDrawer)
2982 if (previousNearbyItem)
3000 else if (typecastPort)
3009 previousNearbyItem = NULL;
3018 if ((event->key() != Qt::Key_Alt) && (event->key() != Qt::Key_Shift) && (event->key() != Qt::Key_Escape))
3021 Qt::KeyboardModifiers modifiers =
event->modifiers();
3022 qreal adjustedNodeMoveRate = nodeMoveRate;
3023 if (modifiers & Qt::ShiftModifier)
3025 adjustedNodeMoveRate *= nodeMoveRateMultiplier;
3028 switch (event->key()) {
3029 case Qt::Key_Backspace:
3034 case Qt::Key_Delete:
3046 if (modifiers & Qt::ControlModifier)
3047 openSelectedEditableNodes();
3064 if (cableInProgress)
3066 cableInProgress->getRenderer()->updateGeometry();
3067 cableInProgress->getCompiler()->setAlwaysEventOnly(
true);
3068 highlightEligibleEndpointsForCable(cableInProgress);
3078 case Qt::Key_Return:
3081 QGraphicsScene::keyPressEvent(event);
3083 if (!event->isAccepted())
3090 if (selectedComments.empty() && !selectedNodes.empty())
3097 case Qt::Key_Escape:
3099 if (duplicateOnNextMouseMove || duplicationDragInProgress)
3103 duplicateOnNextMouseMove =
false;
3104 duplicationDragInProgress =
false;
3105 duplicationCancelled =
true;
3107 else if (cableInProgress)
3116 QGraphicsScene::keyPressEvent(event);
3127 switch (event->key()) {
3130 if (cableInProgress)
3132 cableInProgress->getRenderer()->updateGeometry();
3133 cableInProgress->getCompiler()->setAlwaysEventOnly(
false);
3134 highlightEligibleEndpointsForCable(cableInProgress);
3146 QGraphicsScene::keyReleaseEvent(event);
3155 void VuoEditorComposition::addActionToMenuAndMapper(QMenu *menu, QSignalMapper *mapper, QString name,
int index)
3157 QAction *action =
new QAction(name,
this);
3158 menu->addAction(action);
3159 mapper->setMapping(action, index);
3160 connect(action, &QAction::triggered, mapper,
static_cast<void (QSignalMapper::*)()
>(&QSignalMapper::map));
3172 contextMenu.setSeparatorsCollapsible(
false);
3177 QAction *insertNodeSection =
new QAction(tr(
"Insert Node"), NULL);
3178 insertNodeSection->setEnabled(
false);
3179 contextMenu.addAction(insertNodeSection);
3181 QString spacer(
" ");
3185 QMenu *shareMenu =
new QMenu(&contextMenu);
3186 shareMenu->setSeparatorsCollapsible(
false);
3187 shareMenu->setTitle(spacer + tr(
"Share"));
3188 contextMenu.addMenu(shareMenu);
3191 QAction *action =
new QAction(tr(
"Share Value"), NULL);
3192 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share"))));
3193 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3194 shareMenu->addAction(action);
3198 QAction *action =
new QAction(tr(
"Share List"), NULL);
3199 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.share.list"))));
3200 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3201 shareMenu->addAction(action);
3205 QAction *action =
new QAction(tr(
"Share Event"), NULL);
3206 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.share"))));
3207 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3208 shareMenu->addAction(action);
3214 QMenu *holdMenu =
new QMenu(&contextMenu);
3215 holdMenu->setSeparatorsCollapsible(
false);
3216 holdMenu->setTitle(spacer + tr(
"Hold"));
3217 contextMenu.addMenu(holdMenu);
3220 QAction *action =
new QAction(tr(
"Hold Value"), NULL);
3221 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold2"))));
3222 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3223 holdMenu->addAction(action);
3227 QAction *action =
new QAction(tr(
"Hold List"), NULL);
3228 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.data.hold.list2"))));
3229 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3230 holdMenu->addAction(action);
3236 QMenu *allowMenu =
new QMenu(&contextMenu);
3237 allowMenu->setSeparatorsCollapsible(
false);
3238 allowMenu->setTitle(spacer + tr(
"Allow"));
3239 contextMenu.addMenu(allowMenu);
3242 QAction *action =
new QAction(tr(
"Allow First Event"), NULL);
3243 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirst"))));
3244 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3245 allowMenu->addAction(action);
3249 QAction *action =
new QAction(tr(
"Allow First Value"), NULL);
3250 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowFirstValue"))));
3251 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3252 allowMenu->addAction(action);
3256 QAction *action =
new QAction(tr(
"Allow Periodic Events"), NULL);
3257 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.time.allowPeriodic"))));
3258 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3259 allowMenu->addAction(action);
3263 QAction *action =
new QAction(tr(
"Allow Changes"), NULL);
3264 action->setData(qVariantFromValue(qMakePair(event->scenePos(), QStringLiteral(
"vuo.event.allowChanges2"))));
3265 connect(action, &QAction::triggered,
this, &VuoEditorComposition::insertNode);
3266 allowMenu->addAction(action);
3270 contextMenu.addSeparator();
3272 QAction *contextMenuInsertComment =
new QAction(NULL);
3273 contextMenuInsertComment->setText(tr(
"Insert Comment"));
3274 contextMenuInsertComment->setData(qVariantFromValue(event->scenePos()));
3275 connect(contextMenuInsertComment, &QAction::triggered,
this, &VuoEditorComposition::insertComment);
3276 contextMenu.addAction(contextMenuInsertComment);
3278 QAction *contextMenuInsertSubcomposition =
new QAction(NULL);
3279 contextMenuInsertSubcomposition->setText(tr(
"Insert Subcomposition"));
3280 contextMenuInsertSubcomposition->setData(qVariantFromValue(event->scenePos()));
3281 connect(contextMenuInsertSubcomposition, &QAction::triggered,
this, &VuoEditorComposition::insertSubcomposition);
3282 contextMenu.addAction(contextMenuInsertSubcomposition);
3287 QAction *contextMenuDeleteSelectedSnapshot =
new QAction(NULL);
3294 if (port->
isConstant() && inputEditorManager)
3298 if (inputEditorLoadedForPortDataType)
3300 contextMenuSetPortConstant->setData(qVariantFromValue((
void *)port));
3301 contextMenu.addAction(contextMenuSetPortConstant);
3303 inputEditorLoadedForPortDataType->deleteLater();
3309 contextMenuPublishPort->setText(tr(
"Publish Port"));
3310 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3311 contextMenu.addAction(contextMenuPublishPort);
3316 vector<VuoRendererPublishedPort *> externalPublishedPorts = port->
getPublishedPorts();
3317 bool hasExternalPublishedPortWithMultipleInternalPorts =
false;
3318 bool hasExternalPublishedPortBelongingToActiveProtocol =
false;
3322 hasExternalPublishedPortWithMultipleInternalPorts =
true;
3325 hasExternalPublishedPortBelongingToActiveProtocol =
true;
3332 if (!hasExternalPublishedPortWithMultipleInternalPorts &&!hasExternalPublishedPortBelongingToActiveProtocol)
3334 contextMenuPublishPort->setText(tr(
"Delete Published Port"));
3335 contextMenuPublishPort->setData(qVariantFromValue((
void *)port));
3336 contextMenu.addAction(contextMenuPublishPort);
3341 if (isTriggerPort || port->
getInput())
3343 __block
bool isTopLevelCompositionRunning =
false;
3344 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
3346 isTopLevelCompositionRunning = topLevelComposition->
isRunning();
3349 if (isTopLevelCompositionRunning)
3351 contextMenuFireEvent->setData(qVariantFromValue((
void *)port));
3352 contextMenu.addAction(contextMenuFireEvent);
3358 QMenu *contextMenuThrottling =
new QMenu(&contextMenu);
3359 contextMenuThrottling->setSeparatorsCollapsible(
false);
3360 contextMenuThrottling->setTitle(tr(
"Set Event Throttling"));
3362 foreach (QAction *action, contextMenuThrottlingActions)
3364 contextMenuThrottling->addAction(action);
3365 action->setData(qVariantFromValue((
void *)port));
3366 action->setCheckable(
true);
3367 action->setChecked( i++ == port->getBase()->getEventThrottling() );
3369 contextMenu.addMenu(contextMenuThrottling);
3374 if (genericDataType || isPortCurrentlyRevertible(port))
3376 QMenu *contextMenuSpecializeGenericType =
new QMenu(&contextMenu);
3377 contextMenuSpecializeGenericType->setSeparatorsCollapsible(
false);
3378 contextMenuSpecializeGenericType->setTitle(tr(
"Set Data Type"));
3379 contextMenuSpecializeGenericType->setToolTipsVisible(
true);
3381 QAction *unspecializeAction = contextMenuSpecializeGenericType->addAction(tr(
"Generic"));
3386 contextMenuSpecializeGenericType->addSeparator();
3388 unspecializeAction->setData(qVariantFromValue((
void *)port));
3389 unspecializeAction->setCheckable(
true);
3390 unspecializeAction->setChecked(genericDataType);
3392 contextMenu.addMenu(contextMenuSpecializeGenericType);
3395 set<string> compatibleTypes;
3396 set<string> compatibleTypesInIsolation;
3399 if (genericDataType)
3407 compatibleTypes = set<string>(compatibleTypesVector.begin(), compatibleTypesVector.end());
3413 compatibleTypes.insert(compatibleTypeNames.begin(), compatibleTypeNames.end());
3418 else if (isPortCurrentlyRevertible(port))
3420 map<VuoNode *, string> nodesToReplace;
3421 set<VuoCable *> cablesToDelete;
3423 if (cablesToDelete.size() >= 1)
3429 port->getUnderlyingParentNode()->getBase()->getNodeClass()->getCompiler());
3431 compatibleTypes = getRespecializationOptionsForPortInNetwork(port);
3435 if (genericTypeFromPortClass)
3447 if (genericHostPortDataType)
3449 else if (isPortCurrentlyRevertible(hostPort->
getRenderer()))
3459 vector<string> compatibleTypesInIsolationVector;
3460 if (genericTypeFromPortClass)
3467 foreach (
string type, compatibleTypesInIsolationVector)
3474 const int maxTypeCountForFlatMenuDisplay = 10;
3475 if (compatibleTypesInIsolation.size() <= maxTypeCountForFlatMenuDisplay)
3477 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
false,
"", contextMenuSpecializeGenericType);
3485 QList<QAction *> actions =
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true,
"", contextMenuSpecializeGenericType);
3490 QList<QAction *> allNodeSetActionsToAdd;
3491 for (map<
string, set<VuoCompilerType *> >::iterator i = loadedTypesForNodeSet.begin(); i != loadedTypesForNodeSet.end(); ++i)
3493 string nodeSetName = i->first;
3494 if (!nodeSetName.empty())
3495 allNodeSetActionsToAdd +=
getCompatibleTypesForMenu(port, compatibleTypesInIsolation, compatibleTypes,
true, nodeSetName, contextMenuSpecializeGenericType);
3498 if ((contextMenuSpecializeGenericType->actions().size() > 0) && (allNodeSetActionsToAdd.size() > 0))
3499 contextMenuSpecializeGenericType->addSeparator();
3504 foreach (QAction *action, contextMenuSpecializeGenericType->actions())
3506 QMenu *specializeSubmenu = action->menu();
3507 if (specializeSubmenu)
3509 foreach (QAction *specializeSubaction, specializeSubmenu->actions())
3510 connect(specializeSubaction, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3512 else if (action == unspecializeAction)
3513 connect(action, &QAction::triggered,
this, &VuoEditorComposition::unspecializePortType);
3515 connect(action, &QAction::triggered,
this, &VuoEditorComposition::specializeGenericPortType);
3522 int numVisibleDirectlyConnectedCables = 0;
3523 int numHidableDirectlyConnectedCables = 0;
3524 int numUnhidableDirectlyConnectedCables = 0;
3530 numVisibleDirectlyConnectedCables++;
3532 numHidableDirectlyConnectedCables++;
3536 numUnhidableDirectlyConnectedCables++;
3539 int numVisibleChildPortConnectedCables = 0;
3540 int numHidableChildPortConnectedCables = 0;
3541 int numUnhidableChildPortConnectedCables = 0;
3551 numVisibleChildPortConnectedCables++;
3554 numHidableChildPortConnectedCables++;
3557 numUnhidableChildPortConnectedCables++;
3562 int numVisibleConnectedCables = numVisibleDirectlyConnectedCables + numVisibleChildPortConnectedCables;
3565 int numHidableConnectedCables = numHidableDirectlyConnectedCables + numHidableChildPortConnectedCables;
3568 int numUnhidableConnectedCables = numUnhidableDirectlyConnectedCables + numUnhidableChildPortConnectedCables;
3570 if ((!
renderHiddenCables && ((numHidableConnectedCables >= 1) || (numUnhidableConnectedCables >= 1)))
3571 || (numVisibleConnectedCables >= 1))
3573 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3574 contextMenu.addSeparator();
3579 if (numHidableConnectedCables >= 1)
3584 int numApparentlyHidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3585 contextMenuHideCables->setText(numApparentlyHidableConnectedCables > 1?
"Hide Cables" :
"Hide Cable");
3586 contextMenuHideCables->setData(qVariantFromValue((
void *)port));
3587 contextMenu.addAction(contextMenuHideCables);
3590 if (numUnhidableConnectedCables >= 1)
3592 int numApparentlyUnhidableConnectedCables = numVisibleConnectedCables + numUnhidableConnectedCables;
3593 contextMenuUnhideCables->setText(numApparentlyUnhidableConnectedCables > 1?
"Unhide Cables" :
"Unhide Cable");
3594 contextMenuUnhideCables->setData(qVariantFromValue((
void *)port));
3595 contextMenu.addAction(contextMenuUnhideCables);
3599 if (numVisibleConnectedCables >= 1)
3601 contextMenuDeleteCables->setText(numVisibleConnectedCables > 1?
"Delete Cables" :
"Delete Cable");
3602 contextMenuDeleteCables->setData(qVariantFromValue((
void *)port));
3603 contextMenu.addAction(contextMenuDeleteCables);
3610 if (! item->isSelected())
3613 item->setSelected(
true);
3616 QList<QGraphicsItem *> selectedComponents = selectedItems();
3617 bool onlyCommentsSelected =
true;
3618 bool onlyCablesSelected =
true;
3619 bool selectionContainsMissingNode =
false;
3620 foreach (QGraphicsItem *item, selectedComponents)
3623 onlyCommentsSelected =
false;
3626 onlyCablesSelected =
false;
3629 selectionContainsMissingNode =
true;
3632 contextMenuDeleteSelectedSnapshot->setText(contextMenuDeleteSelected->text());
3640 if (onlyCommentsSelected)
3641 contextMenu.addAction(contextMenuEditSelectedComments);
3646 contextMenu.addSeparator();
3649 contextMenu.addAction(contextMenuRefactorSelected);
3651 contextMenu.addSeparator();
3654 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3664 contextMenu.addAction(contextMenuHideSelectedCables);
3667 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3682 contextMenuAddInputPort->setData(qVariantFromValue((
void *)node));
3683 contextMenuRemoveInputPort->setData(qVariantFromValue((
void *)node));
3686 contextMenuRemoveInputPort->setEnabled(listItemCount >= 1);
3688 contextMenu.addAction(contextMenuAddInputPort);
3689 contextMenu.addAction(contextMenuRemoveInputPort);
3691 contextMenu.addSeparator();
3695 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3706 if (originalGenericNodeClass)
3707 nodeClass = originalGenericNodeClass->
getBase();
3710 QString actionText, sourcePath;
3714 int numSelectedNonAttachmentNodes = 0;
3715 QList<QGraphicsItem *> selectedComponents = selectedItems();
3716 foreach (QGraphicsItem *item, selectedComponents)
3719 numSelectedNonAttachmentNodes++;
3722 if ((numSelectedNonAttachmentNodes == 1) && nodeClassIsEditable)
3725 QAction *editAction =
new QAction(NULL);
3726 editAction->setText(actionText);
3727 editAction->setData(qVariantFromValue(node));
3730 contextMenu.addAction(editAction);
3731 contextMenu.addSeparator();
3736 contextMenu.addAction(contextMenuRenameSelected);
3741 contextMenu.addSeparator();
3744 if (numSelectedNonAttachmentNodes == 1)
3746 if (contextMenuChangeNode)
3747 contextMenuChangeNode->deleteLater();
3750 contextMenuChangeNode->setSeparatorsCollapsible(
false);
3751 contextMenuChangeNode->setTitle(tr(
"Change To"));
3754 if (!contextMenuChangeNode->actions().isEmpty())
3755 contextMenu.addMenu(contextMenuChangeNode);
3759 if (!selectionContainsMissingNode)
3760 contextMenu.addAction(contextMenuRefactorSelected);
3762 if ((numSelectedNonAttachmentNodes == 1) && (nodeClassIsEditable || nodeClassIs3rdParty))
3766 if (!modulePath.isEmpty())
3768 QString enclosingDirUrl =
"file://" + QFileInfo(modulePath).dir().absolutePath();
3769 QAction *openEnclosingFolderAction =
new QAction(NULL);
3770 openEnclosingFolderAction->setText(
"Show in Finder");
3771 openEnclosingFolderAction->setData(qVariantFromValue(enclosingDirUrl));
3773 contextMenu.addAction(openEnclosingFolderAction);
3777 if (!contextMenu.actions().empty() && !contextMenu.actions().last()->isSeparator())
3778 contextMenu.addSeparator();
3781 contextMenu.addAction(contextMenuDeleteSelectedSnapshot);
3787 if (!contextMenu.actions().isEmpty())
3792 menuSelectionInProgress =
true;
3793 contextMenu.exec(event->screenPos());
3794 menuSelectionInProgress =
false;
3797 delete contextMenuDeleteSelectedSnapshot;
3805 QAction *sender = (QAction *)QObject::sender();
3817 vector<string> typeOptions;
3819 map<string, VuoCompilerType *> loadedTypes = compiler->
getTypes();
3820 for (map<string, VuoCompilerType *>::iterator i = loadedTypes.begin(); i != loadedTypes.end(); ++i)
3825 (i->first !=
"VuoMathExpressionList"))
3827 typeOptions.push_back(i->first);
3839 set<string> VuoEditorComposition::getRespecializationOptionsForPortInNetwork(
VuoRendererPort *port)
3842 return set<string>();
3849 vector<string> compatibleInnerTypeNames;
3850 vector<string> compatibleTypeNames;
3851 for (
VuoPort *connectedPort : connectedGenericPorts)
3856 if (specializedNodeClass)
3864 vector<string> innermostCompatibleTypeNamesForPort;
3865 for (vector<string>::iterator k = compatibleTypeNamesForPort.begin(); k != compatibleTypeNamesForPort.end(); ++k)
3868 if (! innermostCompatibleTypeNamesForPort.empty())
3870 if (compatibleInnerTypeNames.empty())
3871 compatibleInnerTypeNames = innermostCompatibleTypeNamesForPort;
3874 for (
int k = compatibleInnerTypeNames.size() - 1; k >= 0; --k)
3875 if (find(innermostCompatibleTypeNamesForPort.begin(), innermostCompatibleTypeNamesForPort.end(), compatibleInnerTypeNames[k]) ==
3876 innermostCompatibleTypeNamesForPort.end())
3877 compatibleInnerTypeNames.erase(compatibleInnerTypeNames.begin() + k);
3886 string typeNameForPort = genericTypeFromPortClass->
getModuleKey();
3890 for (vector<string>::iterator k = compatibleInnerTypeNames.begin(); k != compatibleInnerTypeNames.end(); ++k)
3891 compatibleTypeNames.push_back(prefix + *k);
3893 if (compatibleTypeNames.empty())
3903 return set<string>(compatibleTypeNames.begin(), compatibleTypeNames.end());
3926 set<string> compatibleTypesInIsolation,
3927 set<string> compatibleTypesInContext,
3928 bool limitToNodeSet,
3932 QList<QAction *> actionsToAddToMenu;
3934 map<string, VuoCompilerType *> allTypes = compiler->
getTypes();
3936 QList<VuoCompilerType *> compatibleTypesForNodeSetDisplay;
3937 foreach (
string typeName, compatibleTypesInIsolation)
3940 if ((!limitToNodeSet || (loadedTypesForNodeSet[nodeSetName].find(type) != loadedTypesForNodeSet[nodeSetName].end())) &&
3943 (typeName !=
"VuoUrl" && typeName !=
"VuoList_VuoUrl"
3945 && typeName !=
"VuoInteraction" && typeName !=
"VuoList_VuoInteraction"
3946 && typeName !=
"VuoInteractionType" && typeName !=
"VuoList_VuoInteractionType"
3947 && typeName !=
"VuoUuid" && typeName !=
"VuoList_VuoUuid"
3949 && typeName !=
"VuoIconPosition" && typeName !=
"VuoList_VuoIconPosition"
3950 && typeName !=
"VuoMesh" && typeName !=
"VuoList_VuoMesh"
3951 && typeName !=
"VuoWindowProperty" && typeName !=
"VuoList_VuoWindowProperty"
3952 && typeName !=
"VuoWindowReference" && typeName !=
"VuoList_VuoWindowReference"))
3953 compatibleTypesForNodeSetDisplay.append(type);
3956 if (!compatibleTypesForNodeSetDisplay.isEmpty())
3958 QMenu *contextMenuNodeSetTypes = NULL;
3959 QList<QAction *> actionsToAddToNodeSetSubmenu;
3960 bool enabledContentAdded =
false;
3963 if (!nodeSetName.empty())
3965 contextMenuNodeSetTypes =
new QMenu(menu);
3966 contextMenuNodeSetTypes->setSeparatorsCollapsible(
false);
3968 contextMenuNodeSetTypes->setToolTipsVisible(
true);
3969 actionsToAddToMenu.append(contextMenuNodeSetTypes->menuAction());
3975 QList<QVariant> portAndSpecializedType;
3976 portAndSpecializedType.append(qVariantFromValue((
void *)genericPort));
3977 portAndSpecializedType.append(typeName.c_str());
3979 QAction *specializeAction;
3983 if (!nodeSetName.empty())
3985 specializeAction =
new QAction(typeTitle, contextMenuNodeSetTypes);
3986 actionsToAddToNodeSetSubmenu.append(specializeAction);
3992 specializeAction =
new QAction(typeTitle, menu);
3993 actionsToAddToMenu.append(specializeAction);
3996 specializeAction->setData(QVariant(portAndSpecializedType));
3997 specializeAction->setCheckable(
true);
3998 specializeAction->setChecked(genericPort && (genericPort->getDataType()->getModuleKey() == type->
getBase()->
getModuleKey()));
4000 if (compatibleTypesInContext.find(typeName) == compatibleTypesInContext.end())
4002 specializeAction->setEnabled(
false);
4004 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."));
4007 enabledContentAdded =
true;
4010 if (contextMenuNodeSetTypes)
4014 if (!enabledContentAdded)
4015 contextMenuNodeSetTypes->setEnabled(
false);
4019 QList<QAction *> actionListWithPromotions = promoteSingletonsFromSubmenus(actionsToAddToMenu);
4020 return actionListWithPromotions;
4027 QList<QAction *> VuoEditorComposition::promoteSingletonsFromSubmenus(QList<QAction *> actionList)
4029 QList<QAction *> modifiedActionList;
4030 foreach (QAction *action, actionList)
4032 if (action->menu() && (action->menu()->actions().size() == 1))
4034 QAction *singleSubaction = action->menu()->actions().first();
4035 action->menu()->removeAction(singleSubaction);
4036 modifiedActionList.append(singleSubaction);
4039 modifiedActionList.append(action);
4042 return modifiedActionList;
4050 std::sort(actionList.begin(), actionList.end(), nodeSetMenuActionLessThan);
4051 foreach (QAction *action, actionList)
4052 menu->addAction(action);
4058 void VuoEditorComposition::updatePopoversForActiveWindowChange(QWidget *old, QWidget *now)
4071 void VuoEditorComposition::updatePopoversForApplicationStateChange(
bool active)
4073 if (ignoreApplicationStateChangeEvents)
4077 if (activeWindow && (activeWindow->
getComposition() ==
this) && (!activeWindow->isMinimized()))
4091 return findNearbyComponent(scenePos, VuoEditorComposition::targetTypePort, limitPortCollisionRange);
4099 QGraphicsItem *item =
findNearbyComponent(scenePos, VuoEditorComposition::targetTypeNodeHeader);
4117 bool limitPortCollisionRange)
4123 bool ignoreComments;
4127 case VuoEditorComposition::targetTypePort:
4129 ignoreCables =
true;
4131 ignorePorts =
false;
4132 ignoreComments =
true;
4135 case VuoEditorComposition::targetTypeNodeHeader:
4137 ignoreCables =
true;
4140 ignoreComments =
true;
4145 ignoreCables =
false;
4146 ignoreNodes =
false;
4147 ignorePorts =
false;
4148 ignoreComments =
false;
4156 QGraphicsItem *topmostItemUnderCursor = itemAt(scenePos, views()[0]->transform());
4157 if (topmostItemUnderCursor && (!topmostItemUnderCursor->isEnabled()))
4158 topmostItemUnderCursor = NULL;
4162 QRectF searchRect(scenePos.x()-0.5*rectLength, scenePos.y()-0.5*rectLength, rectLength, rectLength);
4163 QList<QGraphicsItem *> itemsInRange = items(searchRect);
4164 for (QList<QGraphicsItem *>::iterator i = itemsInRange.begin(); i != itemsInRange.end(); ++i)
4166 if (!(*i)->isEnabled())
4173 bool makeListDragHandle =
4176 if (makeListDragHandle &&
4177 ((! topmostItemUnderCursor) ||
4178 (topmostItemUnderCursor == makeListDrawer) ||
4179 (topmostItemUnderCursor->zValue() < makeListDrawer->zValue())))
4181 return makeListDrawer;
4192 ((! topmostItemUnderCursor) ||
4193 (topmostItemUnderCursor == port) ||
4195 (topmostItemUnderCursor->zValue() < port->zValue()))
4197 ((! limitPortCollisionRange) ||
4211 ((! topmostItemUnderCursor) ||
4212 (topmostItemUnderCursor == cable) ||
4213 (topmostItemUnderCursor->zValue() < cable->zValue())))
4220 if (! ignoreComments)
4227 ((! topmostItemUnderCursor) ||
4228 (topmostItemUnderCursor == (*i)) ||
4229 (topmostItemUnderCursor->zValue() < (*i)->zValue())))
4236 if (targetType == VuoEditorComposition::targetTypeNodeHeader)
4242 headerRect = node->mapToScene(headerRect).
boundingRect();
4243 if (headerRect.intersects(searchRect) && scenePos.y() <= headerRect.bottom())
4255 return topmostItemUnderCursor;
4260 return ((
VuoRendererPort *)(topmostItemUnderCursor))->getRenderedParentNode();
4277 if (! cableInProgress)
4280 VuoPort *fromPort = cableInProgress->getFromPort();
4281 VuoPort *toPort = cableInProgress->getToPort();
4282 VuoPort *fixedPort = (fromPort? fromPort: toPort);
4297 vector<VuoRendererPort *> portList;
4312 if (portList.size() > firstPortIndex)
4314 targetPort = portList[firstPortIndex];
4320 for (
int i = firstPortIndex; i < portList.size(); ++i)
4324 firstPortWithoutWall = portList[i];
4328 if (firstPortWithoutWall)
4329 targetPort = firstPortWithoutWall;
4338 if (portList.size() > firstPortIndex)
4339 targetPort = portList[firstPortIndex];
4360 QRectF boundingRect;
4361 foreach (QGraphicsItem *item, items())
4365 boundingRect |= item->sceneBoundingRect();
4368 return boundingRect;
4377 QRectF boundingRect;
4379 foreach (QGraphicsItem *item, selectedItems())
4383 boundingRect |= item->sceneBoundingRect();
4386 return boundingRect;
4395 QRectF boundingRect;
4397 foreach (QGraphicsItem *item, selectedItems())
4401 boundingRect |= item->mapToScene(item->childrenBoundingRect()).boundingRect();
4411 boundingRect |= drawer->mapToScene(drawer->childrenBoundingRect()).boundingRect();
4416 return boundingRect;
4425 VuoPort *port = portWithStaticIdentifier[portID];
4456 if (updateInRunningComposition)
4465 if (updateInRunningComposition)
4491 if (!errorMarkingUpdatesEnabled)
4494 errorMarkingUpdatesEnabled =
false;
4511 set<VuoCompilerCable *> potentialCables;
4513 if (targetPort && cableInProgress)
4519 if (cableInProgress->getFromNode())
4521 fromNode = cableInProgress->getFromNode();
4522 fromPort = cableInProgress->getFromPort();
4524 toPort = targetPort->
getBase();
4529 fromPort = targetPort->
getBase();
4530 toNode = cableInProgress->getToNode();
4531 toPort = cableInProgress->getToPort();
4536 potentialCable->
setAlwaysEventOnly(! cableInProgress->getRenderer()->effectivelyCarriesData() ||
4537 cableInProgress->getRenderer()->isFloatingEndpointAboveEventPort());
4541 potentialCables.insert(potentialCable);
4552 VUserLog(
"%s: Showing error popover: %s",
4560 set<VuoRendererNode *> nodesToMark;
4561 set<VuoRendererCable *> cablesToMark;
4563 set<VuoNode *> problemNodes = issue.
getNodes();
4564 foreach (
VuoNode *node, problemNodes)
4568 set<VuoCable *> problemCables = issue.
getCables();
4569 foreach (
VuoCable *cable, problemCables)
4571 VuoCable *cableToMark = (cable->
getCompiler() == potentialCable ? cableInProgress : cable);
4580 errorPopovers.insert(errorPopover);
4584 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
4588 if (targetPort && cableInProgress && nodesToMark.find(targetPort->
getRenderedParentNode()) != nodesToMark.end())
4592 else if (! nodesToMark.empty())
4595 qreal topY = viewportRect.bottom();
4601 QPointF scenePos = node->scenePos();
4602 if (viewportRect.contains(scenePos) && (scenePos.y() < topY))
4604 topmostVisibleNode = node;
4605 topY = scenePos.y();
4609 if (topmostVisibleNode)
4610 nearbyNode = topmostVisibleNode;
4619 VUserLog(
"Warning: no nearby node (no marked nodes).");
4624 const QPoint offsetFromNode(0,10);
4625 QPoint popoverTopLeftInScene = (nearbyNode?
4626 (nearbyNode->scenePos().toPoint() +
4629 QPoint(viewportRect.center().x() - 0.5*errorPopover->sizeHint().width(),
4630 viewportRect.center().y() - 0.5*errorPopover->sizeHint().height()));
4634 const int margin = 5;
4635 popoverTopLeftInScene = (QPoint(fmin(popoverTopLeftInScene.x(), viewportRect.bottomRight().x() - errorPopover->sizeHint().width() - margin),
4636 fmin(popoverTopLeftInScene.y(), viewportRect.bottomRight().y() - errorPopover->sizeHint().height() - margin)));
4638 popoverTopLeftInScene = (QPoint(fmax(popoverTopLeftInScene.x(), viewportRect.topLeft().x() + margin),
4639 fmax(popoverTopLeftInScene.y(), viewportRect.topLeft().y() + margin)));
4641 QPoint popoverTopLeftInView = views()[0]->mapFromScene(popoverTopLeftInScene);
4642 QPoint popoverTopLeftGlobal = views()[0]->mapToGlobal(popoverTopLeftInView);
4644 errorPopover->move(popoverTopLeftGlobal);
4645 errorPopover->show();
4653 delete potentialCable;
4655 errorMarkingUpdatesEnabled =
true;
4661 bool VuoEditorComposition::hasFeedbackErrors(
void)
4663 return this->errorMark;
4671 if (hasFeedbackErrors())
4680 void VuoEditorComposition::buildComposition(
string compositionSnapshot,
const set<string> &dependenciesUninstalled)
4686 if (! dependenciesUninstalled.empty())
4688 vector<string> dependenciesRemovedVec(dependenciesUninstalled.begin(), dependenciesUninstalled.end());
4690 throw VuoException(
"Some modules that the composition needs were uninstalled: " + dependenciesStr);
4693 delete runningComposition;
4694 runningComposition = NULL;
4698 if (runningCompositionActiveDriver)
4702 string dir, file, ext;
4704 linkedCompositionPath = dir + file +
".dylib";
4709 compiler->
compileComposition(runningComposition, compiledCompositionPath,
true, issues);
4713 remove(compiledCompositionPath.c_str());
4719 delete runningComposition;
4720 runningComposition = NULL;
4732 bool VuoEditorComposition::isRunningThreadUnsafe(
void)
4734 return runner != NULL && ! stopRequested && ! runner->
isStopped();
4744 __block
bool running;
4745 dispatch_sync(runCompositionQueue, ^{
4746 running = isRunningThreadUnsafe();
4772 if (matchingComposition->showEventsMode)
4776 stopRequested =
false;
4777 dispatch_async(runCompositionQueue, ^{
4782 buildComposition(compositionSnapshot);
4796 if (matchingComposition->showEventsMode)
4797 this->runner->subscribeToEventTelemetry(matchingCompositionIdentifier);
4799 dispatch_sync(activePortPopoversQueue, ^{
4800 for (
auto i : matchingComposition->activePortPopovers)
4802 string portID = i.first;
4803 updateDataInPortPopoverFromRunningTopLevelComposition(matchingComposition, matchingCompositionIdentifier, portID);
4824 stopRequested =
true;
4825 dispatch_async(runCompositionQueue, ^{
4829 runner->waitUntilStopped();
4834 linkedCompositionPath =
"";
4835 runningCompositionLibraries = NULL;
4837 delete runningComposition;
4838 runningComposition = NULL;
4846 subcompositionRouter->applyToAllLinkedCompositions(
this, ^
void (
VuoEditorComposition *matchingComposition,
string matchingCompositionIdentifier)
4848 if (matchingComposition->showEventsMode)
4851 dispatch_sync(activePortPopoversQueue, ^{
4852 for (
auto i : matchingComposition->activePortPopovers)
4854 VuoPortPopover *popover = i.second;
4855 popover->setCompositionRunning(false);
4878 dispatch_async(runCompositionQueue, ^{
4879 if (isRunningThreadUnsafe())
4885 runningCompositionLibraries->enqueueLibraryContainingDependencyToUnload(moduleKey);
4888 string oldBuiltCompositionSnapshot = oldCompositionSnapshot;
4890 if (previouslyActiveDriver)
4893 previouslyActiveDriver->applyToComposition(oldBuiltComposition, compiler);
4897 buildComposition(newCompositionSnapshot, dependenciesUninstalled);
4899 string compositionDiff = diffInfo->
diff(oldBuiltCompositionSnapshot, runningComposition, compiler);
4902 catch (exception &e)
4904 VUserLog(
"Composition stopped itself: %s", e.what());
4909 VUserLog(
"Composition stopped itself.");
4915 dispatch_async(dispatch_get_main_queue(), ^{
4934 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, reloadSubcompositionIfUnsaved);
4943 VuoPort *port = portWithStaticIdentifier[runningPortID];
4952 if (
this == topLevelComposition)
4954 dispatch_async(runCompositionQueue, ^{
4955 if (isRunningThreadUnsafe())
4957 json_object *constantObject = json_tokener_parse(constant.c_str());
4958 runner->
setInputPortValue(thisCompositionIdentifier, runningPortID, constantObject);
4966 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
4968 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
4994 map<VuoPort *, string>::iterator i = staticIdentifierForPort.find(port->
getBase());
4995 if (i == staticIdentifierForPort.end())
4997 string runningPortIdentifier = i->second;
5002 if (
this == topLevelComposition)
5004 dispatch_async(runCompositionQueue, ^{
5005 if (isRunningThreadUnsafe())
5007 json_object *constantObject = json_tokener_parse(constant.c_str());
5008 runner->
setInputPortValue(thisCompositionIdentifier, runningPortIdentifier, constantObject);
5016 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyIfInstalledAsSubcomposition(
this, ^(
VuoEditorComposition *currComposition,
string subcompositionPath)
5018 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToAllOtherTopLevelCompositions(
this, ^(
VuoEditorComposition *topLevelComposition)
5031 dispatch_async(runCompositionQueue, ^{
5032 if (isRunningThreadUnsafe())
5034 json_object *constantObject = json_tokener_parse(constant.c_str());
5036 foreach (
string subcompositionIdentifier, subcompositionIdentifiers)
5038 runner->
setInputPortValue(subcompositionIdentifier, portIdentifier, constantObject);
5049 dispatch_async(runCompositionQueue, ^{
5050 if (isRunningThreadUnsafe())
5055 json_object *constantObject = json_tokener_parse(constant.c_str());
5056 map<VuoRunner::Port *, json_object *> m;
5057 m[publishedPort] = constantObject;
5070 return contextMenuDeleteSelected;
5080 QMenu *contextMenuTints =
new QMenu(parent);
5081 contextMenuTints->setSeparatorsCollapsible(
false);
5082 contextMenuTints->setTitle(tr(
"Tint"));
5083 foreach (QAction *tintAction, contextMenuTintActions)
5084 contextMenuTints->addAction(tintAction);
5085 contextMenuTints->insertSeparator(contextMenuTintActions.last());
5087 return contextMenuTints;
5093 void VuoEditorComposition::expandChangeNodeMenu()
5095 QAction *sender = (QAction *)QObject::sender();
5100 int currentMatchesListed = contextMenuChangeNode->actions().size()-1;
5101 if (currentMatchesListed <= initialChangeNodeSuggestionCount)
5104 int verticalSpacePerItem = 21;
5108 int targetMatches = availableVerticalSpace/verticalSpacePerItem - 2;
5117 contextMenuChangeNode->exec();
5132 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
5133 vector<VuoCompilerNodeClass *> loadedNodeClasses;
5134 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
5135 loadedNodeClasses.push_back(i->second);
5138 vector<string> bestMatches;
5139 map<string, double> matchScores;
5140 matchScores[
""] = 0;
5142 int targetMatchCount = (matchLimit > 0? matchLimit : loadedNodeClasses.size());
5143 for (
int i = 0; i < targetMatchCount; ++i)
5144 bestMatches.push_back(
"");
5147 bool overflow =
false;
5150 string originalGenericNodeClassName;
5154 originalGenericNodeClassName = nodeClass->
getClassName();
5159 if (loadedNodeClassName == originalGenericNodeClassName)
5162 bool canSwapNondestructively = canSwapWithoutBreakingCables(node, loadedNodeClass->
getBase());
5163 double matchScore = (canSwapNondestructively? calculateNodeSimilarity(nodeClass, loadedNodeClass->
getBase()) : 0);
5164 int highestIndexWithCompetitiveScore = -1;
5165 for (
int i = targetMatchCount-1; (i >= 0) && (highestIndexWithCompetitiveScore == -1); --i)
5166 if (matchScore <= matchScores[bestMatches[i] ])
5167 highestIndexWithCompetitiveScore = i;
5169 if (highestIndexWithCompetitiveScore < targetMatchCount-1)
5171 if (matchScores[bestMatches[targetMatchCount-1] ] > 0)
5174 for (
int j = targetMatchCount-2; j > highestIndexWithCompetitiveScore; --j)
5175 bestMatches[j+1] = bestMatches[j];
5177 bestMatches[highestIndexWithCompetitiveScore+1] = loadedNodeClassName;
5178 matchScores[loadedNodeClassName] = matchScore;
5182 for (
int i = 0; i < targetMatchCount; ++i)
5184 if (matchScores[bestMatches[i] ] > 0)
5189 matchDisplayText += QString(
" (%1)").arg(bestMatches[i].c_str());
5191 QAction *changeAction = menu->addAction(matchDisplayText);
5193 QList<QVariant> currentNodeAndNewClass;
5194 currentNodeAndNewClass.append(qVariantFromValue((
void *)node));
5195 currentNodeAndNewClass.append(bestMatches[i].c_str());
5196 changeAction->setData(QVariant(currentNodeAndNewClass));
5197 connect(changeAction, &QAction::triggered,
this, &VuoEditorComposition::swapNode);
5204 QAction *showMoreAction = menu->addAction(tr(
"More…"));
5205 showMoreAction->setData(qVariantFromValue(
static_cast<void *
>(node)));
5206 connect(showMoreAction, &QAction::triggered,
this, &VuoEditorComposition::expandChangeNodeMenu);
5217 map<string, int> requiredInputs;
5218 bool inputEventSourceRequired =
false;
5221 bool hasDrawerWithNoIncomingCables =
false;
5222 bool hasDrawerWithNoIncomingDataCables =
false;
5229 hasDrawerWithNoIncomingCables =
true;
5230 hasDrawerWithNoIncomingDataCables =
true;
5231 vector<VuoRendererPort *> childPorts = inputDrawer->
getDrawerPorts();
5235 hasDrawerWithNoIncomingCables =
false;
5237 hasDrawerWithNoIncomingDataCables =
false;
5242 if (!hasDrawerWithNoIncomingDataCables)
5249 requiredInputs[typeKey] = ((requiredInputs.find(typeKey) == requiredInputs.end())? 1 : requiredInputs[typeKey]+1);
5254 if (hasIncomingCables && !hasDrawerWithNoIncomingCables)
5255 inputEventSourceRequired =
true;
5259 map<string, int> requiredOutputs;
5260 bool outputEventSourceRequired =
false;
5272 requiredOutputs[typeKey] = ((requiredOutputs.find(typeKey) == requiredOutputs.end())? 1 : requiredOutputs[typeKey]+1);
5276 outputEventSourceRequired =
true;
5280 bool inputEventSourceAvailable =
false;
5281 map<string, int> availableInputs;
5289 availableInputs[typeKey] = ((availableInputs.find(typeKey) == availableInputs.end())? 1 : availableInputs[typeKey]+1);
5294 inputEventSourceAvailable =
true;
5297 bool outputEventSourceAvailable =
false;
5298 map<string, int> availableOutputs;
5306 availableOutputs[typeKey] = ((availableOutputs.find(typeKey) == availableOutputs.end())? 1 : availableOutputs[typeKey]+1);
5311 outputEventSourceAvailable =
true;
5314 for (std::map<string,int>::iterator it=requiredInputs.begin(); it!=requiredInputs.end(); ++it)
5316 string typeKey = it->first;
5317 int typeRequiredCount = it->second;
5318 if (availableInputs[typeKey] < typeRequiredCount)
5323 for (std::map<string,int>::iterator it=requiredOutputs.begin(); it!=requiredOutputs.end(); ++it)
5325 string typeKey = it->first;
5326 int typeRequiredCount = it->second;
5327 if (availableOutputs[typeKey] < typeRequiredCount)
5331 if (inputEventSourceRequired && !inputEventSourceAvailable)
5334 if (outputEventSourceRequired && !outputEventSourceAvailable)
5344 bool VuoEditorComposition::isPortCurrentlyRevertible(
VuoRendererPort *port)
5350 if (!specializedNodeClass)
5355 if (!originalGenericType)
5364 if (hostPort && (!isPortCurrentlyRevertible(hostPort->
getRenderer())))
5391 string publishedPortName = ((! name.empty())?
5402 bool performedMerge =
false;
5405 publishedPort = (isPublishedInput ?
5411 if (isPublishedInput && portType && type && !forceEventOnlyPublication)
5419 performedMerge =
true;
5426 if (! performedMerge)
5429 if (isPublishedInput && type)
5444 if (! existingPublishedCable)
5450 if (mergePerformed != NULL)
5451 *mergePerformed = performedMerge;
5453 return rendererPublishedPort;
5465 if (creatingPublishedInputCable)
5468 VuoPort *fromPort = externalPort;
5471 VuoPort *toPort = internalPort;
5478 publishedCable->
setFrom(fromNode, fromPort);
5485 VuoPort *fromPort = internalPort;
5488 VuoPort *toPort = externalPort;
5495 publishedCable->
setTo(toNode, toPort);
5498 if (forceEventOnlyPublication)
5501 return publishedCable;
5517 vector<VuoPublishedPort *> publishedPortsToAdd;
5518 map<VuoPublishedPort *, string> publishedPortsToRename;
5521 VuoProtocol *previousActiveProtocol = this->activeProtocol;
5522 bool removingPreviousProtocol = previousActiveProtocol && (previousActiveProtocol != protocol);
5524 bool portChangesMadeDuringProtocolRemoval =
false;
5525 if (removingPreviousProtocol)
5528 if (portChangesMadeDuringProtocolRemoval && !useUndoStack)
5530 VUserLog(
"Warning: Unexpected combination: Removing protocol ports, but useUndoStack=false");
5531 useUndoStack =
true;
5535 this->activeProtocol = protocol;
5540 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5542 string portName = i->first;
5543 string portType = i->second;
5545 bool compositionHadCompatiblePort =
false;
5547 if (preexistingPublishedPort)
5551 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5552 (!preexistingType && (portType ==
"")));
5555 compositionHadCompatiblePort =
true;
5560 compositionHadCompatiblePort =
false;
5565 if (!compositionHadCompatiblePort)
5574 publishedPortsToAdd.push_back(publishedPort);
5581 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5583 string portName = i->first;
5584 string portType = i->second;
5586 bool compositionHadCompatiblePort =
false;
5588 if (preexistingPublishedPort)
5591 bool portTypesMatch = ((preexistingType && (preexistingType->
getModuleKey() == portType)) ||
5592 (!preexistingType && (portType ==
"")));
5595 compositionHadCompatiblePort =
true;
5600 compositionHadCompatiblePort =
false;
5605 if (!compositionHadCompatiblePort)
5614 publishedPortsToAdd.push_back(publishedPort);
5622 bool undoStackMacroBegunAlready = (removingPreviousProtocol && portChangesMadeDuringProtocolRemoval);
5623 if (!publishedPortsToRename.empty() || !publishedPortsToAdd.empty() || undoStackMacroBegunAlready)
5625 set<VuoPublishedPort *> publishedPortsToRemove;
5626 bool beginUndoStackMacro = !undoStackMacroBegunAlready;
5627 bool endUndoStackMacro =
true;
5628 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5642 string VuoEditorComposition::getNonProtocolVariantForPortName(
string portName)
5644 string modifiedPortName = portName;
5645 if (modifiedPortName.length() > 0)
5646 modifiedPortName[0] = toupper(modifiedPortName[0]);
5647 modifiedPortName =
"some" + modifiedPortName;
5649 return modifiedPortName;
5673 set<VuoPublishedPort *> publishedPortsToRemove;
5674 map<VuoPublishedPort *, string> publishedPortsToRename;
5677 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5679 string portName = i->first;
5680 string portType = i->second;
5683 if (preexistingPublishedPort)
5685 bool portCompatibleAcrossProtocolTransition =
false;
5686 if (replacementProtocol)
5689 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5691 string replacementPortName = i->first;
5692 string replacementPortType = i->second;
5694 if ((portName == replacementPortName) && (portType == replacementPortType))
5695 portCompatibleAcrossProtocolTransition =
true;
5700 publishedPortsToRemove.insert(preexistingPublishedPort);
5701 else if (!portCompatibleAcrossProtocolTransition)
5707 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5709 string portName = i->first;
5710 string portType = i->second;
5713 if (preexistingPublishedPort)
5715 bool portCompatibleAcrossProtocolTransition =
false;
5716 if (replacementProtocol)
5719 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end() && !portCompatibleAcrossProtocolTransition; ++i)
5721 string replacementPortName = i->first;
5722 string replacementPortType = i->second;
5724 if ((portName == replacementPortName) && (portType == replacementPortType))
5725 portCompatibleAcrossProtocolTransition =
true;
5730 publishedPortsToRemove.insert(preexistingPublishedPort);
5731 else if (!portCompatibleAcrossProtocolTransition)
5738 if (!publishedPortsToRemove.empty())
5739 publishedPortsToRename.clear();
5741 bool portChangesRequired = (!publishedPortsToRename.empty() || !publishedPortsToRemove.empty());
5742 if (portChangesRequired)
5744 vector<VuoPublishedPort *> publishedPortsToAdd;
5745 bool beginUndoStackMacro =
true;
5746 bool endUndoStackMacro = !replacementProtocol;
5747 emit
protocolPortChangesRequested(publishedPortsToRename, publishedPortsToRemove, publishedPortsToAdd, beginUndoStackMacro, endUndoStackMacro);
5752 return portChangesRequired;
5763 if ((activeProtocol != protocol) || !activeProtocol)
5766 activeProtocol = NULL;
5769 for (vector<pair<string, string> >::iterator i = protocolInputs.begin(); i != protocolInputs.end(); ++i)
5771 string portName = i->first;
5772 string portType = i->second;
5775 if (preexistingPublishedPort)
5780 for (vector<pair<string, string> >::iterator i = protocolOutputs.begin(); i != protocolOutputs.end(); ++i)
5782 string portName = i->first;
5783 string portType = i->second;
5786 if (preexistingPublishedPort)
5799 return activeProtocol;
5808 if (!activeProtocol)
5811 return static_cast<VuoEditor *
>(qApp)->getBuiltInDriverForProtocol(activeProtocol);
5822 portWithStaticIdentifier[staticPortIdentifier] = publishedPort;
5823 staticIdentifierForPort[publishedPort] = staticPortIdentifier;
5846 return removalResult;
5862 portWithStaticIdentifier[staticPortIdentifier] = publishedPort->
getBase();
5863 staticIdentifierForPort[publishedPort->
getBase()] = staticPortIdentifier;
5875 void VuoEditorComposition::highlightEligibleEndpointsForCable(
VuoCable *cable)
5888 highlightInternalPortsConnectableToPort(fixedPort, cable->
getRenderer());
5900 QList<QGraphicsItem *> compositionComponents = items();
5901 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5903 QGraphicsItem *compositionComponent = *i;
5909 for(vector<VuoPort *>::iterator inputPort = inputPorts.begin(); inputPort != inputPorts.end(); ++inputPort)
5910 updateEligibilityHighlightingForPort((*inputPort)->getRenderer(), port, !cable->
effectivelyCarriesData());
5914 for(vector<VuoPort *>::iterator outputPort = outputPorts.begin(); outputPort != outputPorts.end(); ++outputPort)
5915 updateEligibilityHighlightingForPort((*outputPort)->getRenderer(), port, !cable->
effectivelyCarriesData());
5920 if (rc && rc != cable)
5922 QGraphicsItem::CacheMode normalCacheMode = rc->cacheMode();
5923 rc->setCacheMode(QGraphicsItem::NoCache);
5940 rc->setCacheMode(normalCacheMode);
5945 for (QList<QGraphicsItem *>::iterator i = compositionComponents.begin(); i != compositionComponents.end(); ++i)
5946 updateEligibilityHighlightingForNode(
dynamic_cast<VuoRendererNode *
>(*i));
5953 void VuoEditorComposition::updateEligibilityHighlightingForPort(
VuoRendererPort *portToHighlight,
5955 bool eventOnlyConnection)
5957 QGraphicsItem::CacheMode normalCacheMode = portToHighlight->cacheMode();
5958 portToHighlight->setCacheMode(QGraphicsItem::NoCache);
5966 if (typecastPortToHighlight)
5969 portToHighlight->setCacheMode(normalCacheMode);
5971 if (typecastPortToHighlight)
5972 updateEligibilityHighlightingForPort(typecastPortToHighlight->
getChildPort(), fixedPort, eventOnlyConnection);
5992 bool forwardConnection;
5995 fromPort = fixedPort;
5996 toPort = portToHighlight;
5997 forwardConnection =
true;
6001 fromPort = portToHighlight;
6003 forwardConnection =
false;
6006 bool directConnectionPossible;
6010 if (fixedExternalPublishedPort && externalPublishedPortToHighlight)
6011 directConnectionPossible =
false;
6012 else if (fixedExternalPublishedPort && !externalPublishedPortToHighlight)
6014 else if (!fixedExternalPublishedPort && externalPublishedPortToHighlight)
6020 if (directConnectionPossible)
6024 else if (fixedPort == portToHighlight)
6047 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6049 bool eventOnlyConnection,
6050 bool forwardConnection)
6053 string respecializedTypeName =
"";
6055 return canConnectDirectlyWithRespecializationNondestructively(fromPort,
6057 eventOnlyConnection,
6059 &portToRespecialize,
6060 respecializedTypeName);
6073 bool VuoEditorComposition::canConnectDirectlyWithRespecializationNondestructively(
VuoRendererPort *fromPort,
6075 bool eventOnlyConnection,
6076 bool forwardConnection,
6078 string &respecializedTypeName)
6080 *portToRespecialize = NULL;
6081 respecializedTypeName =
"";
6083 bool canConnectWithRespecialization = canConnectDirectlyWithRespecialization(fromPort,
6085 eventOnlyConnection,
6088 respecializedTypeName);
6089 if (!canConnectWithRespecialization)
6092 if (canConnectWithRespecialization && !portToRespecialize)
6095 bool nondestructive = portCanBeUnspecializedNondestructively((*portToRespecialize)->getBase());
6096 if (!nondestructive)
6098 *portToRespecialize = NULL;
6099 respecializedTypeName =
"";
6101 return nondestructive;
6109 bool VuoEditorComposition::portCanBeUnspecializedNondestructively(
VuoPort *portToUnspecialize)
6111 map<VuoNode *, string> nodesToReplace;
6112 set<VuoCable *> cablesToDelete;
6117 if (cablesToDelete.empty())
6120 else if ((cablesToDelete.size() == 1) && ((*(cablesToDelete.begin()))->getToPort() == portToUnspecialize))
6145 bool VuoEditorComposition::canConnectDirectlyWithRespecialization(
VuoRendererPort *fromPort,
6147 bool eventOnlyConnection,
6148 bool forwardConnection,
6150 string &respecializedTypeName)
6154 *portToRespecialize = NULL;
6155 respecializedTypeName =
"";
6165 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6166 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6168 if (!(fromPortIsEnabledOutput && toPortIsEnabledInput))
6174 if (!(currentFromDataType && currentToDataType))
6189 if (fromSpecializedNodeClass)
6201 if (toSpecializedNodeClass)
6210 bool fromPortIsGeneric = currentFromGenericType;
6211 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6212 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6214 bool toPortIsGeneric = currentToGenericType;
6215 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6216 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6219 set<string> compatibleTypes;
6220 string specializedType =
"";
6224 if ((fromPortIsStatic && toPortIsSpecialized) || (fromPortIsSpecialized && toPortIsStatic))
6228 portToTryToRespecialize = (fromPortIsSpecialized? fromPort : toPort);
6232 else if ((fromPortIsSpecialized || toPortIsSpecialized) && !fromPortIsStatic && !toPortIsStatic)
6235 bool dragSourceIsGeneric = (forwardConnection? fromPortIsGeneric : toPortIsGeneric);
6237 VuoRendererPort *dragDestination = (forwardConnection? toPort : fromPort);
6238 bool dragDestinationIsGeneric = (forwardConnection? toPortIsGeneric : fromPortIsGeneric);
6255 if (!dragSourceIsGeneric && !dragDestinationIsGeneric)
6258 portToTryToRespecialize = dragDestination;
6266 if (portToTryToRespecialize)
6267 compatibleTypes = getRespecializationOptionsForPortInNetwork(portToTryToRespecialize);
6269 bool portsAreCompatible = (compatibleTypes.find(specializedType) != compatibleTypes.end());
6271 if (portsAreCompatible)
6273 *portToRespecialize = portToTryToRespecialize;
6274 respecializedTypeName = specializedType;
6277 return portsAreCompatible;
6286 void VuoEditorComposition::updateEligibilityHighlightingForNode(
VuoRendererNode *node)
6301 QGraphicsItem::CacheMode normalCacheMode = drawer->cacheMode();
6302 drawer->setCacheMode(QGraphicsItem::NoCache);
6307 drawer->setCacheMode(normalCacheMode);
6316 QGraphicsItem::CacheMode normalCacheMode = hostPort->cacheMode();
6317 hostPort->setCacheMode(QGraphicsItem::NoCache);
6319 hostPort->setCacheMode(normalCacheMode);
6353 bool toPortIsDragDestination,
6355 string &specializedTypeName,
6356 string &typecastToInsert)
6358 *portToSpecialize = NULL;
6359 specializedTypeName =
"";
6361 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6362 map<string, string> specializedTypeNameForTypecast;
6364 vector<string> candidateTypecasts =
findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast);
6365 bool solutionSelected = selectBridgingSolutionFromOptions(candidateTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, typecastToInsert);
6367 if (!solutionSelected)
6370 if (portToSpecializeForTypecast.find(typecastToInsert) != portToSpecializeForTypecast.end())
6371 *portToSpecialize = portToSpecializeForTypecast[typecastToInsert];
6372 if (specializedTypeNameForTypecast.find(typecastToInsert) != specializedTypeNameForTypecast.end())
6373 specializedTypeName = specializedTypeNameForTypecast[typecastToInsert];
6396 bool VuoEditorComposition::selectBridgingSolutionFromOptions(vector<string> suitableTypecasts,
6397 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6398 map<string, string> specializedTypeNameForTypecast,
6399 string &selectedTypecast)
6401 if (suitableTypecasts.empty())
6403 selectedTypecast =
"";
6407 else if (suitableTypecasts.size() == 1)
6409 selectedTypecast = suitableTypecasts[0];
6414 return promptForBridgingSelectionFromOptions(suitableTypecasts, portToSpecializeForTypecast, specializedTypeNameForTypecast, selectedTypecast);
6424 bool fromPortIsEnabledOutput = (fromPort && fromPort->
getOutput() && fromPort->isEnabled());
6425 bool toPortIsEnabledInput = (toPort && toPort->
getInput() && toPort->isEnabled());
6427 return (fromPortIsEnabledOutput && toPortIsEnabledInput &&
6439 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6455 if (toNodeUsesIndex)
6481 bool toPortIsDragDestination)
6483 map<string, VuoRendererPort *> portToSpecializeForTypecast;
6484 map<string, string> specializedTypeNameForTypecast;
6485 return findBridgingSolutions(fromPort, toPort, toPortIsDragDestination, portToSpecializeForTypecast, specializedTypeNameForTypecast);
6499 bool toPortIsDragDestination,
6500 map<string, VuoRendererPort *> &portToSpecializeForTypecast,
6501 map<string, string> &specializedTypeNameForTypecast)
6508 const bool limitCombinations =
true;
6510 portToSpecializeForTypecast.clear();
6511 specializedTypeNameForTypecast.clear();
6512 vector<string> suitableTypecasts;
6514 if (!portsPassSanityCheckToBridge(fromPort, toPort))
6515 return suitableTypecasts;
6520 return suitableTypecasts;
6525 string specializedTypeName =
"";
6528 suitableTypecasts.push_back(
"");
6529 portToSpecializeForTypecast[
""] = portToSpecialize;
6530 specializedTypeNameForTypecast[
""] = specializedTypeName;
6532 return suitableTypecasts;
6539 if (!(currentFromDataType && currentToDataType))
6540 return suitableTypecasts;
6550 if (fromSpecializedNodeClass)
6562 if (toSpecializedNodeClass)
6573 bool fromPortIsGeneric = currentFromGenericType;
6574 bool fromPortIsSpecialized = originalFromGenericType && !currentFromGenericType && isPortCurrentlyRevertible(fromPort);
6575 bool fromPortIsStatic = (!fromPortIsGeneric && !fromPortIsSpecialized);
6577 bool toPortIsGeneric = currentToGenericType;
6578 bool toPortIsSpecialized = originalToGenericType && !currentToGenericType && isPortCurrentlyRevertible(toPort);
6579 bool toPortIsStatic = (!toPortIsGeneric && !toPortIsSpecialized);
6582 if (fromPortIsGeneric && toPortIsGeneric)
6583 return suitableTypecasts;
6586 else if (fromPortIsStatic && toPortIsStatic)
6588 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6590 return suitableTypecasts;
6595 bool specializeToPort =
true;
6596 if (toPortIsGeneric)
6597 specializeToPort =
true;
6598 else if (fromPortIsGeneric)
6599 specializeToPort =
false;
6600 else if (fromPortIsSpecialized && toPortIsStatic)
6601 specializeToPort =
false;
6602 else if (fromPortIsStatic && toPortIsSpecialized)
6603 specializeToPort =
true;
6604 else if (fromPortIsSpecialized && toPortIsSpecialized)
6605 specializeToPort = toPortIsDragDestination;
6609 set<string> compatibleTypes;
6610 if (specializeToPort && (toPortIsGeneric || (toPortIsSpecialized && portCanBeUnspecializedNondestructively(toPort->
getBase()))))
6611 compatibleTypes = getRespecializationOptionsForPortInNetwork(toPort);
6612 else if (!specializeToPort && (fromPortIsGeneric || (fromPortIsSpecialized && portCanBeUnspecializedNondestructively(fromPort->
getBase()))))
6613 compatibleTypes = getRespecializationOptionsForPortInNetwork(fromPort);
6619 if (limitCombinations)
6621 vector<string> limitedSuitableTypecasts;
6624 if (portsPassSanityCheckToTypeconvert(fromPort, toPort))
6627 foreach (
string typecastName, limitedSuitableTypecasts)
6629 portToSpecializeForTypecast[typecastName] = specializeToPort? toPort : fromPort;
6630 specializedTypeNameForTypecast[typecastName] = specializeToPort? currentToDataType->
getModuleKey() :
6637 if (compatibleTypes.find(fixedDataType) != compatibleTypes.end())
6639 limitedSuitableTypecasts.push_back(
"");
6640 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6641 specializedTypeNameForTypecast[
""] = fixedDataType;
6644 if (limitedSuitableTypecasts.size() >= 1)
6645 return limitedSuitableTypecasts;
6648 foreach (
string compatibleTypeName, compatibleTypes)
6651 VuoType *candidateFromType = specializeToPort? currentFromDataType : compatibleSpecializedType->
getBase();
6652 VuoType *candidateToType = specializeToPort? compatibleSpecializedType->
getBase() : currentToDataType;
6654 if (compatibleSpecializedType)
6657 if (candidateFromType == candidateToType)
6659 suitableTypecasts.push_back(
"");
6660 portToSpecializeForTypecast[
""] = specializeToPort? toPort : fromPort;
6661 specializedTypeNameForTypecast[
""] = compatibleSpecializedType->
getBase()->
getModuleKey();
6664 if (portsPassSanityCheckToTypeconvert(fromPort,
6670 foreach (
string typecast, suitableTypecastsForCurrentTypes)
6672 suitableTypecasts.push_back(typecast);
6673 portToSpecializeForTypecast[typecast] = specializeToPort? toPort : fromPort;
6674 specializedTypeNameForTypecast[typecast] = compatibleSpecializedType->
getBase()->
getModuleKey();
6680 return suitableTypecasts;
6695 bool VuoEditorComposition::promptForBridgingSelectionFromOptions(vector<string> suitableTypecasts,
6696 map<string, VuoRendererPort *> portToSpecializeForTypecast,
6697 map<string, string> specializedTypeNameForTypecast,
6698 string &selectedTypecast)
6700 QMenu typecastMenu(views()[0]->viewport());
6701 typecastMenu.setSeparatorsCollapsible(
false);
6702 QString spacer(
" ");
6705 set <pair<VuoRendererPort *, string> > specializationDetails;
6706 vector<string> typeconversionOptionsRequiringNoSpecialization;
6707 foreach (
string typecastClassName, suitableTypecasts)
6709 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typecastClassName];
6710 string specializedTypeName = specializedTypeNameForTypecast[typecastClassName];
6711 specializationDetails.insert(std::make_pair(portToSpecialize,
6712 specializedTypeName));
6714 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6715 if (portAlreadyHasTargetType)
6716 typeconversionOptionsRequiringNoSpecialization.push_back(typecastClassName);
6722 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()))
6724 QString menuText = getDisplayTextForSpecializationOption(portToSpecializeForTypecast[
""], specializedTypeNameForTypecast[
""]);
6725 QAction *typecastAction = typecastMenu.addAction(menuText);
6726 typecastAction->setData(QVariant(
""));
6729 bool foundSpecializationOptionsRequiringNoTypeconversion = typecastMenu.actions().size() >= 1;
6730 bool foundTypeconversionOptionsRequiringNoSpecialization = typeconversionOptionsRequiringNoSpecialization.size() >= 1;
6733 bool includingTypeconvertWithNoSpecializationHeader = foundSpecializationOptionsRequiringNoTypeconversion;
6734 if (foundTypeconversionOptionsRequiringNoSpecialization)
6736 if (foundSpecializationOptionsRequiringNoTypeconversion)
6737 typecastMenu.addSeparator();
6739 VuoRendererPort *portToSpecialize = portToSpecializeForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6740 string specializedTypeName = specializedTypeNameForTypecast[typeconversionOptionsRequiringNoSpecialization[0] ];
6742 if (portToSpecialize && !specializedTypeName.empty())
6744 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6745 QAction *typecastAction = typecastMenu.addAction(menuText);
6746 typecastAction->setEnabled(
false);
6747 includingTypeconvertWithNoSpecializationHeader =
true;
6751 foreach (
string typecastClassName, typeconversionOptionsRequiringNoSpecialization)
6757 typecastAction->setData(QVariant(typecastClassName.c_str()));
6762 for (set<pair<VuoRendererPort *, string> >::iterator i = specializationDetails.begin(); i != specializationDetails.end(); ++i)
6765 string specializedTypeName = i->second;
6768 if ((std::find(suitableTypecasts.begin(), suitableTypecasts.end(),
"") != suitableTypecasts.end()) &&
6769 (portToSpecializeForTypecast[
""] == portToSpecialize) &&
6770 (specializedTypeNameForTypecast[
""] == specializedTypeName))
6776 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6777 if (portAlreadyHasTargetType)
6782 if (typecastMenu.actions().size() >= 1)
6783 typecastMenu.addSeparator();
6785 QString menuText = getDisplayTextForSpecializationOption(portToSpecialize, specializedTypeName);
6786 QAction *typecastAction = typecastMenu.addAction(menuText);
6787 typecastAction->setEnabled(
false);
6790 foreach (
string typecastClassName, suitableTypecasts)
6792 if ((portToSpecializeForTypecast[typecastClassName] == portToSpecialize) &&
6793 (specializedTypeNameForTypecast[typecastClassName] == specializedTypeName))
6799 typecastAction->setData(QVariant(typecastClassName.c_str()));
6805 menuSelectionInProgress =
true;
6806 QAction *selectedTypecastAction = typecastMenu.exec(QCursor::pos());
6807 menuSelectionInProgress =
false;
6809 selectedTypecast = (selectedTypecastAction? selectedTypecastAction->data().toString().toUtf8().constData() :
"");
6810 return selectedTypecastAction;
6816 QString VuoEditorComposition::getDisplayTextForSpecializationOption(
VuoRendererPort *portToSpecialize,
string specializedTypeName)
6818 if (!portToSpecialize || specializedTypeName.empty())
6821 bool isInput = portToSpecialize && portToSpecialize->
getInput();
6822 QString typeDisplayName = compiler->
getType(specializedTypeName)?
6824 specializedTypeName.c_str();
6826 bool portAlreadyHasTargetType = (!portToSpecialize || (portToSpecialize->
getDataType() && (portToSpecialize->
getDataType()->
getModuleKey() == specializedTypeName)));
6828 QString displayText;
6829 if (portAlreadyHasTargetType)
6834 displayText = tr(
"Keep Input Port as %1");
6839 displayText = tr(
"Keep Output Port as %1");
6847 displayText = tr(
"Change Input Port to %1");
6852 displayText = tr(
"Change Output Port to %1");
6856 return displayText.arg(typeDisplayName);
6870 string runningPortIdentifier = staticIdentifierForPort[port];
6875 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
6876 if (topLevelComposition->isRunningThreadUnsafe())
6878 portValue = isInput ?
6879 topLevelComposition->runner->getInputPortValue(thisCompositionIdentifier, runningPortIdentifier) :
6880 topLevelComposition->runner->getOutputPortValue(thisCompositionIdentifier, runningPortIdentifier);
6884 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, getPortValue);
6892 string VuoEditorComposition::getIdentifierForRunningPort(
VuoPort *runningPort)
6909 return dynamic_cast<VuoPublishedPort *
>(staticPort)->getClass()->getName();
6915 string nodeIdentifier =
"";
6925 if (staticPort->
hasCompiler() && !nodeIdentifier.empty())
6939 return portWithStaticIdentifier[portID];
6965 if (candidateInputPort == port)
6969 if (candidateOutputPort == port)
6985 map<string, VuoPortPopover *>::iterator popover = activePortPopovers.find(portID);
6986 if (popover != activePortPopovers.end())
6987 return popover->second;
7000 void VuoEditorComposition::enableInactivePopoverForPort(
VuoRendererPort *rp)
7002 string portID = staticIdentifierForPort[rp->
getBase()];
7003 bool popoverJustClosedAtLastEvent = portsWithPopoversClosedAtLastEvent.find(portID) != portsWithPopoversClosedAtLastEvent.end();
7004 if (!popoverJustClosedAtLastEvent)
7013 if (!popoverEventsEnabled)
7017 string portID = staticIdentifierForPort[port];
7019 VUserLog(
"%s: Open popover for %s",
7023 dispatch_sync(runCompositionQueue, ^{
7025 dispatch_sync(activePortPopoversQueue, ^{
7027 if (activePortPopovers.find(portID) == activePortPopovers.end())
7031 VuoPortPopover *popover = new VuoPortPopover(port, this, views()[0]->viewport());
7033 connect(popover, &VuoPortPopover::popoverClosedForPort, this, &VuoEditorComposition::disablePopoverForPortThreadSafe);
7034 connect(popover, &VuoPortPopover::popoverDetachedFromPort, [=]{
7035 VUserLog(
"%s: Detach popover for %s",
7036 window->getWindowTitleWithoutPlaceholder().toUtf8().data(),
7048 const int cutoffMargin = 16;
7049 QRectF viewportRect = views()[0]->mapToScene(views()[0]->viewport()->rect()).boundingRect();
7050 if (portLeftInScene.x() + popover->size().width() + cutoffMargin > viewportRect.right())
7051 portLeftInScene = QPoint(viewportRect.right() - popover->size().width() - cutoffMargin, portLeftInScene.y());
7052 if (portLeftInScene.y() + popover->size().height() + cutoffMargin > viewportRect.bottom())
7053 portLeftInScene = QPoint(portLeftInScene.x(), viewportRect.bottom() - popover->size().height() - cutoffMargin);
7055 QPoint popoverLeftInView = views()[0]->mapFromScene(portLeftInScene);
7057 const QPoint offset = QPoint(12, 6);
7059 QPoint popoverTopLeft = popoverLeftInView + offset;
7060 popover->move(popoverTopLeft);
7063 activePortPopovers[portID] = popover;
7065 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7092 VUserLog(
"%s: Close popover for %s",
7097 map<string, VuoPortPopover *>::iterator i = activePortPopovers.find(portID);
7098 if (i != activePortPopovers.end())
7100 popover = i->second;
7101 activePortPopovers.erase(i);
7110 VuoPort *port = portWithStaticIdentifier[portID];
7116 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
7117 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7119 dispatch_async(topLevelComposition->runCompositionQueue, ^{
7120 if (topLevelComposition->isRunningThreadUnsafe())
7123 topLevelComposition->runner->unsubscribeFromInputPortTelemetry(thisCompositionIdentifier, portID) :
7124 topLevelComposition->runner->unsubscribeFromOutputPortTelemetry(thisCompositionIdentifier, portID));
7134 void VuoEditorComposition::disablePopoverForPortThreadSafe(
string portID)
7136 dispatch_sync(activePortPopoversQueue, ^{
7146 disablePortPopovers();
7157 errorPopover->hide();
7158 errorPopover->deleteLater();
7161 errorPopovers.clear();
7170 dispatch_sync(activePortPopoversQueue, ^{
7171 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7172 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7174 string portID = i->first;
7175 VuoPort *port = portWithStaticIdentifier[portID];
7176 if ((! node) || (port && port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node)))
7177 disablePopoverForPort(portID);
7187 dispatch_sync(activePortPopoversQueue, ^{
7188 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7189 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7191 string portID = i->first;
7192 VuoPort *port = portWithStaticIdentifier[portID];
7194 disablePopoverForPort(portID);
7205 if (recordWhichPopoversClosed)
7206 portsWithPopoversClosedAtLastEvent.clear();
7208 dispatch_sync(activePortPopoversQueue, ^{
7209 map<string, VuoPortPopover *> popoversToDisable = activePortPopovers;
7210 for (map<string, VuoPortPopover *>::iterator i = popoversToDisable.begin(); i != popoversToDisable.end(); ++i)
7212 string portID = i->first;
7213 VuoPort *port = portWithStaticIdentifier[portID];
7214 if ((! node) || (port && port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node)))
7216 VuoPortPopover *popover = getActivePopoverForPort(portID);
7217 if (! (popover && popover->getDetached()))
7219 disablePopoverForPort(portID);
7220 portsWithPopoversClosedAtLastEvent.insert(portID);
7232 moveDetachedPortPopoversBy(dx, dy);
7233 moveErrorPopoversBy(dx, dy);
7239 void VuoEditorComposition::moveErrorPopoversBy(
int dx,
int dy)
7242 errorPopover->move(errorPopover->pos().x()+dx, errorPopover->pos().y()+dy);
7248 void VuoEditorComposition::moveDetachedPortPopoversBy(
int dx,
int dy)
7250 dispatch_sync(activePortPopoversQueue, ^{
7251 map<string, VuoPortPopover *> portPopovers = activePortPopovers;
7252 for (map<string, VuoPortPopover *>::iterator i = portPopovers.begin(); i != portPopovers.end(); ++i)
7254 VuoPortPopover *popover = i->second;
7255 if (popover && popover->getDetached())
7256 popover->move(popover->pos().x()+dx, popover->pos().y()+dy);
7264 void VuoEditorComposition::setPopoversHideOnDeactivate(
bool shouldHide)
7266 dispatch_sync(activePortPopoversQueue, ^{
7267 auto portPopovers = activePortPopovers;
7268 for (
auto i : portPopovers)
7273 id nsWindow = (id)VuoPopover::getWindowForPopover(popover);
7274 objc_msgSend(nsWindow, sel_getUid(
"setHidesOnDeactivate:"), shouldHide);
7286 dispatch_sync(activePortPopoversQueue, ^{
7287 for (map<string, VuoPortPopover *>::iterator i = activePortPopovers.begin(); i != activePortPopovers.end(); ++i)
7289 string portID = i->first;
7290 VuoPort *port = portWithStaticIdentifier[portID];
7291 VuoPortPopover *popover = i->second;
7293 if ((! node) || (port && port->hasRenderer() && (port->getRenderer()->getUnderlyingParentNode() == node)))
7294 QMetaObject::invokeMethod(popover,
"updateTextAndResize", Qt::QueuedConnection);
7311 string popoverCompositionIdentifier,
7314 VuoPort *port = popoverComposition->portWithStaticIdentifier[portID];
7322 dispatch_async(popoverComposition->activePortPopoversQueue, ^{
7323 VuoPortPopover *popover = popoverComposition->getActivePopoverForPort(portID);
7326 QMetaObject::invokeMethod(popover,
"updateCachedDataValue", Qt::QueuedConnection, Q_ARG(QString, portSummary.c_str()));
7327 QMetaObject::invokeMethod(popover,
"setCompositionRunning", Qt::QueuedConnection, Q_ARG(bool, true), Q_ARG(bool, false));
7341 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, ^
void (
VuoEditorComposition *topLevelComposition,
string thisCompositionIdentifier)
7343 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7344 if (topLevelComposition->isRunningThreadUnsafe())
7345 topLevelComposition->updateDataInPortPopoverFromRunningTopLevelComposition(this, thisCompositionIdentifier, portID);
7355 bool receivedEvent,
bool receivedData,
string dataSummary)
7359 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7360 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7364 if (receivedEvent && receivedData)
7365 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndCachedDataValue", Qt::QueuedConnection, Q_ARG(QString, dataSummary.c_str()));
7366 else if (receivedEvent)
7367 QMetaObject::invokeMethod(popover,
"updateLastEventTime", Qt::QueuedConnection);
7368 else if (receivedData)
7369 QMetaObject::invokeMethod(popover,
"updateCachedDataValue", Qt::QueuedConnection, Q_ARG(QString, dataSummary.c_str()));
7373 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7381 bool sentEvent,
bool sentData,
string dataSummary)
7385 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7386 VuoPort *port = matchingComposition->portWithStaticIdentifier[portIdentifier];
7387 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7391 if (sentEvent && sentData)
7392 QMetaObject::invokeMethod(popover,
"updateLastEventTimeAndCachedDataValue", Qt::QueuedConnection, Q_ARG(QString, dataSummary.c_str()));
7394 QMetaObject::invokeMethod(popover,
"updateLastEventTime", Qt::QueuedConnection);
7396 QMetaObject::invokeMethod(popover,
"updateCachedDataValue", Qt::QueuedConnection, Q_ARG(QString, dataSummary.c_str()));
7399 if (matchingComposition->showEventsMode && sentEvent)
7401 if (port && dynamic_cast<VuoCompilerTriggerPort *>(port->getCompiler()) && port->hasRenderer())
7403 port->getRenderer()->setFiredEvent();
7404 matchingComposition->animatePort(port->getRenderer());
7409 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7420 dispatch_async(matchingComposition->runCompositionQueue, ^{
7421 if (matchingComposition->isRunningThreadUnsafe())
7423 dispatch_sync(matchingComposition->activePortPopoversQueue, ^{
7424 VuoPortPopover *popover = matchingComposition->getActivePopoverForPort(portIdentifier);
7426 QMetaObject::invokeMethod(popover,
"incrementDroppedEventCount", Qt::QueuedConnection);
7431 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updatePortDisplay);
7442 if (matchingComposition->showEventsMode)
7444 dispatch_async(this->runCompositionQueue, ^{
7445 if (this->isRunningThreadUnsafe())
7447 map<string, VuoNode *>::iterator i = matchingComposition->nodeWithGraphvizIdentifier.find(nodeIdentifier);
7448 if (i != nodeWithGraphvizIdentifier.end())
7450 VuoNode *nodeInBaseComposition = i->second;
7451 VuoRendererNode *rn = nodeInBaseComposition->getRenderer();
7452 rn->setExecutionBegun();
7458 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7469 if (matchingComposition->showEventsMode)
7471 dispatch_async(this->runCompositionQueue, ^{
7472 if (this->isRunningThreadUnsafe())
7474 map<string, VuoNode *>::iterator i = matchingComposition->nodeWithGraphvizIdentifier.find(nodeIdentifier);
7475 if (i != nodeWithGraphvizIdentifier.end())
7477 VuoNode *nodeInBaseComposition = i->second;
7478 VuoRendererNode *rn = nodeInBaseComposition->getRenderer();
7479 rn->setExecutionEnded();
7485 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedCompositionWithIdentifier(
this, compositionIdentifier, updateNodeDisplay);
7514 return showEventsMode;
7522 this->showEventsMode = showEventsMode;
7530 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7531 if (topLevelComposition->isRunningThreadUnsafe())
7532 topLevelComposition->runner->subscribeToEventTelemetry(thisCompositionIdentifier);
7535 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, subscribe);
7543 dispatch_sync(topLevelComposition->runCompositionQueue, ^{
7544 if (topLevelComposition->isRunningThreadUnsafe())
7545 topLevelComposition->runner->unsubscribeFromEventTelemetry(thisCompositionIdentifier);
7548 static_cast<VuoEditor *
>(qApp)->getSubcompositionRouter()->applyToLinkedTopLevelComposition(
this, unsubscribe);
7584 QGraphicsItemAnimation * VuoEditorComposition::setUpAnimationForPort(QGraphicsItemAnimation *animation,
VuoRendererPort *port)
7592 animatedPort->setZValue(VuoRendererItem::triggerAnimationZValue);
7595 animation->setItem(animatedPort);
7596 animation->setScaleAt(0.0, 1, 1);
7597 animation->setScaleAt(0.999, 3, 3);
7598 animation->setScaleAt(1.0, 1, 1);
7600 QTimeLine *animationTimeline = animation->timeLine();
7601 animationTimeline->setFrameRange(0, 100);
7602 animationTimeline->setUpdateInterval(showEventsModeUpdateInterval);
7603 animationTimeline->setCurveShape(QTimeLine::LinearCurve);
7605 preparedAnimations.insert(animation);
7606 animationForTimeline[animation->timeLine()] = animation;
7608 connect(animationTimeline, &QTimeLine::valueChanged,
this, &VuoEditorComposition::updatePortAnimation);
7609 connect(animationTimeline, &QTimeLine::finished,
this, &VuoEditorComposition::endPortAnimation);
7619 dispatch_async(dispatch_get_main_queue(), ^{
7620 QGraphicsItemAnimation *animation = getAvailableAnimationForPort(port);
7626 if (animation->timeLine()->state() == QTimeLine::Running)
7627 animation->timeLine()->setCurrentTime(0);
7631 animatedPort->setPos(port->pos());
7632 animatedPort->setVisible(
true);
7633 animation->timeLine()->start();
7642 QGraphicsItemAnimation * VuoEditorComposition::getAvailableAnimationForPort(
VuoRendererPort *port)
7644 vector<QGraphicsItemAnimation *> animations = port->
getAnimations();
7646 QGraphicsItemAnimation *mostAdvancedAnimation = NULL;
7647 qreal maxPercentAdvanced = -1;
7649 for (
int i = 0; i < animations.size(); ++i)
7651 QGraphicsItemAnimation *animation = animations[i];
7652 bool animationPrepared = (preparedAnimations.find(animation) != preparedAnimations.end());
7653 bool animationRunning = (animation->timeLine()->state() == QTimeLine::Running);
7655 if (! animationPrepared)
7656 return setUpAnimationForPort(animation, port);
7658 else if (! animationRunning)
7663 qreal percentAdvanced = animation->timeLine()->currentValue();
7664 if (percentAdvanced > maxPercentAdvanced)
7666 mostAdvancedAnimation = animation;
7667 maxPercentAdvanced = percentAdvanced;
7673 return (maxPercentAdvanced >= 0.5? mostAdvancedAnimation : NULL);
7681 void VuoEditorComposition::updatePortAnimation(qreal value)
7683 QTimeLine *animationTimeline = (QTimeLine *)sender();
7684 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7686 const qreal multiplier = 1000.;
7694 void VuoEditorComposition::endPortAnimation(
void)
7696 QTimeLine *animationTimeline = (QTimeLine *)sender();
7697 QGraphicsItemAnimation *animation = animationForTimeline[animationTimeline];
7699 animatedPort->setVisible(
false);
7705 void VuoEditorComposition::setDisableDragStickiness(
bool disable)
7707 this->dragStickinessDisabled = disable;
7715 this->ignoreApplicationStateChangeEvents = ignore;
7725 this->popoverEventsEnabled = enable;
7734 nodeWithGraphvizIdentifier.clear();
7735 portWithStaticIdentifier.clear();
7736 staticIdentifierForPort.clear();
7739 registerNodeID(node);
7744 portWithStaticIdentifier[staticPortIdentifier] = publishedPort;
7745 staticIdentifierForPort[publishedPort] = staticPortIdentifier;
7748 foreach (
VuoPort *publishedPort,
getBase()->getPublishedOutputPorts())
7751 portWithStaticIdentifier[staticPortIdentifier] = publishedPort;
7752 staticIdentifierForPort[publishedPort] = staticPortIdentifier;
7759 void VuoEditorComposition::registerNodeID(
VuoNode *node)
7767 portWithStaticIdentifier[staticPortIdentifier] = port;
7768 staticIdentifierForPort[port] = staticPortIdentifier;
7774 portWithStaticIdentifier[staticPortIdentifier] = port;
7775 staticIdentifierForPort[port] = staticPortIdentifier;
7785 refreshComponentAlphaLevelTimer->start();
7793 refreshComponentAlphaLevelTimer->stop();
7816 if (!activeProtocol)
7823 if (!
getBase()->getCompiler()->getCachedGraph()->mayEventsReachPublishedOutputPorts())
7825 QString errorHeadline = tr(
"<b>This composition doesn't send any images to <code>outputImage</code>.</b>");
7826 QString errorDetails = tr(
"<p>To export, your composition should use the data and events from the published input ports "
7827 "to output a stream of images through the <code>outputImage</code> published output port.</p>");
7829 if (isExportingMovie)
7830 errorDetails.append(
"<p>Alternatively, you can record a realtime movie by running the composition and selecting File > Start Recording.</p>");
7833 QMessageBox messageBox(window);
7834 messageBox.setWindowFlags(Qt::Sheet);
7835 messageBox.setWindowModality(Qt::WindowModal);
7837 messageBox.setTextFormat(Qt::RichText);
7839 messageBox.setStandardButtons(QMessageBox::Help | QMessageBox::Ok);
7840 messageBox.setButtonText(QMessageBox::Help, tr(
"Open an Example"));
7841 messageBox.setButtonText(QMessageBox::Ok, tr(
"OK"));
7842 messageBox.setDefaultButton(QMessageBox::Ok);
7844 messageBox.setText(errorHeadline);
7845 messageBox.setInformativeText(
"<style>p{" + fonts->
getCSS(fonts->
dialogBodyFont()) +
"}</style>" + errorDetails);
7847 if (messageBox.exec() == QMessageBox::Help)
7849 map<QString, QString> examples =
static_cast<VuoEditor *
>(qApp)->getExampleCompositionsForProtocol(activeProtocol);
7850 map<QString, QString>::iterator i = examples.begin();
7851 if (i != examples.end())
7881 string dir, file, ext;
7895 if (! customizedName.empty())
7896 return QString::fromStdString(customizedName);
7914 string fileNameContentPart = (fileNameParts.size() >= 2 && fileNameParts[fileNameParts.size()-1] ==
"vuo"?
7915 fileNameParts[fileNameParts.size()-2] :
7916 (fileNameParts.size() >= 1? fileNameParts[fileNameParts.size()-1] :
""));
7919 if (QRegExp(
"\\s").indexIn(fileNameContentPart.c_str()) != -1)
7921 string formattedName = fileNameContentPart;
7922 if (formattedName.size() >= 1)
7923 formattedName[0] = toupper(formattedName[0]);
7925 return QString(formattedName.c_str());
7939 QStringList wordsInName = nodeSetName.split(QRegularExpression(
"\\."));
7940 if (wordsInName.size() < 2 || wordsInName[0] !=
"vuo")
7946 map<QString, QString> wordsToReformat;
7947 wordsToReformat[
"artnet"] =
"Art-Net";
7948 wordsToReformat[
"bcf2000"] =
"BCF2000";
7949 wordsToReformat[
"hid"] =
"HID";
7950 wordsToReformat[
"midi"] =
"MIDI";
7951 wordsToReformat[
"osc"] =
"OSC";
7952 wordsToReformat[
"rss"] =
"RSS";
7953 wordsToReformat[
"ui"] =
"UI";
7954 wordsToReformat[
"url"] =
"URL";
7956 QString nodeSetDisplayName =
"";
7957 for (
int i = 1; i < wordsInName.size(); ++i)
7959 QString currentWord = wordsInName[i];
7960 if (currentWord.size() >= 1)
7962 if (wordsToReformat.find(currentWord.toLower()) != wordsToReformat.end())
7963 currentWord = wordsToReformat.at(currentWord.toLower());
7965 currentWord[0] = currentWord[0].toUpper();
7967 nodeSetDisplayName += currentWord;
7969 if (i < wordsInName.size()-1)
7970 nodeSetDisplayName +=
" ";
7973 return nodeSetDisplayName;
7986 string formattedTypeName =
"";
7994 formattedTypeName =
"List of " + formattedInnerTypeName +
" elements";
8000 return formattedTypeName.c_str();
8019 return "Transform2D";
8021 return "Transform3D";
8029 bool VuoEditorComposition::nodeSetMenuActionLessThan(QAction *action1, QAction *action2)
8031 QString item1Text = action1->text();
8032 QString item2Text = action2->text();
8035 const QString listPrefix =
"List of ";
8036 const QString builtInTypePrefix =
"Vuo";
8038 if (item1Text.startsWith(listPrefix))
8040 item1Text.remove(0, listPrefix.length());
8041 if (item1Text.startsWith(builtInTypePrefix))
8042 item1Text.remove(0, builtInTypePrefix.length());
8045 if (item2Text.startsWith(listPrefix))
8047 item2Text.remove(0, listPrefix.length());
8048 if (item2Text.startsWith(builtInTypePrefix))
8049 item2Text.remove(0, builtInTypePrefix.length());
8053 return (item1Text.compare(item2Text, Qt::CaseInsensitive) < 0);
8060 bool VuoEditorComposition::itemHigherOnCanvas(QGraphicsItem *item1, QGraphicsItem *item2)
8062 qreal item1Y = item1->scenePos().y();
8063 qreal item2Y = item2->scenePos().y();
8065 qreal item1X = item1->scenePos().x();
8066 qreal item2X = item2->scenePos().x();
8068 if (item1Y == item2Y)
8069 return (item1X < item2X);
8071 return item1Y < item2Y;
8084 string originalGenericNode1ClassName, originalGenericNode2ClassName;
8100 vector<string> node1Keywords = node1->
getKeywords();
8101 vector<string> node2Keywords = node2->
getKeywords();
8113 node1Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8117 node2Keywords.push_back(nodeTitleToken.toLower().toUtf8().constData());
8119 set<string> node1KeywordSet(node1Keywords.begin(), node1Keywords.end());
8120 set<string> node2KeywordSet(node2Keywords.begin(), node2Keywords.end());
8122 set<string> nodeKeywordsIntersection;
8123 std::set_intersection(node1KeywordSet.begin(), node1KeywordSet.end(),
8124 node2KeywordSet.begin(), node2KeywordSet.end(),
8125 std::inserter(nodeKeywordsIntersection, nodeKeywordsIntersection.end()));
8127 set<string> nodeKeywordsUnion = node1KeywordSet;
8128 nodeKeywordsUnion.insert(node2KeywordSet.begin(), node2KeywordSet.end());
8131 if (nodeKeywordsUnion.size() == 0)
8135 double nodeSimilarity = nodeKeywordsIntersection.size()/(1.0*nodeKeywordsUnion.size());
8137 return nodeSimilarity;
8156 VuoEditorComposition::~VuoEditorComposition()
8158 dispatch_sync(runCompositionQueue, ^{});
8159 dispatch_release(runCompositionQueue);
8161 preparedAnimations.clear();
8162 animationForTimeline.clear();
8188 vector<VuoRendererPort *> sortedPortsToPublish;
8189 foreach (
string portID, portsToPublish)
8193 sortedPortsToPublish.push_back(port->
getRenderer());
8195 std::sort(sortedPortsToPublish.begin(), sortedPortsToPublish.end(), itemHigherOnCanvas);
8197 map<string, string> publishedPortNames;
8204 string publishedPortName = (!specializedPublishedPortName.empty()?
8205 specializedPublishedPortName :
8214 return publishedPortNames;
8242 void VuoEditorComposition::repositionPopover()
8247 const int cutoffMargin = 16;
8248 if (popover->pos().x()+popover->size().width()+cutoffMargin > views()[0]->viewport()->rect().right())
8249 popover->move(QPoint(views()[0]->viewport()->rect().right()-popover->size().width()-cutoffMargin, popover->pos().y()));
8251 if (popover->pos().y()+popover->size().height()+cutoffMargin > views()[0]->viewport()->rect().bottom())
8252 popover->move(QPoint(popover->pos().x(), views()[0]->viewport()->rect().bottom()-popover->size().height()-cutoffMargin));