From f777036acb07adb0cc0a8e7076f9c0777e86e0cd Mon Sep 17 00:00:00 2001
From: Aaron Cuevas Lopez <aacuelo@teleco.upv.es>
Date: Thu, 19 Nov 2015 19:54:47 +0100
Subject: [PATCH] Make possible to save and load processor signal chains

---
 .../GenericProcessor/GenericProcessor.h       |  6 +-
 .../PlaceholderProcessor.cpp                  |  6 +-
 .../PlaceholderProcessor.h                    |  2 +-
 .../PlaceholderProcessorEditor.cpp            |  8 +-
 .../Processors/PluginManager/PluginClass.cpp  | 88 +++++++++++++++++++
 Source/Processors/PluginManager/PluginClass.h |  9 ++
 .../PluginManager/PluginManager.cpp           | 34 +++++--
 .../Processors/PluginManager/PluginManager.h  |  5 +-
 .../ProcessorGraph/ProcessorGraph.cpp         | 34 +++++--
 .../ProcessorManager/ProcessorManager.cpp     | 79 +++++++++++++++--
 .../ProcessorManager/ProcessorManager.h       |  1 +
 Source/UI/EditorViewport.cpp                  | 22 ++++-
 Source/UI/ProcessorList.cpp                   |  2 +
 13 files changed, 261 insertions(+), 35 deletions(-)

diff --git a/Source/Processors/GenericProcessor/GenericProcessor.h b/Source/Processors/GenericProcessor/GenericProcessor.h
index 320c568b6..d9aa51173 100755
--- a/Source/Processors/GenericProcessor/GenericProcessor.h
+++ b/Source/Processors/GenericProcessor/GenericProcessor.h
@@ -36,7 +36,7 @@ enum ChannelType {HEADSTAGE_CHANNEL = 0, AUX_CHANNEL = 1, ADC_CHANNEL = 2, EVENT
 #include "../Parameter/Parameter.h"
 #include "../Channel/Channel.h"
 #include "../../CoreServices.h"
-#include "../PluginManager/OpenEphysPlugin.h"
+#include "../PluginManager/PluginClass.h"
 
 #include <time.h>
 #include <stdio.h>
@@ -66,7 +66,7 @@ class Channel;
 
 */
 
-class PLUGIN_API GenericProcessor : public AudioProcessor
+class PLUGIN_API GenericProcessor : public AudioProcessor, public PluginClass
 {
 public:
 
@@ -101,7 +101,7 @@ public:
     virtual AudioProcessorEditor* createEditor();
 
     /** The default is to have no editor.*/
-    bool hasEditor() const;
+    virtual bool hasEditor() const;
 
     /** JUCE method. Not used.*/
     void reset();
diff --git a/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.cpp b/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.cpp
index 88ebc3934..3208a816b 100644
--- a/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.cpp
+++ b/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.cpp
@@ -43,7 +43,8 @@ bool PlaceholderProcessor::hasEditor() const
 
 AudioProcessorEditor* PlaceholderProcessor::createEditor()
 {
-	return new PlaceholderProcessorEditor(this, processorName, libName, libVersion);
+	editor = new PlaceholderProcessorEditor(this, processorName, libName, libVersion);
+	return editor;
 }
 
 void PlaceholderProcessor::process(AudioSampleBuffer& continuousBuffer,	MidiBuffer& eventBuffer)
@@ -60,7 +61,8 @@ bool PlaceholderProcessor::isSink()
 	return processorSink;
 }
 
-bool PlaceholderProcessor::enable()
+bool PlaceholderProcessor::isReady()
 {
+	CoreServices::sendStatusMessage("Cannot acquire with placeholder nodes");
 	return false; //This processor never allows processing
 }
\ No newline at end of file
diff --git a/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.h b/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.h
index 5da989288..ae89b3a71 100644
--- a/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.h
+++ b/Source/Processors/PlaceholderProcessor/PlaceholderProcessor.h
@@ -39,7 +39,7 @@ public:
 		MidiBuffer& eventBuffer) override;
 	bool isSource() override;
 	bool isSink() override;
