Blob Blame History Raw
/*
 *  qlo10k1 - GUI frontend for ld10k1
 *
 *  Copyright (c) 2004 by Peter Zubaj
 *
 *   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 2 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, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
 
#include <qdockarea.h>
#include <qtoolbutton.h>
#include <qspinbox.h>
#include <qregion.h>
#include <qpushbutton.h>
#include <qpainter.h>
#include <qtooltip.h>

#include "routingwidget.h"
#include "structure.h"
#include "strglobal.h"
#include "structure_io.h"
#include "structure_patch.h"

//icons
#include "icons/routing_fx.xpm"
#include "icons/routing_in.xpm"
#include "icons/routing_out.xpm"
#include "icons/routing_route.xpm"
#include "icons/routing_effect.xpm"
#include "icons/routing_effect_stack.xpm"
#include "icons/routing_zoom_plus.xpm"
#include "icons/routing_zoom_minus.xpm"
#include "icons/routing_normal.xpm"

class RoutingToolTip : public QToolTip
{
protected:
	virtual void maybeTip(const QPoint &p);
public:
	RoutingToolTip(QWidget *widget, QToolTipGroup *group = 0);
};

RoutingDrawWidget::RoutingDrawWidget(RoutingWidget *parent, const char *name, WFlags f)
	: QScrollView(parent, name, f)
{
	setFocusPolicy(QWidget::StrongFocus);
	resizeContents(4000,3000);
	this->viewport()->setEraseColor(QColor(255,255,255));
	resizeItem = NULL;
	
	mode = None;
	
	//enable tooltips
	new RoutingToolTip(this);
}

RoutingDrawWidget::~RoutingDrawWidget()
{
}

void RoutingDrawWidget::drawContents(QPainter* p, int cx, int cy, int cw, int ch)
{
	RoutingWidget *parent = (RoutingWidget *)parentWidget();
	StrGlobal *structure = parent->getStructure();
	
	if (!structure)
		return;
	
	RSItemBaseWithType *item;
	DrawingParams dp(parent->getZoomLevel(), this->viewport(), NULL, p);
	
	p->setFont(createFont(parent->getZoomLevel()));
		
	for(item = structure->UsedItems.first(); item; item = structure->UsedItems.next() )
	{
		if (item->flagNew() || item->flagChanged())
		{
			item->calcSize();
			item->setFlagNew(false);
			item->setFlagChanged(false);
		}
		
 		item->draw(&dp);
	}
	
	// if in link mode
	if (mode == DragLink)
	{
		QPainter p(this->viewport());
		int piox, pioy;
		int ex = selectEndPoint.x();
		int ey = selectEndPoint.y();
		
		((StrPatch *)linkStart->getOwner())->getIOPoint(linkStart, &piox, &pioy);
		contentsToViewport(parent->zoomVal(piox), parent->zoomVal(pioy), piox, pioy);
		contentsToViewport(parent->zoomVal(ex), parent->zoomVal(ey), ex, ey);
		
		p.setPen(LINE_PEN);
		p.drawLine(QPoint(piox, pioy), QPoint(ex, ey));
		p.setPen(DEF_PAINTING_PEN);
	}
}

void RoutingDrawWidget::mousePressEvent(QMouseEvent *e)
{
	QScrollView::mousePressEvent(e);
	setFocus();
	
	RoutingWidget *parent = (RoutingWidget *)parentWidget();
	StrGlobal *structure = parent->getStructure();
	
	if (!structure)
		return;
		
	//treba prepocet na zoom a ofset qscrollview - a niekde to pada
	int xp = e->x();
	int yp = e->y();
	
	viewportToContents(xp, yp, xp, yp);
	
	xp = parent->deZoomVal(xp);
	yp = parent->deZoomVal(yp);
	
	RSItemBaseWithType *item;
	
	QPtrList <RSItemBaseWithType> toClear;
	
	if (e->button() == MidButton)
		return;

	if (mode == DragLink)
	{
		QPoint tmpp = mapToGlobal(QPoint(e->x(), e->y()));
		connectLinkDrag(xp, yp, tmpp.x(), tmpp.y());
		return;
	}
		
	// get mode
	if (parent->getWidgetMode() != RoutingWidget::Normal &&
		e->button() == LeftButton)
	{
		parent->putNewObjectAt(xp, yp);
		return;
	}
		
		
	// find item under this point
	bool oneSelected = false;
	bool clearOthers = false;
	
	RSItemBase::RSItemHandle resHandle = RSItemBase::None;
	RSItemBaseWithType *itemToResize = NULL;
	RSItemBaseWithType *itemOn = NULL;
	linkHandle = -1;
	
	for(item = structure->UsedItems.first(); item; item = structure->UsedItems.next())
	{
		bool redraw = false;
		
		if (!oneSelected && item->containsPoint(xp, yp))
		{
			if (!item->flagSelected())
			{
				item->setFlagSelected(true);
				redraw = true;
				clearOthers = true;
			}
			else if (e->state() & Qt::ControlButton)
			{
				item->setFlagSelected(false);
				redraw = true;
			}
			oneSelected = true;
			
			itemOn = item;
			
			// check resize point
			resHandle = item->getHandlePoint(xp, yp);
			if (resHandle == RSItemBase::Other)
			{
				itemToResize = item;
				linkHandle = item->getHandlePointNum(xp, yp);
			}
			else
			{
				linkHandle = -1;			
				if (resHandle != RSItemBase::None)
					itemToResize = item;
			}
		}
		else
		{
			if (item->flagSelected())
				toClear.append(item);
		}
				
		if (redraw)
		{
			this->updateContents(parent->zoomVal(item->x()),
				parent->zoomVal(item->y()),
				parent->zoomVal(item->width()),
				parent->zoomVal(item->height()));
		}
	}
	
	EditMode newMode = None;
	RoutingWidget::MenuMode newMenuMode = RoutingWidget::MenuNone;
	
	parent->itemOn = NULL;
	
	bool doClearOthers = false;
	
	if (e->button() == LeftButton)
	{
		if (!oneSelected)
		{
			// set select mode
			newMode = Select;
		}
		else 
		{
			if (e->state() & Qt::ControlButton)
			{
				newMode = None;
			}
			else
			{
				if (clearOthers)
					doClearOthers = true;
				if (itemToResize)
				{
					resizeItem = itemToResize;
						
					if (linkHandle < 0)
					{
						switch (resHandle)
						{
							case RSItemBase::TopLeft:
								newMode = ResizeTopLeft;
								break;
							case RSItemBase::TopRight:
								newMode = ResizeTopRight;
								break;
							case RSItemBase::BottomLeft:
								newMode = ResizeBottomLeft;
								break;
							case RSItemBase::BottomRight:
								newMode = ResizeBottomRight;
								break;
							default:
								break;
						}
					}
					else
						newMode = HandleMove;
				}
				else if(itemOn->type() != RSItemBaseWithType::Link)
					newMode = Move;
				else
					newMode = None;
			}
		}
	}
	else
	{
		if (!oneSelected)
		{
			newMenuMode = RoutingWidget::MenuNone;
			doClearOthers = true;
		}
		else
		{
			if (clearOthers)
			{
				doClearOthers = true;
			}
				
			if (!doClearOthers && toClear.count() > 0)
				newMenuMode = RoutingWidget::MenuObjects;
			else
			{
				switch (itemOn->type())
				{
					case RSItemBaseWithType::In:
					case RSItemBaseWithType::Out:
					case RSItemBaseWithType::FX:
						newMenuMode = RoutingWidget::MenuIO;
						break;
					case RSItemBaseWithType::Link:
						newMenuMode = RoutingWidget::MenuLink;
						break;
					case RSItemBaseWithType::Patch:
						newMenuMode = RoutingWidget::MenuPatch;
						break;
					default:
						break;
				}
			}
		}
		
		parent->itemOn = itemOn;
		parent->posXOn = xp;
		parent->posYOn = yp;
	}
	
	if (doClearOthers)
	{
		for(item = toClear.first(); item; item = toClear.next())
		{
			item->setFlagSelected(false);
			
			this->updateContents(parent->zoomVal(item->x()),
				parent->zoomVal(item->y()),
				parent->zoomVal(item->width()),
				parent->zoomVal(item->height()));
		}		
	}
	
	if (e->button() == LeftButton)
		mode = newMode;
	else
	{
		QPoint p = mapToGlobal(QPoint(e->x(), e->y()));
		parent->openObjectMenuAt(itemOn, newMenuMode, xp, yp, p.x(), p.y());
	}
	
	selectStartPoint.setX(e->x());
	selectStartPoint.setY(e->y());
	selectEndPoint = selectStartPoint;

}

void RoutingDrawWidget::mouseMoveEvent(QMouseEvent *e)
{
	RoutingWidget *parent = (RoutingWidget *)parentWidget();
	StrGlobal *structure = parent->getStructure();
	
	if (!structure)
		return;
		
	if (mode == Select && 
		(e->x() != selectStartPoint.x() || e->y() != selectStartPoint.y()))
	{
		QPainter p(this->viewport());
		
		p.drawWinFocusRect(selectStartPoint.x(),
			selectStartPoint.y(),
			selectEndPoint.x() - selectStartPoint.x(),
			selectEndPoint.y() - selectStartPoint.y());
			
		selectEndPoint.setX(e->x());
		selectEndPoint.setY(e->y());
			
		p.drawWinFocusRect(selectStartPoint.x(),
			selectStartPoint.y(),
			selectEndPoint.x() - selectStartPoint.x(),
			selectEndPoint.y() - selectStartPoint.y());
	}
	else
	{
		int dx = parent->deZoomVal((selectEndPoint.x() - selectStartPoint.x()));
		int dy = parent->deZoomVal((selectEndPoint.y() - selectStartPoint.y()));
		
		if (mode == Move)
		{
			RSItemBaseWithType *item;
			for(item = structure->UsedItems.first(); item; item = structure->UsedItems.next() )
			{
				if (item->flagSelected())
				{
					// update old
					item->updateContents(this, parent->getZoomLevel());
						
					item->moveRel(dx, dy);
					// update new
					item->updateContents(this, parent->getZoomLevel());
				}
			}
		}
		else if (mode == HandleMove)
		{
			// update old
			resizeItem->updateContents(this, parent->getZoomLevel());
				
			resizeItem->moveHandleRel(linkHandle, dx, dy);
			// update new
			resizeItem->updateContents(this, parent->getZoomLevel());
		}
		else if (mode == ResizeTopLeft || 
			mode == ResizeTopRight ||
			mode == ResizeBottomLeft ||
			mode == ResizeBottomRight)
		{
			int nx = resizeItem->x();
			int ny = resizeItem->y();
			int nw = resizeItem->width();
			int nh = resizeItem->height();
								
			switch (mode)
			{
				case ResizeTopLeft:
					nx = nx + dx;
					ny = ny + dy;
					nw = nw - dx;
					nh = nh - dy;
					break;
				case ResizeTopRight:
					ny = ny + dy;
					nw = nw + dx;
					nh = nh - dy;
					break;
				case ResizeBottomLeft:
					nx = nx + dx;
					nw = nw - dx;
					nh = nh + dy;
					break;
				case ResizeBottomRight:
					nw = nw + dx;
					nh = nh + dy;
					break;
				default:
					break;
			}
			
			if (nx < 0)
				nx = 0;
			if (ny < 0)
				ny = 0;

			if (nw < resizeItem->minWidth())
				nw = resizeItem->minWidth();
			if (nh < resizeItem->minHeight())
				nh = resizeItem->minHeight();
				
			resizeItem->updateContents(this, parent->getZoomLevel());

			resizeItem->setPosition(nx, ny);
			resizeItem->setSize(nw, nh);
			// update new
			resizeItem->updateContents(this, parent->getZoomLevel());
		}
		else if (mode == DragLink)
		{
			updateDragLink();
		}
		
		selectStartPoint = selectEndPoint;
	
		selectEndPoint.setX(e->x());
		selectEndPoint.setY(e->y());
		
		if (mode == DragLink)
		{
			int ex = selectEndPoint.x();
			int ey = selectEndPoint.y();
			
			viewportToContents(parent->deZoomVal(ex), parent->deZoomVal(ey), ex, ey);
			
			selectEndPoint.setX(ex);
			selectEndPoint.setY(ey);
		}
	}
}

void RoutingDrawWidget::updateDragLink()
{
	RoutingWidget *parent = (RoutingWidget *)parentWidget();
	
	int piox, pioy;
			
	int ex = selectEndPoint.x();
	int ey = selectEndPoint.y();

	((StrPatch *)linkStart->getOwner())->getIOPoint(linkStart, &piox, &pioy);
	
	QRect rect(QPoint(piox, pioy), QPoint(ex, ey));
	rect = rect.normalize();
	rect.setX(rect.x() - 1);
	rect.setY(rect.y() - 1);
	rect.setWidth(rect.width() + 2 * 1);
	rect.setHeight(rect.height() + 2 * 1);
	this->updateContents(parent->zoomVal(rect.x()),
		parent->zoomVal(rect.y()),
		parent->zoomVal(rect.width()),
		parent->zoomVal(rect.height()));
}

void RoutingDrawWidget::mouseReleaseEvent(QMouseEvent *e)
{
	RoutingWidget *parent = (RoutingWidget *)parentWidget();
	StrGlobal *structure = parent->getStructure();
	
	if (!structure)
		return;
		
	if (mode == Select)
	{
		selectEndPoint.setX(e->x());
		selectEndPoint.setY(e->y());
		
		QRect selArea(selectStartPoint, selectEndPoint);
		selArea = selArea.normalize();
		
		// transform
		int xt1;
		int yt1;
		
		int xt2;
		int yt2;
		
		viewportToContents(selArea.x(), selArea.y(), xt1, yt1);
		xt1 = parent->deZoomVal(xt1);
		yt1 = parent->deZoomVal(yt1);
		
		viewportToContents(selArea.right(), selArea.bottom(), xt2, yt2);
		xt2 = parent->deZoomVal(xt2);
		yt2 = parent->deZoomVal(yt2);
		
		// set flag for selected
		QRect selAreaTrans(QPoint(xt1, yt1), QPoint(xt2, yt2));
		
		RSItemBaseWithType *item;		
		for(item = structure->UsedItems.first(); item; item = structure->UsedItems.next() )
		{
			if (selAreaTrans.contains(item->x(), item->y()) &&
				selAreaTrans.contains(item->x() + item->width(), item->y() + item->height()))
			{
				if (!item->flagSelected())
					item->setFlagSelected(true);
			}
			else
			{
				if (!(e->state() & Qt::ControlButton) && item->flagSelected()) 
				{
					item->setFlagSelected(false);
					
					this->updateContents(parent->zoomVal(item->x()),
						parent->zoomVal(item->y()),
						parent->zoomVal(item->width()),
						parent->zoomVal(item->height()));
				}
			}
			
			
		}
		
		//this->updateContents(selArea);
		this->viewport()->update(selArea);
	}
	
	if (mode != DragLink)
		mode = None;
}

void RoutingDrawWidget::keyPressEvent(QKeyEvent *e)
{
	if (e->key() == Key_Escape && isDragLink())
		stopLinkDrag();
	
	if (e->key() == Key_Delete && !isDragLink())
	{
		deleteAllSelected();
	}
}

void RoutingDrawWidget::startLinkDrag(RSItemIO *si, int xp, int yp)
{
	mode = DragLink;
	linkStart = si;
	
	selectStartPoint.setX(xp);
	selectStartPoint.setY(yp);
	selectEndPoint = selectStartPoint;
	
	setMouseTracking(true);
	viewport()->setMouseTracking(true);
}

void RoutingDrawWidget::stopLinkDrag()
{
	mode = None;
	updateDragLink();
}

QFont &RoutingDrawWidget::createFont(float zoom)
{
	//QFont f("Helvetica", 10 * zoom);
	titleFont.setStyleHint(QFont::Helvetica);
	titleFont.setPointSize((int)(10 * zoom));
	return titleFont;
}


RoutingWidget::RoutingWidget(QWidget *parent, const char *name, WFlags f)
	: QWidget(parent, name, f)
{
	structure = NULL;
	setFocusPolicy(QWidget::StrongFocus);

	RoutingWidgetLayout = new QVBoxLayout(this);

	createButtons();

	drawing = new RoutingDrawWidget(this);

	RoutingWidgetLayout->addWidget(drawing/*, 1, 1*/);
	drawing->setUpdatesEnabled(TRUE);

	widgetMode = Normal;
	toggleMode(widgetMode);

	setZoomLevel(1.00);
	updateZoomLevelCtrl(100);
}

