Skip to content
Snippets Groups Projects
ProcessorList.cpp 15.56 KiB
/*
	 ------------------------------------------------------------------

	 This file is part of the Open Ephys GUI
	 Copyright (C) 2014 Open Ephys

	 ------------------------------------------------------------------

	 This program is free software: you can redistribute it and/or modify
	 it under the terms of the GNU General Public License as published by
	 the Free Software Foundation, either version 3 of the License, or
	 (at your option) any later version.

	 This program is distributed in the hope that it will be useful,
	 but WITHOUT ANY WARRANTY; without even the implied warranty of
	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	 GNU General Public License for more details.

	 You should have received a copy of the GNU General Public License
	 along with this program.  If not, see <http://www.gnu.org/licenses/>.

 */


#include "ProcessorList.h"
#include <stdio.h>

#include "UIComponent.h"
#include "../AccessClass.h"
#include "../Processors/ProcessorManager/ProcessorManager.h"
#include "../Processors/ProcessorGraph/ProcessorGraph.h"


enum colorIds
{
	PROCESSOR_COLOR = 801,
	FILTER_COLOR = 802,
	SINK_COLOR = 803,
	SOURCE_COLOR = 804,
	UTILITY_COLOR = 805,
};

	ProcessorList::ProcessorList()
: isDragging(false), totalHeight(800), itemHeight(32), subItemHeight(22),
	xBuffer(1), yBuffer(1)
{

	listFontLight = Font("Default Light", 25, Font::plain);
	listFontPlain = Font("Default", 20, Font::plain);

	setColour(PROCESSOR_COLOR, Colour(59, 59, 59));
	setColour(FILTER_COLOR, Colour(0, 174, 239));
	setColour(SINK_COLOR, Colour(0, 166, 81));
	setColour(SOURCE_COLOR, Colour(241, 90, 41));
	setColour(UTILITY_COLOR, Colour(147, 149, 152));
	ProcessorListItem* sources = new ProcessorListItem("Sources");
	ProcessorListItem* filters = new ProcessorListItem("Filters");
	ProcessorListItem* sinks = new ProcessorListItem("Sinks");
	ProcessorListItem* utilities = new ProcessorListItem("Utilities");


	baseItem = new ProcessorListItem("Processors");
	baseItem->addSubItem(sources);
	baseItem->addSubItem(filters);
	baseItem->addSubItem(sinks);
	baseItem->addSubItem(utilities);

	// set parent names / colors
	baseItem->setParentName("Processors");

	for (int n = 0; n < baseItem->getNumSubItems(); n++)
	{
		const String category = baseItem->getSubItem(n)->getName();
		baseItem->getSubItem(n)->setParentName(category);
		for (int m = 0; m < baseItem->getSubItem(n)->getNumSubItems(); m++)
		{
			baseItem->getSubItem(n)->getSubItem(m)->setParentName(category);// = category;
		}
	}
}

ProcessorList::~ProcessorList()
{

}

void ProcessorList::resized()
{
	setBounds(0,0,195,getTotalHeight());
}



bool ProcessorList::isOpen()
{
	return baseItem->isOpen();
}

void ProcessorList::paint(Graphics& g)
{

	drawItems(g);

	///drawButton(g, true);

}


void ProcessorList::drawItems(Graphics& g)
{
	totalHeight = yBuffer + itemHeight;

	category = baseItem->getName();

	drawItem(g, baseItem);

	if (baseItem->isOpen())
	{
		for (int n = 0; n < baseItem->getNumSubItems(); n++)
		{
			setViewport(g, baseItem->hasSubItems());
			category = baseItem->getSubItem(n)->getName();
			drawItem(g, baseItem->getSubItem(n));

			if (baseItem->getSubItem(n)->isOpen())
			{
				for (int m = 0; m < baseItem->getSubItem(n)->getNumSubItems(); m++)
				{

					setViewport(g, baseItem->
							getSubItem(n)->
							getSubItem(m)->
							hasSubItems());
					drawItem(g, baseItem->getSubItem(n)->getSubItem(m));

				}
			}
		}
	}

	if (isOpen())
		setSize(getWidth(),totalHeight);

	//resized();

}

void ProcessorList::drawItem(Graphics& g, ProcessorListItem* item)
{

	Colour c = findColour(item->colorId);

	g.setColour(c);

	if (item->hasSubItems())
		g.fillRect(1.0, 0.0, getWidth()-2, itemHeight);
	else
		g.fillRect(1.0, 10.0, getWidth()-2, subItemHeight);

	drawItemName(g,item);

	if (item->hasSubItems())
	{
		drawButton(g, item->isOpen());
	}
}

