Vuo  2.0.0
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"
21 #include "VuoCompilerIssue.hh"
22 #include "VuoCompilerNode.hh"
23 #include "VuoCompilerNodeClass.hh"
24 #include "VuoCompilerPortClass.hh"
26 #include "VuoCompilerType.hh"
27 #include "VuoEditorComposition.hh"
28 #include "VuoEditorUtilities.hh"
29 #include "VuoErrorDialog.hh"
30 #include "VuoModule.hh"
31 #include "VuoNodeClass.hh"
32 #include "VuoRendererFonts.hh"
33 
34 set<VuoModuleManager *> VuoModuleManager::allModuleManagers;
35 
42 {
43  this->compiler = compiler;
44  compiler->setDelegate(this);
45 
46  composition = nullptr;
47  codeWindow = nullptr;
48  nodeLibrary = nullptr;
49 
50  updateGroup = dispatch_group_create();
51 
52  allModuleManagers.insert(this);
53 }
54 
59 {
60  delete compiler;
61  dispatch_release(updateGroup);
62 }
63 
68 {
69  composition = nullptr;
70  codeWindow = nullptr;
71  nodeLibrary = nullptr;
72 
73  for (auto callbackIter : callbackForNodeClass)
74  Block_release(callbackIter.second.first);
75  callbackForNodeClass.clear();
76 
77  allModuleManagers.erase(this);
78 
79  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
80 
81  compiler->setDelegate(nullptr);
82  dispatch_group_wait(updateGroup, DISPATCH_TIME_FOREVER);
83 
84  delete this;
85  });
86 }
87 
92 {
93  this->composition = composition;
94 }
95 
100 {
101  this->codeWindow = codeWindow;
102 }
103 
108 {
109  this->nodeLibrary = nodeLibrary;
110  if (nodeLibrary)
111  nodeLibrary->setCompiler(compiler);
112 }
113 
118 {
119  return nodeLibrary;
120 }
121 
126 {
127  map<pair<VuoType *, VuoType *>, vector<string> >::iterator iter = loadedTypecastClasses.find( make_pair(inType, outType) );
128  if (iter != loadedTypecastClasses.end())
129  return iter->second;
130 
131  return vector<string>();
132 }
133 
144 map<string, set<VuoCompilerType *> > VuoModuleManager::getLoadedTypesForNodeSet(void)
145 {
146  return loadedTypesForNodeSet;
147 }
148 
153 void VuoModuleManager::doNextTimeNodeClassIsLoaded(const string &nodeClassName, CallbackType callback)
154 {
155  cancelCallbackForNodeClass(nodeClassName);
156 
157  CallbackType callbackOnHeap = Block_copy(callback);
158  callbackForNodeClass[nodeClassName] = make_pair(callbackOnHeap, false);
159 }
160 
165 void VuoModuleManager::doEveryTimeNodeClassIsLoaded(const string &nodeClassName, CallbackType callback)
166 {
167  cancelCallbackForNodeClass(nodeClassName);
168 
169  CallbackType callbackOnHeap = Block_copy(callback);
170  callbackForNodeClass[nodeClassName] = make_pair(callbackOnHeap, true);
171 }
172 
176 void VuoModuleManager::cancelCallbackForNodeClass(const string &nodeClassName)
177 {
178  auto callbackIter = callbackForNodeClass.find(nodeClassName);
179  if (callbackIter != callbackForNodeClass.end())
180  {
181  Block_release(callbackIter->second.first);
182  callbackForNodeClass.erase(callbackIter);
183  }
184 }
185 
195 set<string> VuoModuleManager::findInstancesOfNodeClass(const string &sourcePath)
196 {
197  set<string> compositionIdentifiers;
198 
199  VuoCompilerNodeClass *nodeClass = compiler->getNodeClass( VuoCompiler::getModuleKeyForPath(sourcePath) );
200  if (nodeClass && VuoFileUtilities::arePathsEqual(nodeClass->getSourcePath(), sourcePath))
201  {
202  list< pair<string, VuoCompilerNodeClass *> > nodesToVisit;
203 
204  for (VuoNode *node : composition->getBase()->getNodes())
205  {
206  if (node->hasCompiler())
207  {
209  node->getCompiler()->getIdentifier());
210  nodesToVisit.push_back(make_pair(fullyQualifiedNodeIdentifier, node->getNodeClass()->getCompiler()));
211  }
212  }
213 
214  while (! nodesToVisit.empty())
215  {
216  pair<string, VuoCompilerNodeClass *> v = nodesToVisit.front();
217  string currNodeIdentifier = v.first;
218  VuoCompilerNodeClass *currNodeClass = v.second;
219  nodesToVisit.pop_front();
220 
221  if (currNodeClass == nodeClass)
222  compositionIdentifiers.insert(currNodeIdentifier);
223  else
224  {
225  for (pair<string, string> containedNode : currNodeClass->getContainedNodes())
226  {
227  VuoCompilerNodeClass *containedNodeClass = compiler->getNodeClass(containedNode.second);
228  if (containedNodeClass)
229  {
230  string fullyQualifiedNodeIdentifier = VuoStringUtilities::buildCompositionIdentifier(currNodeIdentifier, containedNode.first);
231  nodesToVisit.push_back(make_pair(fullyQualifiedNodeIdentifier, containedNodeClass));
232  }
233  }
234  }
235  }
236  }
237 
238  return compositionIdentifiers;
239 }
240 
244 void VuoModuleManager::loadedModules(const map<string, VuoCompilerModule *> &modulesAdded,
245  const map<string, pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
246  const map<string, VuoCompilerModule *> &modulesRemoved, VuoCompilerIssues *issues)
247 {
248  //VLog("%p -- %lu %lu %lu, %lu", this,
249  //modulesAdded.size(), modulesModified.size(), modulesRemoved.size(), issues->getList().size());
250  //if (modulesAdded.size() == 1) VLog(" %s", modulesAdded.begin()->first.c_str());
251  //if (modulesModified.size() == 1) VLog(" %s", modulesModified.begin()->first.c_str());
252  //if (modulesRemoved.size() == 1) VLog(" %s", modulesRemoved.begin()->first.c_str());
253  //if (! issues->isEmpty()) VLog(" %s", issues->getLongDescription(false).c_str());
254 
255  vector<VuoCompilerModule *> modulesAddedOrModified;
256  for (auto m : modulesAdded)
257  modulesAddedOrModified.push_back(m.second);
258  for (auto m : modulesModified)
259  modulesAddedOrModified.push_back(m.second.second);
260 
261  vector<VuoCompilerModule *> modulesRemovedOrModified;
262  for (auto m : modulesRemoved)
263  modulesRemovedOrModified.push_back(m.second);
264  for (auto m : modulesModified)
265  modulesRemovedOrModified.push_back(m.second.first);
266 
267  vector<VuoCompilerNodeClass *> nodeClassesAddedOrModified;
268  vector<VuoCompilerType *> typesAddedOrModified;
269  foreach (VuoCompilerModule *module, modulesAddedOrModified)
270  {
271  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
272  if (nodeClass)
273  nodeClassesAddedOrModified.push_back(nodeClass);
274  else
275  {
276  VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
277  if (type)
278  typesAddedOrModified.push_back(type);
279  }
280  }
281 
282  vector<string> nodeClassesRemovedOrModified;
283  vector<string> typesRemovedOrModified;
284  vector<string> librariesRemovedorModified;
285  foreach (VuoCompilerModule *module, modulesRemovedOrModified)
286  {
287  string moduleKey = module->getPseudoBase()->getModuleKey();
288  VuoCompilerNodeClass *nodeClass = dynamic_cast<VuoCompilerNodeClass *>(module);
289  if (nodeClass)
290  nodeClassesRemovedOrModified.push_back(moduleKey);
291  else
292  {
293  VuoCompilerType *type = dynamic_cast<VuoCompilerType *>(module);
294  if (type)
295  typesRemovedOrModified.push_back(moduleKey);
296  else
297  librariesRemovedorModified.push_back(moduleKey);
298  }
299  }
300 
301  set< pair<VuoCompilerModule *, VuoCompilerModule *> > modulesModifiedSet;
302  for (auto m : modulesModified)
303  modulesModifiedSet.insert(m.second);
304 
305  dispatch_group_async(updateGroup, dispatch_get_main_queue(), ^{
306  update(nodeClassesRemovedOrModified, nodeClassesAddedOrModified, typesRemovedOrModified, typesAddedOrModified,
307  librariesRemovedorModified, modulesModifiedSet, issues);
309  });
310 }
311 
316 {
317  map<string, VuoCompilerNodeClass *> nodeClassesMap = compiler->getNodeClasses();
318  map<string, VuoCompilerType *> typesMap = compiler->getTypes();
319 
320  vector<VuoCompilerNodeClass *> nodeClasses;
321  for (map<string, VuoCompilerNodeClass *>::iterator i = nodeClassesMap.begin(); i != nodeClassesMap.end(); ++i)
322  nodeClasses.push_back(i->second);
323 
324  vector<VuoCompilerType *> types;
325  for (map<string, VuoCompilerType *>::iterator i = typesMap.begin(); i != typesMap.end(); ++i)
326  types.push_back(i->second);
327 
328  VuoCompilerIssues issues;
329  update(vector<string>(), nodeClasses, vector<string>(), types, vector<string>(), set< pair<VuoCompilerModule *, VuoCompilerModule *> >(), &issues);
330 }
331 
341 void VuoModuleManager::update(const vector<string> &nodeClassesToRemove, const vector<VuoCompilerNodeClass *> &nodeClassesToAdd,
342  const vector<string> &typesToRemove, const vector<VuoCompilerType *> &typesToAdd,
343  const vector<string> &librariesToRemove,
344  const set< pair<VuoCompilerModule *, VuoCompilerModule *> > &modulesModified,
345  VuoCompilerIssues *issues)
346 {
347  // Update cached info.
348 
349  updateLoadedTypecastClasses(nodeClassesToRemove, nodeClassesToAdd, typesToRemove, typesToAdd);
350  updateLoadedTypesByNodeSet(typesToRemove, typesToAdd);
351 
352  if (composition)
353  {
354  // Check if the composition depends on any of the invalidated modules.
355 
356  map<string, VuoCompilerNodeClass *> dependenciesInvalidated;
357  set<string> dependenciesLost;
358  map<string, string> originalGenericNodeClassName;
359  if (! (nodeClassesToRemove.empty() && typesToRemove.empty() && librariesToRemove.empty()) )
360  {
361  // Invalidate modules that are in nodeClassesToRemove, typesToRemove, or librariesToRemove.
362 
363  set<string> dependencies = compiler->getDependenciesForComposition(composition->getBase()->getCompiler());
364 
365  vector<string> sortedDependencies(dependencies.begin(), dependencies.end());
366  std::sort(sortedDependencies.begin(), sortedDependencies.end());
367 
368  vector<string> sortedNodeClassesToRemove(nodeClassesToRemove.begin(), nodeClassesToRemove.end());
369  std::sort(sortedNodeClassesToRemove.begin(), sortedNodeClassesToRemove.end());
370 
371  vector<string> sortedTypesToRemove(typesToRemove.begin(), typesToRemove.end());
372  std::sort(sortedTypesToRemove.begin(), sortedTypesToRemove.end());
373 
374  vector<string> sortedLibrariesToRemove(librariesToRemove.begin(), librariesToRemove.end());
375  std::sort(sortedLibrariesToRemove.begin(), sortedLibrariesToRemove.end());
376 
377  set<string> dependenciesInvalidatedSet;
378  std::set_intersection(sortedNodeClassesToRemove.begin(), sortedNodeClassesToRemove.end(),
379  sortedDependencies.begin(), sortedDependencies.end(),
380  std::inserter(dependenciesInvalidatedSet, dependenciesInvalidatedSet.end()));
381  std::set_intersection(sortedTypesToRemove.begin(), sortedTypesToRemove.end(),
382  sortedDependencies.begin(), sortedDependencies.end(),
383  std::inserter(dependenciesInvalidatedSet, dependenciesInvalidatedSet.end()));
384  std::set_intersection(sortedLibrariesToRemove.begin(), sortedLibrariesToRemove.end(),
385  sortedDependencies.begin(), sortedDependencies.end(),
386  std::inserter(dependenciesInvalidatedSet, dependenciesInvalidatedSet.end()));
387 
388  // Invalidate node classes that depend on types in typesToRemove.
389 
390  for (VuoNode *node : composition->getBase()->getNodes())
391  {
392  if (node->getNodeClass()->hasCompiler())
393  {
394  set<string> nodeClassDependencies = node->getNodeClass()->getCompiler()->getDependencies();
395  set<string> nodeClassDependenciesInvalidated;
396  std::set_intersection(nodeClassDependencies.begin(), nodeClassDependencies.end(),
397  typesToRemove.begin(), typesToRemove.end(),
398  std::inserter(nodeClassDependenciesInvalidated, nodeClassDependenciesInvalidated.end()));
399 
400  if (! nodeClassDependenciesInvalidated.empty())
401  dependenciesInvalidatedSet.insert(node->getNodeClass()->getClassName());
402  }
403  }
404 
405  // Invalidate node classes that are specializations of generic node classes in nodeClassesToRemove.
406 
407  for (VuoNode *node : composition->getBase()->getNodes())
408  {
409  if (node->getNodeClass()->hasCompiler())
410  {
411  VuoCompilerSpecializedNodeClass *specializedNodeClass = dynamic_cast<VuoCompilerSpecializedNodeClass *>(node->getNodeClass()->getCompiler());
412  if (specializedNodeClass)
413  {
414  string genericNodeClassName = specializedNodeClass->getOriginalGenericNodeClassName();
415  if (find(nodeClassesToRemove.begin(), nodeClassesToRemove.end(), genericNodeClassName) != nodeClassesToRemove.end())
416  {
417  dependenciesInvalidatedSet.insert(genericNodeClassName);
418  originalGenericNodeClassName[ specializedNodeClass->getBase()->getClassName() ] = genericNodeClassName;
419  }
420  }
421  }
422  }
423 
424  // Divvy up dependenciesInvalidatedSet into dependenciesInvalidated and dependenciesLost.
425 
426  for (set<string>::iterator i = dependenciesInvalidatedSet.begin(); i != dependenciesInvalidatedSet.end(); ++i)
427  {
428  string dependency = *i;
429 
430  bool foundReplacement = false;
431  for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::const_iterator j = modulesModified.begin(); j != modulesModified.end(); ++j)
432  {
433  if (j->first->getPseudoBase()->getModuleKey() == dependency)
434  {
435  VuoCompilerNodeClass *replacementNodeClass = dynamic_cast<VuoCompilerNodeClass *>(j->second);
436  if (replacementNodeClass)
437  dependenciesInvalidated[dependency] = replacementNodeClass;
438 
439  foundReplacement = true;
440  break;
441  }
442  }
443 
444  if (! foundReplacement)
445  dependenciesLost.insert(dependency);
446  }
447  }
448 
449  // Check if the composition depends on any missing node classes that have been added.
450 
451  map<string, VuoCompilerNodeClass *> dependenciesAdded;
452  if (! nodeClassesToAdd.empty())
453  {
454  foreach (VuoNode *node, composition->getBase()->getNodes())
455  {
456  if (! node->getNodeClass()->hasCompiler())
457  {
458  string nodeClassName = node->getNodeClass()->getClassName();
459  for (VuoCompilerNodeClass *addedNodeClass : nodeClassesToAdd)
460  {
461  string addedNodeClassName = addedNodeClass->getBase()->getClassName();
462  if (addedNodeClassName == nodeClassName)
463  {
464  dependenciesAdded[nodeClassName] = addedNodeClass;
465  }
466  else if (VuoCompilerSpecializedNodeClass::isSpecializationOfNodeClass(nodeClassName, addedNodeClass))
467  {
468  dependenciesAdded[addedNodeClassName] = addedNodeClass;
469  originalGenericNodeClassName[nodeClassName] = addedNodeClassName;
470  break;
471  }
472  }
473  }
474  }
475  }
476 
477  if (! (dependenciesInvalidated.empty() && dependenciesLost.empty() && dependenciesAdded.empty()) )
478  {
479  // Replace each node whose node class has been invalidated (removed or modified)
480  // or whose previously missing node class has been added.
481 
482  composition->modifyComponents(^{
483 
484  foreach (VuoNode *node, composition->getBase()->getNodes())
485  {
486  string nodeClassName = node->getNodeClass()->getClassName();
487 
488  auto nameIter = originalGenericNodeClassName.find(nodeClassName);
489  bool isSpecializedNodeClass = (nameIter != originalGenericNodeClassName.end());
490  if (isSpecializedNodeClass)
491  nodeClassName = nameIter->second;
492 
493  VuoCompilerNodeClass *replacementNodeClass = NULL;
494  map<string, VuoCompilerNodeClass *>::const_iterator invIter = dependenciesInvalidated.find(nodeClassName);
495  if (invIter != dependenciesInvalidated.end())
496  {
497  replacementNodeClass = invIter->second;
498  }
499  else
500  {
501  map<string, VuoCompilerNodeClass *>::const_iterator addIter = dependenciesAdded.find(nodeClassName);
502  if (addIter != dependenciesAdded.end())
503  {
504  replacementNodeClass = addIter->second;
505  }
506  }
507 
508  VuoNode *replacementNode = nullptr;
509  if (replacementNodeClass)
510  {
511  if (isSpecializedNodeClass)
512  replacementNodeClass = compiler->getNodeClass( node->getNodeClass()->getClassName() );
513 
514  replacementNode = composition->createBaseNode(replacementNodeClass, node);
515  }
516  else if (dependenciesLost.find(nodeClassName) != dependenciesLost.end())
517  {
518  replacementNode = composition->createNodeWithMissingImplementation(node->getNodeClass(), node);
519 
520  if (node->getNodeClass()->hasCompiler() && node->getNodeClass()->getCompiler()->isSubcomposition()) {
521  static_cast<VuoEditor *>(qApp)->getSubcompositionRouter()->unlinkNodeInSupercompositionFromSubcomposition(composition, node->getCompiler()->getIdentifier());
522  }
523  }
524 
525  if (replacementNode)
526  {
527  composition->updateNodeImplementationInPlace(node->getRenderer(), replacementNode);
528  }
529  }
530 
531  });
532 
533  // If the composition is running, do a live-coding reload.
534 
536 
537  for (set< pair<VuoCompilerModule *, VuoCompilerModule *> >::const_iterator i = modulesModified.begin(); i != modulesModified.end(); ++i)
538  {
539  VuoCompilerNodeClass *oldNodeClass = dynamic_cast<VuoCompilerNodeClass *>(i->first);
540  VuoCompilerNodeClass *newNodeClass = dynamic_cast<VuoCompilerNodeClass *>(i->second);
541 
542  if (oldNodeClass && newNodeClass)
543  diffInfo->addNodeClassReplacement(oldNodeClass, newNodeClass);
544  else
545  diffInfo->addModuleReplacement(i->first->getPseudoBase()->getModuleKey());
546  }
547 
548  string snapshotWithNewModules = composition->takeSnapshot();
549  composition->updateRunningComposition(snapshotWithNewModules, snapshotWithNewModules, diffInfo, dependenciesLost);
550  }
551  }
552 
553  if (nodeLibrary)
554  {
555  // Update the node library.
556 
557  // Do this after updating the composition so focus won't be taken away from the node library,
558  // turning highlighted node library items from blue to gray.
559 
560  nodeLibrary->updateNodeClassList(nodeClassesToRemove, nodeClassesToAdd);
561  }
562 
563  // Call any callbacks that have been scheduled for the node classes added.
564 
565  for (vector<VuoCompilerNodeClass *>::const_iterator i = nodeClassesToAdd.begin(); i != nodeClassesToAdd.end(); ++i)
566  {
567  string nodeClassName = (*i)->getBase()->getClassName();
568  auto callbackIter = callbackForNodeClass.find(nodeClassName);
569  if (callbackIter != callbackForNodeClass.end())
570  {
571  CallbackType callback = callbackIter->second.first;
572  callback();
573 
574  if (! callbackIter->second.second)
575  cancelCallbackForNodeClass(nodeClassName);
576  }
577  }
578 
579  // Display any errors that this module manager is responsible for displaying.
580 
581  VuoCompilerIssues *errors = issues->getErrors();
582  if (isResponsibleForReportingErrors(this, errors, nodeClassesToAdd))
583  {
584  if (! errors->isEmpty())
585  {
586  if (codeWindow)
587  codeWindow->setIssues(errors);
588  else
589  showErrorDialog(errors);
590  }
591  else
592  {
593  if (codeWindow)
594  {
595  VuoCompilerIssues noIssues;
596  codeWindow->setIssues(&noIssues);
597  }
598  }
599  }
600 }
601 
606 bool VuoModuleManager::isResponsibleForReportingErrors(VuoModuleManager *currManager, VuoCompilerIssues *errors,
607  const vector<VuoCompilerNodeClass *> &nodeClassesToAdd)
608 {
609  // Order of precedence for handling errors:
610  // 1. If the errors are for a node class whose code editor is open, the code editor gets them.
611  // 2. If the errors are for a composition-local module, the composition window(s) at that scope get them.
612  // 3. Otherwise, the app-wide module manager gets them.
613 
614  VuoModuleManager *managerWithCodeEditor = nullptr;
615  VuoModuleManager *managerWithCompositionLocal = nullptr;
616  VuoModuleManager *managerAppWide = nullptr;
617 
618  // Assumption: All errors pertain to the same composition / code editor.
619  string issuePath;
620  string nodeClassName;
621  if (! errors->isEmpty())
622  {
623  VuoCompilerIssue issue = errors->getList().front();
624  issuePath = issue.getFilePath();
625  nodeClassName = VuoCompiler::getModuleKeyForPath(issuePath);
626  }
627  else if (! nodeClassesToAdd.empty())
628  {
629  issuePath = "";
630  nodeClassName = nodeClassesToAdd.front()->getBase()->getClassName();
631  }
632 
633  for (VuoModuleManager *m : allModuleManagers)
634  {
635  if (m->codeWindow)
636  {
637  if (! nodeClassName.empty())
638  {
639  if (m->codeWindow->getNodeClassName() == nodeClassName)
640  managerWithCodeEditor = m;
641  }
642  }
643  else if (m->composition)
644  {
645  if (! issuePath.empty())
646  {
647  string issueDir, file, ext;
648  VuoFileUtilities::splitPath(issuePath, issueDir, file, ext);
649  if (VuoFileUtilities::arePathsEqual(m->compiler->getCompositionLocalModulesPath(), issueDir))
650  managerWithCompositionLocal = m;
651  }
652  }
653  else
654  {
655  managerAppWide = m;
656  }
657  }
658 
659  VuoModuleManager *responsibleManager = (managerWithCodeEditor ? managerWithCodeEditor :
660  (managerWithCompositionLocal ? managerWithCompositionLocal :
661  managerAppWide));
662  return responsibleManager == currManager;
663 }
664 
668 void VuoModuleManager::showErrorDialog(VuoCompilerIssues *errors)
669 {
670  QString errorSummary = VuoEditor::tr("There was a problem loading one or more nodes into your library.");
671  QString errorDisclosureDetails = QString::fromStdString(errors->getLongDescription(false));
672 
673  QStringList brokenModulePaths;
674  foreach (VuoCompilerIssue issue, errors->getList())
675  brokenModulePaths.append(QString::fromStdString(issue.getFilePath()));
676  brokenModulePaths.removeDuplicates();
677 
678  QMessageBox messageBox;
679 
680  // On OS X, this combination of flags displays the minimize, maximimize, and close buttons, but all in a disabled state.
681  messageBox.setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::WindowMaximizeButtonHint);
682 
684  messageBox.setFont(fonts->dialogHeadingFont());
685  messageBox.setTextFormat(Qt::RichText);
686  messageBox.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel);
687  messageBox.setButtonText(QMessageBox::Discard, VuoEditor::tr("Move Broken Nodes to Trash"));
688  messageBox.setButtonText(QMessageBox::Cancel, VuoEditor::tr("Not Now"));
689  messageBox.setDefaultButton(QMessageBox::Discard);
690  messageBox.setText(errorSummary);
691  messageBox.setStyleSheet("#qt_msgbox_informativelabel, QMessageBoxDetailsText {" + fonts->getCSS(fonts->dialogBodyFont()) + "}");
692  messageBox.setDetailedText(errorDisclosureDetails);
693 
694  // Give the "Not Now" button keyboard focus (without "Default" status) so that it can be activated by spacebar.
695  static_cast<QPushButton *>(messageBox.button(QMessageBox::Cancel))->setAutoDefault(false);
696  messageBox.button(QMessageBox::Cancel)->setFocus();
697 
698  messageBox.setIconPixmap(VuoEditorUtilities::vuoLogoForDialogs());
699 
700  if (messageBox.exec() == QMessageBox::Discard)
701  {
702  try
703  {
704  // Move broken modules to the trash.
705  foreach (QString path, brokenModulePaths)
706  VuoFileUtilities::moveFileToTrash(path.toUtf8().constData());
707  }
708  catch (const VuoException &e)
709  {
710  VuoErrorDialog::show(NULL, VuoEditor::tr("Couldn't move a broken node to the Trash"), e.what());
711  }
712  }
713 }
714 
718 void VuoModuleManager::updateLoadedTypecastClasses(const vector<string> &nodeClassesToRemove,
719  const vector<VuoCompilerNodeClass *> &nodeClassesToAdd,
720  const vector<string> &typesToRemove,
721  const vector<VuoCompilerType *> &typesToAdd)
722 {
723  // Unlist type-converter node classes for which the input or output type has been invalidated.
724  for (map<pair<VuoType *, VuoType *>, vector<string> >::iterator i = loadedTypecastClasses.begin(); i != loadedTypecastClasses.end(); )
725  {
726  if (find(typesToRemove.begin(), typesToRemove.end(), i->first.first->getModuleKey()) != typesToRemove.end() ||
727  find(typesToRemove.begin(), typesToRemove.end(), i->first.second->getModuleKey()) != typesToRemove.end())
728  loadedTypecastClasses.erase(i++);
729  else
730  ++i;
731  }
732 
733  // Unlist type-converter node classes that have been invalidated.
734  for (map<pair<VuoType *, VuoType *>, vector<string> >::iterator i = loadedTypecastClasses.begin(); i != loadedTypecastClasses.end(); ++i)
735  {
736  vector<string> stillLoaded;
737  std::set_difference(i->second.begin(), i->second.end(),
738  nodeClassesToRemove.begin(), nodeClassesToRemove.end(),
739  std::back_inserter(stillLoaded));
740  i->second = stillLoaded;
741  }
742 
743  // Add type-converter node classes that have been (re)loaded.
744  for (vector<VuoCompilerNodeClass *>::const_iterator i = nodeClassesToAdd.begin(); i != nodeClassesToAdd.end(); ++i)
745  {
746  VuoNodeClass *nodeClass = (*i)->getBase();
747  if (nodeClass->isTypecastNodeClass() && ! nodeClass->getDeprecated())
748  {
751 
752  if ((typecastInPortClass->hasCompiler() && typecastOutPortClass->hasCompiler()) &&
753  (static_cast<VuoCompilerPortClass *>(typecastInPortClass->getCompiler())->getDataVuoType() !=
754  static_cast<VuoCompilerPortClass *>(typecastOutPortClass->getCompiler())->getDataVuoType()))
755  {
756  pair<VuoType *, VuoType *> inTypeOutType = make_pair
757  (
758  static_cast<VuoCompilerPortClass *>(typecastInPortClass->getCompiler())->getDataVuoType(),
759  static_cast<VuoCompilerPortClass *>(typecastOutPortClass->getCompiler())->getDataVuoType()
760  );
761  loadedTypecastClasses[inTypeOutType].push_back(nodeClass->getClassName());
762  }
763  }
764  }
765 
766  // Temporary workaround so that specialized versions of certain generic node classes can be type converters.
768  map<string, VuoCompilerType *> loadedTypes = compiler->getTypes();
769  for (vector<VuoCompilerType *>::const_iterator i = typesToAdd.begin(); i != typesToAdd.end(); ++i)
770  {
771  VuoType *type = (*i)->getBase();
772  string typeName = type->getModuleKey();
773 
774  if (!VuoType::isListTypeName(typeName))
775  {
776  map<string, VuoCompilerType *>::iterator listTypeIter = loadedTypes.find("VuoList_" + typeName);
777  if (listTypeIter != loadedTypes.end())
778  {
779  VuoType *listType = listTypeIter->second->getBase();
780 
781  string nodeClassNamePrefix[3] = {"vuo.list.get.first.", "vuo.list.get.last.", "vuo.list.get.random."};
782  for (int p = 0; p < 3; ++p)
783  {
784  string nodeClassName = nodeClassNamePrefix[p] + typeName;
785  pair<VuoType *, VuoType *> inTypeOutType = make_pair(listType, type);
786 
787  if (std::find(loadedTypecastClasses[inTypeOutType].begin(), loadedTypecastClasses[inTypeOutType].end(), nodeClassName) == loadedTypecastClasses[inTypeOutType].end())
788  loadedTypecastClasses[inTypeOutType].push_back(nodeClassName);
789  }
790 
791  {
792  string nodeClassName = "vuo.list.count." + typeName;
793  pair<VuoType *, VuoType *> inTypeOutType = make_pair(listType, loadedTypes["VuoInteger"]->getBase());
794 
795  if (std::find(loadedTypecastClasses[inTypeOutType].begin(), loadedTypecastClasses[inTypeOutType].end(), nodeClassName) == loadedTypecastClasses[inTypeOutType].end())
796  loadedTypecastClasses[inTypeOutType].push_back(nodeClassName);
797  }
798 
799  {
800  string nodeClassName = "vuo.list.populated." + typeName;
801  pair<VuoType *, VuoType *> inTypeOutType = make_pair(listType, loadedTypes["VuoBoolean"]->getBase());
802 
803  if (std::find(loadedTypecastClasses[inTypeOutType].begin(), loadedTypecastClasses[inTypeOutType].end(), nodeClassName) == loadedTypecastClasses[inTypeOutType].end())
804  loadedTypecastClasses[inTypeOutType].push_back(nodeClassName);
805  }
806 
807  {
808  string nodeClassName = "vuo.data.summarize." + typeName;
809  pair<VuoType *, VuoType *> inTypeOutType = make_pair(type, loadedTypes["VuoText"]->getBase());
810 
811  if (std::find(loadedTypecastClasses[inTypeOutType].begin(), loadedTypecastClasses[inTypeOutType].end(), nodeClassName) == loadedTypecastClasses[inTypeOutType].end() &&
812  typeName != "VuoData")
813  loadedTypecastClasses[inTypeOutType].push_back(nodeClassName);
814  }
815 
816  {
817  string nodeClassName = "vuo.type.tree.value." + typeName;
818  pair<VuoType *, VuoType *> inTypeOutType = make_pair(loadedTypes["VuoTree"]->getBase(), type);
819 
820  if (std::find(loadedTypecastClasses[inTypeOutType].begin(), loadedTypecastClasses[inTypeOutType].end(), nodeClassName) == loadedTypecastClasses[inTypeOutType].end())
821  loadedTypecastClasses[inTypeOutType].push_back(nodeClassName);
822  }
823 
824  if (VuoStringUtilities::beginsWith(typeName, "VuoPoint"))
825  {
826  string nodeClassName = "vuo.point.length." + typeName;
827  pair<VuoType *, VuoType *> inTypeOutType = make_pair(type, loadedTypes["VuoReal"]->getBase());
828 
829  if (std::find(loadedTypecastClasses[inTypeOutType].begin(), loadedTypecastClasses[inTypeOutType].end(), nodeClassName) == loadedTypecastClasses[inTypeOutType].end())
830  loadedTypecastClasses[inTypeOutType].push_back(nodeClassName);
831  }
832  }
833  }
834  }
835 
836  {
837  VuoCompilerNodeClass *nodeClass = compiler->getNodeClass("vuo.type.real.enum");
838  VuoPortClass *outputPortClass = nodeClass->getBase()->getOutputPortClasses()[0];
839  VuoGenericType *outputType = static_cast<VuoGenericType *>(static_cast<VuoCompilerPortClass *>(outputPortClass->getCompiler())->getDataVuoType());
840 
841  for (vector<VuoCompilerType *>::const_iterator i = typesToAdd.begin(); i != typesToAdd.end(); ++i)
842  {
843  VuoType *type = (*i)->getBase();
844  string typeName = type->getModuleKey();
845  if (!outputType->isSpecializedTypeCompatible(typeName))
846  continue;
847 
848  string nodeClassName = "vuo.type.real.enum." + typeName;
849  pair<VuoType *, VuoType *> inTypeOutType = make_pair(loadedTypes["VuoReal"]->getBase(), type);
850 
851  if (std::find(loadedTypecastClasses[inTypeOutType].begin(), loadedTypecastClasses[inTypeOutType].end(), nodeClassName) == loadedTypecastClasses[inTypeOutType].end())
852  loadedTypecastClasses[inTypeOutType].push_back(nodeClassName);
853  }
854  }
855 }
856 
860 void VuoModuleManager::updateLoadedTypesByNodeSet(const vector<string> &typesToRemove, const vector<VuoCompilerType *> &typesToAdd)
861 {
862  set<string> explicitCoreTypes;
863  explicitCoreTypes.insert("VuoLayer"); // Temporary workaround until https://b33p.net/kosada/node/9465
864 
865  // Unlist types that have been invalidated.
866  for (map<string, set<VuoCompilerType *> >::iterator i = loadedTypesForNodeSet.begin(); i != loadedTypesForNodeSet.end(); ++i)
867  {
868  for (set<VuoCompilerType *>::iterator j = i->second.begin(); j != i->second.end(); )
869  {
870  if (find(typesToRemove.begin(), typesToRemove.end(), (*j)->getBase()->getModuleKey()) != typesToRemove.end())
871  i->second.erase(j++);
872  else
873  ++j;
874  }
875  }
876 
877  // Add types that have been (re)loaded.
878  map<string, VuoCompilerType *> loadedTypes = compiler->getTypes();
879  for (vector<VuoCompilerType *>::const_iterator i = typesToAdd.begin(); i != typesToAdd.end(); ++i)
880  {
881  VuoCompilerType *type = *i;
882  string typeName = type->getBase()->getModuleKey();
883  VuoNodeSet *nodeSet = type->getBase()->getNodeSet();
884 
885  string nodeSetName = (explicitCoreTypes.find(typeName) != explicitCoreTypes.end()? "" :
886  (nodeSet? nodeSet->getName() :
887  getPrimaryAffiliatedNodeSetForType(typeName)));
888 
889  if (! VuoCompilerType::isListType(type))
890  {
891  loadedTypesForNodeSet[nodeSetName].insert(type);
892 
893  map<string, VuoCompilerType *>::const_iterator listTypeIter = loadedTypes.find(VuoType::listTypeNamePrefix + typeName);
894  if (listTypeIter != loadedTypes.end())
895  loadedTypesForNodeSet[nodeSetName].insert(listTypeIter->second);
896  }
897  }
898 }
899 
904 string VuoModuleManager::getPrimaryAffiliatedNodeSetForType(const string &typeName)
905 {
906  map<string, string> nodeSetForType;
907  nodeSetForType["VuoAnchor"] = "vuo.layer";
908  nodeSetForType["VuoAudioSamples"] = "vuo.audio";
909  nodeSetForType["VuoBlendMode"] = "vuo.image";
910  nodeSetForType["VuoCoordinateUnit"] = "vuo.window";
911  nodeSetForType["VuoCubemap"] = "vuo.image";
912  nodeSetForType["VuoCursor"] = "vuo.window";
913  nodeSetForType["VuoCurve"] = "vuo.motion";
914  nodeSetForType["VuoCurveEasing"] = "vuo.motion";
915  nodeSetForType["VuoDiode"] = "vuo.math";
916  nodeSetForType["VuoFont"] = "vuo.font";
917  nodeSetForType["VuoHorizontalAlignment"] = "vuo.layer";
918  nodeSetForType["VuoImageColorDepth"] = "vuo.image";
919  nodeSetForType["VuoImageWrapMode"] = "vuo.image";
920  nodeSetForType["VuoIntegerRange"] = "vuo.math";
921  nodeSetForType["VuoInteraction"] = "vuo.ui";
922  nodeSetForType["VuoInteractionType"] = "vuo.ui";
923  nodeSetForType["VuoListPosition"] = "vuo.list";
924  nodeSetForType["VuoLoopType"] = "vuo.motion";
925  nodeSetForType["VuoMathExpressionList"] = "vuo.math";
926  nodeSetForType["VuoMesh"] = "vuo.mesh";
927  nodeSetForType["VuoModifierKey"] = "vuo.keyboard";
928  nodeSetForType["VuoOrientation"] = "vuo.ui";
929  nodeSetForType["VuoRange"] = "vuo.math";
930  nodeSetForType["VuoRectangle"] = "vuo.image";
931  nodeSetForType["VuoRenderedLayers"] = "vuo.ui";
932  nodeSetForType["VuoScreen"] = "vuo.screen";
933  nodeSetForType["VuoShader"] = "vuo.shader";
934  nodeSetForType["VuoSortOrder"] = "vuo.list";
935  nodeSetForType["VuoTextCase"] = "vuo.text";
936  nodeSetForType["VuoTextComparison"] = "vuo.text";
937  nodeSetForType["VuoTextSort"] = "vuo.text";
938  nodeSetForType["VuoTransform"] = "vuo.transform";
939  nodeSetForType["VuoTransform2d"] = "vuo.transform";
940  nodeSetForType["VuoTree"] = "vuo.tree";
941  nodeSetForType["VuoUrl"] = "vuo.url";
942  nodeSetForType["VuoUuid"] = "vuo.ui"; // @todo hide?
943  nodeSetForType["VuoVerticalAlignment"] = "vuo.layer";
944  nodeSetForType["VuoWave"] = "vuo.motion";
945  nodeSetForType["VuoWindowDescription"] = "vuo.window";
946  nodeSetForType["VuoWindowProperty"] = "vuo.window";
947  nodeSetForType["VuoWindowReference"] = "vuo.window";
948  nodeSetForType["VuoWrapMode"] = "vuo.math";
949 
950  map<string, string>::iterator i = nodeSetForType.find(typeName);
951  if (i != nodeSetForType.end())
952  return i->second;
953  else
954  return "";
955 }