void RoutingWidget::createButtons()
{
	QHBoxLayout *hbox = new QHBoxLayout(RoutingWidgetLayout);


	pbRoutingNormal = new QPushButton(this);
	pbRoutingNormal->setIconSet(QIconSet(QPixmap((const char**)routing_normal)));
	hbox->addWidget(pbRoutingNormal);
	pbRoutingNormal->setToggleButton(true);

	pbRoutingFX = new QPushButton(this);
	pbRoutingFX->setIconSet(QIconSet(QPixmap((const char**)routing_fx)));
	hbox->addWidget(pbRoutingFX);
	pbRoutingFX->setToggleButton(true);

	pbRoutingIn = new QPushButton(this);
	pbRoutingIn->setIconSet(QIconSet(QPixmap((const char**)routing_in)));
	hbox->addWidget(pbRoutingIn);
	pbRoutingIn->setToggleButton(true);

	pbRoutingOut = new QPushButton(this);
	pbRoutingOut->setIconSet(QIconSet(QPixmap((const char**)routing_out)));
	hbox->addWidget(pbRoutingOut);
	pbRoutingOut->setToggleButton(true);

	pbRoutingRoute = new QPushButton(this);
	pbRoutingRoute->setIconSet(QIconSet(QPixmap((const char**)routing_route)));
	hbox->addWidget(pbRoutingRoute);
	pbRoutingRoute->setToggleButton(true);

	pbRoutingEffect = new QPushButton(this);
	pbRoutingEffect->setIconSet(QIconSet(QPixmap((const char**)routing_effect)));
	hbox->addWidget(pbRoutingEffect);
	pbRoutingEffect->setToggleButton(true);

	pbRoutingEffectStack = new QPushButton(this);
	pbRoutingEffectStack->setIconSet(QIconSet(QPixmap((const char**)routing_effect_stack)));
	hbox->addWidget(pbRoutingEffectStack);
	pbRoutingEffectStack->setToggleButton(true);
	
	pbRoutingEffectStack->hide();

	QSpacerItem *spacer1 = new QSpacerItem(10, 0, QSizePolicy::Minimum, QSizePolicy::Minimum);
	hbox->addItem(spacer1);

	QPushButton *pbRoutingZoomPlus = new QPushButton(this);
	pbRoutingZoomPlus->setIconSet(QIconSet(QPixmap((const char**)routing_zoom_plus)));
	hbox->addWidget(pbRoutingZoomPlus);

	sbRoutingZoom = new QSpinBox(this);
	hbox->addWidget(sbRoutingZoom);
	sbRoutingZoom->setMinValue(10);
	sbRoutingZoom->setMaxValue(500);

	QPushButton *pbRoutingZoomMinus = new QPushButton(this);
	pbRoutingZoomMinus->setIconSet(QIconSet(QPixmap((const char**)routing_zoom_minus)));
	hbox->addWidget(pbRoutingZoomMinus);


	QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
	hbox->addItem(spacer);

	connect(pbRoutingNormal, SIGNAL(clicked()), this, SLOT(modeNormalClicked()));
	connect(pbRoutingFX, SIGNAL(clicked()), this, SLOT(modeFxClicked()));
	connect(pbRoutingIn, SIGNAL(clicked()), this, SLOT(modeInClicked()));
	connect(pbRoutingOut, SIGNAL(clicked()), this, SLOT(modeOutClicked()));
	connect(pbRoutingRoute, SIGNAL(clicked()), this, SLOT(modeRouteClicked()));
	connect(pbRoutingEffect, SIGNAL(clicked()), this, SLOT(modeEffectClicked()));
	connect(pbRoutingEffectStack, SIGNAL(clicked()), this, SLOT(modeEffectStackClicked()));

	connect(sbRoutingZoom, SIGNAL(valueChanged(int)), this, SLOT(zoomValueChanged(int)));

	connect(pbRoutingZoomMinus, SIGNAL(clicked()), this, SLOT(zoomOutClicked()));
	connect(pbRoutingZoomPlus, SIGNAL(clicked()), this, SLOT(zoomInClicked()));
}