void ProcessorList::drawItemName(Graphics& g, ProcessorListItem* item)
{

	String name;

	g.setColour(Colours::white);
	g.setFont(listFontPlain);

	float offsetX, offsetY;

	if (item->getNumSubItems() == 0)
	{
		if (item->isSelected())
		{
			g.drawText(">", 5, 5, getWidth()-9, itemHeight, Justification::left, false);
			// glRasterPos2f(9.0/getWidth(),0.72);
			// getFont(cpmono_plain)->FaceSize(15);
			// getFont(cpmono_plain)->Render(">");
		}

		name = item->getName();

		offsetX = 20.0f;

		offsetY = 0.72f;
	}
	else
	{
		name = item->getName().toUpperCase();
		offsetX = 5.0f;
		offsetY = 0.75f;
	}

	if (item->getNumSubItems() == 0)
	{
		g.setFont(listFontPlain);
		g.drawText(name, offsetX, 5, getWidth()-offsetX, itemHeight, Justification::left, false);

	}
	else
	{
		g.setFont(listFontLight);
		g.drawText(name, offsetX, 0, getWidth()-offsetX, itemHeight, Justification::left, false);
	}


}

void ProcessorList::drawButton(Graphics& g, bool isOpen)
{



	// glColor4f(1.0f,1.0f,1.0f,1.0f);
	// glLineWidth(1.0f);
	// glBegin(GL_LINE_LOOP);

	// if (isOpen)
	// {
	// 	glVertex2f(0.875,0.35);
	// 	glVertex2f(0.9,0.65);
	// } else {
	// 	glVertex2f(0.925,0.65);
	// 	glVertex2f(0.875,0.5);
	// }
	// glVertex2f(0.925,0.35);
	// glEnd();

}

void ProcessorList::clearSelectionState()
{
	baseItem->setSelected(false);

	for (int n = 0; n < baseItem->getNumSubItems(); n++)
	{
		baseItem->getSubItem(n)->setSelected(false);

		for (int m = 0; m < baseItem->getSubItem(n)->getNumSubItems(); m++)
		{
			baseItem->getSubItem(n)->getSubItem(m)->setSelected(false);
		}
	}
}

ProcessorListItem* ProcessorList::getListItemForYPos(int y)
{
	int bottom = (yBuffer + itemHeight); // - getScrollAmount();

	//std::cout << "Bottom: " << bottom << std::endl;
	//std::cout << "Y coordinate: " << y << std::endl;

	if (y < bottom)
	{
		return baseItem;

	}
	else
	{

		if (baseItem->isOpen())
		{
			for (int n = 0; n < baseItem->getNumSubItems(); n++)
			{
				bottom += (yBuffer + itemHeight);

				if (y < bottom)
				{
					return baseItem->getSubItem(n);
				}

				if (baseItem->getSubItem(n)->isOpen())
				{
					for (int m = 0; m < baseItem->getSubItem(n)->getNumSubItems(); m++)
					{
						bottom += (yBuffer + subItemHeight);

						if (y < bottom)
						{
							return baseItem->getSubItem(n)->getSubItem(m);
						}

					}
				}
			}
		}

	}

	return 0;

}

void ProcessorList::setViewport(Graphics& g, bool hasSubItems)
{

	int height;

	if (hasSubItems)
	{
		height = itemHeight;
	}
	else
	{
		height = subItemHeight;
	}

	g.setOrigin(0, yBuffer + height); //xBuffer, getHeight()-(totalHeight) - height + getScrollAmount());


	totalHeight += yBuffer + height;

	//std::cout << totalHeight << std::endl;
}

int ProcessorList::getTotalHeight()
{
	return totalHeight;
}

void ProcessorList::toggleState()
{
	ProcessorListItem* fli = getListItemForYPos(0);
	fli->reverseOpenState();
	AccessClass::getUIComponent()->childComponentChanged();
	repaint();
}