-	bool enable() override;
+	bool isReady() override;
 private:
 	const String processorName;
 	const String libName;
diff --git a/Source/Processors/PlaceholderProcessor/PlaceholderProcessorEditor.cpp b/Source/Processors/PlaceholderProcessor/PlaceholderProcessorEditor.cpp
index 43df12bf1..7cf012f17 100644
--- a/Source/Processors/PlaceholderProcessor/PlaceholderProcessorEditor.cpp
+++ b/Source/Processors/PlaceholderProcessor/PlaceholderProcessorEditor.cpp
@@ -27,18 +27,18 @@ PlaceholderProcessorEditor::PlaceholderProcessorEditor(GenericProcessor* parentN
 	: GenericEditor(parentNode, true), processorName(pName), libName(lName), libVersion(lVer)
 {
 	notfoundLabel = new Label("Not found", "Plugin not found");
-	notfoundLabel->setBounds(30, 30, 100, 20);
+	notfoundLabel->setBounds(10, 25, 100, 20);
 	addAndMakeVisible(notfoundLabel);
 
 	libLabel = new Label("Plugin", libName + " ver. " + String(libVersion));
-	libLabel->setBounds(30, 70, 100, 20);
+	libLabel->setBounds(10, 40, 160, 40);
 	addAndMakeVisible(libLabel);
 
 	nameLabel = new Label("Processor", "Missing processor: " + processorName);
-	libLabel->setBounds(30, 110, 100, 20);
+	nameLabel->setBounds(10, 75, 160, 40);
 	addAndMakeVisible(nameLabel);
 
-	desiredWidth = 150;
+	desiredWidth = 180;
 	setEnabledState(false);
 }
 
diff --git a/Source/Processors/PluginManager/PluginClass.cpp b/Source/Processors/PluginManager/PluginClass.cpp
index f40c0f2e5..1ad338aee 100644
--- a/Source/Processors/PluginManager/PluginClass.cpp
+++ b/Source/Processors/PluginManager/PluginClass.cpp
@@ -22,3 +22,91 @@
  */
 
 #include "PluginClass.h"
+#include "PluginManager.h"
+#include "../../AccessClass.h"
+#include "../ProcessorManager/ProcessorManager.h"
+
+PluginClass::PluginClass()
+{
+	libName = String::empty;
+	pluginName = String::empty;
+	libVersion = -1;
+	pluginType = Plugin::NotAPlugin;
+}
+
+PluginClass::~PluginClass()
+{
+
+}
+
+void PluginClass::setPluginData(Plugin::PluginType type, int index)
+{
+	PluginManager* pm = AccessClass::getPluginManager();
+	String name;
+	pluginType = type;
+	pluginIndex = index;
+	switch (type)
+	{
+	case Plugin::ProcessorPlugin:
+	{
+		Plugin::ProcessorInfo i = pm->getProcessorInfo(index);
+		name = i.name;
+	}
+	break;
+	case Plugin::RecordEnginePlugin:
+	{
+		Plugin::RecordEngineInfo i = pm->getRecordEngineInfo(index);
+		name = i.name;
+	}
+	break;
+	case Plugin::DatathreadPlugin:
+	{
+		Plugin::DataThreadInfo i = pm->getDataThreadInfo(index);
+		name = i.name;
+	}
+	break;
+	case Plugin::FileSourcePlugin:
+	{
+		Plugin::FileSourceInfo i = pm->getFileSourceInfo(index);
+		name = i.name;
+	}
+	break;
+	case Plugin::NotAPlugin:
+	{
+		String pName;
+		int pType;
+		ProcessorManager::getProcessorNameAndType(BuiltInProcessor, index, pName, pType);
+		name = pName;
+	}
+	default:
+		return;
+	}
+	pluginName = name;
+	libName = pm->getLibraryName(pm->getLibraryIndexFromPlugin(type, index));
+	libVersion = pm->getLibraryVersion(pm->getLibraryIndexFromPlugin(type, index));
+}
+
+String PluginClass::getLibName() const
+{
+	return libName;
+}
+
+String PluginClass::getPluginName() const
+{
+	return pluginName;
+}
+
+int PluginClass::getLibVersion() const
+{
+	return libVersion;
+}
+
+Plugin::PluginType PluginClass::getPluginType() const
+{
+	return pluginType;
+}
+
+int PluginClass::getIndex() const
+{
+	return pluginIndex;
+}
\ No newline at end of file
diff --git a/Source/Processors/PluginManager/PluginClass.h b/Source/Processors/PluginManager/PluginClass.h
index 0fb7f84d1..d72bc54de 100644
--- a/Source/Processors/PluginManager/PluginClass.h
+++ b/Source/Processors/PluginManager/PluginClass.h
@@ -28,11 +28,20 @@
 class PluginClass
 {
 public:
+	PluginClass();
+	~PluginClass();
 	void setPluginData(Plugin::PluginType type, int index);
+	String getLibName() const;
+	String getPluginName() const;
+	int getLibVersion() const;
+	Plugin::PluginType getPluginType() const;
+	int getIndex() const;
 private:
 	String libName;
 	String pluginName;
+	Plugin::PluginType pluginType;
 	int libVersion;
+	int pluginIndex;
 };
 
 
diff --git a/Source/Processors/PluginManager/PluginManager.cpp b/Source/Processors/PluginManager/PluginManager.cpp
index 2e80659e7..f02d7a029 100644
--- a/Source/Processors/PluginManager/PluginManager.cpp
+++ b/Source/Processors/PluginManager/PluginManager.cpp
@@ -179,6 +179,7 @@ int PluginManager::loadPlugin(const String& pluginLoc) {
 	LoadedLibInfo lib;
 	lib.apiVersion = libInfo.apiVersion;
 	lib.name = libInfo.name;
+	lib.libVersion = libInfo.libVersion;
 	lib.numPlugins = libInfo.numPlugins;
 	lib.handle = handle;
 
@@ -197,7 +198,7 @@ int PluginManager::loadPlugin(const String& pluginLoc) {
 			info.creator = pInfo.processor.creator;
 			info.name = pInfo.processor.name;
 			info.type = pInfo.processor.type;
-			info.libIndex = libArray.size();
+			info.libIndex = libArray.size()-1;
 			processorPlugins.add(info);
 			break;
 		}
@@ -314,14 +315,37 @@ Plugin::FileSourceInfo PluginManager::getFileSourceInfo(String name, String libN
 	return i;
 }
 
-String PluginManager::getPluginName(int index) const
+String PluginManager::getLibraryName(int index) const
+{
+	if (index < 0 || index >= libArray.size())
+		return String::empty;
+	else
+		return libArray[index].name;
+}
+
+int PluginManager::getLibraryVersion(int index) const
 {
-	return libArray[index].name;
+	if (index < 0 || index >= libArray.size())
+		return -1;
+	else
+		return libArray[index].libVersion;
 }
 
-int PluginManager::getPluginVersion(int index) const
+int PluginManager::getLibraryIndexFromPlugin(Plugin::PluginType type, int index)
 {
-	return libArray[index].libVersion;
+	switch (type)
+	{
+	case Plugin::ProcessorPlugin:
+		return processorPlugins[index].libIndex;
+	case Plugin::RecordEnginePlugin:
+		return recordEnginePlugins[index].libIndex;
+	case Plugin::DatathreadPlugin:
+		return dataThreadPlugins[index].libIndex;
+	case Plugin::FileSourcePlugin:
+		return fileSourcePlugins[index].libIndex;
+	default:
+		return -1;
+	}
 }
 
 Plugin::ProcessorInfo PluginManager::getEmptyProcessorInfo()
diff --git a/Source/Processors/PluginManager/PluginManager.h b/Source/Processors/PluginManager/PluginManager.h
index fac06d638..ccc50462b 100644
--- a/Source/Processors/PluginManager/PluginManager.h
+++ b/Source/Processors/PluginManager/PluginManager.h
@@ -74,8 +74,9 @@ public:
 	Plugin::RecordEngineInfo getRecordEngineInfo(String name, String libName = String::empty) const;
 	Plugin::FileSourceInfo getFileSourceInfo(int index) const;
 	Plugin::FileSourceInfo getFileSourceInfo(String name, String libName = String::empty) const;
-	String getPluginName(int index) const;
-	int getPluginVersion(int index) const;
+	String getLibraryName(int index) const;
+	int getLibraryVersion(int index) const;
+	int getLibraryIndexFromPlugin(Plugin::PluginType type, int index);
 
 private:
 	Array<LoadedLibInfo> libArray;
diff --git a/Source/Processors/ProcessorGraph/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph/ProcessorGraph.cpp
index b2a27b133..68111e777 100644
--- a/Source/Processors/ProcessorGraph/ProcessorGraph.cpp
+++ b/Source/Processors/ProcessorGraph/ProcessorGraph.cpp
@@ -470,16 +470,34 @@ void ProcessorGraph::connectProcessorToAudioAndRecordNodes(GenericProcessor* sou
 
 GenericProcessor* ProcessorGraph::createProcessorFromDescription(Array<var>& description)
 {
-	String processorName = description[0];
-	int processorType = description[1];
-	int processorIndex = description[2];
-	String processorCategory = description[3];
+	GenericProcessor* processor = nullptr;
 
-	std::cout << "Creating from description..." << std::endl;
-	std::cout << processorCategory << "::" << processorName << " (" << processorType << "-" << processorIndex << ")" << std::endl;
+	bool fromProcessorList = description[0];
+	String processorName = description[1];
+	int processorType = description[2];
+	int processorIndex = description[3];
 
-    GenericProcessor* processor = nullptr;
-	processor = ProcessorManager::createProcessor((ProcessorClasses)processorType, processorIndex);
+	if (fromProcessorList)
+	{
+		String processorCategory = description[4];
+
+		std::cout << "Creating from description..." << std::endl;
+		std::cout << processorCategory << "::" << processorName << " (" << processorType << "-" << processorIndex << ")" << std::endl;
+
+		processor = ProcessorManager::createProcessor((ProcessorClasses)processorType, processorIndex);
+	}
+	else
+	{
+		String libName = description[4];
+		int libVersion = description[5];
+		bool isSource = description[6];
+		bool isSink = description[7];
+
+		std::cout << "Creating from plugin info..." << std::endl;
+		std::cout << libName << "(" << libVersion << ")::" << processorName << std::endl;
+
+		processor = ProcessorManager::createProcessorFromPluginInfo((Plugin::PluginType)processorType, processorIndex, processorName, libName, libVersion, isSource, isSink);
+	}
    
 	String msg = "New " + processorName + " created";
 	CoreServices::sendStatusMessage(msg);
diff --git a/Source/Processors/ProcessorManager/ProcessorManager.cpp b/Source/Processors/ProcessorManager/ProcessorManager.cpp
index d83479477..9ec180260 100644
--- a/Source/Processors/ProcessorManager/ProcessorManager.cpp
+++ b/Source/Processors/ProcessorManager/ProcessorManager.cpp
@@ -33,6 +33,8 @@
 #include "../Splitter/Splitter.h"
 #include "../DataThreads/RhythmNode/RHD2000Thread.h"
 
+#include "../PlaceholderProcessor/PlaceholderProcessor.h"
+
 /** Total number of builtin processors **/
 #define BUILTIN_PROCESSORS 4
 
@@ -44,6 +46,10 @@ namespace ProcessorManager
 	{
 		switch (index)
 		{
+		case -1:
+			name = "Placeholder processor";
+			type = UtilityProcessor;
+			break;
 		case 0:
 			name = "Rhythm FPGA";
 			type = SourceProcessor;
@@ -70,24 +76,29 @@ namespace ProcessorManager
 	/** Built-in constructors **/
 	GenericProcessor* createBuiltInProcessor(int index)
 	{
+		GenericProcessor* proc;
 		switch (index)
 		{
+		case -1:
+			proc = new PlaceholderProcessor("Empty placeholder", "Undefined", 0, false, false);
+			break;
 		case 0:
-			return new SourceNode("Rhythm FPGA", &RHD2000Thread::createDataThread);
+			proc = new SourceNode("Rhythm FPGA", &RHD2000Thread::createDataThread);
 			break;
 		case 1:
-			return new Merger();
+			proc = new Merger();
 			break;
 		case 2:
-			return new Splitter();
+			proc = new Splitter();
 			break;
 		case 3:
-			return new FileReader();
+			proc = new FileReader();
 			break;
 		default:
 			return nullptr;
-			break;
 		}
+		proc->setPluginData(Plugin::NotAPlugin, index);
+		return proc;
 	}
 
 	int getNumProcessors(ProcessorClasses pClass)
@@ -146,13 +157,17 @@ namespace ProcessorManager
 		case PluginProcessor:
 			{
 				Plugin::ProcessorInfo info = AccessClass::getPluginManager()->getProcessorInfo(index);
-				return info.creator();
+				GenericProcessor* proc = info.creator();
+				proc->setPluginData(Plugin::ProcessorPlugin, index);
+				return proc;
 				break;
 			}
 		case DataThreadProcessor:
 		{
 			Plugin::DataThreadInfo info = AccessClass::getPluginManager()->getDataThreadInfo(index);
-			return new SourceNode(info.name, info.creator);
+			GenericProcessor* proc = new SourceNode(info.name, info.creator);
+			proc->setPluginData(Plugin::DatathreadPlugin, index);
+			return proc;
 			break;
 		}
 				
@@ -160,4 +175,54 @@ namespace ProcessorManager
 			return nullptr;
 		}
 	}
+
+	GenericProcessor* createProcessorFromPluginInfo(Plugin::PluginType type, int index, String procName, String libName, int libVersion, bool source, bool sink)
+	{
+		PluginManager* pm = AccessClass::getPluginManager();
+		GenericProcessor* proc = nullptr;
+		if (index > -1)
+		{
+			if (type == Plugin::NotAPlugin)
+			{
+				return createBuiltInProcessor(index);
+			}
+			else if (type == Plugin::ProcessorPlugin)
+			{
+				for (int i = 0; i < pm->getNumProcessors(); i++)
+				{
+					Plugin::ProcessorInfo info = pm->getProcessorInfo(i);
+					if (procName.equalsIgnoreCase(info.name))
+					{
+						int libIndex = pm->getLibraryIndexFromPlugin(Plugin::ProcessorPlugin, i);
+						if (libName.equalsIgnoreCase(pm->getLibraryName(libIndex)) && libVersion == pm->getLibraryVersion(libIndex))
+						{
+							proc = info.creator();
+							proc->setPluginData(Plugin::ProcessorPlugin, i);
+							return proc;
+						}
+					}
+				}
+			}
+			else if (type == Plugin::DatathreadPlugin)
+			{
+				for (int i = 0; i < pm->getNumDataThreads(); i++)
+				{
+					Plugin::DataThreadInfo info = pm->getDataThreadInfo(i);
+					if (procName.equalsIgnoreCase(info.name))
+					{
+						int libIndex = pm->getLibraryIndexFromPlugin(Plugin::DatathreadPlugin, i);
+						if (libName.equalsIgnoreCase(pm->getLibraryName(libIndex)) && libVersion == pm->getLibraryVersion(libIndex))
+						{
+							proc = new SourceNode(info.name, info.creator);
+							proc->setPluginData(Plugin::DatathreadPlugin, i);
+							return proc;
+						}
+					}
+				}
+			}
+		}		
+		proc = new PlaceholderProcessor(procName, libName, libVersion, source, sink);
+		proc->setPluginData(Plugin::NotAPlugin, -1);
+		return proc;
+	}
 };
\ No newline at end of file
diff --git a/Source/Processors/ProcessorManager/ProcessorManager.h b/Source/Processors/ProcessorManager/ProcessorManager.h
index 9f1c1bcba..d643bdbf7 100644
--- a/Source/Processors/ProcessorManager/ProcessorManager.h
+++ b/Source/Processors/ProcessorManager/ProcessorManager.h
@@ -38,6 +38,7 @@ namespace ProcessorManager
 	int getNumProcessors(ProcessorClasses pClass);
 	void getProcessorNameAndType(ProcessorClasses pClass, int index, String& name, int& type);
 	GenericProcessor* createProcessor(ProcessorClasses pClass, int index);
+	GenericProcessor* createProcessorFromPluginInfo(Plugin::PluginType type, int index, String procName, String libName, int libVersion, bool source = false, bool sink = false);
 };
 
 
diff --git a/Source/UI/EditorViewport.cpp b/Source/UI/EditorViewport.cpp
index fc837d4d5..1f69f9ec9 100755
--- a/Source/UI/EditorViewport.cpp
+++ b/Source/UI/EditorViewport.cpp
@@ -239,7 +239,7 @@ void EditorViewport::itemDropped(const SourceDetails& dragSourceDetails)
     if (canEdit)
     {
 
-        message = "last filter dropped: " + (*description)[0].toString();
+        message = "last filter dropped: " + (*description)[1].toString();
 
         std::cout << "Item dropped at insertion point " << insertionPoint << std::endl;
 
@@ -1195,6 +1195,13 @@ XmlElement* EditorViewport::createNodeXml(GenericEditor* editor,
 
     e->setAttribute("name", name);
     e->setAttribute("insertionPoint", insertionPt);
+	e->setAttribute("pluginName", source->getPluginName());
+	e->setAttribute("pluginType", (int)(source->getPluginType()));
+	e->setAttribute("pluginIndex", source->getIndex());
+	e->setAttribute("libraryName", source->getLibName());
+	e->setAttribute("libraryVersion", source->getLibVersion());
+	e->setAttribute("isSource", source->isSource());
+	e->setAttribute("isSink", source->isSink());
 
     /**Saves individual processor parameters to XML */
     std::cout << "Create subnodes with parameters" << std::endl;
@@ -1510,8 +1517,17 @@ const String EditorViewport::loadState(File fileToLoad)
                         insertionPoint = 0;
                     }
 
-                    //Point<int> pt =
-                    SourceDetails sd = SourceDetails(processor->getStringAttribute("name"),
+                    //See ProcessorGraph::createProcessorFromDescription for description info
+					Array<var> procDesc;
+					procDesc.add(false);
+					procDesc.add(processor->getStringAttribute("pluginName"));
+					procDesc.add(processor->getIntAttribute("pluginType"));
+					procDesc.add(processor->getIntAttribute("pluginIndex"));
+					procDesc.add(processor->getStringAttribute("libraryName"));
+					procDesc.add(processor->getIntAttribute("libraryVersion"));
+					procDesc.add(processor->getBoolAttribute("isSource"));
+					procDesc.add(processor->getBoolAttribute("isSink"));
+                    SourceDetails sd = SourceDetails(procDesc,
                                                      0,
                                                      Point<int>(0,0));
 
diff --git a/Source/UI/ProcessorList.cpp b/Source/UI/ProcessorList.cpp
index 6df56c58d..f0089cfeb 100755
--- a/Source/UI/ProcessorList.cpp
+++ b/Source/UI/ProcessorList.cpp
@@ -490,7 +490,9 @@ void ProcessorList::mouseDrag(const MouseEvent& e)
 
 						Point<int> imageOffset(20,10);
 
+						//See ProcessorGraph::createProcesorFromDescription for description info
 						Array<var> dragData;
+						dragData.add(true);
 						dragData.add(dragDescription);
 						dragData.add(listItem->processorType);
 						dragData.add(listItem->processorId);
-- 
GitLab