RoutingWidget::~RoutingWidget()
{
}

void RoutingWidget::untoggleMode(EditMode m)
{
	if (m != Normal)
		pbRoutingNormal->setOn(false);
	if (m != FX)
		pbRoutingFX->setOn(false);
	if (m != In)
		pbRoutingIn->setOn(false);
	if (m != Out)
		pbRoutingOut->setOn(false);
	if (m != Route)
		pbRoutingRoute->setOn(false);
	if (m != Effect)
		pbRoutingEffect->setOn(false);
	if (m != EffectStack)
		pbRoutingEffectStack->setOn(false);
}

void RoutingWidget::toggleMode(EditMode m)
{
	switch (m)
	{
		case Normal:
			pbRoutingNormal->setOn(true);
			break;
		case FX:
			pbRoutingFX->setOn(true);
			break;
		case In:
			pbRoutingIn->setOn(true);
			break;
		case Out:
			pbRoutingOut->setOn(true);
			break;
		case Route:
			pbRoutingRoute->setOn(true);
			break;
		case Effect:
			pbRoutingEffect->setOn(true);
			break;
		case EffectStack:
			pbRoutingEffectStack->setOn(true);
			break;
	}
}

void RoutingWidget::modeNormalClicked()
{
	widgetMode = Normal;
	toggleMode(widgetMode);
	untoggleMode(widgetMode);
}