void ProcessorList::mouseDown(const MouseEvent& e)
{

	isDragging = false;

	Point<int> pos = e.getPosition();
	int xcoord = pos.getX();
	int ycoord = pos.getY();

	//std::cout << xcoord << " " << ycoord << std::endl;

	ProcessorListItem* listItem = getListItemForYPos(ycoord);

	if (listItem != 0)
	{
		//std::cout << "Selecting: " << fli->getName() << std::endl;
		if (!listItem->hasSubItems())
		{
			clearSelectionState();
			listItem->setSelected(true);
		}

	}
	else
	{
		//std::cout << "No selection." << std::endl;
	}

	if (listItem != 0)
	{
		if (xcoord < getWidth())
		{
			if (e.mods.isRightButtonDown() || e.mods.isCtrlDown())
			{

				if (listItem->getName().equalsIgnoreCase("Sources"))
				{
					currentColor = SOURCE_COLOR;
				}
				else if (listItem->getName().equalsIgnoreCase("Filters"))
				{
					currentColor = FILTER_COLOR;
				}
				else if (listItem->getName().equalsIgnoreCase("Utilities"))
				{
					currentColor = UTILITY_COLOR;
				}
				else if (listItem->getName().equalsIgnoreCase("Sinks"))
				{
					currentColor = SINK_COLOR;
				}
				else
				{
					return;
				}

				int options=0;
				options += (0 << 0); // showAlpha
				options += (0 << 1); // showColorAtTop
				options += (0 << 2); // showSliders
				options += (1 << 3); // showColourSpace

				ColourSelector colourSelector(options);
				colourSelector.setName("background");
				colourSelector.setCurrentColour(findColour(currentColor));
				colourSelector.addChangeListener(this);
				colourSelector.addChangeListener(AccessClass::getProcessorGraph());
				colourSelector.setColour(ColourSelector::backgroundColourId, Colours::transparentBlack);
				colourSelector.setSize(300, 275);

				juce::Rectangle<int> rect = juce::Rectangle<int>(0,0,10,10);

				CallOutBox callOut(colourSelector, rect, nullptr);
				callOut.setTopLeftPosition(e.getScreenX(), e.getScreenY());
				callOut.setArrowSize(0.0f);

				callOut.runModalLoop();

			}
			else
			{
				listItem->reverseOpenState();
			}
		}

		if (listItem == baseItem)
		{
			if (listItem->isOpen())
			{
				AccessClass::getUIComponent()->childComponentChanged();
			}
			else
			{
				AccessClass::getUIComponent()->childComponentChanged();
				// totalHeight = itemHeight + 2*yBuffer;
			}

		}
	}

	repaint();
}

void ProcessorList::changeListenerCallback(ChangeBroadcaster* source)
{
	ColourSelector* cs = dynamic_cast <ColourSelector*>(source);

	setColour(currentColor, cs->getCurrentColour());

	repaint();

}

void ProcessorList::mouseDrag(const MouseEvent& e)
{

	if (e.getMouseDownX() < getWidth() && !(isDragging))
	{

		ProcessorListItem* listItem = getListItemForYPos(e.getMouseDownY());

		if (listItem != 0)
		{

			if (!listItem->hasSubItems())
			{
				isDragging = true;

				String b = listItem->getName();

				const String dragDescription = b;

				//std::cout << dragDescription << std::endl;

				if (dragDescription.isNotEmpty())
				{
					DragAndDropContainer* const dragContainer
						= DragAndDropContainer::findParentDragContainerFor(this);

					if (dragContainer != 0)
					{
						//pos.setSize (pos.getWidth(), 10);

						Image dragImage(Image::ARGB, 100, 15, true);

						Graphics g(dragImage);
						g.setColour(findColour(listItem->colorId));
						g.fillAll();
						g.setColour(Colours::white);
						g.setFont(14);
						g.drawSingleLineText(listItem->getName(),10,12);//,75,15,Justification::centredRight,true);

						dragImage.multiplyAllAlphas(0.6f);
						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);
						dragData.add(listItem->getParentName());

						dragContainer->startDragging(dragData, this,
								dragImage, true, &imageOffset);
					}
				}
			}
		}
	}

}

void ProcessorList::saveStateToXml(XmlElement* xml)
{
	XmlElement* processorListState = xml->createNewChildElement("PROCESSORLIST");

	for (int i = 0; i < 5; i++)
	{
		XmlElement* colorState = processorListState->createNewChildElement("COLOR");

		int id;

		switch (i)
		{
			case 0:
				id = PROCESSOR_COLOR;
				break;
			case 1:
				id = SOURCE_COLOR;
				break;
			case 2:
				id = FILTER_COLOR;
				break;
			case 3:
				id = SINK_COLOR;
				break;
			case 4:
				id = UTILITY_COLOR;
				break;
			default:
				// do nothing
				;
		}

		Colour c = findColour(id);

		colorState->setAttribute("ID", (int) id);
		colorState->setAttribute("R", (int) c.getRed());
		colorState->setAttribute("G", (int) c.getGreen());
		colorState->setAttribute("B", (int) c.getBlue());

	}
}

