42 #include <Carbon/Carbon.h>
43 #include <IOKit/IOKitLib.h>
45 const QString VuoEditor::recentFileListSettingsKey =
"recentFileList";
46 const QString VuoEditor::subcompositionPrefixSettingsKey =
"subcompositionPrefix";
47 const QString VuoEditor::nodeLibraryDisplayModeSettingsKey =
"nodeLibraryDisplayMode";
48 const QString VuoEditor::nodeLibraryVisibilityStateSettingsKey =
"nodeLibraryVisible";
49 const QString VuoEditor::nodeLibraryDockingStateSettingsKey =
"nodeLibraryDocked";
50 const QString VuoEditor::nodeLibraryFloatingPositionSettingsKey =
"nodeLibraryFloatingPosition";
51 const QString VuoEditor::nodeLibraryWidthSettingsKey =
"nodeLibraryWidth";
52 const QString VuoEditor::nodeLibraryHeightSettingsKey =
"nodeLibraryHeight";
53 const QString VuoEditor::nodeDocumentationPanelHeightSettingsKey =
"nodeDocumentationPanelHeight";
54 const QString VuoEditor::shaderDocumentationVisibilitySettingsKey =
"shaderDocumentationVisible";
55 const QString VuoEditor::gridTypeSettingsKey =
"canvasGridType";
56 const QString VuoEditor::gridOpacitySettingsKey =
"canvasGridOpacity";
57 const qreal VuoEditor::defaultGridOpacity = 1;
58 const QString VuoEditor::snapToGridSettingsKey =
"canvasGridSnap";
59 const QString VuoEditor::darkInterfaceSettingsKey =
"darkInterface";
60 const QString VuoEditor::canvasOpacitySettingsKey =
"canvasOpacity";
61 const QString VuoEditor::movieExportWidthSettingsKey =
"movieExportWidth";
62 const QString VuoEditor::movieExportHeightSettingsKey =
"movieExportHeight";
63 const QString VuoEditor::movieExportTimeSettingsKey =
"movieExportTime";
64 const QString VuoEditor::movieExportDurationSettingsKey =
"movieExportDuration";
65 const QString VuoEditor::movieExportFramerateSettingsKey =
"movieExportFramerate";
66 const QString VuoEditor::movieExportSpatialSupersampleSettingsKey =
"movieExportSpatialSupersample";
67 const QString VuoEditor::movieExportTemporalSupersampleSettingsKey =
"movieExportTemporalSupersample";
68 const QString VuoEditor::movieExportShutterAngleSettingsKey =
"movieExportShutterAngle";
69 const QString VuoEditor::movieExportImageFormatSettingsKey =
"movieExportImageFormat";
70 const QString VuoEditor::movieExportQualitySettingsKey =
"movieExportQuality";
81 string VuoEditor::documentationGenerationDirectory =
"";
87 : QApplication(argc,argv)
90 QCoreApplication::setOrganizationName(
"Vuo");
91 QCoreApplication::setOrganizationDomain(
"vuo.org");
92 QCoreApplication::setApplicationName(
"Editor");
93 settings =
new QSettings();
97 if (settings->value(
"translation/enable",
true).toBool())
99 QLocale locale = QLocale::system();
101 QList<QString> contexts;
102 contexts <<
"qtbase" <<
"vuo";
103 foreach (QString context, contexts)
105 QTranslator *t =
new QTranslator();
106 t->load(locale, context,
"_", translationsPath,
".qm");
107 installTranslator(t);
117 setWheelScrollLines(2);
120 uiInitialized =
false;
123 setQuitOnLastWindowClosed(
false);
125 ownedNodeLibrary = NULL;
126 documentationQueue = dispatch_queue_create(
"org.vuo.editor.documentation", NULL);
127 userSubscriptionLevel = VuoEditor::CommunityUser;
128 reportAbsenceOfUpdates =
false;
129 networkManager = NULL;
131 moduleManager =
nullptr;
136 const bool defaultNodeLibraryVisibilityState =
true;
137 const bool defaultNodeLibraryDockingState =
true;
138 const int defaultNodeLibraryDocumentationPanelHeight = 280;
140 qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents,
false);
141 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents,
false);
143 qApp->setAttribute(Qt::AA_UseHighDpiPixmaps);
148 settings->setValue(
"ApplePersistenceIgnoreStateQuietly",
true);
150 QStringList storedRecentFileList = settings->value(recentFileListSettingsKey).toStringList();
160 subcompositionPrefix = settings->contains(subcompositionPrefixSettingsKey)?
161 settings->value(subcompositionPrefixSettingsKey).toString() :
164 canvasOpacity = settings->contains(canvasOpacitySettingsKey)?
165 settings->value(canvasOpacitySettingsKey).toInt() :
168 nodeLibraryDisplayMode = settings->contains(nodeLibraryDisplayModeSettingsKey)?
170 defaultNodeLibraryDisplayMode;
172 nodeLibraryCurrentlyVisible = settings->contains(nodeLibraryVisibilityStateSettingsKey)?
173 settings->value(nodeLibraryVisibilityStateSettingsKey).toBool() :
174 defaultNodeLibraryVisibilityState;
176 nodeLibraryCurrentlyDocked = settings->contains(nodeLibraryDockingStateSettingsKey)?
177 settings->value(nodeLibraryDockingStateSettingsKey).toBool() :
178 defaultNodeLibraryDockingState;
180 if (settings->contains(nodeLibraryFloatingPositionSettingsKey))
182 settingsContainedNodeLibraryFloatingPosition =
true;
183 nodeLibraryFloatingPosition = settings->value(nodeLibraryFloatingPositionSettingsKey).toPoint();
187 settingsContainedNodeLibraryFloatingPosition =
false;
188 nodeLibraryFloatingPosition = QPoint();
191 if (settings->contains(nodeLibraryWidthSettingsKey))
192 nodeLibraryWidth = settings->value(nodeLibraryWidthSettingsKey).toInt();
194 nodeLibraryWidth = -1;
196 if (settings->contains(nodeLibraryHeightSettingsKey))
197 nodeLibraryHeight = settings->value(nodeLibraryHeightSettingsKey).toInt();
199 nodeLibraryHeight = -1;
201 if (settings->contains(nodeDocumentationPanelHeightSettingsKey))
202 nodeDocumentationPanelHeight = settings->value(nodeDocumentationPanelHeightSettingsKey).toInt();
204 nodeDocumentationPanelHeight = defaultNodeLibraryDocumentationPanelHeight;
206 previousVisibleNodeLibraryStateWasDocked = nodeLibraryCurrentlyDocked;
207 currentFloatingNodeLibrary = NULL;
209 shaderDocumentationVisible = settings->contains(shaderDocumentationVisibilitySettingsKey)?
210 settings->value(shaderDocumentationVisibilitySettingsKey).toBool() :
213 applyStoredMovieExportSettings();
227 builtInDriversQueue = dispatch_queue_create(
"org.vuo.editor.drivers", NULL);
228 dispatch_async(builtInDriversQueue, ^{
229 initializeBuiltInDrivers();
235 if (!documentationGenerationDirectory.empty())
237 dispatch_async(dispatch_get_main_queue(), ^{
238 generateAllNodeSetHtmlDocumentation(documentationGenerationDirectory);
245 QDesktopServices::setUrlHandler(
vuoHelpBookScheme,
this,
"openHelpBookPageFromUrl");
257 menuBar =
new QMenuBar();
259 menuFile =
new QMenu(menuBar);
260 menuFile->setSeparatorsCollapsible(
false);
261 menuFile->setTitle(tr(
"&File"));
263 menuFile->addAction(tr(
"&New Composition"),
this, SLOT(
newComposition()), QKeySequence(
"Ctrl+N"));
265 menuNewCompositionWithTemplate =
new QMenu(tr(
"New Composition from Template"));
266 menuNewCompositionWithTemplate->setSeparatorsCollapsible(
false);
268 menuFile->addMenu(menuNewCompositionWithTemplate);
270 QMenu *menuNewShader =
new QMenu(tr(
"New Shader"));
271 menuNewShader->setSeparatorsCollapsible(
false);
273 menuFile->addMenu(menuNewShader);
275 menuFile->addSeparator();
276 menuFile->addAction(tr(
"&Open…"),
this, SLOT(
openFile()), QKeySequence(
"Ctrl+O"));
281 menuFile->addMenu(menuOpenRecent);
287 menuFile->addMenu(menuOpenExample);
291 menuFile->addAction(tr(
"Quit"),
this, SLOT(
quitCleanly()), QKeySequence(
"Ctrl+Q"));
294 menuFile->addAction(tr(
"About Vuo…"),
this, SLOT(
about()));
300 QAction *aboutAction =
new QAction(NULL);
301 aboutAction->setText(tr(
"About Vuo…"));
303 aboutAction->setMenuRole(QAction::NoRole);
304 menuFile->addSeparator();
305 menuFile->addAction(aboutAction);
307 QAction *quitAction =
new QAction(NULL);
308 quitAction->setText(tr(
"&Quit Vuo"));
309 quitAction->setShortcut(QKeySequence(
"Ctrl+Q"));
311 quitAction->setMenuRole(QAction::NoRole);
312 menuFile->addSeparator();
313 menuFile->addAction(quitAction);
315 menuBar->addAction(menuFile->menuAction());
318 enableMenuItems(menuFile, canCloseWelcomeWindow());
323 menuView =
new QMenu(menuBar);
324 menuView->setSeparatorsCollapsible(
false);
325 menuView->setTitle(tr(
"&View"));
328 showNodeLibraryAction =
new QAction(NULL);
329 showNodeLibraryAction->setText(tr(
"Show Node &Library"));
330 showNodeLibraryAction->setShortcut(QKeySequence(
"Ctrl+Return"));
332 menuView->addAction(showNodeLibraryAction);
341 bool snapToGrid = (settings->contains(snapToGridSettingsKey)?
342 settings->value(snapToGridSettingsKey).toBool() :
346 connect(
snapToGridAction, &QAction::toggled,
this, &VuoEditor::updateSnapToGrid);
350 if (settings->contains(gridOpacitySettingsKey))
351 gridOpacity = settings->value(gridOpacitySettingsKey).toInt();
354 gridOpacity = VuoEditor::defaultGridOpacity;
355 settings->setValue(gridOpacitySettingsKey, gridOpacity);
360 int gridType = (settings->contains(gridTypeSettingsKey)
361 ? settings->value(gridTypeSettingsKey).toInt()
362 : VuoRendererComposition::LineGrid);
405 if (action->data().toInt() == canvasOpacity)
407 action->setChecked(
true);
408 action->setEnabled(
false);
418 menuBar->addAction(menuView->menuAction());
423 menuWindow =
new QMenu(menuBar);
424 menuWindow->setSeparatorsCollapsible(
false);
425 menuWindow->setTitle(tr(
"&Window"));
429 connect(menuWindow, &QMenu::aboutToShow,
this, &VuoEditor::updateUI);
431 menuBar->addAction(menuWindow->menuAction());
435 menuHelp =
new QMenu(menuBar);
436 menuHelp->setSeparatorsCollapsible(
false);
437 menuHelp->setTitle(tr(
"&Help"));
444 connect(menuHelp, &QMenu::aboutToShow, [
this] {
populateHelpMenu(menuHelp); });
446 menuBar->addAction(menuHelp->menuAction());
453 dockContextMenu =
new QMenu();
454 dockContextMenu->setSeparatorsCollapsible(
false);
455 dockContextMenu->setAsDockMenu();
457 connect(dockContextMenu, &QMenu::aboutToShow,
this, &VuoEditor::updateUI);
461 initializeTopLevelNodeLibrary(compiler,
462 nodeLibraryDisplayMode,
463 settingsContainedNodeLibraryFloatingPosition,
464 nodeLibraryFloatingPosition,
467 if (!nodeLibraryCurrentlyDocked)
468 designateNewFloatingNodeLibrary(ownedNodeLibrary);
476 conformToGlobalNodeLibraryVisibility(
477 getGlobalNodeLibraryStateForAttributes(nodeLibraryCurrentlyVisible, nodeLibraryCurrentlyDocked),
478 currentFloatingNodeLibrary);
485 connect(ownedNodeLibrary, &VuoNodeLibrary::visibilityChanged,
this, &VuoEditor::updateUI);
486 enableGlobalStateConformanceToLibrary(ownedNodeLibrary);
490 QApplication::processEvents();
491 if (queuedCompositionsToOpen.empty())
500 if (d.history().isEmpty())
503 uiInitialized =
true;
513 dispatch_sync(builtInDriversQueue, ^{});
515 menuBar->deleteLater();
516 delete subcompositionRouter;
525 vector<QString> queuedCompositionsToOpenSnapshot(queuedCompositionsToOpen);
526 queuedCompositionsToOpen.clear();
531 foreach (QString queuedUrl, queuedCompositionsToOpenSnapshot)
542 enableMenuItems(menuFile, canCloseWelcomeWindow());
544 if (!queuedCompositionsToOpen.empty())
545 closeWelcomeWindow();
554 void VuoEditor::enableMenuItems(QMenu *menu,
bool enable)
559 foreach (QAction *action, menu->actions())
568 enableMenuItems(action->menu(), enable);
572 bool isQuitOrHelp = action->text().contains(
"Quit") || action->text().contains(
"About");
573 bool isTemplateHeader = (menu == menuNewCompositionWithTemplate) &&
574 action->data().value<QString>().isEmpty() &&
575 !action->data().value<
void *>();
576 if (!(isQuitOrHelp || isTemplateHeader))
577 action->setEnabled(enable);
603 if (ownedNodeLibrary)
604 disableGlobalStateConformanceToLibrary(ownedNodeLibrary);
611 if (windowsRemainingAfterQuitRequested.empty())
615 windowsRemainingAfterQuitRequested[0]->close();
626 if (windowsRemainingAfterQuitRequested.empty())
631 foreach (QMainWindow *window, windowsRemainingAfterQuitRequested)
634 if (compositionWindow)
638 if (ownedNodeLibrary)
639 enableGlobalStateConformanceToLibrary(ownedNodeLibrary);
641 windowsRemainingAfterQuitRequested.clear();
652 if (windowsRemainingAfterQuitRequested.empty())
656 windowsRemainingAfterQuitRequested.removeOne(window);
658 if (windowsRemainingAfterQuitRequested.empty())
662 windowsRemainingAfterQuitRequested[0]->close();
668 void VuoEditor::reallyQuit()
681 QApplication::quit();
689 return nodeLibraryCurrentlyDocked;
697 void VuoEditor::enableGlobalStateConformanceToLibrary(
VuoNodeLibrary *library)
700 connect(library, &VuoNodeLibrary::topLevelChanged,
this, &VuoEditor::updateGlobalNodeLibraryDockedState);
712 void VuoEditor::disableGlobalStateConformanceToLibrary(
VuoNodeLibrary *library)
715 disconnect(library, &VuoNodeLibrary::topLevelChanged,
this, &VuoEditor::updateGlobalNodeLibraryDockedState);
725 void VuoEditor::initializeTopLevelNodeLibrary(
VuoCompiler *nodeLibraryCompiler,
727 bool setFloatingPosition,
728 QPoint floatingPosition,
729 int nodeLibraryWidth,
730 int nodeLibraryHeight)
732 ownedNodeLibrary =
new VuoNodeLibrary(nodeLibraryCompiler, NULL, nodeLibraryDisplayMode);
733 ownedNodeLibrary->setObjectName(
"Top-level node library");
735 ownedNodeLibrary->setFloating(
true);
737 if (setFloatingPosition)
738 ownedNodeLibrary->move(floatingPosition);
740 if (nodeLibraryHeight >= 0)
741 ownedNodeLibrary->resize(ownedNodeLibrary->rect().width(), nodeLibraryHeight);
752 aboutBox->showNormal();
754 aboutBox->activateWindow();
774 m->addAction(tr(
"Minimize"), currentWindow, &QMainWindow::showMinimized, QKeySequence(
"Ctrl+M"));
775 m->addAction(tr(
"Zoom"), currentWindow, &QMainWindow::showMaximized);
781 populateWindowMenu_Pro(m);
785 if (! openWindows.empty())
789 for (QMainWindow *openWindow : openWindows)
792 m->addAction(raiseDocumentAction);
795 raiseDocumentAction->setChecked(openWindow == currentWindow);
796 else if (openWindow->isMinimized())
797 raiseDocumentAction->setChecked(
false);
810 QAction *vuoManualAction =
new QAction(m);
811 vuoManualAction->setText(tr(
"Vuo Manual"));
814 m->addAction(vuoManualAction);
817 QAction *vuoManualPDFAction =
new QAction(m);
818 vuoManualPDFAction->setText(tr(
"Vuo Manual (PDF)"));
821 m->addAction(vuoManualPDFAction);
824 QAction *videoTutorialsAction =
new QAction(m);
825 videoTutorialsAction->setText(tr(
"Video Tutorials"));
828 m->addAction(videoTutorialsAction);
833 QAction *searchVuoOrgAction =
new QAction(m);
834 searchVuoOrgAction->setText(tr(
"Search vuo.org"));
835 searchVuoOrgAction->setData(QUrl(
"https://vuo.org/search"));
837 m->addAction(searchVuoOrgAction);
842 QAction *communityActivityAction =
new QAction(m);
843 communityActivityAction->setText(tr(
"View Community Activity"));
844 communityActivityAction->setData(QUrl(
"https://vuo.org/community"));
846 m->addAction(communityActivityAction);
849 QAction *shareCompositionAction =
new QAction(m);
850 shareCompositionAction->setText(tr(
"Share a Composition"));
851 shareCompositionAction->setData(QUrl(
"https://vuo.org/node/add/vuo-composition"));
853 m->addAction(shareCompositionAction);
856 QAction *startDiscussionAction =
new QAction(m);
857 startDiscussionAction->setText(tr(
"Start a Discussion"));
858 startDiscussionAction->setData(QUrl(
"https://vuo.org/node/add/discussion"));
860 m->addAction(startDiscussionAction);
863 QAction *reportBugAction =
new QAction(m);
864 reportBugAction->setText(tr(
"Report a Bug"));
865 reportBugAction->setData(QUrl(
"https://vuo.org/bug"));
867 m->addAction(reportBugAction);
870 QAction *requestFeatureAction =
new QAction(m);
871 requestFeatureAction->setText(tr(
"Request a Feature"));
872 requestFeatureAction->setData(QUrl(
"https://vuo.org/feature-request"));
874 m->addAction(requestFeatureAction);
879 QAction *improveVuoAction =
new QAction(m);
880 improveVuoAction->setText(tr(
"Help Us Improve Vuo"));
881 improveVuoAction->setData(QUrl(
"https://vuo.org/community-edition"));
883 m->addAction(improveVuoAction);
886 QAction *contactTeamVuoAction =
new QAction(m);
887 contactTeamVuoAction->setText(tr(
"Contact Team Vuo"));
888 contactTeamVuoAction->setData(QUrl(
"https://vuo.org/contact"));
890 m->addAction(contactTeamVuoAction);
893 VuoEditor::populateHelpMenu_Pro(m);
902 OSStatus ret = AHGotoPage(CFSTR(
"org.vuo.Editor.help"), NULL, NULL);
906 VUserLog(
"Error: Couldn't open Vuo Manual in HelpViewer.app: %s", description);
916 void VuoEditor::openHelpBookPageFromUrl(
const QUrl &url)
918 CFStringRef relativePath = CFStringCreateWithCString(NULL, url.url(QUrl::RemoveScheme).toUtf8().constData(), kCFStringEncodingUTF8);
919 OSStatus ret = AHGotoPage(CFSTR(
"org.vuo.Editor.help"), relativePath, NULL);
923 VUserLog(
"Error: Couldn't open Vuo Manual in HelpViewer.app: %s", description);
926 CFRelease(relativePath);
940 VuoEditorWindow *w = createEditorWindow(filename, filename, compositionAsString, activeProtocol);
953 VuoEditorWindow * VuoEditor::createEditorWindow(QString documentIdentifier, QString filename,
const string &compositionAsString,
954 VuoProtocol *activeProtocol,
string nodeClassToHighlight)
965 nodeLibraryDisplayMode,
966 getGlobalNodeLibraryStateForAttributes(nodeLibraryCurrentlyVisible, nodeLibraryCurrentlyDocked),
967 currentFloatingNodeLibrary,
969 nodeClassToHighlight);
977 documentIdentifierAssigned[filename.isEmpty() ? documentIdentifier : filename] = w;
979 const int defaultWindowXOffset = 40;
980 const int defaultWindowYOffset = 40;
987 if (ownedNodeLibrary && nodeLibraryCurrentlyVisible && !nodeLibraryCurrentlyDocked)
990 int initialWindowXOffset = defaultWindowXOffset;
991 int initialWindowYOffset = defaultWindowYOffset;
993 int nodeLibraryLeftPos = ownedNodeLibrary->mapToGlobal(ownedNodeLibrary->rect().topLeft()).x();
994 int nodeLibraryRightPos = ownedNodeLibrary->mapToGlobal(ownedNodeLibrary->rect().topRight()).x();
995 int nodeLibraryCenterPos = 0.5*(nodeLibraryLeftPos + nodeLibraryRightPos);
997 int availableSpaceLeftBoundary = QApplication::desktop()->availableGeometry(w).left();
998 int availableSpaceRightBoundary = QApplication::desktop()->availableGeometry(w).right();
1000 bool nodeLibraryCloserToLeft = (nodeLibraryCenterPos - availableSpaceLeftBoundary) <
1001 (availableSpaceRightBoundary - nodeLibraryCenterPos);
1003 const int horizontalBuffer = 10;
1004 bool spaceForWindowToRightOfNodeLibrary = (availableSpaceRightBoundary - nodeLibraryRightPos) >=
1005 (w->geometry().width() + horizontalBuffer);
1009 if ((nodeLibraryCloserToLeft && spaceForWindowToRightOfNodeLibrary))
1010 initialWindowXOffset = nodeLibraryRightPos + horizontalBuffer;
1017 const int titleBarHeight = 16;
1018 int nodeLibraryTopPos = ownedNodeLibrary->mapToGlobal(ownedNodeLibrary->rect().topLeft()).y() - titleBarHeight;
1020 if (nodeLibraryTopPos < defaultWindowYOffset)
1021 initialWindowYOffset = nodeLibraryTopPos;
1023 w->move(initialWindowXOffset, initialWindowYOffset);
1030 yScreenSpaceShortage = qMax(0, (w->geometry().bottom()) - QApplication::desktop()->availableGeometry(w).bottom() + 5);
1036 w->move(activeWindow->pos() + QPoint(defaultWindowXOffset, defaultWindowYOffset));
1042 w->resize(w->width(), w->height()-yScreenSpaceShortage);
1052 createEditorWindow_Pro(w);
1073 QString summary = tr(
"This composition contains nodes that aren't installed.");
1075 QString details = tr(
"<p>If you save the composition while it contains these nodes, some information will be lost.</p>"
1076 "<p>Try searching for these nodes in the <a href=\"%1\">Node Gallery</a>. "
1077 "Or if you don't need them, just delete them from the composition.</p>")
1078 .arg(
"https://vuo.org/nodes")
1096 connect(window, &QMainWindow::destroyed,
this, &VuoEditor::updateUI);
1112 dispatch_async(dispatch_get_main_queue(), ^{
1121 if (!closeWelcomeWindow())
1124 QString identifier = assignUntitledDocumentIdentifier();
1125 VUserLog(
"%s: New empty composition", identifier.toUtf8().data());
1126 createEditorWindow(identifier,
"",
"", NULL);
1132 if (!closeWelcomeWindow())
1136 QString identifier = assignUntitledDocumentIdentifier();
1137 VUserLog(
"%s: New empty composition", identifier.toUtf8().data());
1138 createEditorWindow(identifier,
"",
"", NULL);
1147 QString identifier = assignUntitledDocumentIdentifier();
1148 VUserLog(
"%s: New Composition with content", identifier.toUtf8().data());
1149 return createEditorWindow(identifier, QString::fromStdString(compositionDir), content, NULL);
1158 QAction *sender = (QAction *)QObject::sender();
1161 closeUnmodifiedUntitledComposition();
1163 if (!closeWelcomeWindow())
1167 QString identifier = assignUntitledDocumentIdentifier();
1168 VUserLog(
"%s: New Composition with %s protocol", identifier.toUtf8().data(), selectedProtocol->
getName().c_str());
1169 VuoEditorWindow *w = createEditorWindow(identifier,
false, selectedProtocol);
1173 string nodeLibraryFilterText = getFilterTextForTemplate(selectedProtocol->
getId());
1174 if (!nodeLibraryFilterText.empty())
1188 QAction *sender = (QAction *)QObject::sender();
1189 QString selectedTemplate =
static_cast<QString
>(sender->data().value<QString>());
1192 closeUnmodifiedUntitledComposition();
1194 if (!closeWelcomeWindow())
1199 QString identifier = assignUntitledDocumentIdentifier();
1200 VUserLog(
"%s: New Composition with %s template", identifier.toUtf8().data(), selectedTemplate.toUtf8().data());
1201 VuoEditorWindow *w = createEditorWindow(identifier,
"", compositionAsString, NULL);
1205 string nodeLibraryFilterText = getFilterTextForTemplate(selectedTemplate.toUtf8().constData());
1206 if (!nodeLibraryFilterText.empty())
1221 string VuoEditor::getFilterTextForTemplate(
string templateID)
1223 map<string, string> filterTextForTemplate;
1231 filterTextForTemplate[
"imageTemplate"] =
"vuo.image";
1232 filterTextForTemplate[
"layersTemplate"] =
"vuo.layer shape";
1233 filterTextForTemplate[
"sceneTemplate"] =
"vuo.scene shape";
1247 map<string, string>::iterator i = filterTextForTemplate.find(templateID);
1248 if (i != filterTextForTemplate.end())
1257 QString VuoEditor::assignUntitledDocumentIdentifier(
void)
1260 int documentIdentifierInstanceNum = 1;
1262 while(documentIdentifierAssigned[uniqueDocumentIdentifier])
1264 std::ostringstream oss;
1265 oss <<
" " << ++documentIdentifierInstanceNum;
1269 return uniqueDocumentIdentifier;
1277 QFileDialog d(NULL,
"",
"",
"Vuo Composition (*.vuo);;Fragment Shader (*.fs)");
1278 d.setFileMode(QFileDialog::ExistingFiles);
1285 QStringList fileNames;
1286 if (d.exec() == QDialog::Accepted)
1287 fileNames = d.selectedFiles();
1289 foreach (QString fileName, fileNames)
1298 closeUnmodifiedUntitledComposition();
1300 if (!closeWelcomeWindow())
1302 QString fileURL = QString(
"file://").append(filename);
1303 queuedCompositionsToOpen.push_back(QString(
"file://").append(filename));
1317 VuoErrorDialog::show(NULL, tr(
"You do not have permission to open the document \"%1\".").arg(QFileInfo(filename).fileName()),
"");
1321 string dir, file, ext;
1325 QMainWindow *window =
nullptr;
1328 VUserLog(
"%s.%s: Open", file.c_str(), ext.c_str());
1329 window = createEditorWindow(filename,
true);
1330 dynamic_cast<VuoEditorWindow *
>(window)->setIncludeInRecentFileMenu(addToRecentFileMenu);
1337 dynamic_cast<VuoCodeWindow *
>(window)->setIncludeInRecentFileMenu(addToRecentFileMenu);
1348 if (addToRecentFileMenu)
1363 VUserLog(
"%s: Open", filename.toUtf8().data());
1365 string filenameStr = filename.toStdString();
1368 QString compositionPath = QString::fromStdString(workingDir +
"/" + filenameStr);
1371 closeUnmodifiedUntitledComposition();
1373 if (!closeWelcomeWindow())
1376 createEditorWindow(compositionPath, compositionPath, compositionContents, NULL, nodeClassToHighlight);
1380 .append(nodeSet->
getName().c_str())
1392 void VuoEditor::closeUnmodifiedUntitledComposition()
1414 if ((ownedNodeLibrary->isHidden()))
1418 designateNewFloatingNodeLibrary(ownedNodeLibrary);
1419 updateGlobalNodeLibraryState(
true,
false);
1432 if ((visibility == VuoNodeLibrary::nodeLibraryHidden) ||
1433 (visibility == VuoNodeLibrary::nodeLibraryDocked))
1436 ownedNodeLibrary->setVisible(
false);
1439 else if (visibility == VuoNodeLibrary::nodeLibraryFloating)
1444 if (ownedNodeLibrary == floater)
1446 if (! ownedNodeLibrary->isFloating())
1447 ownedNodeLibrary->setFloating(
true);
1451 ownedNodeLibrary->setFocus();
1462 void VuoEditor::updateUI()
1468 QMainWindow *frontWindow = (openWindows.empty() ? nullptr : openWindows.front());
1471 menuWindow->clear();
1476 dockContextMenu->clear();
1483 action->setEnabled(!action->isChecked());
1493 case QEvent::FileOpen:
1494 openUrl(
static_cast<QFileOpenEvent *
>(e)->url().toString());
1497 case QEvent::ApplicationActivate:
1501 case QEvent::ApplicationDeactivate:
1512 return QApplication::event(e);
1522 QAction *sender = (QAction *)QObject::sender();
1523 QDesktopServices::openUrl(sender->data().toUrl());
1532 QAction *sender = (QAction *)QObject::sender();
1533 QString filePath = sender->data().value<QString>();
1535 if (! filePath.startsWith(
"file://"))
1536 filePath = QString(
"file://").append(filePath);
1547 QAction *sender = (QAction *)QObject::sender();
1548 QString filePath = sender->data().value<QString>();
1549 moveFileToTrash(filePath);
1555 void VuoEditor::moveFileToTrash(QString filePath)
1581 queuedCompositionsToOpen.push_back(url);
1587 string dir, file, ext;
1593 string sourceNodePath = dir +
"/" + file +
"." + ext;
1594 string targetNodePath = installedNodeDir +
"/" + file +
"." + ext;
1600 QMessageBox messageBox;
1601 string errorSummary =
"A node named \"" + file +
"\" already exists. Do you want to replace it with the one you're installing?";
1604 messageBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowMaximizeButtonHint);
1606 messageBox.setTextFormat(Qt::RichText);
1607 messageBox.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel);
1608 messageBox.setButtonText(QMessageBox::Discard, tr(
"Replace"));
1609 messageBox.setButtonText(QMessageBox::Cancel, tr(
"Cancel"));
1610 messageBox.setDefaultButton(QMessageBox::Discard);
1611 messageBox.setText(errorSummary.c_str());
1612 messageBox.setStyleSheet(
"#qt_msgbox_informativelabel, QMessageBoxDetailsText { font-weight: normal; font-size: 11pt; }");
1615 static_cast<QPushButton *
>(messageBox.button(QMessageBox::Cancel))->setAutoDefault(
false);
1616 messageBox.button(QMessageBox::Cancel)->setFocus();
1620 if (messageBox.exec() == QMessageBox::Discard)
1644 VUserLog(
"Install node %s.%s", file.c_str(), ext.c_str());
1656 QMessageBox installationSuccessMessageBox;
1657 installationSuccessMessageBox.setText(tr(
"Your node has been installed!"));
1659 installationSuccessMessageBox.exec();
1666 queuedCompositionsToOpen.push_back(url);
1672 QDesktopServices::openUrl(url);
1681 shaderDocumentationVisible = isVisible;
1682 settings->setValue(shaderDocumentationVisibilitySettingsKey, shaderDocumentationVisible);
1690 return shaderDocumentationVisible;
1696 void VuoEditor::applyStoredMovieExportSettings()
1698 movieExportWidth = (settings->contains(movieExportWidthSettingsKey)? settings->value(movieExportWidthSettingsKey).toInt() : 1024);
1699 movieExportHeight = (settings->contains(movieExportHeightSettingsKey)? settings->value(movieExportHeightSettingsKey).toInt() : 768);
1700 movieExportTime = (settings->contains(movieExportTimeSettingsKey)? settings->value(movieExportTimeSettingsKey).toDouble() : 0.);
1701 movieExportDuration = (settings->contains(movieExportDurationSettingsKey)? settings->value(movieExportDurationSettingsKey).toDouble() : 10.);
1702 movieExportFramerate = (settings->contains(movieExportFramerateSettingsKey)? settings->value(movieExportFramerateSettingsKey).toDouble() : 30.);
1703 movieExportSpatialSupersample = (settings->contains(movieExportSpatialSupersampleSettingsKey)? settings->value(movieExportSpatialSupersampleSettingsKey).toInt() : 1.);
1704 movieExportTemporalSupersample = (settings->contains(movieExportTemporalSupersampleSettingsKey)? settings->value(movieExportTemporalSupersampleSettingsKey).toInt() : 1.);
1705 movieExportShutterAngle = (settings->contains(movieExportShutterAngleSettingsKey)?
static_cast<float>(settings->value(movieExportShutterAngleSettingsKey).toDouble()) : 360.);
1706 movieExportImageFormat = (settings->contains(movieExportImageFormatSettingsKey)? settings->value(movieExportImageFormatSettingsKey).toString() :
"H.264");
1707 movieExportQuality = (settings->contains(movieExportQualitySettingsKey)? settings->value(movieExportQualitySettingsKey).toDouble() : 1.);
1713 void VuoEditor::updateGlobalMovieExportSettings(
int width,
1718 int spatialSupersample,
1719 int temporalSupersample,
1721 QString imageFormat,
1724 if (width != movieExportWidth)
1726 movieExportWidth = width;
1727 settings->setValue(movieExportWidthSettingsKey, movieExportWidth);
1730 if (height != movieExportHeight)
1732 movieExportHeight = height;
1733 settings->setValue(movieExportHeightSettingsKey, movieExportHeight);
1736 if (time != movieExportTime)
1738 movieExportTime = time;
1739 settings->setValue(movieExportTimeSettingsKey, movieExportTime);
1742 if (duration != movieExportDuration)
1744 movieExportDuration = duration;
1745 settings->setValue(movieExportDurationSettingsKey, movieExportDuration);
1748 if (framerate != movieExportFramerate)
1750 movieExportFramerate = framerate;
1751 settings->setValue(movieExportFramerateSettingsKey, movieExportFramerate);
1754 if (spatialSupersample != movieExportSpatialSupersample)
1756 movieExportSpatialSupersample = spatialSupersample;
1757 settings->setValue(movieExportSpatialSupersampleSettingsKey, movieExportSpatialSupersample);
1760 if (temporalSupersample != movieExportTemporalSupersample)
1762 movieExportTemporalSupersample = temporalSupersample;
1763 settings->setValue(movieExportTemporalSupersampleSettingsKey, movieExportTemporalSupersample);
1766 if (shutterAngle != movieExportShutterAngle)
1768 movieExportShutterAngle = shutterAngle;
1769 settings->setValue(movieExportShutterAngleSettingsKey,
static_cast<double>(movieExportShutterAngle));
1772 if (imageFormat != movieExportImageFormat)
1774 movieExportImageFormat = imageFormat;
1775 settings->setValue(movieExportImageFormatSettingsKey, movieExportImageFormat);
1778 if (quality != movieExportQuality)
1780 movieExportQuality = quality;
1781 settings->setValue(movieExportQualitySettingsKey, movieExportQuality);
1793 int &spatialSupersample,
1794 int &temporalSupersample,
1795 float &shutterAngle,
1796 QString &imageFormat,
1799 width = movieExportWidth;
1800 height = movieExportHeight;
1801 time = movieExportTime;
1802 duration = movieExportDuration;
1803 framerate = movieExportFramerate;
1804 spatialSupersample = movieExportSpatialSupersample;
1805 temporalSupersample = movieExportTemporalSupersample;
1806 shutterAngle = movieExportShutterAngle;
1807 imageFormat = movieExportImageFormat;
1808 quality = movieExportQuality;
1818 return ((! visible)? VuoNodeLibrary::nodeLibraryHidden :
1819 (docked? VuoNodeLibrary::nodeLibraryDocked :
1820 VuoNodeLibrary::nodeLibraryFloating));
1828 void VuoEditor::updateNodeLibraryDisplayMode(
bool humanReadable)
1830 nodeLibraryDisplayMode = (humanReadable? VuoNodeLibrary::displayByName : VuoNodeLibrary::displayByClass);
1831 settings->setValue(nodeLibraryDisplayModeSettingsKey, nodeLibraryDisplayMode);
1836 if ((ownedNodeLibrary->isHidden()) && (ownedNodeLibrary->
getHumanReadable() != humanReadable))
1847 void VuoEditor::updateGlobalNodeLibraryFloatingPosition(QPoint newPos)
1849 nodeLibraryFloatingPosition = newPos;
1850 settings->setValue(nodeLibraryFloatingPositionSettingsKey, nodeLibraryFloatingPosition);
1856 void VuoEditor::updateGlobalNodeLibraryWidth(
int newWidth)
1858 nodeLibraryWidth = newWidth;
1859 settings->setValue(nodeLibraryWidthSettingsKey, nodeLibraryWidth);
1865 void VuoEditor::updateGlobalNodeLibraryHeight(
int newHeight)
1867 nodeLibraryHeight = newHeight;
1868 settings->setValue(nodeLibraryHeightSettingsKey, nodeLibraryHeight);
1874 void VuoEditor::updateGlobalNodeDocumentationPanelHeight(
int newSize)
1876 nodeDocumentationPanelHeight = newSize;
1877 settings->setValue(nodeDocumentationPanelHeightSettingsKey, nodeDocumentationPanelHeight);
1883 void VuoEditor::updateGlobalNodeLibraryVisibilityState(
bool visible)
1885 VUserLog(
"%s node library", visible ?
"Show" :
"Hide");
1888 bool updatedDockingState = ((visible && (! nodeLibraryCurrentlyVisible))? previousVisibleNodeLibraryStateWasDocked : nodeLibraryCurrentlyDocked);
1889 updateGlobalNodeLibraryState(visible, updatedDockingState);
1895 void VuoEditor::updateGlobalNodeLibraryDockedState(
bool floating)
1897 VUserLog(
"%s node library", floating ?
"Detach" :
"Attach");
1901 designateNewFloatingNodeLibrary(floater);
1902 updateGlobalNodeLibraryState(this->nodeLibraryCurrentlyVisible, (! floating));
1905 updateFloatingNodeLibraryModules();
1907 updateDockedNodeLibraryModules();
1915 void VuoEditor::updateGlobalNodeLibraryState(
bool visible,
bool docked)
1917 VuoNodeLibrary::nodeLibraryState currentNodeLibraryState = getGlobalNodeLibraryStateForAttributes(nodeLibraryCurrentlyVisible, nodeLibraryCurrentlyDocked);
1920 if (currentNodeLibraryState != VuoNodeLibrary::nodeLibraryHidden)
1921 previousVisibleNodeLibraryStateWasDocked = nodeLibraryCurrentlyDocked;
1923 nodeLibraryCurrentlyVisible = visible;
1924 nodeLibraryCurrentlyDocked = docked;
1926 currentFloatingNodeLibrary = NULL;
1928 settings->setValue(nodeLibraryVisibilityStateSettingsKey, nodeLibraryCurrentlyVisible);
1929 settings->setValue(nodeLibraryDockingStateSettingsKey, nodeLibraryCurrentlyDocked);
1938 void VuoEditor::updateFloatingNodeLibraryModules()
1940 if (!currentFloatingNodeLibrary)
1947 if (activeCompositionModuleManager->
getNodeLibrary() != currentFloatingNodeLibrary)
1953 if (window != activeWindow)
1956 if (windowModuleManager->
getNodeLibrary() == currentFloatingNodeLibrary)
1962 if (moduleManager->
getNodeLibrary() == currentFloatingNodeLibrary)
1968 if (updatedModuleManager->
getNodeLibrary() != currentFloatingNodeLibrary)
1971 QString origFilterText;
1972 set<string> origSelectedNodeClasses;
1973 string origDocumentedNodeClass;
1974 currentFloatingNodeLibrary->
getState(origFilterText, origSelectedNodeClasses, origDocumentedNodeClass);
1978 updatedModuleManager->
setNodeLibrary(currentFloatingNodeLibrary);
1982 currentFloatingNodeLibrary->
setState(origFilterText, origSelectedNodeClasses, origDocumentedNodeClass);
1986 QString newFilterText;
1987 set<string> newSelectedNodeClasses;
1988 string newDocumentedNodeClass;
1989 currentFloatingNodeLibrary->
getState(newFilterText, newSelectedNodeClasses, newDocumentedNodeClass);
1990 if (newDocumentedNodeClass.empty())
2032 void VuoEditor::updateDockedNodeLibraryModules()
2034 if (currentFloatingNodeLibrary)
2044 if (windowModuleManager->
getNodeLibrary() != windowOwnedNodeLibrary)
2065 void VuoEditor::designateNewFloatingNodeLibrary(
VuoNodeLibrary *library)
2067 if (currentFloatingNodeLibrary && (currentFloatingNodeLibrary != ownedNodeLibrary))
2070 if (library && (library != ownedNodeLibrary))
2073 currentFloatingNodeLibrary = library;
2080 void VuoEditor::assignTopLevelLibraryAsReplacementFloater()
2082 currentFloatingNodeLibrary = ownedNodeLibrary;
2085 currentFloatingNodeLibrary,
true);
2095 menuOpenRecent->
addFile(filePath);
2105 this->closedFiles.push_back(filePath.toUtf8().constData());
2139 settings->setValue(recentFileListSettingsKey, menuOpenRecent->
getRecentFiles());
2148 QString bundledManual = QDir::cleanPath(QApplication::applicationDirPath().append(
"/../Resources"))
2149 .append(QDir::separator())
2151 .append(VUO_VERSION_STRING)
2152 .append(
"-manual.pdf");
2154 if (QFile(bundledManual).exists())
2155 return QString(
"file://").append(bundledManual);
2157 return "https://vuo.org/manual.pdf";
2165 return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
2173 void VuoEditor::generateAllNodeSetHtmlDocumentation(
string saveDirectory)
2175 const bool publishInternalVuoLinks =
true;
2179 QString indexFilename(QString::fromStdString(saveDirectory) +
"/index.html");
2180 QFile indexFile(indexFilename);
2181 if (!indexFile.open(QFile::WriteOnly | QFile::Truncate))
2182 throw VuoException((
"Couldn't open " + indexFilename).toUtf8().data());
2184 QTextStream indexWriter(&indexFile);
2185 indexWriter << VUO_QSTRINGIFY(
2188 <title>Vuo %1 node documentation</title>
2190 * { font-size: 13pt; font-family:
'PT Sans',Avenir,
'Trebuchet MS',Tahoma,sans-serif; }
2191 body { padding: 5em; }
2192 h1 { font-size: 24pt; color: #aaa; font-weight: normal; }
2193 a, a:visited { font-weight: bold; color: #69a; }
2194 p { margin-top: 0; color: #aaa; }
2195 p a, p a:visited { font-weight: normal; color: #aaa; }
2196 li { list-style: none; }
2200 <h1>Vuo %1 node documentation</h1>
2203 .arg(VUO_VERSION_STRING);
2205 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
2206 vector<VuoCompilerNodeClass *> nodeClasses;
2207 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
2208 nodeClasses.push_back(i->second);
2211 set<string> nodeSetNames;
2216 foreach (
string nodeSetName, nodeSetNames)
2218 string nodeSetSaveDirectory = saveDirectory +
"/" + nodeSetName;
2221 string saveNodeSetHtml = nodeSetSaveDirectory +
"/index.html";
2229 string firstLineOfDescription = description.substr(0, description.find(
'\n') - 1);
2231 indexWriter << VUO_QSTRINGIFY(
2232 <li><a href=
"%1/">%2</a><p>%3</p></li>
2234 .arg(QString::fromStdString(nodeSetName))
2236 .arg(QString::fromStdString(filteredDescription));
2239 indexWriter << VUO_QSTRINGIFY(
2255 const bool publishInternalVuoLinks =
false;
2257 string nodeSetName = url.host().toUtf8().constData();
2266 string tmpSaveNodeSetHtml = tmpSaveDir +
"/index.html";
2268 if (tmpSaveDir != preexistingResourceDir)
2277 if (QDesktopServices::openUrl(QString(
"file://").append(tmpSaveNodeSetHtml.c_str())))
2281 if (tmpSaveDir != preexistingResourceDir)
2297 string nodeClassName = url.host().toUtf8().constData();
2302 if (topmostNodeLibrary)
2314 return VUO_QSTRINGIFY(<style>
2316 border: 1px solid #ccc;
2317 border-collapse: collapse;
2321 font-family:
'Monaco';
2323 background-color: %2;
2328 white-space: pre-wrap;
2331 .arg(forBrowser ?
"0.4em" :
"0")
2332 .arg(isDark ?
"#383838" :
"#ececec");
2341 string nodeSetName = nodeSet->
getName();
2346 QString htmlHeader =
"<html><head><meta charset=\"utf-8\"><title>" + tr(
"Vuo Node Set Documentation") +
": "
2347 + QString::fromStdString(nodeSetName)
2353 QString title = QString(
"<h2>").append(nodeSetDisplayName);
2354 if (nodeSetDisplayName != nodeSetName.c_str())
2355 title.append(
" (").append(nodeSetName.c_str()).append(
")");
2356 title.append(
"</h2>");
2360 externalizeVuoLinks(nodeSetDocumentationContent) :
2364 QString htmlFooter =
"</body></html>";
2368 QString nodeSetExampleCompositionText =
"";
2370 foreach (
string compositionFileName, nodeSetExampleCompositionFileNames)
2380 string filteredDescription = (publishInternalVuoLinks? externalizeVuoLinks(description) :
removeVuoLinks(description));
2383 nodeSetExampleCompositionText.append(
"<li>")
2384 .append(
"<a href=\"")
2387 .append(nodeSetName.c_str())
2389 .append(compositionFileName.c_str())
2390 .append(
"\"><font size=+1>")
2391 .append(name.c_str())
2392 .append(
"</font></a>");
2394 if (!compositionDescription.isEmpty())
2395 nodeSetExampleCompositionText.append(
": ").append(compositionDescription);
2397 nodeSetExampleCompositionText.append(
"</li>\n");
2400 if (nodeSetExampleCompositionText.size() > 0)
2402 nodeSetExampleCompositionText =
"<BR><HR><h3>" + tr(
"Example composition(s)",
"", nodeSetExampleCompositionFileNames.size()) +
":</h3>\n<ul>\n" + nodeSetExampleCompositionText +
"</ul>";
2405 QString nodeSetClassesText =
"";
2408 std::sort(nodeSetClassNames.begin(), nodeSetClassNames.end(), [=](
const string nodeClassName1,
const string nodeClassName2) {
2409 VuoCompilerNodeClass *nodeClass1 = compiler->getNodeClass(nodeClassName1);
2410 VuoCompilerNodeClass *nodeClass2 = compiler->getNodeClass(nodeClassName2);
2411 string nodeClass1Title = nodeClass1? nodeClass1->getBase()->getDefaultTitle() :
"";
2412 string nodeClass2Title = nodeClass2? nodeClass2->getBase()->getDefaultTitle() :
"";
2414 return nodeClass1Title < nodeClass2Title;
2417 foreach (
string nodeClassName, nodeSetClassNames)
2425 QString nodeClassProNodeIndicator;
2427 if (nodeClass->
getBase()->isPro())
2429 nodeClassProNodeIndicator =
" <b>[<a href=\"https://vuo.org/pro-nodes\">" + tr(
"Pro node") +
"</a>]</b>";
2432 QString nodeClassDocumentationLink = QString((nodeClassName +
".html").c_str());
2435 nodeClassDescription.remove(QRegExp(
"<[^>]*>"));
2436 nodeClassDescription.replace(QRegExp(
"\\.\\s.*"),
".");
2438 nodeSetClassesText.append(
"<li>")
2439 .append(
"<a href=\"")
2440 .append(nodeClassDocumentationLink)
2442 .append(
"<font size=+1>")
2443 .append(nodeClassTitle)
2447 .append(nodeClassName.c_str())
2450 nodeSetClassesText.append(nodeClassProNodeIndicator);
2452 if (!nodeClassDescription.isEmpty())
2453 nodeSetClassesText.append(
": ").append(nodeClassDescription);
2455 nodeSetClassesText.append(
"</li>\n");
2458 if (nodeSetClassesText.size() > 0)
2460 nodeSetClassesText =
"<BR><HR><h3>" + tr(
"Node(s)",
"", nodeSetClassNames.size()) +
":</h3>\n<ul>\n" + nodeSetClassesText +
"</ul>";
2463 ofstream savedNodeSetFile(saveFileName.c_str(), ios::trunc);
2465 savedNodeSetFile << htmlHeader.append(
"\n\n").toUtf8().constData();
2466 savedNodeSetFile << title.append(
"\n\n").toUtf8().constData();
2467 savedNodeSetFile << filteredNodeSetDocumentationContent.append(
"\n\n").toUtf8().constData();
2468 savedNodeSetFile << nodeSetExampleCompositionText.append(
"\n\n").toUtf8().constData();
2469 savedNodeSetFile << nodeSetClassesText.append(
"\n\n").toUtf8().constData();
2470 savedNodeSetFile << htmlFooter.append(
"\n\n").toUtf8().constData();
2472 savedNodeSetFile.close();
2488 string nodeSetName = nodeSet->
getName();
2490 foreach (
string nodeClassName, nodeSetClassNames)
2498 string filteredNodeClassDescription = (publishInternalVuoLinks? externalizeVuoLinks(nodeClassDescription) :
2504 set<string> sortedUniqueKeywords;
2505 foreach (
string keyword, manualKeywords)
2506 sortedUniqueKeywords.insert(keyword);
2508 foreach (
string keyword, automaticKeywords)
2509 sortedUniqueKeywords.insert(keyword);
2513 QString nodeClassHtmlHeader =
"<html><head><meta charset=\"utf-8\"><title>" + tr(
"Vuo Node Documentation") +
": "
2514 + QString::fromStdString(nodeClassTitle)
2515 +
" (" + QString::fromStdString(nodeClassName) +
")"
2519 QString nodeClassDocumentationTitle = QString(
"<h2>")
2520 .append(nodeClassTitle.c_str())
2521 .append(
" (").append(nodeClassName.c_str()).append(
")")
2525 QString nodeClassKeywordsIntro =
"<p><b>" + tr(
"Keyword(s)",
"", sortedUniqueKeywords.size()) +
"</b>: ";
2526 QString nodeClassKeywordsText = nodeClassKeywordsIntro;
2527 foreach (
string keyword, sortedUniqueKeywords)
2529 if (nodeClassKeywordsText != nodeClassKeywordsIntro)
2530 nodeClassKeywordsText.append(
", ");
2532 nodeClassKeywordsText.append(
"<i>").append(keyword.c_str()).append(
"</i>");
2534 nodeClassKeywordsText.append(
"</p>");
2543 QSizeF size = composition->itemsBoundingRect().size().toSize();
2544 QSizeF retinaSize(size.width()*2, size.height()*2);
2545 QPixmap pixmap(retinaSize.toSize());
2546 pixmap.fill(Qt::transparent);
2548 QPainter painter(&pixmap);
2549 painter.setRenderHint(QPainter::Antialiasing,
true);
2550 painter.setRenderHint(QPainter::HighQualityAntialiasing,
true);
2551 painter.setRenderHint(QPainter::TextAntialiasing,
true);
2553 composition->render(&painter);
2555 string nodeClassRenderedPreviewFileName = nodeClassName +
".png";
2556 string tmpSaveNodeClassImage = saveDir +
"/" + nodeClassRenderedPreviewFileName;
2557 QFile file(tmpSaveNodeClassImage.c_str());
2558 file.open(QIODevice::WriteOnly);
2559 pixmap.save(&file,
"PNG");
2564 QString nodeClassRenderedPreview = QString(
"<img src=\"%1\" width=\"%2\" height=\"%3\" />")
2565 .arg(nodeClassRenderedPreviewFileName.c_str())
2567 .arg(size.height());
2571 QString nodeClassExampleCompositionText =
"";
2573 foreach (
string compositionFileName, nodeClassExampleCompositionFileNames)
2589 nodeClassExampleCompositionText.append(
"<li>")
2590 .append(
"<a href=\"")
2593 .append(nodeSetName.c_str())
2595 .append(compositionFileName.c_str())
2600 .append(
"\"><font size=+1>")
2601 .append(name.c_str())
2602 .append(
"</font></a>");
2604 if (!compositionDescription.isEmpty())
2605 nodeClassExampleCompositionText.append(
": ").append(compositionDescription);
2607 nodeClassExampleCompositionText.append(
"</li>\n");
2610 if (nodeClassExampleCompositionFileNames.size() > 0)
2612 nodeClassExampleCompositionText =
"<HR><h3>" + tr(
"Example composition(s)",
"", nodeClassExampleCompositionFileNames.size()) +
":</h3>\n<ul>\n" + nodeClassExampleCompositionText +
"</ul>";
2615 QString nodeClassProNodeIndicator;
2617 if (nodeClass->
getBase()->isPro())
2618 nodeClassProNodeIndicator = QString(VuoNodePopover::installedProNodeText).append(
"<br><br>");
2621 QString nodeClassSetReference =
"<HR>" + nodeClassProNodeIndicator + tr(
"Back to %1 node set documentation.")
2622 .arg(
"<a href=\"index.html\">" + QString::fromStdString(nodeSetName) +
"</a>");
2623 QString nodeClassHtmlFooter =
"</body></html>";
2625 string saveNodeClassHtml = saveDir +
"/" + nodeClassName +
".html";
2626 ofstream savedNodeClassFile(saveNodeClassHtml.c_str(), ios::trunc);
2628 savedNodeClassFile << nodeClassHtmlHeader.append(
"\n\n").toUtf8().constData();
2629 savedNodeClassFile << nodeClassDocumentationTitle.append(
"\n\n").toUtf8().constData();
2630 savedNodeClassFile << nodeClassRenderedPreview.append(
"\n\n").toUtf8().constData();
2631 savedNodeClassFile << nodeClassDocumentationContent.append(
"\n\n").toUtf8().constData();
2632 savedNodeClassFile << nodeClassKeywordsText.append(
"\n\n").toUtf8().constData();
2633 savedNodeClassFile << nodeClassExampleCompositionText.append(
"\n\n").toUtf8().constData();
2634 savedNodeClassFile << nodeClassSetReference.append(
"\n\n").toUtf8().constData();
2635 savedNodeClassFile << nodeClassHtmlFooter.append(
"\n\n").toUtf8().constData();
2637 savedNodeClassFile.close();
2645 string VuoEditor::externalizeVuoLinks(
string markdownText)
2647 QString filteredText(markdownText.c_str());
2648 QRegularExpression vuoNodeLink(
"\\[(.*)\\](\\(vuo-node://(.*)\\))", QRegularExpression::InvertedGreedinessOption);
2649 QRegularExpression vuoNodeSetLink(
"\\[(.*)\\](\\(vuo-nodeset://(.*)\\))", QRegularExpression::InvertedGreedinessOption);
2652 size_t startPos = 0;
2653 QRegularExpressionMatch match = vuoNodeLink.match(filteredText, startPos);
2654 while (match.hasMatch()) {
2655 QString nodeClassDisplayTitle = match.captured(1);
2656 QString nodeClassName = match.captured(3);
2660 QString mappedLink = QString(
"[")
2661 .append(nodeClassDisplayTitle)
2663 .append(nodeSetName)
2665 .append(nodeClassName)
2668 filteredText.replace(match.capturedStart(), match.capturedLength(), mappedLink);
2669 startPos = (match.capturedStart() + mappedLink.length());
2670 match = vuoNodeLink.match(filteredText, startPos);
2675 match = vuoNodeSetLink.match(filteredText, startPos);
2676 while (match.hasMatch()) {
2677 QString nodeSetDisplayTitle = match.captured(1);
2678 QString nodeSetName = match.captured(3);
2680 QString mappedLink = QString(
"[")
2681 .append(nodeSetDisplayTitle)
2683 .append(nodeSetName)
2684 .append(
"/index.html)");
2686 filteredText.replace(match.capturedStart(), match.capturedLength(), mappedLink);
2687 startPos = (match.capturedStart() + mappedLink.length());
2688 match = vuoNodeSetLink.match(filteredText, startPos);
2691 return filteredText.toUtf8().constData();
2700 QString filteredText(markdownText.c_str());
2701 QRegularExpression vuoNodeLink(
"\\[(.*)\\](\\(vuo-node://(.*)\\))", QRegularExpression::InvertedGreedinessOption);
2702 filteredText.replace(vuoNodeLink,
"`\\1`");
2704 QRegularExpression vuoNodeSetLink(
"\\[(.*)\\](\\(vuo-nodeset://(.*)\\))", QRegularExpression::InvertedGreedinessOption);
2705 filteredText.replace(vuoNodeSetLink,
"`\\1`");
2707 return filteredText.toUtf8().constData();
2719 string nodeSetName = url.host().toUtf8().constData();
2720 string nodeClassToHighlight =
"";
2722 QUrlQuery query(url.query());
2743 QClipboard *clipboard = QApplication::clipboard();
2744 const QMimeData *mimeData = clipboard->mimeData();
2746 if (!mimeData->hasFormat(
"text/plain") || mimeData->text().isNull())
2749 return mimeData->text();
2758 return this->nodeDocumentationPanelHeight;
2767 return this->nodeLibraryWidth;
2779 if (resourceDirectoryForNodeSet.find(nodeSetName) != resourceDirectoryForNodeSet.end())
2780 return resourceDirectoryForNodeSet[nodeSetName];
2791 resourceDirectoryForNodeSet[nodeSetName] = directory;
2799 return subcompositionRouter;
2805 void VuoEditor::initializeBuiltInDrivers()
2814 if (!imageFilterDriverAsString.empty())
2818 builtInDriverForProtocol[imageFilterProtocol] = imageFilterDriver;
2828 if (!imageGeneratorDriverAsString.empty())
2832 builtInDriverForProtocol[imageGeneratorProtocol] = imageGeneratorDriver;
2842 if (!imageTransitionDriverAsString.empty())
2846 builtInDriverForProtocol[imageTransitionProtocol] = imageTransitionDriver;
2860 dispatch_sync(builtInDriversQueue, ^{});
2862 map<VuoProtocol *, VuoCompilerDriver *>::iterator driver = builtInDriverForProtocol.find(protocol);
2863 if (driver != builtInDriverForProtocol.end())
2864 return driver->second;
2877 QPixmap emptyPixmap(32, 32);
2878 emptyPixmap.fill(Qt::transparent);
2879 QIcon indentIcon(emptyPixmap);
2882 QAction *windowHeading =
new QAction(
this);
2883 windowHeading->setText(tr(
"Window"));
2884 windowHeading->setEnabled(
false);
2885 m->addAction(windowHeading);
2889 QAction *templateAction =
new QAction(
this);
2890 templateAction->setText(tr(
"Image"));
2891 templateAction->setData(
"imageTemplate");
2892 templateAction->setIcon(indentIcon);
2894 m->addAction(templateAction);
2899 QAction *templateAction =
new QAction(
this);
2900 templateAction->setText(tr(
"Layers"));
2901 templateAction->setData(
"layersTemplate");
2902 templateAction->setIcon(indentIcon);
2904 m->addAction(templateAction);
2909 QAction *templateAction =
new QAction(
this);
2910 templateAction->setText(tr(
"Scene"));
2911 templateAction->setData(
"sceneTemplate");
2912 templateAction->setIcon(indentIcon);
2914 m->addAction(templateAction);
2918 QAction *protocolHeading =
new QAction(
this);
2919 protocolHeading->setText(tr(
"Protocol"));
2920 protocolHeading->setEnabled(
false);
2922 m->addAction(protocolHeading);
2927 QAction *protocolAction =
new QAction(
this);
2928 protocolAction->setText(tr(protocol->
getName().c_str()));
2929 protocolAction->setData(qVariantFromValue(
static_cast<void *
>(protocol)));
2930 protocolAction->setIcon(indentIcon);
2932 m->addAction(protocolAction);
2939 QAction *heading =
new QAction(
this);
2940 heading->setText(tr(
"Export"));
2941 heading->setEnabled(
false);
2942 m->addAction(heading);
2945 QAction *templateAction =
new QAction(
this);
2946 templateAction->setText(tr(
"Movie"));
2947 templateAction->setData(
"movie");
2948 templateAction->setIcon(indentIcon);
2950 m->addAction(templateAction);
2954 QAction *templateAction =
new QAction(
this);
2955 templateAction->setText(tr(
"Screen Saver"));
2956 templateAction->setData(
"screensaver");
2957 templateAction->setIcon(indentIcon);
2959 m->addAction(templateAction);
2963 QAction *heading =
new QAction(
this);
2964 heading->setText(tr(
"FFGL"));
2965 heading->setIcon(indentIcon);
2966 heading->setEnabled(
false);
2967 m->addAction(heading);
2970 QAction *templateAction =
new QAction(
this);
2971 templateAction->setText(
" " + tr(
"Source"));
2972 templateAction->setData(
"FFGLSource");
2973 templateAction->setIcon(indentIcon);
2975 m->addAction(templateAction);
2979 QAction *templateAction =
new QAction(
this);
2980 templateAction->setText(
" " + tr(
"Effect"));
2981 templateAction->setData(
"FFGLEffect");
2982 templateAction->setIcon(indentIcon);
2984 m->addAction(templateAction);
2988 QAction *templateAction =
new QAction(
this);
2989 templateAction->setText(
" " + tr(
"Blend Mode"));
2990 templateAction->setData(
"FFGLBlendMode");
2991 templateAction->setIcon(indentIcon);
2993 m->addAction(templateAction);
2998 QAction *heading =
new QAction(
this);
2999 heading->setText(tr(
"FxPlug"));
3000 heading->setIcon(indentIcon);
3001 heading->setEnabled(
false);
3002 m->addAction(heading);
3005 QAction *templateAction =
new QAction(
this);
3006 templateAction->setText(
" " + tr(
"Generator"));
3007 templateAction->setData(
"FxPlugGenerator");
3008 templateAction->setIcon(indentIcon);
3010 m->addAction(templateAction);
3014 QAction *templateAction =
new QAction(
this);
3015 templateAction->setText(
" " + tr(
"Effect"));
3016 templateAction->setData(
"FxPlugEffect");
3017 templateAction->setIcon(indentIcon);
3019 m->addAction(templateAction);
3023 QAction *templateAction =
new QAction(
this);
3024 templateAction->setText(
" " + tr(
"Transition"));
3025 templateAction->setData(
"FxPlugTransition");
3026 templateAction->setIcon(indentIcon);
3028 m->addAction(templateAction);
3040 QAction *action =
new QAction(m);
3041 action->setText(tr(
"Image Filter"));
3042 action->setData(
"GLSLImageFilter");
3043 connect(action, &QAction::triggered, [=] {
3044 closeUnmodifiedUntitledComposition();
3046 if (!closeWelcomeWindow())
3051 m->addAction(action);
3055 QAction *action =
new QAction(m);
3056 action->setText(tr(
"Image Generator"));
3057 action->setData(
"GLSLImageGenerator");
3058 connect(action, &QAction::triggered, [=] {
3059 closeUnmodifiedUntitledComposition();
3061 if (!closeWelcomeWindow())
3066 m->addAction(action);
3070 QAction *action =
new QAction(m);
3071 action->setText(tr(
"Image Transition"));
3072 action->setData(
"GLSLImageTransition");
3073 connect(action, &QAction::triggered, [=] {
3074 closeUnmodifiedUntitledComposition();
3076 if (!closeWelcomeWindow())
3081 m->addAction(action);
3091 return documentationQueue;
3100 return darkInterfaceAction->isChecked();
3112 return this->canvasOpacity;
3118 void VuoEditor::showGridLinesToggled(
bool show)
3124 type = VuoRendererComposition::LineGrid;
3127 type = VuoRendererComposition::NoGrid;
3129 settings->setValue(gridTypeSettingsKey, type);
3138 void VuoEditor::showGridPointsToggled(
bool show)
3144 type = VuoRendererComposition::PointGrid;
3147 type = VuoRendererComposition::NoGrid;
3149 settings->setValue(gridTypeSettingsKey, type);
3158 void VuoEditor::updateSnapToGrid(
bool snap)
3160 settings->setValue(snapToGridSettingsKey, snap);
3168 void VuoEditor::updateColor(
bool isDark)
3170 settings->setValue(darkInterfaceSettingsKey, isDark);
3179 void VuoEditor::updateCanvasOpacity(QAction *setOpacityAction)
3181 updateCanvasOpacityTo(setOpacityAction->data().toInt());
3189 void VuoEditor::updateCanvasOpacityTo(
int opacity)
3191 settings->setValue(canvasOpacitySettingsKey, opacity);
3192 canvasOpacity = opacity;
3204 VuoEditor::documentationGenerationDirectory = dir;
3214 map<QString, QString> modelExampleCompositionsAndNodeSets;
3215 modelExampleCompositionsAndNodeSets[
"LaserDiscoball.vuo"] =
"vuo.audio";
3216 modelExampleCompositionsAndNodeSets[
"Tschuri.vuo"] =
"vuo.image";
3217 modelExampleCompositionsAndNodeSets[
"SlitscanMixingInk.vuo"] =
"vuo.layer";
3218 modelExampleCompositionsAndNodeSets[
"DrawInSpace.vuo"] =
"vuo.scene";
3219 return modelExampleCompositionsAndNodeSets;
3228 map<QString, QString> modelExampleCompositionsAndNodeSets;
3232 modelExampleCompositionsAndNodeSets[
"PixellateImageRadially.vuo"] =
"vuo.image";
3237 modelExampleCompositionsAndNodeSets[
"GenerateCheckerboardImage.vuo"] =
"vuo.image";
3238 modelExampleCompositionsAndNodeSets[
"MakeDriftingClouds.vuo"] =
"vuo.image";
3239 modelExampleCompositionsAndNodeSets[
"MakeOvalPatterns.vuo"] =
"vuo.image";
3240 modelExampleCompositionsAndNodeSets[
"RippleImageGradients.vuo"] =
"vuo.image";
3241 modelExampleCompositionsAndNodeSets[
"SpinKaleidoscope.vuo"] =
"vuo.event";
3245 modelExampleCompositionsAndNodeSets[
"BlendImages.vuo"] =
"vuo.image";
3247 return modelExampleCompositionsAndNodeSets;
3257 .append(nodeSetName)
3259 .append(compositionName);
3260 return compositionURL;
3269 return getStoredUserName_Pro();
3271 return getenv(
"USER");
3281 return getStoredUserProfileURL_Pro();
3293 settings->setValue(subcompositionPrefixSettingsKey, prefix);
3294 subcompositionPrefix = prefix;