44 #include <Carbon/Carbon.h>
45 #include <IOKit/IOKitLib.h>
47 const QString VuoEditor::recentFileListSettingsKey =
"recentFileList";
48 const QString VuoEditor::subcompositionPrefixSettingsKey =
"subcompositionPrefix";
49 const QString VuoEditor::nodeLibraryDisplayModeSettingsKey =
"nodeLibraryDisplayMode";
50 const QString VuoEditor::nodeLibraryVisibilityStateSettingsKey =
"nodeLibraryVisible";
51 const QString VuoEditor::nodeLibraryDockingStateSettingsKey =
"nodeLibraryDocked";
52 const QString VuoEditor::nodeLibraryFloatingPositionSettingsKey =
"nodeLibraryFloatingPosition";
53 const QString VuoEditor::nodeLibraryWidthSettingsKey =
"nodeLibraryWidth";
54 const QString VuoEditor::nodeLibraryHeightSettingsKey =
"nodeLibraryHeight";
55 const QString VuoEditor::nodeDocumentationPanelHeightSettingsKey =
"nodeDocumentationPanelHeight";
56 const QString VuoEditor::shaderDocumentationVisibilitySettingsKey =
"shaderDocumentationVisible";
57 const QString VuoEditor::gridTypeSettingsKey =
"canvasGridType";
58 const QString VuoEditor::gridOpacitySettingsKey =
"canvasGridOpacity";
59 const qreal VuoEditor::defaultGridOpacity = 1;
60 const QString VuoEditor::snapToGridSettingsKey =
"canvasGridSnap";
61 const QString VuoEditor::darkInterfaceSettingsKey =
"darkInterface";
62 const QString VuoEditor::canvasOpacitySettingsKey =
"canvasOpacity";
63 const QString VuoEditor::movieExportWidthSettingsKey =
"movieExportWidth";
64 const QString VuoEditor::movieExportHeightSettingsKey =
"movieExportHeight";
65 const QString VuoEditor::movieExportTimeSettingsKey =
"movieExportTime";
66 const QString VuoEditor::movieExportDurationSettingsKey =
"movieExportDuration";
67 const QString VuoEditor::movieExportFramerateSettingsKey =
"movieExportFramerate";
68 const QString VuoEditor::movieExportSpatialSupersampleSettingsKey =
"movieExportSpatialSupersample";
69 const QString VuoEditor::movieExportTemporalSupersampleSettingsKey =
"movieExportTemporalSupersample";
70 const QString VuoEditor::movieExportShutterAngleSettingsKey =
"movieExportShutterAngle";
71 const QString VuoEditor::movieExportImageFormatSettingsKey =
"movieExportImageFormat";
72 const QString VuoEditor::movieExportQualitySettingsKey =
"movieExportQuality";
83 string VuoEditor::documentationGenerationDirectory =
"";
89 : QApplication(argc,argv)
92 QCoreApplication::setOrganizationName(
"Vuo");
93 QCoreApplication::setOrganizationDomain(
"vuo.org");
94 QCoreApplication::setApplicationName(
"Editor");
95 settings =
new QSettings();
105 setWheelScrollLines(2);
108 uiInitialized =
false;
111 setQuitOnLastWindowClosed(
false);
113 ownedNodeLibrary = NULL;
114 documentationQueue = dispatch_queue_create(
"org.vuo.editor.documentation", NULL);
115 userSubscriptionLevel = VuoEditor::CommunityUser;
116 reportAbsenceOfUpdates =
false;
117 networkManager = NULL;
119 moduleManager =
nullptr;
124 const bool defaultNodeLibraryVisibilityState =
true;
125 const bool defaultNodeLibraryDockingState =
true;
126 const int defaultNodeLibraryDocumentationPanelHeight = 280;
128 qApp->setAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents,
false);
129 qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents,
false);
131 qApp->setAttribute(Qt::AA_UseHighDpiPixmaps);
136 settings->setValue(
"ApplePersistenceIgnoreStateQuietly",
true);
138 QStringList storedRecentFileList = settings->value(recentFileListSettingsKey).toStringList();
148 subcompositionPrefix = settings->contains(subcompositionPrefixSettingsKey)?
149 settings->value(subcompositionPrefixSettingsKey).toString() :
152 canvasOpacity = settings->contains(canvasOpacitySettingsKey)?
153 settings->value(canvasOpacitySettingsKey).toInt() :
156 nodeLibraryDisplayMode = settings->contains(nodeLibraryDisplayModeSettingsKey)?
158 defaultNodeLibraryDisplayMode;
160 nodeLibraryCurrentlyVisible = settings->contains(nodeLibraryVisibilityStateSettingsKey)?
161 settings->value(nodeLibraryVisibilityStateSettingsKey).toBool() :
162 defaultNodeLibraryVisibilityState;
164 nodeLibraryCurrentlyDocked = settings->contains(nodeLibraryDockingStateSettingsKey)?
165 settings->value(nodeLibraryDockingStateSettingsKey).toBool() :
166 defaultNodeLibraryDockingState;
168 if (settings->contains(nodeLibraryFloatingPositionSettingsKey))
170 settingsContainedNodeLibraryFloatingPosition =
true;
171 nodeLibraryFloatingPosition = settings->value(nodeLibraryFloatingPositionSettingsKey).toPoint();
175 settingsContainedNodeLibraryFloatingPosition =
false;
176 nodeLibraryFloatingPosition = QPoint();
179 if (settings->contains(nodeLibraryWidthSettingsKey))
180 nodeLibraryWidth = settings->value(nodeLibraryWidthSettingsKey).toInt();
182 nodeLibraryWidth = -1;
184 if (settings->contains(nodeLibraryHeightSettingsKey))
185 nodeLibraryHeight = settings->value(nodeLibraryHeightSettingsKey).toInt();
187 nodeLibraryHeight = -1;
189 if (settings->contains(nodeDocumentationPanelHeightSettingsKey))
190 nodeDocumentationPanelHeight = settings->value(nodeDocumentationPanelHeightSettingsKey).toInt();
192 nodeDocumentationPanelHeight = defaultNodeLibraryDocumentationPanelHeight;
194 previousVisibleNodeLibraryStateWasDocked = nodeLibraryCurrentlyDocked;
195 currentFloatingNodeLibrary = NULL;
197 shaderDocumentationVisible = settings->contains(shaderDocumentationVisibilitySettingsKey)?
198 settings->value(shaderDocumentationVisibilitySettingsKey).toBool() :
201 applyStoredMovieExportSettings();
215 builtInDriversQueue = dispatch_queue_create(
"org.vuo.editor.drivers", NULL);
216 dispatch_async(builtInDriversQueue, ^{
217 initializeBuiltInDrivers();
223 if (!documentationGenerationDirectory.empty())
225 dispatch_async(dispatch_get_main_queue(), ^{
226 generateAllNodeSetHtmlDocumentation(documentationGenerationDirectory);
233 QDesktopServices::setUrlHandler(
vuoHelpBookScheme,
this,
"openHelpBookPageFromUrl");
245 menuBar =
new QMenuBar();
247 menuFile =
new QMenu(menuBar);
248 menuFile->setSeparatorsCollapsible(
false);
249 menuFile->setTitle(tr(
"&File"));
251 menuFile->addAction(tr(
"&New Composition"),
this, SLOT(
newComposition()), QKeySequence(
"Ctrl+N"));
253 menuNewCompositionWithTemplate =
new QMenu(tr(
"New Composition from Template"));
254 menuNewCompositionWithTemplate->setSeparatorsCollapsible(
false);
256 menuFile->addMenu(menuNewCompositionWithTemplate);
258 QMenu *menuNewShader =
new QMenu(tr(
"New Shader"));
259 menuNewShader->setSeparatorsCollapsible(
false);
261 menuFile->addMenu(menuNewShader);
263 menuFile->addSeparator();
264 menuFile->addAction(tr(
"&Open…"),
this, SLOT(
openFile()), QKeySequence(
"Ctrl+O"));
269 menuFile->addMenu(menuOpenRecent);
275 menuFile->addMenu(menuOpenExample);
279 menuFile->addAction(tr(
"Quit"),
this, SLOT(
quitCleanly()), QKeySequence(
"Ctrl+Q"));
282 menuFile->addAction(tr(
"About Vuo…"),
this, SLOT(
about()));
288 QAction *aboutAction =
new QAction(NULL);
289 aboutAction->setText(tr(
"About Vuo…"));
291 aboutAction->setMenuRole(QAction::NoRole);
292 menuFile->addSeparator();
293 menuFile->addAction(aboutAction);
295 QAction *quitAction =
new QAction(NULL);
296 quitAction->setText(tr(
"&Quit Vuo"));
297 quitAction->setShortcut(QKeySequence(
"Ctrl+Q"));
299 quitAction->setMenuRole(QAction::NoRole);
300 menuFile->addSeparator();
301 menuFile->addAction(quitAction);
303 menuBar->addAction(menuFile->menuAction());
306 enableMenuItems(menuFile, canCloseWelcomeWindow());
311 menuView =
new QMenu(menuBar);
312 menuView->setSeparatorsCollapsible(
false);
313 menuView->setTitle(tr(
"&View"));
316 showNodeLibraryAction =
new QAction(NULL);
317 showNodeLibraryAction->setText(tr(
"Show Node &Library"));
318 showNodeLibraryAction->setShortcut(QKeySequence(
"Ctrl+Return"));
320 menuView->addAction(showNodeLibraryAction);
329 bool snapToGrid = (settings->contains(snapToGridSettingsKey)?
330 settings->value(snapToGridSettingsKey).toBool() :
334 connect(
snapToGridAction, &QAction::toggled,
this, &VuoEditor::updateSnapToGrid);
338 if (settings->contains(gridOpacitySettingsKey))
339 gridOpacity = settings->value(gridOpacitySettingsKey).toInt();
342 gridOpacity = VuoEditor::defaultGridOpacity;
343 settings->setValue(gridOpacitySettingsKey, gridOpacity);
348 int gridType = (settings->contains(gridTypeSettingsKey)
349 ? settings->value(gridTypeSettingsKey).toInt()
350 : VuoRendererComposition::LineGrid);
393 if (action->data().toInt() == canvasOpacity)
395 action->setChecked(
true);
396 action->setEnabled(
false);
406 menuBar->addAction(menuView->menuAction());
411 menuWindow =
new QMenu(menuBar);
412 menuWindow->setSeparatorsCollapsible(
false);
413 menuWindow->setTitle(tr(
"&Window"));
417 connect(menuWindow, &QMenu::aboutToShow,
this, &VuoEditor::updateUI);
419 menuBar->addAction(menuWindow->menuAction());
423 menuHelp =
new QMenu(menuBar);
424 menuHelp->setSeparatorsCollapsible(
false);
425 menuHelp->setTitle(tr(
"&Help"));
432 connect(menuHelp, &QMenu::aboutToShow, [
this] {
populateHelpMenu(menuHelp); });
434 menuBar->addAction(menuHelp->menuAction());
441 dockContextMenu =
new QMenu();
442 dockContextMenu->setSeparatorsCollapsible(
false);
443 dockContextMenu->setAsDockMenu();
445 connect(dockContextMenu, &QMenu::aboutToShow,
this, &VuoEditor::updateUI);
449 initializeTopLevelNodeLibrary(compiler,
450 nodeLibraryDisplayMode,
451 settingsContainedNodeLibraryFloatingPosition,
452 nodeLibraryFloatingPosition,
455 if (!nodeLibraryCurrentlyDocked)
456 designateNewFloatingNodeLibrary(ownedNodeLibrary);
464 conformToGlobalNodeLibraryVisibility(
465 getGlobalNodeLibraryStateForAttributes(nodeLibraryCurrentlyVisible, nodeLibraryCurrentlyDocked),
466 currentFloatingNodeLibrary);
473 connect(ownedNodeLibrary, &VuoNodeLibrary::visibilityChanged,
this, &VuoEditor::updateUI);
474 enableGlobalStateConformanceToLibrary(ownedNodeLibrary);
478 QApplication::processEvents();
479 if (queuedCompositionsToOpen.empty())
488 if (d.history().isEmpty())
491 uiInitialized =
true;
501 dispatch_sync(builtInDriversQueue, ^{});
503 menuBar->deleteLater();
504 delete subcompositionRouter;
511 QString VuoEditor::qtFindTranslation(
const QLocale &locale,
const QString &filename,
const QString &prefix,
const QString &directory,
const QString &suffix)
514 if (QFileInfo(filename).isRelative()) {
516 if (!path.isEmpty() && !path.endsWith(QLatin1Char(
'/')))
517 path += QLatin1Char(
'/');
522 QStringList fuzzyLocales;
526 QStringList languages = locale.uiLanguages();
527 for (
int i = languages.size()-1; i >= 0; --i) {
528 QString lang = languages.at(i);
529 QString lowerLang = lang.toLower();
530 if (lang != lowerLang)
531 languages.insert(i+1, lowerLang);
535 foreach (QString localeName, languages) {
536 localeName.replace(QLatin1Char(
'-'), QLatin1Char(
'_'));
538 realname = path + filename + prefix + localeName + (suffix.isNull() ? QLatin1String(
".qm") : suffix);
539 fi.setFile(realname);
540 if (fi.isReadable() && fi.isFile())
543 realname = path + filename + prefix + localeName;
544 fi.setFile(realname);
545 if (fi.isReadable() && fi.isFile())
548 fuzzyLocales.append(localeName);
552 foreach (QString localeName, fuzzyLocales) {
554 int rightmost = localeName.lastIndexOf(QLatin1Char(
'_'));
558 localeName.truncate(rightmost);
560 realname = path + filename + prefix + localeName + (suffix.isNull() ? QLatin1String(
".qm") : suffix);
561 fi.setFile(realname);
562 if (fi.isReadable() && fi.isFile())
565 realname = path + filename + prefix + localeName;
566 fi.setFile(realname);
567 if (fi.isReadable() && fi.isFile())
572 if (!suffix.isNull()) {
573 realname = path + filename + suffix;
574 fi.setFile(realname);
575 if (fi.isReadable() && fi.isFile())
579 realname = path + filename + prefix;
580 fi.setFile(realname);
581 if (fi.isReadable() && fi.isFile())
584 realname = path + filename;
585 fi.setFile(realname);
586 if (fi.isReadable() && fi.isFile())
595 void VuoEditor::loadTranslations()
597 if (settings->value(
"translation/enable",
true).toBool())
599 QLocale locale = QLocale::system();
603 VUserLog(
" NL codeset : %s", nl_langinfo(CODESET));
604 VUserLog(
" LANG : %s", getenv(
"LANG"));
605 VUserLog(
" LC_ALL : %s", getenv(
"LC_ALL"));
608 CFStringRef appleLocale = (CFStringRef)CFPreferencesCopyAppValue(CFSTR(
"AppleLocale"), kCFPreferencesCurrentApplication);
610 CFRelease(appleLocale);
612 CFLocaleRef localeCF = CFLocaleCopyCurrent();
616 auto logCFArray = ^(
const char *label, CFArrayRef array){
617 CFIndex count = CFArrayGetCount(array);
619 for (CFIndex i = 0; i < count; ++i)
625 logCFArray(
"UI languages (pref)", (CFArrayRef)CFPreferencesCopyAppValue(CFSTR(
"AppleLanguages"), kCFPreferencesCurrentApplication));
627 logCFArray(
"UI languages (CF) ", CFLocaleCopyPreferredLanguages());
630 VUserLog(
" ISO : %s", locale.name().toUtf8().data());
631 VUserLog(
" BCP47 : %s", locale.bcp47Name().toUtf8().data());
632 VUserLog(
" country : %s (%s)", QLocale::countryToString(locale.country()).toUtf8().data(), locale.nativeCountryName().toUtf8().data());
633 VUserLog(
" language : %s (%s)", QLocale::languageToString(locale.language()).toUtf8().data(), locale.nativeLanguageName().toUtf8().data());
634 VUserLog(
" UI languages : %s", locale.uiLanguages().join(
',').toUtf8().data());
635 VUserLog(
" script : %s", QLocale::scriptToString(locale.script()).toUtf8().data());
639 QList<QString> contexts;
640 contexts <<
"qtbase" <<
"vuo";
642 QString suffix(
".qm");
643 foreach (QString context, contexts)
645 VDebugLog(
"Context %s:", context.toUtf8().data());
647 QTranslator *t =
new QTranslator();
648 QString translationFilename = qtFindTranslation(locale, context, prefix, translationsPath, suffix);
649 if (translationFilename.isEmpty())
651 VDebugLog(
" No translation file found.");
655 bool loaded = t->load(locale, context, prefix, translationsPath, suffix);
656 VDebugLog(
" Loading '%s': %s", translationFilename.toUtf8().data(), loaded ?
"ok" :
"error");
660 bool installed = installTranslator(t);
661 VDebugLog(
" Installing '%s': %s", translationFilename.toUtf8().data(), installed ?
"ok" :
"error");
665 VDebugLog(
"Disabling translations since preference `translation.enable` is false.");
673 vector<QString> queuedCompositionsToOpenSnapshot(queuedCompositionsToOpen);
674 queuedCompositionsToOpen.clear();
679 foreach (QString queuedUrl, queuedCompositionsToOpenSnapshot)
690 enableMenuItems(menuFile, canCloseWelcomeWindow());
692 if (!queuedCompositionsToOpen.empty())
693 closeWelcomeWindow();
702 void VuoEditor::enableMenuItems(QMenu *menu,
bool enable)
707 foreach (QAction *action, menu->actions())
716 enableMenuItems(action->menu(), enable);
720 bool isQuitOrHelp = action->text().contains(
"Quit") || action->text().contains(
"About");
721 bool isTemplateHeader = (menu == menuNewCompositionWithTemplate) &&
722 action->data().value<QString>().isEmpty() &&
723 !action->data().value<
void *>();
724 if (!(isQuitOrHelp || isTemplateHeader))
725 action->setEnabled(enable);
751 if (ownedNodeLibrary)
752 disableGlobalStateConformanceToLibrary(ownedNodeLibrary);
759 if (windowsRemainingAfterQuitRequested.empty())
763 windowsRemainingAfterQuitRequested[0]->close();
774 if (windowsRemainingAfterQuitRequested.empty())
779 foreach (QMainWindow *window, windowsRemainingAfterQuitRequested)
782 if (compositionWindow)
786 if (ownedNodeLibrary)
787 enableGlobalStateConformanceToLibrary(ownedNodeLibrary);
789 windowsRemainingAfterQuitRequested.clear();
800 if (windowsRemainingAfterQuitRequested.empty())
804 windowsRemainingAfterQuitRequested.removeOne(window);
806 if (windowsRemainingAfterQuitRequested.empty())
810 windowsRemainingAfterQuitRequested[0]->close();
816 void VuoEditor::reallyQuit()
829 QApplication::quit();
837 return nodeLibraryCurrentlyDocked;
845 void VuoEditor::enableGlobalStateConformanceToLibrary(
VuoNodeLibrary *library)
848 connect(library, &VuoNodeLibrary::topLevelChanged,
this, &VuoEditor::updateGlobalNodeLibraryDockedState);
860 void VuoEditor::disableGlobalStateConformanceToLibrary(
VuoNodeLibrary *library)
863 disconnect(library, &VuoNodeLibrary::topLevelChanged,
this, &VuoEditor::updateGlobalNodeLibraryDockedState);
873 void VuoEditor::initializeTopLevelNodeLibrary(
VuoCompiler *nodeLibraryCompiler,
875 bool setFloatingPosition,
876 QPoint floatingPosition,
877 int nodeLibraryWidth,
878 int nodeLibraryHeight)
880 ownedNodeLibrary =
new VuoNodeLibrary(nodeLibraryCompiler, NULL, nodeLibraryDisplayMode);
881 ownedNodeLibrary->setObjectName(
"Top-level node library");
883 ownedNodeLibrary->setFloating(
true);
885 if (setFloatingPosition)
886 ownedNodeLibrary->move(floatingPosition);
888 if (nodeLibraryHeight >= 0)
889 ownedNodeLibrary->resize(ownedNodeLibrary->rect().width(), nodeLibraryHeight);
900 aboutBox->showNormal();
902 aboutBox->activateWindow();
922 m->addAction(tr(
"Minimize"), currentWindow, &QMainWindow::showMinimized, QKeySequence(
"Ctrl+M"));
923 m->addAction(tr(
"Zoom"), currentWindow, &QMainWindow::showMaximized);
929 populateWindowMenu_Pro(m);
933 if (! openWindows.empty())
937 for (QMainWindow *openWindow : openWindows)
940 m->addAction(raiseDocumentAction);
943 raiseDocumentAction->setChecked(openWindow == currentWindow);
944 else if (openWindow->isMinimized())
945 raiseDocumentAction->setChecked(
false);
958 QAction *vuoManualAction =
new QAction(m);
959 vuoManualAction->setText(tr(
"Vuo Manual"));
962 m->addAction(vuoManualAction);
965 QAction *vuoManualPDFAction =
new QAction(m);
966 vuoManualPDFAction->setText(tr(
"Vuo Manual (PDF)"));
969 m->addAction(vuoManualPDFAction);
972 QAction *videoTutorialsAction =
new QAction(m);
973 videoTutorialsAction->setText(tr(
"Video Tutorials"));
976 m->addAction(videoTutorialsAction);
981 QAction *searchVuoOrgAction =
new QAction(m);
982 searchVuoOrgAction->setText(tr(
"Search vuo.org"));
983 searchVuoOrgAction->setData(QUrl(
"https://vuo.org/search"));
985 m->addAction(searchVuoOrgAction);
990 QAction *communityActivityAction =
new QAction(m);
991 communityActivityAction->setText(tr(
"View Community Activity"));
992 communityActivityAction->setData(QUrl(
"https://vuo.org/community"));
994 m->addAction(communityActivityAction);
997 QAction *shareCompositionAction =
new QAction(m);
998 shareCompositionAction->setText(tr(
"Share a Composition"));
999 shareCompositionAction->setData(QUrl(
"https://vuo.org/node/add/vuo-composition"));
1001 m->addAction(shareCompositionAction);
1004 QAction *startDiscussionAction =
new QAction(m);
1005 startDiscussionAction->setText(tr(
"Start a Discussion"));
1006 startDiscussionAction->setData(QUrl(
"https://vuo.org/node/add/discussion"));
1008 m->addAction(startDiscussionAction);
1011 QAction *reportBugAction =
new QAction(m);
1012 reportBugAction->setText(tr(
"Report a Bug"));
1013 reportBugAction->setData(QUrl(
"https://vuo.org/bug"));
1015 m->addAction(reportBugAction);
1018 QAction *requestFeatureAction =
new QAction(m);
1019 requestFeatureAction->setText(tr(
"Request a Feature"));
1020 requestFeatureAction->setData(QUrl(
"https://vuo.org/feature-request"));
1022 m->addAction(requestFeatureAction);
1027 QAction *improveVuoAction =
new QAction(m);
1028 improveVuoAction->setText(tr(
"Help Us Improve Vuo"));
1029 improveVuoAction->setData(QUrl(
"https://vuo.org/community-edition"));
1031 m->addAction(improveVuoAction);
1034 QAction *contactTeamVuoAction =
new QAction(m);
1035 contactTeamVuoAction->setText(tr(
"Contact Team Vuo"));
1036 contactTeamVuoAction->setData(QUrl(
"https://vuo.org/contact"));
1038 m->addAction(contactTeamVuoAction);
1041 VuoEditor::populateHelpMenu_Pro(m);
1050 OSStatus ret = AHGotoPage(CFSTR(
"org.vuo.Editor.help"), NULL, NULL);
1054 VUserLog(
"Error: Couldn't open Vuo Manual in HelpViewer.app: %s", description);
1064 void VuoEditor::openHelpBookPageFromUrl(
const QUrl &url)
1066 CFStringRef relativePath = CFStringCreateWithCString(NULL, url.url(QUrl::RemoveScheme).toUtf8().constData(), kCFStringEncodingUTF8);
1067 OSStatus ret = AHGotoPage(CFSTR(
"org.vuo.Editor.help"), relativePath, NULL);
1071 VUserLog(
"Error: Couldn't open Vuo Manual in HelpViewer.app: %s", description);
1074 CFRelease(relativePath);
1088 VuoEditorWindow *w = createEditorWindow(filename, filename, compositionAsString, activeProtocol);
1101 VuoEditorWindow * VuoEditor::createEditorWindow(QString documentIdentifier, QString filename,
const string &compositionAsString,
1102 VuoProtocol *activeProtocol,
string nodeClassToHighlight)
1112 compositionAsString,
1113 nodeLibraryDisplayMode,
1114 getGlobalNodeLibraryStateForAttributes(nodeLibraryCurrentlyVisible, nodeLibraryCurrentlyDocked),
1115 currentFloatingNodeLibrary,
1117 nodeClassToHighlight);
1125 documentIdentifierAssigned[filename.isEmpty() ? documentIdentifier : filename] = w;
1127 const int defaultWindowXOffset = 40;
1128 const int defaultWindowYOffset = 40;
1135 if (ownedNodeLibrary && nodeLibraryCurrentlyVisible && !nodeLibraryCurrentlyDocked)
1138 int initialWindowXOffset = defaultWindowXOffset;
1139 int initialWindowYOffset = defaultWindowYOffset;
1141 int nodeLibraryLeftPos = ownedNodeLibrary->mapToGlobal(ownedNodeLibrary->rect().topLeft()).x();
1142 int nodeLibraryRightPos = ownedNodeLibrary->mapToGlobal(ownedNodeLibrary->rect().topRight()).x();
1143 int nodeLibraryCenterPos = 0.5*(nodeLibraryLeftPos + nodeLibraryRightPos);
1145 int availableSpaceLeftBoundary = QApplication::desktop()->availableGeometry(w).left();
1146 int availableSpaceRightBoundary = QApplication::desktop()->availableGeometry(w).right();
1148 bool nodeLibraryCloserToLeft = (nodeLibraryCenterPos - availableSpaceLeftBoundary) <
1149 (availableSpaceRightBoundary - nodeLibraryCenterPos);
1151 const int horizontalBuffer = 10;
1152 bool spaceForWindowToRightOfNodeLibrary = (availableSpaceRightBoundary - nodeLibraryRightPos) >=
1153 (w->geometry().width() + horizontalBuffer);
1157 if ((nodeLibraryCloserToLeft && spaceForWindowToRightOfNodeLibrary))
1158 initialWindowXOffset = nodeLibraryRightPos + horizontalBuffer;
1165 const int titleBarHeight = 16;
1166 int nodeLibraryTopPos = ownedNodeLibrary->mapToGlobal(ownedNodeLibrary->rect().topLeft()).y() - titleBarHeight;
1168 if (nodeLibraryTopPos < defaultWindowYOffset)
1169 initialWindowYOffset = nodeLibraryTopPos;
1171 w->move(initialWindowXOffset, initialWindowYOffset);
1178 yScreenSpaceShortage = qMax(0, (w->geometry().bottom()) - QApplication::desktop()->availableGeometry(w).bottom() + 5);
1184 w->move(activeWindow->pos() + QPoint(defaultWindowXOffset, defaultWindowYOffset));
1190 w->resize(w->width(), w->height()-yScreenSpaceShortage);
1200 createEditorWindow_Pro(w);
1221 QString summary = tr(
"This composition contains nodes that aren't installed.");
1223 QString details = tr(
"<p>If you save the composition while it contains these nodes, some information will be lost.</p>"
1224 "<p>Try searching for these nodes in the <a href=\"%1\">Node Gallery</a>. "
1225 "Or if you don't need them, just delete them from the composition.</p>")
1226 .arg(
"https://vuo.org/nodes")
1244 connect(window, &QMainWindow::destroyed,
this, &VuoEditor::updateUI);
1260 dispatch_async(dispatch_get_main_queue(), ^{
1269 if (!closeWelcomeWindow())
1272 QString identifier = assignUntitledDocumentIdentifier();
1273 VUserLog(
"%s: New empty composition", identifier.toUtf8().data());
1274 createEditorWindow(identifier,
"",
"", NULL);
1280 if (!closeWelcomeWindow())
1284 QString identifier = assignUntitledDocumentIdentifier();
1285 VUserLog(
"%s: New empty composition", identifier.toUtf8().data());
1286 createEditorWindow(identifier,
"",
"", NULL);
1295 QString identifier = assignUntitledDocumentIdentifier();
1296 VUserLog(
"%s: New Composition with content", identifier.toUtf8().data());
1297 return createEditorWindow(identifier, QString::fromStdString(compositionDir), content, NULL);
1306 QAction *sender = (QAction *)QObject::sender();
1309 closeUnmodifiedUntitledComposition();
1311 if (!closeWelcomeWindow())
1315 QString identifier = assignUntitledDocumentIdentifier();
1316 VUserLog(
"%s: New Composition with %s protocol", identifier.toUtf8().data(), selectedProtocol->
getName().c_str());
1317 VuoEditorWindow *w = createEditorWindow(identifier,
false, selectedProtocol);
1321 string nodeLibraryFilterText = getFilterTextForTemplate(selectedProtocol->
getId());
1322 if (!nodeLibraryFilterText.empty())
1336 QAction *sender = (QAction *)QObject::sender();
1337 QString selectedTemplate =
static_cast<QString
>(sender->data().value<QString>());
1340 closeUnmodifiedUntitledComposition();
1342 if (!closeWelcomeWindow())
1347 QString identifier = assignUntitledDocumentIdentifier();
1348 VUserLog(
"%s: New Composition with %s template", identifier.toUtf8().data(), selectedTemplate.toUtf8().data());
1349 VuoEditorWindow *w = createEditorWindow(identifier,
"", compositionAsString, NULL);
1353 string nodeLibraryFilterText = getFilterTextForTemplate(selectedTemplate.toUtf8().constData());
1354 if (!nodeLibraryFilterText.empty())
1369 string VuoEditor::getFilterTextForTemplate(
string templateID)
1371 map<string, string> filterTextForTemplate;
1379 filterTextForTemplate[
"imageTemplate"] =
"vuo.image";
1380 filterTextForTemplate[
"layersTemplate"] =
"vuo.layer shape";
1381 filterTextForTemplate[
"sceneTemplate"] =
"vuo.scene shape";
1395 map<string, string>::iterator i = filterTextForTemplate.find(templateID);
1396 if (i != filterTextForTemplate.end())
1405 QString VuoEditor::assignUntitledDocumentIdentifier(
void)
1408 int documentIdentifierInstanceNum = 1;
1410 while(documentIdentifierAssigned[uniqueDocumentIdentifier])
1412 std::ostringstream oss;
1413 oss <<
" " << ++documentIdentifierInstanceNum;
1417 return uniqueDocumentIdentifier;
1425 QFileDialog d(NULL,
"",
"",
"Vuo Composition (*.vuo);;Fragment Shader (*.fs)");
1426 d.setFileMode(QFileDialog::ExistingFiles);
1433 QStringList fileNames;
1434 if (d.exec() == QDialog::Accepted)
1435 fileNames = d.selectedFiles();
1437 foreach (QString fileName, fileNames)
1446 closeUnmodifiedUntitledComposition();
1448 if (!closeWelcomeWindow())
1450 QString fileURL = QString(
"file://").append(filename);
1451 queuedCompositionsToOpen.push_back(QString(
"file://").append(filename));
1465 VuoErrorDialog::show(NULL, tr(
"You do not have permission to open the document \"%1\".").arg(QFileInfo(filename).fileName()),
"");
1469 string dir, file, ext;
1473 QMainWindow *window =
nullptr;
1476 VUserLog(
"%s.%s: Open", file.c_str(), ext.c_str());
1477 window = createEditorWindow(filename,
true);
1481 dynamic_cast<VuoEditorWindow *
>(window)->setIncludeInRecentFileMenu(addToRecentFileMenu);
1491 dynamic_cast<VuoCodeWindow *
>(window)->setIncludeInRecentFileMenu(addToRecentFileMenu);
1502 if (addToRecentFileMenu)
1517 VUserLog(
"%s: Open", filename.toUtf8().data());
1519 string filenameStr = filename.toStdString();
1522 QString compositionPath = QString::fromStdString(workingDir +
"/" + filenameStr);
1525 closeUnmodifiedUntitledComposition();
1527 if (!closeWelcomeWindow())
1530 createEditorWindow(compositionPath, compositionPath, compositionContents, NULL, nodeClassToHighlight);
1534 .append(nodeSet->
getName().c_str())
1546 void VuoEditor::closeUnmodifiedUntitledComposition()
1568 if ((ownedNodeLibrary->isHidden()))
1572 designateNewFloatingNodeLibrary(ownedNodeLibrary);
1573 updateGlobalNodeLibraryState(
true,
false);
1586 if ((visibility == VuoNodeLibrary::nodeLibraryHidden) ||
1587 (visibility == VuoNodeLibrary::nodeLibraryDocked))
1590 ownedNodeLibrary->setVisible(
false);
1593 else if (visibility == VuoNodeLibrary::nodeLibraryFloating)
1598 if (ownedNodeLibrary == floater)
1600 if (! ownedNodeLibrary->isFloating())
1601 ownedNodeLibrary->setFloating(
true);
1605 ownedNodeLibrary->setFocus();
1616 void VuoEditor::updateUI()
1622 QMainWindow *frontWindow = (openWindows.empty() ? nullptr : openWindows.front());
1625 menuWindow->clear();
1630 dockContextMenu->clear();
1637 action->setEnabled(!action->isChecked());
1647 case QEvent::FileOpen:
1648 openUrl(
static_cast<QFileOpenEvent *
>(e)->url().toString());
1651 case QEvent::ApplicationActivate:
1655 case QEvent::ApplicationDeactivate:
1666 return QApplication::event(e);
1676 QAction *sender = (QAction *)QObject::sender();
1677 QDesktopServices::openUrl(sender->data().toUrl());
1686 QAction *sender = (QAction *)QObject::sender();
1687 QString filePath = sender->data().value<QString>();
1689 if (! filePath.startsWith(
"file://"))
1690 filePath = QString(
"file://").append(filePath);
1701 QAction *sender = (QAction *)QObject::sender();
1702 QString filePath = sender->data().value<QString>();
1703 moveFileToTrash(filePath);
1709 void VuoEditor::moveFileToTrash(QString filePath)
1735 queuedCompositionsToOpen.push_back(url);
1741 string dir, file, ext;
1747 string sourceNodePath = dir +
"/" + file +
"." + ext;
1748 string targetNodePath = installedNodeDir +
"/" + file +
"." + ext;
1754 QMessageBox messageBox;
1755 string errorSummary =
"A node named \"" + file +
"\" already exists. Do you want to replace it with the one you're installing?";
1758 messageBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowMaximizeButtonHint);
1760 messageBox.setTextFormat(Qt::RichText);
1761 messageBox.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel);
1762 messageBox.setButtonText(QMessageBox::Discard, tr(
"Replace"));
1763 messageBox.setButtonText(QMessageBox::Cancel, tr(
"Cancel"));
1764 messageBox.setDefaultButton(QMessageBox::Discard);
1765 messageBox.setText(errorSummary.c_str());
1766 messageBox.setStyleSheet(
"#qt_msgbox_informativelabel, QMessageBoxDetailsText { font-weight: normal; font-size: 11pt; }");
1769 static_cast<QPushButton *
>(messageBox.button(QMessageBox::Cancel))->setAutoDefault(
false);
1770 messageBox.button(QMessageBox::Cancel)->setFocus();
1774 if (messageBox.exec() == QMessageBox::Discard)
1798 VUserLog(
"Install node %s.%s", file.c_str(), ext.c_str());
1810 QMessageBox installationSuccessMessageBox;
1811 installationSuccessMessageBox.setText(tr(
"Your node has been installed!"));
1813 installationSuccessMessageBox.exec();
1820 queuedCompositionsToOpen.push_back(url);
1826 QDesktopServices::openUrl(url);
1835 shaderDocumentationVisible = isVisible;
1836 settings->setValue(shaderDocumentationVisibilitySettingsKey, shaderDocumentationVisible);
1844 return shaderDocumentationVisible;
1850 void VuoEditor::applyStoredMovieExportSettings()
1852 movieExportWidth = (settings->contains(movieExportWidthSettingsKey)? settings->value(movieExportWidthSettingsKey).toInt() : 1024);
1853 movieExportHeight = (settings->contains(movieExportHeightSettingsKey)? settings->value(movieExportHeightSettingsKey).toInt() : 768);
1854 movieExportTime = (settings->contains(movieExportTimeSettingsKey)? settings->value(movieExportTimeSettingsKey).toDouble() : 0.);
1855 movieExportDuration = (settings->contains(movieExportDurationSettingsKey)? settings->value(movieExportDurationSettingsKey).toDouble() : 10.);
1856 movieExportFramerate = (settings->contains(movieExportFramerateSettingsKey)? settings->value(movieExportFramerateSettingsKey).toDouble() : 30.);
1857 movieExportSpatialSupersample = (settings->contains(movieExportSpatialSupersampleSettingsKey)? settings->value(movieExportSpatialSupersampleSettingsKey).toInt() : 1.);
1858 movieExportTemporalSupersample = (settings->contains(movieExportTemporalSupersampleSettingsKey)? settings->value(movieExportTemporalSupersampleSettingsKey).toInt() : 1.);
1859 movieExportShutterAngle = (settings->contains(movieExportShutterAngleSettingsKey)?
static_cast<float>(settings->value(movieExportShutterAngleSettingsKey).toDouble()) : 360.);
1860 movieExportImageFormat = (settings->contains(movieExportImageFormatSettingsKey)? settings->value(movieExportImageFormatSettingsKey).toString() :
"H.264");
1861 movieExportQuality = (settings->contains(movieExportQualitySettingsKey)? settings->value(movieExportQualitySettingsKey).toDouble() : 1.);
1867 void VuoEditor::updateGlobalMovieExportSettings(
int width,
1872 int spatialSupersample,
1873 int temporalSupersample,
1875 QString imageFormat,
1878 if (width != movieExportWidth)
1880 movieExportWidth = width;
1881 settings->setValue(movieExportWidthSettingsKey, movieExportWidth);
1884 if (height != movieExportHeight)
1886 movieExportHeight = height;
1887 settings->setValue(movieExportHeightSettingsKey, movieExportHeight);
1890 if (time != movieExportTime)
1892 movieExportTime = time;
1893 settings->setValue(movieExportTimeSettingsKey, movieExportTime);
1896 if (duration != movieExportDuration)
1898 movieExportDuration = duration;
1899 settings->setValue(movieExportDurationSettingsKey, movieExportDuration);
1902 if (framerate != movieExportFramerate)
1904 movieExportFramerate = framerate;
1905 settings->setValue(movieExportFramerateSettingsKey, movieExportFramerate);
1908 if (spatialSupersample != movieExportSpatialSupersample)
1910 movieExportSpatialSupersample = spatialSupersample;
1911 settings->setValue(movieExportSpatialSupersampleSettingsKey, movieExportSpatialSupersample);
1914 if (temporalSupersample != movieExportTemporalSupersample)
1916 movieExportTemporalSupersample = temporalSupersample;
1917 settings->setValue(movieExportTemporalSupersampleSettingsKey, movieExportTemporalSupersample);
1920 if (shutterAngle != movieExportShutterAngle)
1922 movieExportShutterAngle = shutterAngle;
1923 settings->setValue(movieExportShutterAngleSettingsKey,
static_cast<double>(movieExportShutterAngle));
1926 if (imageFormat != movieExportImageFormat)
1928 movieExportImageFormat = imageFormat;
1929 settings->setValue(movieExportImageFormatSettingsKey, movieExportImageFormat);
1932 if (quality != movieExportQuality)
1934 movieExportQuality = quality;
1935 settings->setValue(movieExportQualitySettingsKey, movieExportQuality);
1947 int &spatialSupersample,
1948 int &temporalSupersample,
1949 float &shutterAngle,
1950 QString &imageFormat,
1953 width = movieExportWidth;
1954 height = movieExportHeight;
1955 time = movieExportTime;
1956 duration = movieExportDuration;
1957 framerate = movieExportFramerate;
1958 spatialSupersample = movieExportSpatialSupersample;
1959 temporalSupersample = movieExportTemporalSupersample;
1960 shutterAngle = movieExportShutterAngle;
1961 imageFormat = movieExportImageFormat;
1962 quality = movieExportQuality;
1972 return ((! visible)? VuoNodeLibrary::nodeLibraryHidden :
1973 (docked? VuoNodeLibrary::nodeLibraryDocked :
1974 VuoNodeLibrary::nodeLibraryFloating));
1982 void VuoEditor::updateNodeLibraryDisplayMode(
bool humanReadable)
1984 nodeLibraryDisplayMode = (humanReadable? VuoNodeLibrary::displayByName : VuoNodeLibrary::displayByClass);
1985 settings->setValue(nodeLibraryDisplayModeSettingsKey, nodeLibraryDisplayMode);
1990 if ((ownedNodeLibrary->isHidden()) && (ownedNodeLibrary->
getHumanReadable() != humanReadable))
2001 void VuoEditor::updateGlobalNodeLibraryFloatingPosition(QPoint newPos)
2003 nodeLibraryFloatingPosition = newPos;
2004 settings->setValue(nodeLibraryFloatingPositionSettingsKey, nodeLibraryFloatingPosition);
2010 void VuoEditor::updateGlobalNodeLibraryWidth(
int newWidth)
2012 nodeLibraryWidth = newWidth;
2013 settings->setValue(nodeLibraryWidthSettingsKey, nodeLibraryWidth);
2019 void VuoEditor::updateGlobalNodeLibraryHeight(
int newHeight)
2021 nodeLibraryHeight = newHeight;
2022 settings->setValue(nodeLibraryHeightSettingsKey, nodeLibraryHeight);
2028 void VuoEditor::updateGlobalNodeDocumentationPanelHeight(
int newSize)
2030 nodeDocumentationPanelHeight = newSize;
2031 settings->setValue(nodeDocumentationPanelHeightSettingsKey, nodeDocumentationPanelHeight);
2037 void VuoEditor::updateGlobalNodeLibraryVisibilityState(
bool visible)
2039 VUserLog(
"%s node library", visible ?
"Show" :
"Hide");
2042 bool updatedDockingState = ((visible && (! nodeLibraryCurrentlyVisible))? previousVisibleNodeLibraryStateWasDocked : nodeLibraryCurrentlyDocked);
2043 updateGlobalNodeLibraryState(visible, updatedDockingState);
2049 void VuoEditor::updateGlobalNodeLibraryDockedState(
bool floating)
2051 VUserLog(
"%s node library", floating ?
"Detach" :
"Attach");
2055 designateNewFloatingNodeLibrary(floater);
2056 updateGlobalNodeLibraryState(this->nodeLibraryCurrentlyVisible, (! floating));
2059 updateFloatingNodeLibraryModules();
2061 updateDockedNodeLibraryModules();
2069 void VuoEditor::updateGlobalNodeLibraryState(
bool visible,
bool docked)
2071 VuoNodeLibrary::nodeLibraryState currentNodeLibraryState = getGlobalNodeLibraryStateForAttributes(nodeLibraryCurrentlyVisible, nodeLibraryCurrentlyDocked);
2074 if (currentNodeLibraryState != VuoNodeLibrary::nodeLibraryHidden)
2075 previousVisibleNodeLibraryStateWasDocked = nodeLibraryCurrentlyDocked;
2077 nodeLibraryCurrentlyVisible = visible;
2078 nodeLibraryCurrentlyDocked = docked;
2080 currentFloatingNodeLibrary = NULL;
2082 settings->setValue(nodeLibraryVisibilityStateSettingsKey, nodeLibraryCurrentlyVisible);
2083 settings->setValue(nodeLibraryDockingStateSettingsKey, nodeLibraryCurrentlyDocked);
2092 void VuoEditor::updateFloatingNodeLibraryModules()
2094 if (!currentFloatingNodeLibrary)
2101 if (activeCompositionModuleManager->
getNodeLibrary() != currentFloatingNodeLibrary)
2107 if (window != activeWindow)
2110 if (windowModuleManager->
getNodeLibrary() == currentFloatingNodeLibrary)
2116 if (moduleManager->
getNodeLibrary() == currentFloatingNodeLibrary)
2122 if (updatedModuleManager->
getNodeLibrary() != currentFloatingNodeLibrary)
2125 QString origFilterText;
2126 set<string> origSelectedNodeClasses;
2127 string origDocumentedNodeClass;
2128 currentFloatingNodeLibrary->
getState(origFilterText, origSelectedNodeClasses, origDocumentedNodeClass);
2132 updatedModuleManager->
setNodeLibrary(currentFloatingNodeLibrary);
2136 currentFloatingNodeLibrary->
setState(origFilterText, origSelectedNodeClasses, origDocumentedNodeClass);
2140 QString newFilterText;
2141 set<string> newSelectedNodeClasses;
2142 string newDocumentedNodeClass;
2143 currentFloatingNodeLibrary->
getState(newFilterText, newSelectedNodeClasses, newDocumentedNodeClass);
2144 if (newDocumentedNodeClass.empty())
2186 void VuoEditor::updateDockedNodeLibraryModules()
2188 if (currentFloatingNodeLibrary)
2198 if (windowModuleManager->
getNodeLibrary() != windowOwnedNodeLibrary)
2219 void VuoEditor::designateNewFloatingNodeLibrary(
VuoNodeLibrary *library)
2221 if (currentFloatingNodeLibrary && (currentFloatingNodeLibrary != ownedNodeLibrary))
2224 if (library && (library != ownedNodeLibrary))
2227 currentFloatingNodeLibrary = library;
2234 void VuoEditor::assignTopLevelLibraryAsReplacementFloater()
2236 currentFloatingNodeLibrary = ownedNodeLibrary;
2239 currentFloatingNodeLibrary,
true);
2249 menuOpenRecent->
addFile(filePath);
2259 this->closedFiles.push_back(filePath.toUtf8().constData());
2293 settings->setValue(recentFileListSettingsKey, menuOpenRecent->
getRecentFiles());
2302 QString bundledManual = QDir::cleanPath(QApplication::applicationDirPath().append(
"/../Resources"))
2303 .append(QDir::separator())
2305 .append(VUO_VERSION_AND_BUILD_STRING)
2306 .append(
"--manual.pdf");
2308 if (QFile(bundledManual).exists())
2309 return QString(
"file://").append(bundledManual);
2311 return "https://vuo.org/manual.pdf";
2319 return QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
2327 void VuoEditor::generateAllNodeSetHtmlDocumentation(
string saveDirectory)
2329 const bool publishInternalVuoLinks =
true;
2333 QString indexFilename(QString::fromStdString(saveDirectory) +
"/index.html");
2334 QFile indexFile(indexFilename);
2335 if (!indexFile.open(QFile::WriteOnly | QFile::Truncate))
2336 throw VuoException((
"Couldn't open " + indexFilename).toUtf8().data());
2338 QTextStream indexWriter(&indexFile);
2339 indexWriter << VUO_QSTRINGIFY(
2342 <title>Vuo %1 node documentation</title>
2344 * { font-size: 13pt; font-family:
'PT Sans',Avenir,
'Trebuchet MS',Tahoma,sans-serif; }
2345 body { padding: 5em; }
2346 h1 { font-size: 24pt; color: #aaa; font-weight: normal; }
2347 a, a:visited { font-weight: bold; color: #69a; }
2348 p { margin-top: 0; color: #aaa; }
2349 p a, p a:visited { font-weight: normal; color: #aaa; }
2350 li { list-style: none; }
2354 <h1>Vuo %1 node documentation</h1>
2357 .arg(VUO_VERSION_STRING);
2359 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->
getNodeClasses();
2360 vector<VuoCompilerNodeClass *> nodeClasses;
2361 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
2362 nodeClasses.push_back(i->second);
2365 set<string> nodeSetNames;
2370 foreach (
string nodeSetName, nodeSetNames)
2372 string nodeSetSaveDirectory = saveDirectory +
"/" + nodeSetName;
2375 string saveNodeSetHtml = nodeSetSaveDirectory +
"/index.html";
2383 string firstLineOfDescription = description.substr(0, description.find(
'\n') - 1);
2385 indexWriter << VUO_QSTRINGIFY(
2386 <li><a href=
"%1/">%2</a><p>%3</p></li>
2388 .arg(QString::fromStdString(nodeSetName))
2390 .arg(QString::fromStdString(filteredDescription));
2393 indexWriter << VUO_QSTRINGIFY(
2409 const bool publishInternalVuoLinks =
false;
2411 string nodeSetName = url.host().toUtf8().constData();
2420 string tmpSaveNodeSetHtml = tmpSaveDir +
"/index.html";
2422 if (tmpSaveDir != preexistingResourceDir)
2431 if (QDesktopServices::openUrl(QString(
"file://").append(tmpSaveNodeSetHtml.c_str())))
2435 if (tmpSaveDir != preexistingResourceDir)
2451 string nodeClassName = url.host().toUtf8().constData();
2456 if (topmostNodeLibrary)
2468 return VUO_QSTRINGIFY(<style>
2470 border: 1px solid #ccc;
2471 border-collapse: collapse;
2475 font-family:
'Monaco';
2477 background-color: %2;
2482 white-space: pre-wrap;
2485 .arg(forBrowser ?
"0.4em" :
"0")
2486 .arg(isDark ?
"#383838" :
"#ececec");
2495 string nodeSetName = nodeSet->
getName();
2500 QString htmlHeader =
"<html><head><meta charset=\"utf-8\"><title>" + tr(
"Vuo Node Set Documentation") +
": "
2501 + QString::fromStdString(nodeSetName)
2507 QString title = QString(
"<h2>").append(nodeSetDisplayName);
2508 if (nodeSetDisplayName != nodeSetName.c_str())
2509 title.append(
" (").append(nodeSetName.c_str()).append(
")");
2510 title.append(
"</h2>");
2518 QString htmlFooter =
"</body></html>";
2522 QString nodeSetExampleCompositionText =
"";
2524 foreach (
string compositionFileName, nodeSetExampleCompositionFileNames)
2537 nodeSetExampleCompositionText.append(
"<li>")
2538 .append(
"<a href=\"")
2541 .append(nodeSetName.c_str())
2543 .append(compositionFileName.c_str())
2544 .append(
"\"><font size=+1>")
2545 .append(name.c_str())
2546 .append(
"</font></a>");
2548 if (!compositionDescription.isEmpty())
2549 nodeSetExampleCompositionText.append(
": ").append(compositionDescription);
2551 nodeSetExampleCompositionText.append(
"</li>\n");
2554 if (nodeSetExampleCompositionText.size() > 0)
2556 nodeSetExampleCompositionText =
"<BR><HR><h3>" + tr(
"Example composition(s)",
"", nodeSetExampleCompositionFileNames.size()) +
":</h3>\n<ul>\n" + nodeSetExampleCompositionText +
"</ul>";
2559 QString nodeSetClassesText =
"";
2562 std::sort(nodeSetClassNames.begin(), nodeSetClassNames.end(), [=](
const string nodeClassName1,
const string nodeClassName2) {
2563 VuoCompilerNodeClass *nodeClass1 = compiler->getNodeClass(nodeClassName1);
2564 VuoCompilerNodeClass *nodeClass2 = compiler->getNodeClass(nodeClassName2);
2565 string nodeClass1Title = nodeClass1? nodeClass1->getBase()->getDefaultTitle() :
"";
2566 string nodeClass2Title = nodeClass2? nodeClass2->getBase()->getDefaultTitle() :
"";
2568 return nodeClass1Title < nodeClass2Title;
2571 foreach (
string nodeClassName, nodeSetClassNames)
2579 QString nodeClassProNodeIndicator;
2581 if (nodeClass->
getBase()->isPro())
2583 nodeClassProNodeIndicator =
" <b>[<a href=\"https://vuo.org/pro-nodes\">" + tr(
"Pro node") +
"</a>]</b>";
2586 QString nodeClassDocumentationLink = QString((nodeClassName +
".html").c_str());
2589 nodeClassDescription.remove(QRegExp(
"<[^>]*>"));
2590 nodeClassDescription.replace(QRegExp(
"\\.\\s.*"),
".");
2592 nodeSetClassesText.append(
"<li>")
2593 .append(
"<a href=\"")
2594 .append(nodeClassDocumentationLink)
2596 .append(
"<font size=+1>")
2597 .append(nodeClassTitle)
2601 .append(nodeClassName.c_str())
2604 nodeSetClassesText.append(nodeClassProNodeIndicator);
2606 if (!nodeClassDescription.isEmpty())
2607 nodeSetClassesText.append(
": ").append(nodeClassDescription);
2609 nodeSetClassesText.append(
"</li>\n");
2612 if (nodeSetClassesText.size() > 0)
2614 nodeSetClassesText =
"<BR><HR><h3>" + tr(
"Node(s)",
"", nodeSetClassNames.size()) +
":</h3>\n<ul>\n" + nodeSetClassesText +
"</ul>";
2617 ofstream savedNodeSetFile(saveFileName.c_str(), ios::trunc);
2619 savedNodeSetFile << htmlHeader.append(
"\n\n").toUtf8().constData();
2620 savedNodeSetFile << title.append(
"\n\n").toUtf8().constData();
2621 savedNodeSetFile << filteredNodeSetDocumentationContent.append(
"\n\n").toUtf8().constData();
2622 savedNodeSetFile << nodeSetExampleCompositionText.append(
"\n\n").toUtf8().constData();
2623 savedNodeSetFile << nodeSetClassesText.append(
"\n\n").toUtf8().constData();
2624 savedNodeSetFile << htmlFooter.append(
"\n\n").toUtf8().constData();
2626 savedNodeSetFile.close();
2642 string nodeSetName = nodeSet->
getName();
2644 foreach (
string nodeClassName, nodeSetClassNames)
2658 set<string> sortedUniqueKeywords;
2659 foreach (
string keyword, manualKeywords)
2660 sortedUniqueKeywords.insert(keyword);
2662 foreach (
string keyword, automaticKeywords)
2663 sortedUniqueKeywords.insert(keyword);
2667 QString nodeClassHtmlHeader =
"<html><head><meta charset=\"utf-8\"><title>" + tr(
"Vuo Node Documentation") +
": "
2668 + QString::fromStdString(nodeClassTitle)
2669 +
" (" + QString::fromStdString(nodeClassName) +
")"
2673 QString nodeClassDocumentationTitle = QString(
"<h2>")
2674 .append(nodeClassTitle.c_str())
2675 .append(
" (").append(nodeClassName.c_str()).append(
")")
2679 QString nodeClassKeywordsIntro =
"<p><b>" + tr(
"Keyword(s)",
"", sortedUniqueKeywords.size()) +
"</b>: ";
2680 QString nodeClassKeywordsText = nodeClassKeywordsIntro;
2681 foreach (
string keyword, sortedUniqueKeywords)
2683 if (nodeClassKeywordsText != nodeClassKeywordsIntro)
2684 nodeClassKeywordsText.append(
", ");
2686 nodeClassKeywordsText.append(
"<i>").append(keyword.c_str()).append(
"</i>");
2688 nodeClassKeywordsText.append(
"</p>");
2697 QSizeF size = composition->itemsBoundingRect().size().toSize();
2698 QSizeF retinaSize(size.width()*2, size.height()*2);
2699 QPixmap pixmap(retinaSize.toSize());
2700 pixmap.fill(Qt::transparent);
2702 QPainter painter(&pixmap);
2703 painter.setRenderHint(QPainter::Antialiasing,
true);
2704 painter.setRenderHint(QPainter::HighQualityAntialiasing,
true);
2705 painter.setRenderHint(QPainter::TextAntialiasing,
true);
2707 composition->render(&painter);
2709 string nodeClassRenderedPreviewFileName = nodeClassName +
".png";
2710 string tmpSaveNodeClassImage = saveDir +
"/" + nodeClassRenderedPreviewFileName;
2711 QFile file(tmpSaveNodeClassImage.c_str());
2712 file.open(QIODevice::WriteOnly);
2713 pixmap.save(&file,
"PNG");
2718 QString nodeClassRenderedPreview = QString(
"<img src=\"%1\" width=\"%2\" height=\"%3\" />")
2719 .arg(nodeClassRenderedPreviewFileName.c_str())
2721 .arg(size.height());
2725 QString nodeClassExampleCompositionText =
"";
2727 foreach (
string compositionFileName, nodeClassExampleCompositionFileNames)
2743 nodeClassExampleCompositionText.append(
"<li>")
2744 .append(
"<a href=\"")
2747 .append(nodeSetName.c_str())
2749 .append(compositionFileName.c_str())
2754 .append(
"\"><font size=+1>")
2755 .append(name.c_str())
2756 .append(
"</font></a>");
2758 if (!compositionDescription.isEmpty())
2759 nodeClassExampleCompositionText.append(
": ").append(compositionDescription);
2761 nodeClassExampleCompositionText.append(
"</li>\n");
2764 if (nodeClassExampleCompositionFileNames.size() > 0)
2766 nodeClassExampleCompositionText =
"<HR><h3>" + tr(
"Example composition(s)",
"", nodeClassExampleCompositionFileNames.size()) +
":</h3>\n<ul>\n" + nodeClassExampleCompositionText +
"</ul>";
2769 QString nodeClassProNodeIndicator;
2771 if (nodeClass->
getBase()->isPro())
2772 nodeClassProNodeIndicator = QString(VuoNodePopover::installedProNodeText).append(
"<br><br>");
2775 QString nodeClassSetReference =
"<HR>" + nodeClassProNodeIndicator + tr(
"Back to %1 node set documentation.")
2776 .arg(
"<a href=\"index.html\">" + QString::fromStdString(nodeSetName) +
"</a>");
2777 QString nodeClassHtmlFooter =
"</body></html>";
2779 string saveNodeClassHtml = saveDir +
"/" + nodeClassName +
".html";
2780 ofstream savedNodeClassFile(saveNodeClassHtml.c_str(), ios::trunc);
2782 savedNodeClassFile << nodeClassHtmlHeader.append(
"\n\n").toUtf8().constData();
2783 savedNodeClassFile << nodeClassDocumentationTitle.append(
"\n\n").toUtf8().constData();
2784 savedNodeClassFile << nodeClassRenderedPreview.append(
"\n\n").toUtf8().constData();
2785 savedNodeClassFile << nodeClassDocumentationContent.append(
"\n\n").toUtf8().constData();
2786 savedNodeClassFile << nodeClassKeywordsText.append(
"\n\n").toUtf8().constData();
2787 savedNodeClassFile << nodeClassExampleCompositionText.append(
"\n\n").toUtf8().constData();
2788 savedNodeClassFile << nodeClassSetReference.append(
"\n\n").toUtf8().constData();
2789 savedNodeClassFile << nodeClassHtmlFooter.append(
"\n\n").toUtf8().constData();
2791 savedNodeClassFile.close();
2802 QString filteredText(markdownText.c_str());
2803 QRegularExpression vuoNodeLink(
"\\[([^\\]]+)\\](\\(vuo-node://([^\\]]+)\\))");
2804 filteredText.replace(vuoNodeLink,
"`\\1`");
2806 QRegularExpression vuoNodeSetLink(
"\\[([^\\]]+)\\](\\(vuo-nodeset://([^\\]]+)\\))");
2807 filteredText.replace(vuoNodeSetLink,
"`\\1`");
2809 return filteredText.toUtf8().constData();
2821 string nodeSetName = url.host().toUtf8().constData();
2822 string nodeClassToHighlight =
"";
2824 QUrlQuery query(url.query());
2845 QClipboard *clipboard = QApplication::clipboard();
2846 const QMimeData *mimeData = clipboard->mimeData();
2848 if (!mimeData->hasFormat(
"text/plain") || mimeData->text().isNull())
2851 return mimeData->text();
2860 return this->nodeDocumentationPanelHeight;
2869 return this->nodeLibraryWidth;
2881 if (resourceDirectoryForNodeSet.find(nodeSetName) != resourceDirectoryForNodeSet.end())
2882 return resourceDirectoryForNodeSet[nodeSetName];
2893 resourceDirectoryForNodeSet[nodeSetName] = directory;
2901 return subcompositionRouter;
2907 void VuoEditor::initializeBuiltInDrivers()
2916 if (!imageFilterDriverAsString.empty())
2920 builtInDriverForProtocol[imageFilterProtocol] = imageFilterDriver;
2930 if (!imageGeneratorDriverAsString.empty())
2934 builtInDriverForProtocol[imageGeneratorProtocol] = imageGeneratorDriver;
2944 if (!imageTransitionDriverAsString.empty())
2948 builtInDriverForProtocol[imageTransitionProtocol] = imageTransitionDriver;
2962 dispatch_sync(builtInDriversQueue, ^{});
2964 map<VuoProtocol *, VuoCompilerDriver *>::iterator driver = builtInDriverForProtocol.find(protocol);
2965 if (driver != builtInDriverForProtocol.end())
2966 return driver->second;
2979 QPixmap emptyPixmap(32, 32);
2980 emptyPixmap.fill(Qt::transparent);
2981 QIcon indentIcon(emptyPixmap);
2984 QAction *windowHeading =
new QAction(
this);
2985 windowHeading->setText(tr(
"Window"));
2986 windowHeading->setEnabled(
false);
2987 m->addAction(windowHeading);
2991 QAction *templateAction =
new QAction(
this);
2992 templateAction->setText(tr(
"Image"));
2993 templateAction->setData(
"imageTemplate");
2994 templateAction->setIcon(indentIcon);
2996 m->addAction(templateAction);
3001 QAction *templateAction =
new QAction(
this);
3002 templateAction->setText(tr(
"Layers"));
3003 templateAction->setData(
"layersTemplate");
3004 templateAction->setIcon(indentIcon);
3006 m->addAction(templateAction);
3011 QAction *templateAction =
new QAction(
this);
3012 templateAction->setText(tr(
"Scene"));
3013 templateAction->setData(
"sceneTemplate");
3014 templateAction->setIcon(indentIcon);
3016 m->addAction(templateAction);
3020 QAction *protocolHeading =
new QAction(
this);
3021 protocolHeading->setText(tr(
"Protocol"));
3022 protocolHeading->setEnabled(
false);
3024 m->addAction(protocolHeading);
3029 QAction *protocolAction =
new QAction(
this);
3030 protocolAction->setText(tr(protocol->
getName().c_str()));
3031 protocolAction->setData(qVariantFromValue(
static_cast<void *
>(protocol)));
3032 protocolAction->setIcon(indentIcon);
3034 m->addAction(protocolAction);
3041 QAction *heading =
new QAction(
this);
3042 heading->setText(tr(
"Export"));
3043 heading->setEnabled(
false);
3044 m->addAction(heading);
3047 QAction *templateAction =
new QAction(
this);
3048 templateAction->setText(tr(
"Movie"));
3049 templateAction->setData(
"movie");
3050 templateAction->setIcon(indentIcon);
3052 m->addAction(templateAction);
3056 QAction *templateAction =
new QAction(
this);
3057 templateAction->setText(tr(
"Screen Saver"));
3058 templateAction->setData(
"screensaver");
3059 templateAction->setIcon(indentIcon);
3061 m->addAction(templateAction);
3065 QAction *heading =
new QAction(
this);
3066 heading->setText(tr(
"FFGL"));
3067 heading->setIcon(indentIcon);
3068 heading->setEnabled(
false);
3069 m->addAction(heading);
3072 QAction *templateAction =
new QAction(
this);
3073 templateAction->setText(
" " + tr(
"Source"));
3074 templateAction->setData(
"FFGLSource");
3075 templateAction->setIcon(indentIcon);
3077 m->addAction(templateAction);
3081 QAction *templateAction =
new QAction(
this);
3082 templateAction->setText(
" " + tr(
"Effect"));
3083 templateAction->setData(
"FFGLEffect");
3084 templateAction->setIcon(indentIcon);
3086 m->addAction(templateAction);
3090 QAction *templateAction =
new QAction(
this);
3091 templateAction->setText(
" " + tr(
"Blend Mode"));
3092 templateAction->setData(
"FFGLBlendMode");
3093 templateAction->setIcon(indentIcon);
3095 m->addAction(templateAction);
3100 QAction *heading =
new QAction(
this);
3101 heading->setText(tr(
"FxPlug"));
3102 heading->setIcon(indentIcon);
3103 heading->setEnabled(
false);
3104 m->addAction(heading);
3107 QAction *templateAction =
new QAction(
this);
3108 templateAction->setText(
" " + tr(
"Generator"));
3109 templateAction->setData(
"FxPlugGenerator");
3110 templateAction->setIcon(indentIcon);
3112 m->addAction(templateAction);
3116 QAction *templateAction =
new QAction(
this);
3117 templateAction->setText(
" " + tr(
"Effect"));
3118 templateAction->setData(
"FxPlugEffect");
3119 templateAction->setIcon(indentIcon);
3121 m->addAction(templateAction);
3125 QAction *templateAction =
new QAction(
this);
3126 templateAction->setText(
" " + tr(
"Transition"));
3127 templateAction->setData(
"FxPlugTransition");
3128 templateAction->setIcon(indentIcon);
3130 m->addAction(templateAction);
3142 QAction *action =
new QAction(m);
3143 action->setText(tr(
"Image Filter"));
3144 action->setData(
"GLSLImageFilter");
3145 connect(action, &QAction::triggered, [=] {
3146 closeUnmodifiedUntitledComposition();
3148 if (!closeWelcomeWindow())
3153 m->addAction(action);
3157 QAction *action =
new QAction(m);
3158 action->setText(tr(
"Image Generator"));
3159 action->setData(
"GLSLImageGenerator");
3160 connect(action, &QAction::triggered, [=] {
3161 closeUnmodifiedUntitledComposition();
3163 if (!closeWelcomeWindow())
3168 m->addAction(action);
3172 QAction *action =
new QAction(m);
3173 action->setText(tr(
"Image Transition"));
3174 action->setData(
"GLSLImageTransition");
3175 connect(action, &QAction::triggered, [=] {
3176 closeUnmodifiedUntitledComposition();
3178 if (!closeWelcomeWindow())
3183 m->addAction(action);
3193 return documentationQueue;
3202 return darkInterfaceAction->isChecked();
3214 return this->canvasOpacity;
3220 void VuoEditor::showGridLinesToggled(
bool show)
3226 type = VuoRendererComposition::LineGrid;
3229 type = VuoRendererComposition::NoGrid;
3231 settings->setValue(gridTypeSettingsKey, type);
3240 void VuoEditor::showGridPointsToggled(
bool show)
3246 type = VuoRendererComposition::PointGrid;
3249 type = VuoRendererComposition::NoGrid;
3251 settings->setValue(gridTypeSettingsKey, type);
3260 void VuoEditor::updateSnapToGrid(
bool snap)
3262 settings->setValue(snapToGridSettingsKey, snap);
3270 void VuoEditor::updateColor(
bool isDark)
3272 settings->setValue(darkInterfaceSettingsKey, isDark);
3281 void VuoEditor::updateCanvasOpacity(QAction *setOpacityAction)
3283 updateCanvasOpacityTo(setOpacityAction->data().toInt());
3291 void VuoEditor::updateCanvasOpacityTo(
int opacity)
3293 settings->setValue(canvasOpacitySettingsKey, opacity);
3294 canvasOpacity = opacity;
3306 VuoEditor::documentationGenerationDirectory = dir;
3316 map<QString, QString> modelExampleCompositionsAndNodeSets;
3317 modelExampleCompositionsAndNodeSets[
"LaserDiscoball.vuo"] =
"vuo.audio";
3318 modelExampleCompositionsAndNodeSets[
"Tschuri.vuo"] =
"vuo.image";
3319 modelExampleCompositionsAndNodeSets[
"SlitscanMixingInk.vuo"] =
"vuo.layer";
3320 modelExampleCompositionsAndNodeSets[
"DrawInSpace.vuo"] =
"vuo.scene";
3321 return modelExampleCompositionsAndNodeSets;
3330 map<QString, QString> modelExampleCompositionsAndNodeSets;
3334 modelExampleCompositionsAndNodeSets[
"PixellateImageRadially.vuo"] =
"vuo.image";
3339 modelExampleCompositionsAndNodeSets[
"GenerateCheckerboardImage.vuo"] =
"vuo.image";
3340 modelExampleCompositionsAndNodeSets[
"MakeDriftingClouds.vuo"] =
"vuo.image";
3341 modelExampleCompositionsAndNodeSets[
"MakeOvalPatterns.vuo"] =
"vuo.image";
3342 modelExampleCompositionsAndNodeSets[
"RippleImageGradients.vuo"] =
"vuo.image";
3343 modelExampleCompositionsAndNodeSets[
"SpinKaleidoscope.vuo"] =
"vuo.event";
3347 modelExampleCompositionsAndNodeSets[
"BlendImages.vuo"] =
"vuo.image";
3349 return modelExampleCompositionsAndNodeSets;
3359 .append(nodeSetName)
3361 .append(compositionName);
3362 return compositionURL;
3371 return getStoredUserName_Pro();
3373 return getenv(
"USER");
3383 return getStoredUserProfileURL_Pro();
3395 settings->setValue(subcompositionPrefixSettingsKey, prefix);
3396 subcompositionPrefix = prefix;