void ProcessorList::loadStateFromXml(XmlElement* xml)
{
	forEachXmlChildElement(*xml, xmlNode)
	{
		if (xmlNode->hasTagName("PROCESSORLIST"))
		{
			forEachXmlChildElement(*xmlNode, colorNode)
			{
				setColour(colorNode->getIntAttribute("ID"),
						Colour(
							colorNode->getIntAttribute("R"),
							colorNode->getIntAttribute("G"),
							colorNode->getIntAttribute("B")));
			}
		}
	}

	repaint();

	AccessClass::getProcessorGraph()->refreshColors();
}

Array<Colour> ProcessorList::getColours()
{
	Array<Colour> c;

	c.add(findColour(PROCESSOR_COLOR));
	c.add(findColour(SOURCE_COLOR));
	c.add(findColour(FILTER_COLOR));
	c.add(findColour(SINK_COLOR));
	c.add(findColour(UTILITY_COLOR));
	return c;
}

void ProcessorList::setColours(Array<Colour> c)
{
	for (int i = 0; i < c.size(); i++)
	{
		switch (i)
		{
			case 0:
				setColour(PROCESSOR_COLOR, c[i]);
				break;
			case 1:
				setColour(SOURCE_COLOR, c[i]);
				break;
			case 2:
				setColour(FILTER_COLOR, c[i]);
				break;
			case 3:
				setColour(SINK_COLOR, c[i]);
				break;
			case 4:
				setColour(UTILITY_COLOR, c[i]);
				break;
			default:
				;// do nothing
		}
	}
}

void ProcessorList::fillItemList()
{
	int num;
	baseItem->getSubItem(0)->clearSubItems(); //Sources
	baseItem->getSubItem(1)->clearSubItems(); //Filters
	baseItem->getSubItem(2)->clearSubItems(); //sinks
	baseItem->getSubItem(3)->clearSubItems(); //Utilities

	for (int pClass = 0; pClass < 3; pClass++)
	{
		num = ProcessorManager::getNumProcessors((ProcessorClasses)pClass);
		for (int i = 0; i < num; i++)
		{
			String name;
			int type = -1;
			ProcessorManager::getProcessorNameAndType((ProcessorClasses)pClass, i, name, type);
			if (type > -1 && type < 4)
			{
				baseItem->getSubItem(type)->addSubItem(new ProcessorListItem(name, i, pClass));
			}
		}
	}


	for (int n = 0; n < baseItem->getNumSubItems(); n++)
	{
		const String category = baseItem->getSubItem(n)->getName();
		baseItem->getSubItem(n)->setParentName(category);
		for (int m = 0; m < baseItem->getSubItem(n)->getNumSubItems(); m++)
		{
			baseItem->getSubItem(n)->getSubItem(m)->setParentName(category);// = category;
		}
	}

}

// ===================================================================

	ProcessorListItem::ProcessorListItem(const String& name_, int pid, int ptype)
		: processorId(pid), processorType(ptype), selected(false), open(true), name(name_)
{
}

ProcessorListItem::~ProcessorListItem()
{ }

bool ProcessorListItem::hasSubItems()
{
	if (subItems.size() > 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}

int ProcessorListItem::getNumSubItems()
{
	return subItems.size();
}

ProcessorListItem* ProcessorListItem::getSubItem(int index)
{
	return subItems[index];
}

void ProcessorListItem::clearSubItems()
{
	subItems.clear();
}

void ProcessorListItem::addSubItem(ProcessorListItem* newItem)
{
	subItems.add(newItem);
}

void ProcessorListItem::removeSubItem(int index)
{
	subItems.remove(index);
}

bool ProcessorListItem::isOpen()
{
	return open;
}

void ProcessorListItem::setOpen(bool t)
{
	open = t;
}

const String& ProcessorListItem::getName()
{
	return name;
}


const String& ProcessorListItem::getParentName()
{
	return parentName;
}

void ProcessorListItem::setParentName(const String& name)
{
	parentName = name;

	if (parentName.equalsIgnoreCase("Processors"))
	{
		colorId = PROCESSOR_COLOR;
	}
	else if (parentName.equalsIgnoreCase("Filters"))
	{
		colorId = FILTER_COLOR;
	}
	else if (parentName.equalsIgnoreCase("Sinks"))
	{
		colorId = SINK_COLOR;
	}
	else if (parentName.equalsIgnoreCase("Sources"))
	{
		colorId = SOURCE_COLOR;
	}
	else
	{
		colorId = UTILITY_COLOR;
	}
}