void RoutingWidget::modeFxClicked()
{
	widgetMode = FX;
	toggleMode(widgetMode);
	untoggleMode(widgetMode);
}

void RoutingWidget::modeInClicked()
{
	widgetMode = In;
	toggleMode(widgetMode);
	untoggleMode(widgetMode);
}

void RoutingWidget::modeOutClicked()
{
	widgetMode = Out;
	toggleMode(widgetMode);
	untoggleMode(widgetMode);
}

void RoutingWidget::modeRouteClicked()
{
	widgetMode = Route;
	toggleMode(widgetMode);
	untoggleMode(widgetMode);
}

void RoutingWidget::modeEffectClicked()
{
	widgetMode = Effect;
	toggleMode(widgetMode);
	untoggleMode(widgetMode);
}

void RoutingWidget::modeEffectStackClicked()
{
	widgetMode = EffectStack;
	toggleMode(widgetMode);
	untoggleMode(widgetMode);
}

void RoutingWidget::setZoomLevel(float level)
{
	if (level < 0.10)
		level = 0.10;
	if (level > 5.00)
		level = 5.00;

	zoomLevel = level;
	drawing->viewport()->update();
	drawing->resizeContents((int)(4000 * zoomLevel), (int)(3000 * zoomLevel));
}

void RoutingWidget::updateZoomLevelCtrl(int level)
{
	sbRoutingZoom->setValue(level);
}

