Vuo 2.4.4
Loading...
Searching...
No Matches
VuoModuleManager.cc
Go to the documentation of this file.
1
10#include "VuoModuleManager.hh"
11
12#include "VuoCodeWindow.hh"
13#include "VuoComposition.hh"
14#include "VuoEditor.hh"
15#include "VuoException.hh"
16#include "VuoGenericType.hh"
17#include "VuoNodeSet.hh"
19#include "VuoStringUtilities.hh"
20#include "VuoCompiler.hh"
22#include "VuoCompilerIssue.hh"
23#include "VuoCompilerNode.hh"
27#include "VuoCompilerType.hh"
29#include "VuoEditorUtilities.hh"
30#include "VuoErrorDialog.hh"
31#include "VuoModule.hh"
32#include "VuoNodeClass.hh"
33#include "VuoRendererFonts.hh"
34
35Q_DECLARE_METATYPE(vector<string>)
36Q_DECLARE_METATYPE(vector<VuoCompilerNodeClass *>)
37Q_DECLARE_METATYPE(vector<VuoCompilerType *>)
38Q_DECLARE_METATYPE(ModulesModifiedSet)
39
40set<VuoModuleManager *> VuoModuleManager::allModuleManagers;
41
48{
49 this->compiler = compiler;
50 compiler->setDelegate(this);
51
52 composition = nullptr;
53 codeWindow = nullptr;
54 nodeLibrary = nullptr;
55
56 allModuleManagers.insert(this);
57
58 qRegisterMetaType< vector<string> >();
59 qRegisterMetaType< vector<VuoCompilerNodeClass *> >();
60 qRegisterMetaType< vector<VuoCompilerType *> >();
61 qRegisterMetaType< ModulesModifiedSet >();
62 connect(this, &VuoModuleManager::loadedModulesAndReadyToUpdate, this, &VuoModuleManager::updateWithModulesBeingLoaded, Qt::QueuedConnection);
63}
64
69{
70 composition = nullptr;
71 codeWindow = nullptr;
72 nodeLibrary = nullptr;
73
74 for (auto callbackIter : callbackForNodeClass)
75 Block_release(callbackIter.second.first);
76 callbackForNodeClass.clear();
77
78 allModuleManagers.erase(this);
79
80 compiler->setDelegate(nullptr);
81 disconnect(this, &VuoModuleManager::loadedModulesAndReadyToUpdate, this, &VuoModuleManager::updateWithModulesBeingLoaded);
82 delete compiler;
83}
84
89{
90 this->composition = composition;
91}
92
97{
98 this->codeWindow = codeWindow;
99}
100
105{
106 this->nodeLibrary = nodeLibrary;
107 if (nodeLibrary)
108 {
109 nodeLibrary->setCompiler(compiler);
110 nodeLibrary->setModuleManager(this);
111 }
112}
113
120{
121 return nodeLibrary;
122}
123
133map<string, VuoCompilerType *> VuoModuleManager::getLoadedSingletonTypes(bool limitToSpecializationTargets)
134{
135 map<string, VuoCompilerType *> types;
136
137 auto shouldCopy = [limitToSpecializationTargets] (pair<string, VuoCompilerType *> i)
138 {
139 return ! limitToSpecializationTargets || VuoRendererPort::isSpecializationImplementedForType(i.first);
140 };
141
142 std::copy_if(loadedSingletonTypes.begin(), loadedSingletonTypes.end(),
143 std::inserter(types, types.end()),
144 shouldCopy);
145
146 return types;
147}
148
152map<string, VuoCompilerType *> VuoModuleManager::getLoadedGenericCompoundTypes(void)
153{
154 return loadedGenericCompoundTypes;
155}
156
161set<string> VuoModuleManager::getKnownListTypeNames(bool limitToSpecializationTargets)
162{
163 set<string> listTypes;
164
165 map<string, VuoCompilerType *> singletonTypes = getLoadedSingletonTypes(limitToSpecializationTargets);
166 for (auto i : singletonTypes)
167 listTypes.insert(VuoType::listTypeNamePrefix + i.first);
168
169 return listTypes;
170}
171
177{
178 return knownNodeSets;
179}
180
186string VuoModuleManager::getNodeSetForType(const string &typeName)
187{
188 // Treat as a core type (https://b33p.net/kosada/node/9465)
189 if (typeName == "VuoLayer")
190 return "";
191
192 auto typeIter = loadedSingletonTypes.find(typeName);
193 if (typeIter != loadedSingletonTypes.end())
194 {
195 VuoNodeSet *nodeSet = typeIter->second->getBase()->getNodeSet();
196 if (nodeSet)
197 return nodeSet->getName();
198 }
199
200 return getPrimaryAffiliatedNodeSetForType(typeName);
201}
202
215vector<string> VuoModuleManager::getCompatibleTypecastClasses(const string &fromTypeName, VuoType *fromType, const string &toTypeName, VuoType *toType)
216{
217 vector<string> typeConverters;
218
219 VuoGenericType *genericInType = dynamic_cast<VuoGenericType *>(fromType);
220 VuoGenericType *genericOutType = dynamic_cast<VuoGenericType *>(toType);
221
222 auto genericTypeCanSpecializeTo = [](VuoGenericType *genericType, const string &otherTypeName, VuoGenericType *otherTypeAsGeneric)
223 {
224 return otherTypeAsGeneric ?
225 genericType->isGenericTypeCompatible(otherTypeAsGeneric) :
226 (genericType->isSpecializedTypeCompatible(otherTypeName) && VuoRendererPort::isSpecializationImplementedForType(otherTypeName));
227 };
228
235 auto typeConverterCanSpecializeToQueriedTypes = [=](pair<VuoType *, VuoType *> converterInOut, string &outSpecialization)
236 {
237 VuoType *converterInType = converterInOut.first;
238 VuoGenericType *converterGenericInType = dynamic_cast<VuoGenericType *>(converterInType);
239 if (converterGenericInType)
240 {
241 if (! genericTypeCanSpecializeTo(converterGenericInType, fromTypeName, genericInType))
242 return false;
243
244 outSpecialization = VuoType::extractInnermostTypeName(fromTypeName);
245 }
246 else if (converterInType->getModuleKey() != fromTypeName)
247 return false;
248
249 VuoType *converterOutType = converterInOut.second;
250 VuoGenericType *converterGenericOutType = dynamic_cast<VuoGenericType *>(converterOutType);
251 if (converterGenericOutType)
252 {
253 if (! genericTypeCanSpecializeTo(converterGenericOutType, toTypeName, genericOutType))
254 return false;
255
256 outSpecialization = VuoType::extractInnermostTypeName(toTypeName);
257 }
258 else if (converterOutType->getModuleKey() != toTypeName)
259 return false;
260
261 if (converterGenericInType && converterGenericOutType &&
264 return false;
265
266 return true;
267 };
268
269 for (auto const &i : loadedTypeConverterNodeClasses)
270 {
271 string specialization;
272 if (typeConverterCanSpecializeToQueriedTypes(i.first, specialization))
273 {
274 if (specialization.empty())
275 typeConverters.insert(typeConverters.end(), i.second.cbegin(), i.second.cend());
276 else
277 for (string unspecializedTypeConverter : i.second)
278 typeConverters.push_back(VuoCompilerSpecializedNodeClass::createSpecializedNodeClassName(unspecializedTypeConverter, { specialization }));
279 }
280 }
281
282 return typeConverters;
283}
284
289void VuoModuleManager::doNextTimeNodeClassIsLoaded(const string &nodeClassName, CallbackType callback)
290{
291 cancelCallbackForNodeClass(nodeClassName);
292
293 CallbackType callbackOnHeap = Block_copy(callback);
294 callbackForNodeClass[nodeClassName] = make_pair(callbackOnHeap, false);
295}
296
301void VuoModuleManager::doEveryTimeNodeClassIsLoaded(const string &nodeClassName, CallbackType callback)
302{
303 cancelCallbackForNodeClass(nodeClassName);
304
305 CallbackType callbackOnHeap = Block_copy(callback);
306 callbackForNodeClass[nodeClassName] = make_pair(callbackOnHeap, true);
307}
308
312void VuoModuleManager::cancelCallbackForNodeClass(const string &nodeClassName)
313{
314 auto callbackIter = callbackForNodeClass.find(nodeClassName);
315 if (callbackIter != callbackForNodeClass.end())
316 {
317 Block_release(callbackIter->second.first);
318 callbackForNodeClass.erase(callbackIter);
319 }
320}
321
331set<string> VuoModuleManager::findInstancesOfNodeClass(const string &sourcePath)
332{
333 set<string> compositionIdentifiers;
334
335 VuoCompilerNodeClass *nodeClass = compiler->getNodeClass( VuoCompiler::getModuleKeyForPath(sourcePath) );
336 if (nodeClass && VuoFileUtilities::arePathsEqual(nodeClass->getSourcePath(), sourcePath))
337 {
338 list< pair<string, VuoCompilerNodeClass *> > nodesToVisit;
339
340 for (VuoNode *node : composition->getBase()->getNodes())
341 {
342 if (node->hasCompiler())
343 {
345 node->getCompiler()->getIdentifier());
346 nodesToVisit.push_back(make_pair(fullyQualifiedNodeIdentifier, node->getNodeClass()->getCompiler()));
347 }
348 }
349
350 while (! nodesToVisit.empty())
351 {
352 pair<string, VuoCompilerNodeClass *> v = nodesToVisit.front();
353 string currNodeIdentifier = v.first;
354 VuoCompilerNodeClass *currNodeClass = v.second;
355 nodesToVisit.pop_front();
356
357 if (currNodeClass == nodeClass)
358 compositionIdentifiers.insert(currNodeIdentifier);
359 else
360 {
361 for (pair<string, string> containedNode : currNodeClass->getContainedNodes())
362 {
363 VuoCompilerNodeClass *containedNodeClass = compiler->getNodeClass(containedNode.second);
364 if (containedNodeClass)
365 {
366 string fullyQualifiedNodeIdentifier = VuoStringUtilities::buildCompositionIdentifier(currNodeIdentifier, containedNode.first);
367 nodesToVisit.push_back(make_pair(fullyQualifiedNodeIdentifier, containedNodeClass));
368 }
369 }
370 }
371 }
372 }
373
374 return compositionIdentifiers;
375}
376
380void VuoModuleManager::loadedModules(const map<string, VuoCompilerModule *> &modulesAdded,
381 const map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
382 const map<string, VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
383{
384 //VLog("%p -- %lu %lu %lu, %lu", this,
385 //modulesAdded.size(), modulesModified.size(), modulesRemoved.size(), issues->getList().size());
386 //if (modulesAdded.size() == 1) VLog(" %s", modulesAdded.begin()->first.c_str());
387 //if (modulesModified.size() == 1) VLog(" %s", modulesModified.begin()->first.c_str());
388 //if (modulesRemoved.size() == 1) VLog(" %s", modulesRemoved.begin()->first.c_str());
389 //if (! issues->isEmpty()) VLog(" %s", issues->getLongDescription(false).c_str());
390
391 vector<VuoCompilerModule *> modulesAddedOrModified;
392 for (auto m : modulesAdded)
393 modulesAddedOrModified.push_back(m.second);
394 for (auto m : modulesModified)
395 modulesAddedOrModified.push_back(m.second.second);
396
397 vector<VuoCompilerModule *> modulesRemovedOrModified;
398 for (auto m : modulesRemoved)
399 modulesRemovedOrModified.push_back(m.second);
400 for (auto m : modulesModified)
401 modulesRemovedOrModified.push_back(m.second.first);
402
403 vector<VuoCompilerNodeClass *> nodeClassesAddedOrModified;
404 vector<VuoCompilerType *> typesAddedOrModified;
405 foreach (VuoCompilerModule *module, modulesAddedOrModified)
406 {
407 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
408 if (nodeClass)
409 nodeClassesAddedOrModified.push_back(nodeClass);
410 else
411 {
412 VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
413 if (type)
414 typesAddedOrModified.push_back(type);
415 }
416 }
417
418 vector<string> nodeClassesRemovedOrModified;
419 vector<string> typesRemovedOrModified;
420 vector<string> librariesRemovedOrModified;
421 foreach (VuoCompilerModule *module, modulesRemovedOrModified)
422 {
423 string moduleKey = module->getPseudoBase()->getModuleKey();
424 VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
425 if (nodeClass)
426 nodeClassesRemovedOrModified.push_back(moduleKey);
427 else
428 {
429 VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
430 if (type)
431 typesRemovedOrModified.push_back(moduleKey);
432 else
433 librariesRemovedOrModified.push_back(moduleKey);
434 }
435 }
436
437 set< pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModifiedSet;
438 for (auto m : modulesModified)
439 modulesModifiedSet.insert(m.second);
440
441 emit loadedModulesAndReadyToUpdate(nodeClassesRemovedOrModified, nodeClassesAddedOrModified, typesRemovedOrModified, typesAddedOrModified,
442 librariesRemovedOrModified, modulesModifiedSet, issues);
443}
444
449void VuoModuleManager::updateWithModulesBeingLoaded(const vector<string> &nodeClassesToRemove, const vector<VuoCompilerNodeClass *> &nodeClassesToAdd,
450 const vector<string> &typesToRemove, const vector<VuoCompilerType *> &typesToAdd,
451 const vector<string> &librariesToRemove,
452 const ModulesModifiedSet &modulesModified,
453 VuoCompilerIssues *issues)
454{
455 update(nodeClassesToRemove, nodeClassesToAdd, typesToRemove, typesToAdd, librariesToRemove, modulesModified, issues);
457}
458
463{
464 map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->getNodeClasses();
465 map<string, VuoCompilerType *> typesMap = compiler->getTypes();
466
467 vector<VuoCompilerNodeClass *> nodeClasses;
468 for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
469 nodeClasses.push_back(i->second);
470
471 vector<VuoCompilerType *> types;
472 for (map<string, VuoCompilerType *>::iterator i = typesMap.begin(); i != typesMap.end(); ++i)
473 types.push_back(i->second);
474
475 VuoCompilerIssues issues;
476 update(vector<string>(), nodeClasses, vector<string>(), types, vector<string>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), &issues);
477}
478
488void VuoModuleManager::update(const vector<string> &nodeClassesToRemove, const vector<VuoCompilerNodeClass *> &nodeClassesToAdd,
489 const vector<string> &typesToRemove, const vector<VuoCompilerType *> &typesToAdd,
490 const vector<string> &librariesToRemove,
491 const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
492 VuoCompilerIssues *issues)
493{
494 // Update cached info.
495
496 updateLoadedNodeClasses(nodeClassesToRemove, nodeClassesToAdd);
497 updateLoadedTypes(typesToRemove, typesToAdd);
498
499 if (composition)
500 {
501 // Check if the composition depends on any of the invalidated modules.
502
503 map<string, VuoCompilerNodeClass *> dependenciesInvalidated;
504 set<string> dependenciesLost;
505 map<string, string> originalGenericNodeClassName;
506 if (! (nodeClassesToRemove.empty() && typesToRemove.empty() && librariesToRemove.empty()) )
507 {
508 // Invalidate modules that are in nodeClassesToRemove, typesToRemove, or librariesToRemove.
509
510 set<string> dependencies = compiler->getDependenciesForComposition(composition->getBase()->getCompiler());
511
512 vector<string> sortedDependencies(dependencies.begin(), dependencies.end());
513 std::sort(sortedDependencies.begin(), sortedDependencies.end());
514
515 vector<string> sortedNodeClassesToRemove(nodeClassesToRemove.begin(), nodeClassesToRemove.end());
516 std::sort(sortedNodeClassesToRemove.begin(), sortedNodeClassesToRemove.end());
517
518 vector<string> sortedTypesToRemove(typesToRemove.begin(), typesToRemove.end());
519 std::sort(sortedTypesToRemove.begin(), sortedTypesToRemove.end());
520
521 vector<string> sortedLibrariesToRemove(librariesToRemove.begin(), librariesToRemove.end());
522 std::sort(sortedLibrariesToRemove.begin(), sortedLibrariesToRemove.end());
523
524 set<string> dependenciesInvalidatedSet;
525 std::set_intersection(sortedNodeClassesToRemove.begin(), sortedNodeClassesToRemove.end(),
526 sortedDependencies.begin(), sortedDependencies.end(),
527 std::inserter(dependenciesInvalidatedSet, dependenciesInvalidatedSet.end()));
528 std::set_intersection(sortedTypesToRemove.begin(), sortedTypesToRemove.end(),
529 sortedDependencies.begin(), sortedDependencies.end(),
530 std::inserter(dependenciesInvalidatedSet, dependenciesInvalidatedSet.end()));
531 std::set_intersection(sortedLibrariesToRemove.begin(), sortedLibrariesToRemove.end(),
532 sortedDependencies.begin(), sortedDependencies.end(),
533 std::inserter(dependenciesInvalidatedSet, dependenciesInvalidatedSet.end()));
534
535 // Invalidate node classes that depend on types in typesToRemove.
536
537 for (VuoNode *node : composition->getBase()->getNodes())
538 {
539 if (node->getNodeClass()->hasCompiler())
540 {
541 set<string> nodeClassDependencies = node->getNodeClass()->getCompiler()->getDependencies();
542 set<string> nodeClassDependenciesInvalidated;
543 std::set_intersection(nodeClassDependencies.begin(), nodeClassDependencies.end(),
544 typesToRemove.begin(), typesToRemove.end(),
545 std::inserter(nodeClassDependenciesInvalidated, nodeClassDependenciesInvalidated.end()));
546
547 if (! nodeClassDependenciesInvalidated.empty())
548 dependenciesInvalidatedSet.insert(node->getNodeClass()->getClassName());
549 }
550 }
551
552 // Invalidate node classes that are specializations of generic node classes in nodeClassesToRemove.
553
554 for (VuoNode *node : composition->getBase()->getNodes())
555 {
556 if (node->getNodeClass()->hasCompiler())
557 {
558 VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(node->getNodeClass()->getCompiler());
559 if (specializedNodeClass)
560 {
561 string genericNodeClassName = specializedNodeClass->getOriginalGenericNodeClassName();
562 if (find(nodeClassesToRemove.begin(), nodeClassesToRemove.end(), genericNodeClassName) != nodeClassesToRemove.end())
563 {
564 dependenciesInvalidatedSet.insert(genericNodeClassName);
565 originalGenericNodeClassName[ specializedNodeClass->getBase()->getClassName() ] = genericNodeClassName;
566 }
567 }
568 }
569 }
570
571 // Divvy up dependenciesInvalidatedSet into dependenciesInvalidated and dependenciesLost.
572
573 for (set<string>::iterator i = dependenciesInvalidatedSet.begin(); i != dependenciesInvalidatedSet.end(); ++i)
574 {
575 string dependency = *i;
576
577 bool foundReplacement = false;
578 for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::const_iterator j = modulesModified.begin(); j != modulesModified.end(); ++j)
579 {
580 if (j->first->getPseudoBase()->getModuleKey() == dependency)
581 {
582 VuoCompilerNodeClass *replacementNodeClass = dynamic_cast<VuoCompilerNodeClass *>(j->second);
583 if (replacementNodeClass)
584 dependenciesInvalidated[dependency] = replacementNodeClass;
585
586 foundReplacement = true;
587 break;
588 }
589 }
590
591 if (! foundReplacement)
592 dependenciesLost.insert(dependency);
593 }
594 }
595
596 // Check if the composition depends on any missing node classes that have been added.
597
598 map<string, VuoCompilerNodeClass *> dependenciesAdded;
599 if (! nodeClassesToAdd.empty())
600 {
601 foreach (VuoNode *node, composition->getBase()->getNodes())
602 {
603 if (! node->getNodeClass()->hasCompiler())
604 {
605 string nodeClassName = node->getNodeClass()->getClassName();
606 for (VuoCompilerNodeClass *addedNodeClass : nodeClassesToAdd)
607 {
608 string addedNodeClassName = addedNodeClass->getBase()->getClassName();
609 if (addedNodeClassName == nodeClassName)
610 {
611 dependenciesAdded[nodeClassName] = addedNodeClass;
612 }
613 else if (VuoCompilerSpecializedNodeClass::isSpecializationOfNodeClass(nodeClassName, addedNodeClass))
614 {
615 dependenciesAdded[addedNodeClassName] = addedNodeClass;
616 originalGenericNodeClassName[nodeClassName] = addedNodeClassName;
617 break;
618 }
619 }
620 }
621 }
622 }
623
624 if (! (dependenciesInvalidated.empty() && dependenciesLost.empty() && dependenciesAdded.empty()) )
625 {
626 // Replace each node whose node class has been invalidated (removed or modified)
627 // or whose previously missing node class has been added.
628
629 composition->modifyComponents(^{
630
631 foreach (VuoNode *node, composition->getBase()->getNodes())
632 {
633 string nodeClassName = node->getNodeClass()->getClassName();
634
635 auto nameIter = originalGenericNodeClassName.find(nodeClassName);
636 bool isSpecializedNodeClass = (nameIter != originalGenericNodeClassName.end());
637 if (isSpecializedNodeClass)
638 nodeClassName = nameIter->second;
639
640 VuoCompilerNodeClass *replacementNodeClass = NULL;
641 map<string, VuoCompilerNodeClass *>::const_iterator invIter = dependenciesInvalidated.find(nodeClassName);
642 if (invIter != dependenciesInvalidated.end())
643 {
644 replacementNodeClass = invIter->second;
645 }
646 else
647 {
648 map<string, VuoCompilerNodeClass *>::const_iterator addIter = dependenciesAdded.find(nodeClassName);
649 if (addIter != dependenciesAdded.end())
650 {
651 replacementNodeClass = addIter->second;
652 }
653 }
654
655 VuoNode *replacementNode = nullptr;
656 if (replacementNodeClass)
657 {
658 if (isSpecializedNodeClass)
659 replacementNodeClass = compiler->getNodeClass( node->getNodeClass()->getClassName() );
660
661 if (replacementNodeClass)
662 replacementNode = composition->createBaseNode(replacementNodeClass, node);
663 }
664 else if (dependenciesLost.find(nodeClassName) != dependenciesLost.end())
665 {
666 replacementNode = composition->createNodeWithMissingImplementation(node->getNodeClass(), node);
667
668 if (node->getNodeClass()->hasCompiler() && node->getNodeClass()->getCompiler()->isSubcomposition()) {
669 static_cast<VuoEditor *>(qApp)->getSubcompositionRouter()->unlinkNodeInSupercompositionFromSubcomposition(composition, node->getCompiler()->getIdentifier());
670 }
671 }
672
673 if (replacementNode)
674 {
675 composition->updateNodeImplementationInPlace(node->getRenderer(), replacementNode);
676 }
677 }
678
679 });
680
681 // If the composition is running, do a live-coding reload.
682
684
685 for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::const_iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
686 {
687 VuoCompilerNodeClass *oldNodeClass = dynamic_cast<VuoCompilerNodeClass *>(i->first);
688 VuoCompilerNodeClass *newNodeClass = dynamic_cast<VuoCompilerNodeClass *>(i->second);
689
690 if (oldNodeClass && newNodeClass)
691 diffInfo->addNodeClassReplacement(oldNodeClass, newNodeClass);
692 else
693 diffInfo->addModuleReplacement(i->first->getPseudoBase()->getModuleKey());
694 }
695
696 string snapshotWithNewModules = composition->takeSnapshot();
697 composition->updateRunningComposition(snapshotWithNewModules, snapshotWithNewModules, diffInfo, dependenciesLost);
698 }
699 }
700
701 if (nodeLibrary)
702 {
703 // Update the node library.
704
705 // Do this after updating the composition so focus won't be taken away from the node library,
706 // turning highlighted node library items from blue to gray.
707
708 nodeLibrary->updateNodeClassList(nodeClassesToRemove, nodeClassesToAdd);
709 }
710
711 // Call any callbacks that have been scheduled for the node classes added.
712
713 for (vector<VuoCompilerNodeClass *>::const_iterator i = nodeClassesToAdd.begin(); i != nodeClassesToAdd.end(); ++i)
714 {
715 string nodeClassName = (*i)->getBase()->getClassName();
716 auto callbackIter = callbackForNodeClass.find(nodeClassName);
717 if (callbackIter != callbackForNodeClass.end())
718 {
719 CallbackType callback = callbackIter->second.first;
720 callback();
721
722 if (! callbackIter->second.second)
723 cancelCallbackForNodeClass(nodeClassName);
724 }
725 }
726
727 // Display any errors that this module manager is responsible for displaying.
728
729 VuoCompilerIssues *errors = issues->getErrors();
730 if (isResponsibleForReportingErrors(this, errors, nodeClassesToAdd))
731 {
732 if (! errors->isEmpty())
733 {
734 if (codeWindow)
735 codeWindow->setIssues(errors);
736 else
737 showErrorDialog(errors);
738 }
739 else
740 {
741 if (codeWindow)
742 {
743 VuoCompilerIssues noIssues;
744 codeWindow->setIssues(&noIssues);
745 }
746 }
747 }
748}
749
754bool VuoModuleManager::isResponsibleForReportingErrors(VuoModuleManager *currManager, VuoCompilerIssues *errors,
755 const vector<VuoCompilerNodeClass *> &nodeClassesToAdd)
756{
757 // Order of precedence for handling errors:
758 // 1. If the errors are for a node class whose code editor is open, the code editor gets them.
759 // 2. If the errors are for a composition-local module, the composition window(s) at that scope get them.
760 // 3. Otherwise, the app-wide module manager gets them.
761
762 VuoModuleManager *managerWithCodeEditor = nullptr;
763 VuoModuleManager *managerWithCompositionLocal = nullptr;
764 VuoModuleManager *managerAppWide = nullptr;
765
766 // Assumption: All errors pertain to the same composition / code editor.
767 string issuePath;
768 string nodeClassName;
769 if (! errors->isEmpty())
770 {
771 VuoCompilerIssue issue = errors->getList().front();
772 issuePath = issue.getFilePath();
773 nodeClassName = VuoCompiler::getModuleKeyForPath(issuePath);
774 }
775 else if (! nodeClassesToAdd.empty())
776 {
777 issuePath = "";
778 nodeClassName = nodeClassesToAdd.front()->getBase()->getClassName();
779 }
780
781 for (VuoModuleManager *m : allModuleManagers)
782 {
783 if (m->codeWindow)
784 {
785 if (! nodeClassName.empty())
786 {
787 if (m->codeWindow->getNodeClassName() == nodeClassName)
788 managerWithCodeEditor = m;
789 }
790 }
791 else if (m->composition)
792 {
793 if (! issuePath.empty())
794 {
795 string issueDir, file, ext;
796 VuoFileUtilities::splitPath(issuePath, issueDir, file, ext);
797 if (VuoFileUtilities::arePathsEqual(m->compiler->getCompositionLocalModulesPath(), issueDir))
798 managerWithCompositionLocal = m;
799 }
800 }
801 else
802 {
803 managerAppWide = m;
804 }
805 }
806
807 VuoModuleManager *responsibleManager = (managerWithCodeEditor ? managerWithCodeEditor :
808 (managerWithCompositionLocal ? managerWithCompositionLocal :
809 managerAppWide));
810 return responsibleManager == currManager;
811}
812
816void VuoModuleManager::showErrorDialog(VuoCompilerIssues *errors)
817{
818 QString errorSummary = VuoEditor::tr("There was a problem loading one or more nodes into your library.");
819 QString errorDisclosureDetails = QString::fromStdString(errors->getLongDescription(false));
820
821 QStringList brokenModulePaths;
822 foreach (VuoCompilerIssue issue, errors->getList())
823 brokenModulePaths.append(QString::fromStdString(issue.getFilePath()));
824 brokenModulePaths.removeDuplicates();
825
826 QMessageBox messageBox;
827
828 // On OS X, this combination of flags displays the minimize, maximimize, and close buttons, but all in a disabled state.
829 messageBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowMaximizeButtonHint);
830
832 messageBox.setFont(fonts->dialogHeadingFont());
833 messageBox.setTextFormat(Qt::RichText);
834 messageBox.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel);
835 messageBox.setButtonText(QMessageBox::Discard, VuoEditor::tr("Move Broken Nodes to Trash"));
836 messageBox.setButtonText(QMessageBox::Cancel, VuoEditor::tr("Not Now"));
837 messageBox.setDefaultButton(QMessageBox::Discard);
838 messageBox.setText(errorSummary);
839 messageBox.setStyleSheet("#qt_msgbox_informativelabel, QMessageBoxDetailsText {" + fonts->getCSS(fonts->dialogBodyFont()) + "}");
840 messageBox.setDetailedText(errorDisclosureDetails);
841
842 // Give the "Not Now" button keyboard focus (without "Default" status) so that it can be activated by spacebar.
843 static_cast<QPushButton *>(messageBox.button(QMessageBox::Cancel))->setAutoDefault(false);
844 messageBox.button(QMessageBox::Cancel)->setFocus();
845
846 messageBox.setIconPixmap(VuoEditorUtilities::vuoLogoForDialogs());
847
848 if (messageBox.exec() == QMessageBox::Discard)
849 {
850 try
851 {
852 // Move broken modules to the trash.
853 foreach (QString path, brokenModulePaths)
854 VuoFileUtilities::moveFileToTrash(path.toUtf8().constData());
855 }
856 catch (const VuoException &e)
857 {
858 VuoErrorDialog::show(NULL, VuoEditor::tr("Couldn't move a broken node to the Trash"), e.what());
859 }
860 }
861}
862
866void VuoModuleManager::updateLoadedNodeClasses(const vector<string> &nodeClassesToRemove,
867 const vector<VuoCompilerNodeClass *> &nodeClassesToAdd)
868{
869 for (VuoCompilerNodeClass *nodeClassToAdd : nodeClassesToAdd)
870 {
871 VuoNodeSet *nodeSet = nodeClassToAdd->getBase()->getNodeSet();
872 if (nodeSet)
873 knownNodeSets.insert(nodeSet->getName());
874 }
875
876 for (auto i = loadedTypeConverterNodeClasses.begin(); i != loadedTypeConverterNodeClasses.end(); ++i)
877 {
878 set<string> stillLoaded;
879 std::set_difference(i->second.begin(), i->second.end(),
880 nodeClassesToRemove.begin(), nodeClassesToRemove.end(),
881 std::inserter(stillLoaded, stillLoaded.end()));
882 i->second = stillLoaded;
883 }
884
885 for (VuoCompilerNodeClass *compilerNodeClass : nodeClassesToAdd)
886 {
887 VuoNodeClass *nodeClass = compilerNodeClass->getBase();
888
889 // For type-converter node classes that have generic port types, only add the generic node class
890 // (e.g. vuo.data.summarize), not specializations of the node class (e.g. vuo.data.summarize.VuoLayer).
891 // Each generic node class will have unique VuoGenericTypes for its input port and output port, so it
892 // will have a unique key in loadedTypeConverterNodeClasses.
893 if (nodeClass->isTypecastNodeClass() &&
894 ! dynamic_cast<VuoCompilerSpecializedNodeClass *>(nodeClass->getCompiler()) &&
895 ! nodeClass->getDeprecated())
896 {
899
900 if (inPortClass->hasCompiler() && outPortClass->hasCompiler())
901 {
902 VuoType *inType = static_cast<VuoCompilerPortClass *>(inPortClass->getCompiler())->getDataVuoType();
903 VuoType *outType = static_cast<VuoCompilerPortClass *>(outPortClass->getCompiler())->getDataVuoType();
904
905 if (inType != outType)
906 loadedTypeConverterNodeClasses[{inType, outType}].insert(nodeClass->getClassName());
907 }
908 }
909 }
910}
911
915void VuoModuleManager::updateLoadedTypes(const vector<string> &typesToRemove, const vector<VuoCompilerType *> &typesToAdd)
916{
917 for (auto i = loadedSingletonTypes.begin(); i != loadedSingletonTypes.end(); )
918 {
919 if (std::find(typesToRemove.begin(), typesToRemove.end(), i->first) != typesToRemove.end())
920 i = loadedSingletonTypes.erase(i);
921 else
922 ++i;
923 }
924
925 for (auto i = loadedGenericCompoundTypes.begin(); i != loadedGenericCompoundTypes.end(); )
926 {
927 if (std::find(typesToRemove.begin(), typesToRemove.end(), i->first) != typesToRemove.end())
928 i = loadedGenericCompoundTypes.erase(i);
929 else
930 ++i;
931 }
932
933 for (VuoCompilerType *typeToAdd : typesToAdd)
934 {
935 string typeName = typeToAdd->getBase()->getModuleKey();
936
937 if (! VuoCompilerType::isListType(typeToAdd))
938 {
939 if (typeName == "VuoList")
940 loadedGenericCompoundTypes[typeName] = typeToAdd;
941 else
942 loadedSingletonTypes[typeName] = typeToAdd;
943
944 VuoNodeSet *nodeSet = typeToAdd->getBase()->getNodeSet();
945 if (nodeSet)
946 knownNodeSets.insert(nodeSet->getName());
947 }
948 }
949}
950
955string VuoModuleManager::getPrimaryAffiliatedNodeSetForType(const string &typeName)
956{
957 map<string, string> nodeSetForType;
958 nodeSetForType["VuoAnchor"] = "vuo.layer";
959 nodeSetForType["VuoAudioSamples"] = "vuo.audio";
960 nodeSetForType["VuoBlendMode"] = "vuo.image";
961 nodeSetForType["VuoCoordinateUnit"] = "vuo.window";
962 nodeSetForType["VuoCubemap"] = "vuo.image";
963 nodeSetForType["VuoCursor"] = "vuo.window";
964 nodeSetForType["VuoCurve"] = "vuo.motion";
965 nodeSetForType["VuoCurveEasing"] = "vuo.motion";
966 nodeSetForType["VuoDiode"] = "vuo.math";
967 nodeSetForType["VuoFont"] = "vuo.font";
968 nodeSetForType["VuoHorizontalAlignment"] = "vuo.layer";
969 nodeSetForType["VuoImageColorDepth"] = "vuo.image";
970 nodeSetForType["VuoImageWrapMode"] = "vuo.image";
971 nodeSetForType["VuoIntegerRange"] = "vuo.math";
972 nodeSetForType["VuoInteraction"] = "vuo.ui";
973 nodeSetForType["VuoInteractionType"] = "vuo.ui";
974 nodeSetForType["VuoListPosition"] = "vuo.list";
975 nodeSetForType["VuoLoopType"] = "vuo.motion";
976 nodeSetForType["VuoMathExpressionList"] = "vuo.math";
977 nodeSetForType["VuoMesh"] = "vuo.mesh";
978 nodeSetForType["VuoModifierKey"] = "vuo.keyboard";
979 nodeSetForType["VuoOrientation"] = "vuo.ui";
980 nodeSetForType["VuoRange"] = "vuo.math";
981 nodeSetForType["VuoRectangle"] = "vuo.image";
982 nodeSetForType["VuoRenderedLayers"] = "vuo.ui";
983 nodeSetForType["VuoScreen"] = "vuo.screen";
984 nodeSetForType["VuoShader"] = "vuo.shader";
985 nodeSetForType["VuoSortOrder"] = "vuo.list";
986 nodeSetForType["VuoTextCase"] = "vuo.text";
987 nodeSetForType["VuoTextComparison"] = "vuo.text";
988 nodeSetForType["VuoTextSort"] = "vuo.text";
989 nodeSetForType["VuoTransform"] = "vuo.transform";
990 nodeSetForType["VuoTransform2d"] = "vuo.transform";
991 nodeSetForType["VuoTree"] = "vuo.tree";
992 nodeSetForType["VuoUrl"] = "vuo.url";
993 nodeSetForType["VuoUuid"] = "vuo.ui"; // @todo hide?
994 nodeSetForType["VuoVerticalAlignment"] = "vuo.layer";
995 nodeSetForType["VuoWave"] = "vuo.motion";
996 nodeSetForType["VuoWindowDescription"] = "vuo.window";
997 nodeSetForType["VuoWindowProperty"] = "vuo.window";
998 nodeSetForType["VuoWindowReference"] = "vuo.window";
999 nodeSetForType["VuoWrapMode"] = "vuo.math";
1000
1001 map<string, string>::iterator i = nodeSetForType.find(typeName);
1002 if (i != nodeSetForType.end())
1003 return i->second;
1004 else
1005 return "";
1006}