float RoutingWidget::getZoomLevel()
{
	return zoomLevel;
}

void RoutingWidget::zoomValueChanged(int value)
{
	setZoomLevel(value / 100.0);
}

void RoutingWidget::zoomInClicked()
{
	setZoomLevel(zoomLevel + 0.10);
	updateZoomLevelCtrl((int)(zoomLevel * 100));
}

void RoutingWidget::zoomOutClicked()
{
	setZoomLevel(zoomLevel - 0.10);
	updateZoomLevelCtrl((int)(zoomLevel * 100));
}

void RoutingWidget::refreshDrawing(StrGlobal *str)
{
	structure = str;
	
	drawing->updateContents();
}

RoutingWidget::EditMode RoutingWidget::getWidgetMode()
{
	return widgetMode;
}

void RoutingWidget::putNewObjectAt(int xp, int yp)
{
	RSItemBaseWithType *newItem = NULL;
	
	switch (widgetMode)
	{
		case FX:
		case In:
		case Out:
			newItem = createNewIO(widgetMode);
			if (widgetMode == FX)
				structure->FXs.append((StrFX *)newItem);
			else if (widgetMode == In)
				structure->Inputs.append((StrInput *)newItem);
			else
				structure->Outputs.append((StrOutput *)newItem);
			break;
		case Effect:
			newItem = createNewPatch();
			structure->updatePatchesOrder();
			break;
		case Route:
			if (!drawing->isDragLink())
				startLinkDrag(xp, yp);
			return;
		default:
			break;
	}
	
	if (newItem)
	{
		if (newItem->type() != RSItemBaseWithType::Patch)
			structure->UsedItems.append(newItem);
		newItem->setPosition(xp, yp);
		
		drawing->updateContents();
	
		//modeNormalClicked();
	}
}




RoutingToolTip::RoutingToolTip(QWidget *widget, QToolTipGroup *group) : QToolTip(widget, group)
{
}

void RoutingToolTip::maybeTip(const QPoint &p)
{
	RoutingDrawWidget *drawWidget = (RoutingDrawWidget *)parentWidget();
	RoutingWidget *parent = (RoutingWidget *)drawWidget->parentWidget();
	StrGlobal *structure = parent->getStructure();
	
	if (!structure)
		return;
		
	int i, j;
	// find object under cursor
	int xp = p.x();
	int yp = p.y();
	
	drawWidget->viewportToContents(xp, yp, xp, yp);
	
	xp = parent->deZoomVal(xp);
	yp = parent->deZoomVal(yp);
	
	RSItemBaseWithType *item;

	for(item = structure->UsedItems.first(); item; item = structure->UsedItems.next())
	{
		if (item->containsPoint(xp, yp))
		{
			int ix;
			int iy;
			int iw;
			int ih;
			
			QString tipText = "";
			
			StrInput *in = NULL;
			StrOutput *out = NULL;
			StrFX *fx = NULL;
			StrPatch *patch = NULL;
			
			ix = item->x();
			iy = item->y();
			iw = item->width();
			ih = item->height();
			
			RSItemIO *pio = NULL;
			
			switch(item->type())
			{
				case RSItemBaseWithType::In:
					in = (StrInput *)item;
					tipText = QString("Input: %1\nName: %2").arg(in->num()).arg(in->name());
					break;
				case RSItemBaseWithType::Out:
					out = (StrOutput *)item;
					tipText = QString("Output: %1\nName: %2").arg(out->num()).arg(out->name());
					break;
				case RSItemBaseWithType::FX:
					fx = (StrFX *)item;
					tipText = QString("FX: %1\nName: %2").arg(fx->num()).arg(fx->name());
					break;
				case RSItemBaseWithType::Patch:
					patch = (StrPatch *)item;
					
					pio = patch->getIOAtPoint(xp, yp);
					if (pio)
					{
						if (pio->isOutput())
							tipText = QString("Patch output: %1\nOutput name: %2\n\n").arg(pio->getIdx()).arg(pio->getDesc());
						else
							tipText = QString("Patch input: %1\nInput name: %2\n\n").arg(pio->getIdx()).arg(pio->getDesc());
						
						QRect r;
						patch->getIORect(pio->isOutput(), pio->getIdx(), r);
						
						ix = r.x();
						iy = r.y();
						iw = r.width();
						ih = r.height();
					}
					else
					{
						// create region without ios
						QRegion reg(ix, iy, iw, ih);
						QRect ior;
						
						for (j = 0; j < 2; j++)
						{
							for (i = 0; i < patch->getMaxIOIdx(j); i++)
							{
								patch->getIORect(j, i, ior);
								reg = reg.subtract(QRegion(ior));
							}
						}
						
						// find rect with point
						QMemArray<QRect> rects = reg.rects();
						
						for (i = 0; i < (int)rects.count(); i++)
						{
							if (rects[i].contains(xp, yp))
							{
								ix = rects[i].x();
								iy = rects[i].y();
								iw = rects[i].width();
								ih = rects[i].height();
								break;
							}
						}
					}
					
					tipText = tipText + QString("Patch: %1\nOrder: %2\nName: %3").arg(patch->num()).arg(patch->order()).arg(patch->name());
					break;
				default:
					break;
			}
			
			if (tipText != "")
			{
				ix = parent->zoomVal(ix);
				iy = parent->zoomVal(iy);
				iw = parent->zoomVal(iw);
				ih = parent->zoomVal(ih);
			
				drawWidget->contentsToViewport(ix, iy, ix, iy);
				
				tip(QRect(ix, iy, iw, ih), tipText);
			}
		}
	}	
}