//
//   File : canvaswidget.cpp
//   Creation date : Mon Jul 30 07 2001 04:50:50 by Szymon Stefanek
//
//   This file is part of the KVirc irc client distribution
//   Copyright (C) 2001 Szymon Stefanek (pragma at kvirc dot net)
//
//   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 opinion) 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. ,59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//

#include "canvaswidget.h"

#ifdef COMPILE_DCC_CANVAS


#include <qcursor.h>
#include <qpainter.h>
#include <qsimplerichtext.h>
#include <qlineedit.h>
#include <qcombobox.h>
#include <qvalidator.h>
#include <stdlib.h>

#include "kvi_string.h"

#include "kvi_locale.h"
#include "kvi_tal_popupmenu.h"

#include <math.h>

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasRectangleItem
//

KviCanvasRectangleItem::KviCanvasRectangleItem(QCanvas * c,int x,int y,int w,int h)
: QCanvasRectangle(x,y,w,h,c)
{
}

KviCanvasRectangleItem::~KviCanvasRectangleItem()
{
}

void KviCanvasRectangleItem::drawSelection(QPainter &p)
{
	p.setRasterOp(NotROP);
	p.fillRect((int)x() + 1,(int)y() + 1,width() - 2,height() - 2,QBrush(Dense6Pattern));
	p.setPen(QPen(DotLine));
	p.drawRect((int)x(),(int)y(),width(),height());
	p.setRasterOp(CopyROP);
}


void KviCanvasRectangleItem::setProperty(const QString &property,const QVariant &val)
{
	if(m_properties[property].isValid())
	{
		m_properties.replace(property,val);
		hide();
		show();
	}
}




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasPolygon
//


KviCanvasPolygon::KviCanvasPolygon(QCanvas * c,int x,int y,QPointArray &pnts,double dScaleFactor)
: QCanvasPolygon(c)
{
	m_properties.insert("clrForeground",QVariant(QColor(0,0,0)));
	m_properties.insert("uLineWidth",QVariant((unsigned int)0));

	m_properties.insert("clrBackground",QVariant(QColor(0,0,0)));
	m_properties.insert("bHasBackground",QVariant(false,1));

	m_dScaleFactor = dScaleFactor;
	m_points = pnts;

	resetPoints();
	move(x,y);
}


KviCanvasPolygon::~KviCanvasPolygon()
{
}

void KviCanvasPolygon::setScaleFactor(double dScaleFactor)
{
	m_dScaleFactor = dScaleFactor;
	resetPoints();
}

void KviCanvasPolygon::setInternalPoints(const QPointArray &pnts)
{
	m_points = pnts;
	resetPoints();
	
}

void KviCanvasPolygon::resetPoints()
{
	QPointArray scaled(m_points.size());
	for(unsigned int i=0;i<m_points.size();i++)
	{
		int px;
		int py;
		m_points.point(i,&px,&py);
		px = (int)(px * m_dScaleFactor);
		py = (int)(py * m_dScaleFactor);
		scaled.setPoint(i,px,py);
	}
	setPoints(scaled);
}

int KviCanvasPolygon::rtti() const
{
	return KVI_CANVAS_RTTI_POLYGON;
}

void KviCanvasPolygon::setProperty(const QString &property,const QVariant &val)
{
	if(m_properties[property].isValid())
	{
		m_properties.replace(property,val);
		if((property == "clrForeground") || (property == "uLineWidth"))
		{
			setPen(QPen(m_properties["clrForeground"].asColor(),m_properties["uLineWidth"].toInt()));
		} else if((property == "clrBackground") || (property == "bHasBackground"))
		{
			if(m_properties["bHasBackground"].asBool())
				setBrush(QBrush(m_properties["clrBackground"].asColor()));
			else
				setBrush(QBrush());
		} else {
			hide(); show();
		}
	}
}

void KviCanvasPolygon::draw(QPainter &p)
{
#if QT_VERSION >= 300
	if(isEnabled())
#else
	if(enabled())
#endif
	{
		p.setBrush(brush());
		p.setPen(pen());
		p.drawPolygon(areaPoints());
	}

#if QT_VERSION >=300
	if(isSelected())
#else
	if(selected())
#endif
	{
		p.setRasterOp(NotROP);
		p.setPen(QPen(DotLine));
		p.drawPolygon(areaPoints());
		p.setBrush(QBrush());
		double dVal = 10;
		p.drawEllipse((int)(x() - dVal),(int)(y() - dVal),(int)(dVal * 2),(int)(dVal * 2));
		p.drawLine((int)(x() - (dVal * 2)),(int)y(),(int)(x() + (dVal * 2)),(int)y());
		p.drawLine((int)x(),(int)(y() - (dVal * 2)),(int)x(),(int)(y() + (dVal * 2)));
		p.setRasterOp(CopyROP);
		canvas()->setChanged(QRect((int)(x() - dVal),(int)(y() - dVal),(int)(dVal * 4),(int)(dVal * 4)));
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasEllipticItem
//

KviCanvasEllipticItem::KviCanvasEllipticItem(QCanvas * c,int x,int y,int w,int h)
: KviCanvasRectangleItem(c,x,y,w,h)
{
	m_properties.insert("clrForeground",QVariant(QColor(0,0,0)));
	m_properties.insert("uLineWidth",QVariant((unsigned int)0));

	m_properties.insert("clrBackground",QVariant(QColor(0,0,0)));
	m_properties.insert("bHasBackground",QVariant(false,1));

//	m_properties.insert("iStartAngle",QVariant(0));
//	m_properties.insert("iEndAngle",QVariant(360));
}

KviCanvasEllipticItem::~KviCanvasEllipticItem()
{
}

void KviCanvasEllipticItem::draw(QPainter &p)
{
#if QT_VERSION >= 300
	if(isEnabled())
#else
	if(enabled())
#endif
	{
		QBrush b = p.brush();
		if(m_properties["bHasBackground"].asBool())p.setBrush(m_properties["clrBackground"].asColor());
		else p.setBrush(QBrush());
		p.setPen(pen());
		drawContent(p);
		p.setBrush(b);
	}

#if QT_VERSION >= 300
	if(isSelected())drawSelection(p);
#else
	if(selected())drawSelection(p);
#endif
}

void KviCanvasEllipticItem::drawContent(QPainter &p)
{
}


void KviCanvasEllipticItem::setProperty(const QString & property,const QVariant &val)
{
	if(m_properties[property].isValid())
	{
		m_properties.replace(property,val);
		if((property == "clrForeground") || (property == "uLineWidth"))
		{
			setPen(QPen(m_properties["clrForeground"].asColor(),m_properties["uLineWidth"].toInt()));
		} else {
			hide(); show();
		}
	}
}

int KviCanvasEllipticItem::rtti() const
{
	return KVI_CANVAS_RTTI_ELLIPSE;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasEllipse
//


KviCanvasEllipse::KviCanvasEllipse(QCanvas * c,int x,int y,int w,int h)
: KviCanvasEllipticItem(c,x,y,w,h)
{
}

KviCanvasEllipse::~KviCanvasEllipse()
{
}

int KviCanvasEllipse::rtti() const
{
	return KVI_CANVAS_RTTI_ELLIPSE;
}

void KviCanvasEllipse::drawContent(QPainter &p)
{
	p.drawEllipse((int)x(),(int)y(),width(),height());
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasPie
//


KviCanvasPie::KviCanvasPie(QCanvas * c,int x,int y,int w,int h)
: KviCanvasEllipticItem(c,x,y,w,h)
{
	m_properties.insert("iStartAngle",QVariant((int)0));
	m_properties.insert("iExtensionAngle",QVariant((int)360));
}

KviCanvasPie::~KviCanvasPie()
{
}

int KviCanvasPie::rtti() const
{
	return KVI_CANVAS_RTTI_PIE;
}

void KviCanvasPie::drawContent(QPainter &p)
{
	int iStartAngle = m_properties["iStartAngle"].asInt() * 16;
	int iEndAngle = m_properties["iExtensionAngle"].asInt() * 16;
	p.drawPie((int)x(),(int)y(),width(),height(),iStartAngle,iEndAngle);
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasChord
//


KviCanvasChord::KviCanvasChord(QCanvas * c,int x,int y,int w,int h)
: KviCanvasEllipticItem(c,x,y,w,h)
{
	m_properties.insert("iStartAngle",QVariant((int)0));
	m_properties.insert("iExtensionAngle",QVariant((int)360));
}

KviCanvasChord::~KviCanvasChord()
{
}

int KviCanvasChord::rtti() const
{
	return KVI_CANVAS_RTTI_CHORD;
}

void KviCanvasChord::drawContent(QPainter &p)
{
	int iStartAngle = m_properties["iStartAngle"].asInt() * 16;
	int iEndAngle = m_properties["iExtensionAngle"].asInt() * 16;
	p.drawChord((int)x(),(int)y(),width(),height(),iStartAngle,iEndAngle);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasRectangle
//

KviCanvasRectangle::KviCanvasRectangle(QCanvas * c,int x,int y,int w,int h)
: KviCanvasRectangleItem(c,x,y,w,h)
{
	m_properties.insert("clrForeground",QVariant(QColor(0,0,0)));
	m_properties.insert("uLineWidth",QVariant((unsigned int)0));

	m_properties.insert("clrBackground",QVariant(QColor(0,0,0)));
	m_properties.insert("bHasBackground",QVariant(false,1));
}

KviCanvasRectangle::~KviCanvasRectangle()
{
}

int KviCanvasRectangle::rtti() const
{
	return KVI_CANVAS_RTTI_RECTANGLE;
}


void KviCanvasRectangle::setProperty(const QString &property,const QVariant &val)
{
	if(m_properties[property].isValid())
	{
		m_properties.replace(property,val);
		if((property == "clrForeground") || (property == "uLineWidth"))
		{
			setPen(QPen(m_properties["clrForeground"].asColor(),m_properties["uLineWidth"].toInt()));
		} else {
			hide(); show();
		}
	}
}

void KviCanvasRectangle::draw(QPainter & p)
{
#if QT_VERSION >= 300
	if(isEnabled())
#else
	if(enabled())
#endif
	{
		if(m_properties["bHasBackground"].asBool())
		{
			p.fillRect((int)x() + 1,(int)y() + 1,width() - 2,height() - 2,m_properties["clrBackground"].asColor());
		}
		p.setPen(pen());
		p.drawRect((int)x(),(int)y(),width(),height());
	}
#if QT_VERSION >= 300
	if(isSelected())drawSelection(p);
#else
	if(selected())drawSelection(p);
#endif
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasRichText
//

KviCanvasRichText::KviCanvasRichText(QCanvas * c,int x,int y,int w,int h)
: KviCanvasRectangleItem(c,x,y,w,h)
{
	QFont f = QFont();
	f.setStyleHint(QFont::SansSerif);
	f.setPointSize(12);
	m_properties.insert("szText",QVariant(QString("<center>Insert here your <font color=\"#FF0000\"><b>RICH TEXT</b></font></center>")));
	m_properties.insert("fntDefault",QVariant(f));
}

KviCanvasRichText::~KviCanvasRichText()
{
}

int KviCanvasRichText::rtti() const
{
	return KVI_CANVAS_RTTI_RICHTEXT;
}

void KviCanvasRichText::draw(QPainter & p)
{
#if QT_VERSION >= 300
	if(isEnabled())
#else
	if(enabled())
#endif
	{
		QString szText = m_properties["szText"].asString();
		QSimpleRichText text(szText,m_properties["fntDefault"].asFont());
		text.setWidth(width());
#if QT_VERSION >= 300
		text.draw(&p,(int)x() + 1,(int)y() + 1,QRegion(QRect((int)x() + 1,(int)y() + 1,width(),height())),QColorGroup());
#else
		text.draw(&p,(int)x() + 1,(int)y() + 1,QRegion(QRect((int)x() + 1,(int)y() + 1,width(),height())),QPalette());
#endif
	}
#if QT_VERSION >= 300
	if(isSelected())drawSelection(p);
#else
	if(selected())drawSelection(p);
#endif
}




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasLine
//


KviCanvasLine::KviCanvasLine(QCanvas * c,int x1,int y1,int x2,int y2)
: QCanvasLine(c)
{
	setPoints(x1,y1,x2,y2);
	m_properties.insert("uLineWidth",QVariant((unsigned int)0));
	m_properties.insert("clrForeground",QVariant(QColor()));
}

KviCanvasLine::~KviCanvasLine()
{
}

void KviCanvasLine::setProperty(const QString &property,const QVariant &val)
{
	m_properties.replace(property,val);
	if((property == "uLineWidth") || (property == "clrForeground"))
	{
		setPen(QPen(m_properties["clrForeground"].asColor(),m_properties["uLineWidth"].toInt()));
	}
}

int KviCanvasLine::rtti() const
{
	return KVI_CANVAS_RTTI_LINE;
}

void KviCanvasLine::draw(QPainter &p)
{
#if QT_VERSION >= 300
	if(isEnabled())
#else
	if(enabled())
#endif
	{
		p.setPen(pen());
		p.drawLine(startPoint(),endPoint());
	}

#if QT_VERSION >= 300
	if(isSelected())
#else
	if(selected())
#endif
	{
		p.setRasterOp(NotROP);
		p.setPen(QPen(DotLine));
		p.drawLine(startPoint(),endPoint());
		p.setRasterOp(CopyROP);
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasView
//

KviCanvasView::KviCanvasView(QCanvas * c,KviCanvasWidget * cw,QWidget * par)
: QCanvasView(c,par)
{
	m_pCanvasWidget = cw;
	m_state = Idle;
	m_dragMode = None;
	m_pSelectedItem = 0;
	viewport()->setMouseTracking(true);
}


KviCanvasView::~KviCanvasView()
{
}


void KviCanvasView::insertRectangle()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = Rectangle;
}

void KviCanvasView::insertRichText()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = RichText;
}

void KviCanvasView::insertLine()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = Line;
}

void KviCanvasView::insertEllipse()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = Ellipse;
}

void KviCanvasView::insertPie()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = Pie;
}

void KviCanvasView::insertChord()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = Chord;
}


void KviCanvasView::insertPolygonTriangle()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = PolygonTriangle;
}


void KviCanvasView::insertPolygonRectangle()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = PolygonRectangle;
}


void KviCanvasView::insertPolygonPentagon()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = PolygonPentagon;
}

void KviCanvasView::insertPolygonHexagon()
{
	m_state = SelectOrigin;
	setCursor(crossCursor);
	m_objectToInsert = PolygonPentagon;
}

#ifndef M_PI
	#define M_PI 3.14159265358979323846
#endif

static void calcPolygonPoints(QPointArray &pnts,unsigned int nVertices)
{
	double dDelta = (2 * M_PI) / nVertices;
	for(unsigned int i=0;i<nVertices;i++)
	{
		double dAng = dDelta * i;
		double theX = 300 * sin(dAng);
		double theY = 300 * cos(dAng);
		pnts.setPoint(i,(int)theX,(int)theY);
	}
}

void KviCanvasView::insertObjectAt(const QPoint & pnt,ObjectType o)
{
	QCanvasItem * r = 0;

	switch(o)
	{
		case Rectangle:
			r = new KviCanvasRectangle(canvas(),pnt.x(),pnt.y(),0,0);
		break;
		case RichText:
			r = new KviCanvasRichText(canvas(),pnt.x(),pnt.y(),0,0);
		break;
		case Line:
			r = new KviCanvasLine(canvas(),pnt.x(),pnt.y(),pnt.x(),pnt.y());
		break;
		case Ellipse:
			r = new KviCanvasEllipse(canvas(),pnt.x(),pnt.y(),0,0);
		break;
		case Pie:
			r = new KviCanvasPie(canvas(),pnt.x(),pnt.y(),0,0);
		break;
		case Chord:
			r = new KviCanvasChord(canvas(),pnt.x(),pnt.y(),0,0);
		break;
		case PolygonTriangle:
		{
			QPointArray pa(3);
			pa.setPoint(0,0,-500);
			pa.setPoint(1,-450,220);
			pa.setPoint(2,450,220);
			r = new KviCanvasPolygon(canvas(),pnt.x(),pnt.y(),pa,0.1);
		}
		break;
		case PolygonRectangle:
		{
			QPointArray pa(4);
			pa.setPoint(0,-350,-350);
			pa.setPoint(1,350,-350);
			pa.setPoint(2,350,350);
			pa.setPoint(3,-350,350);
			r = new KviCanvasPolygon(canvas(),pnt.x(),pnt.y(),pa,0.1);
		}
		break;
		case PolygonPentagon:
		{
			QPointArray pa(5);
			calcPolygonPoints(pa,5);
			r = new KviCanvasPolygon(canvas(),pnt.x(),pnt.y(),pa,0.1);
		}
		break;
		case PolygonHexagon:
		{
			QPointArray pa(6);
			calcPolygonPoints(pa,6);
			r = new KviCanvasPolygon(canvas(),pnt.x(),pnt.y(),pa,0.1);
		}
		break;
	}

	if(r)
	{
		setItemSelected(r);
		r->setEnabled(true);
		r->show();
	}

	switch(KVI_CANVAS_RTTI_CONTROL_TYPE(r))
	{
		case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
			beginDragRectangle((KviCanvasRectangleItem *)r,pnt,true);
		break;
		case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
			beginDragLine((KviCanvasLine *)r,pnt,true);
		break;
//		case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
//			beginDragPolygon((KviCanvasPolygon *)r,pnt,true);
//		break;
	}

//	canvas()->update();
}

void KviCanvasView::setItemSelected(QCanvasItem * it)
{
	clearSelection();
	it->setSelected(true);
	m_pSelectedItem = it;
	m_pCanvasWidget->m_pPropertiesWidget->editItem(it);

}

void KviCanvasView::clearSelection()
{
	if(!m_pSelectedItem)return;
	m_pSelectedItem->setSelected(false);
	m_pSelectedItem = 0;
	m_pCanvasWidget->m_pPropertiesWidget->editItem(0);
}



void KviCanvasView::beginDragLine(KviCanvasLine * it,const QPoint &p,bool bInitial)
{
	QPoint sp = it->startPoint();

	m_dragBegin = p - sp;

	if(bInitial)
	{
		m_dragMode = Bottom;
		setCursor(sizeAllCursor);
		return;
	}

	if((abs(p.x() - sp.x()) < 3) && (abs(p.y() - sp.y()) < 3))
	{
		m_dragMode = Top;
		setCursor(sizeAllCursor);
		return;
	}

	sp = it->endPoint();
	if((abs(p.x() - sp.x()) < 3) && (abs(p.y() - sp.y()) < 3))
	{
		m_dragMode = Bottom;
		setCursor(sizeAllCursor);
		return;
	}

	m_dragMode = All;
	setCursor(pointingHandCursor);
}

void KviCanvasView::dragLine(KviCanvasLine * it,const QPoint &p)
{
	switch(m_dragMode)
	{
		case Bottom:
		{
			QPoint sp = it->startPoint();
			it->setPoints(sp.x(),sp.y(),p.x(),p.y());
		}
		break;
		case Top:
		{
			QPoint ep = it->endPoint();
			it->setPoints(p.x(),p.y(),ep.x(),ep.y());
		}
		break;
		case All:
		{
			QPoint sp = p - m_dragBegin;
			QPoint ep = sp + (it->endPoint() - it->startPoint());
			it->setPoints(sp.x(),sp.y(),ep.x(),ep.y());
		}
		break;
		default: /* make gcc happy */
		break;
	}
	canvas()->update();
}


static double ssm_2d_rotationAngleFromXAxis(double dx,double dy)
{
	//
	//      v1 . v2           dx * 1 + dy * 0              dx
	//acos(---------) = acos(-----------------) = acos(---------)
	//     |v1||v2|            |(dx,dy)| * 1           |(dx,dy)|
	//

	//double dVal = hypot(dx,dy);
	double dVal = sqrt((dx * dx) + (dy * dy));

	if(dVal == 0.0)return 0; // ???

	dVal = acos(dx / dVal);

	return (dy > 0.0) ? dVal : -dVal;
}

static double ssm_2d_rotationAngle(double drefx,double drefy,double drotatedx,double drotatedy)
{
	double dRefAngle = ssm_2d_rotationAngleFromXAxis(drefx,drefy);
	double dRotAngle = ssm_2d_rotationAngleFromXAxis(drotatedx,drotatedy);
	return dRotAngle - dRefAngle;
}

static void ssm_2d_rotate(double &dx,double &dy,double dAngle)
{
	// Rotation matrix:
	//
	// | cos(x)    sin(x) |
	// |                  |
	// | -sin(x)   cos(x) |

	double s = sin(dAngle);
	double c = cos(dAngle);

	double tmpX = (dx * c) - (dy * s);
	double tmpY = (dx * s) + (dy * c);

	dx = tmpX;
	dy = tmpY;
}

static double ssm_hypot(double a,double b)
{
	return sqrt((a * a) + (b * b));
}

void KviCanvasView::beginDragPolygon(KviCanvasPolygon * it,const QPoint &p,bool bShift,bool bCtrl)
{
	m_dragBegin = QPoint((int)(p.x() - it->x()),(int)(p.y() - it->y()));

	QPointArray pa = it->areaPoints();

	for(unsigned int i=0;i<pa.size();i++)
	{
		QPoint pnt = pa.point(i);
		double dX = pnt.x() - p.x();
		double dY = pnt.y() - p.y();
		double dHypot = sqrt((dX * dX) + (dY * dY));
		if(dHypot < 3.0)
		{
			// We're dragging a point
			m_dragMode = SinglePoint;
			m_dragPointIndex = i;
			setCursor(crossCursor);
			return;
		}
	}

	if(bShift)
	{
		m_dragMode = Scale;
		m_dragScaleFactor = it->scaleFactor();
		setCursor(sizeAllCursor);
		return;
	}

	if(bCtrl)
	{
		m_dragMode = Rotate;
		m_dragPointArray = it->internalPoints();
//		debug("Here");
		setCursor(sizeHorCursor);
		return;
	}

	m_dragMode = All;
	setCursor(pointingHandCursor);
}

void KviCanvasView::dragPolygon(KviCanvasPolygon * it,const QPoint &p)
{
	switch(m_dragMode)
	{
		case All:
			it->move(p.x() - m_dragBegin.x(),p.y() - m_dragBegin.y());
		break;
		case SinglePoint:
		{
			QPointArray pnt = it->internalPoints();
			pnt.setPoint(m_dragPointIndex,(int)((p.x() - it->x()) / it->scaleFactor()),(int)((p.y() - it->y()) / it->scaleFactor()));
			it->setInternalPoints(pnt);
		}
		break;
		case Scale:
		{
			double dDistance = ssm_hypot(p.x() - it->x(),p.y() - it->y());
			double dOriginal = ssm_hypot(m_dragBegin.x(),m_dragBegin.y());
			if(dOriginal < 1)dOriginal = 1;
			if(dDistance < 0.1)dDistance = 0.1;
			it->setScaleFactor(m_dragScaleFactor * dDistance / dOriginal);
		}
		break;
		case Rotate:
		{
			QPoint act((int)(p.x() - it->x()),(int)(p.y() - it->y()));
			double dAngle = ssm_2d_rotationAngle(m_dragBegin.x(),m_dragBegin.y(),act.x(),act.y());
//			debug("%d,%d %d,%d %f",m_dragBegin.x(),m_dragBegin.y(),act.x(),act.y(),dAngle);
			QPointArray thePoints = m_dragPointArray.copy();
			for(unsigned int i=0;i<thePoints.size();i++)
			{
				QPoint tmp = thePoints.point(i);
				double dx = tmp.x();
				double dy = tmp.y();
				ssm_2d_rotate(dx,dy,dAngle);
				thePoints.setPoint(i,(int)dx,(int)dy);
			}
			it->setInternalPoints(thePoints);
		}
		break;
		default:
		break;
	}
	canvas()->update();
}

void KviCanvasView::beginDragRectangle(KviCanvasRectangleItem * it,const QPoint & p,bool bInitial)
{
	m_dragBegin = QPoint((int)(p.x() - it->x()),(int)(p.y() - it->y()));

	if(bInitial)
	{
		// Right bottom
		m_dragMode = RightBottom;
		setCursor(sizeFDiagCursor);
		return;
	}

	if(p.x() < (((int)it->x()) + 2))
	{
		// Left edge
		if(p.y() < (((int)it->y()) + 2))
		{
			// Left top
			m_dragMode = LeftTop;
			setCursor(sizeFDiagCursor);
			return;
		}
		if(p.y() > ( it->bottom() - 2))
		{
			// Left bottom
			m_dragMode = LeftBottom;
			setCursor(sizeBDiagCursor);
			return;
		}
		m_dragMode = Left;
		setCursor(sizeHorCursor);
		return;
	}

	if(p.x() > (it->right() - 2))
	{
		// Right edge
		if(p.y() < (((int)it->y()) + 2))
		{
			// Right top
			m_dragMode = RightTop;
			setCursor(sizeBDiagCursor);
			return;
		}
		if(p.y() > ( it->bottom() - 2))
		{
			// Right bottom
			m_dragMode = RightBottom;
			setCursor(sizeFDiagCursor);
			return;
		}
		m_dragMode = Right;
		setCursor(sizeHorCursor);
		return;
	}
	
	// Somewhere in the middle
	if(p.y() < (((int)it->y()) + 2))
	{
		// Top
		m_dragMode = Top;
		setCursor(sizeVerCursor);
		return;
	}
	if(p.y() > ( it->bottom() - 2))
	{
		// Bottom
		m_dragMode = Bottom;
		setCursor(sizeVerCursor);
		return;
	}

	m_dragMode = All;
	setCursor(pointingHandCursor);
}

void KviCanvasView::dragRectangle(KviCanvasRectangleItem * it,const QPoint & p)
{

	int aux1,aux2,aux3,aux4;

	switch(m_dragMode)
	{
		case All:
			it->move(p.x() - m_dragBegin.x(),p.y() - m_dragBegin.y());
		break;
		case Left:
			aux1 = it->width() + (int)(it->x() - p.x());
			aux2 = p.x();
			if(aux1 < 1)
			{
				aux2 += (aux1 - 1);
				aux1 = 1;
			}
			it->move(aux2,it->y());
			it->setSize(aux1,it->height());
		break;
		case Right:
			aux1 = it->width() + (p.x() - it->right());
			if(aux1 < 1)aux1 = 1;
			it->setSize(aux1,it->height());
		break;
		case Top:
			aux1 = it->height()  + (int)(it->y() - p.y());
			aux2 = p.y();
			if(aux1 < 1)
			{
				aux2 += (aux1 - 1);
				aux1 = 1;
			}
			it->move(it->x(),aux2);
			it->setSize(it->width(),aux1);
		break;
		case Bottom:
			aux1 = (int)it->height()  + (p.y() - it->bottom());
			if(aux1 < 1)aux1 = 1;
			it->setSize(it->width(),aux1);
		break;
		case LeftTop:
			aux1 = it->width() + (int)(it->x() - p.x());
			aux3 = p.x();
			if(aux1 < 1)
			{
				aux3 += (aux1 - 1);
				aux1 = 1;
			}
			aux2 = it->height()  + (int)(it->y() - p.y());
			aux4 = p.y();
			if(aux2 < 1)
			{
				aux4 += (aux2 - 1);
				aux2 = 1;
			}
			it->setSize(aux1,aux2);
			it->move(aux3,aux4);
		break;
		case RightTop:
			aux1 = it->width() + (int)(p.x() - it->right());
			if(aux1 < 1)aux1 = 1;
			aux2 = it->height()  + (int)(it->y() - p.y());
			aux4 = p.y();
			if(aux2 < 1)
			{
				aux4 += (aux2 - 1);
				aux2 = 1;
			}
			it->setSize(aux1,aux2);
			it->move(it->x(),aux4);
		break;
		case LeftBottom:
			aux1 = it->width() + (int)(it->x() - p.x());
			aux3 = p.x();
			if(aux1 < 1)
			{
				aux3 += (aux1 - 1);
				aux1 = 1;
			}
			aux2 = it->height()  + (int)(p.y() - it->bottom());
			if(aux2 < 1)aux2 = 1;
			it->setSize(aux1,aux2);
			it->move(aux3,it->y());
		break;
		case RightBottom:
			aux1 = it->width() + (int)(p.x() - it->right());
			if(aux1 < 1)aux1 = 1;
			aux2 = it->height()  + (int)(p.y() - it->bottom());
			if(aux2 < 1)aux2 = 1;
			it->setSize(aux1,aux2);
		break;
		default:
		break;
	}

	canvas()->update();
}

void KviCanvasView::contentsMouseMoveEvent(QMouseEvent *e)
{
//	QPoint p = inverseWorldMatrix().map(e->pos());
	QPoint p = e->pos();
	if(e->state() & Qt::LeftButton)
	{
		if((m_dragMode != None) && (m_pSelectedItem))
		{
#if QT_VERSION >= 300
			if(m_pSelectedItem->isEnabled())m_pSelectedItem->setEnabled(false);
#else
			if(m_pSelectedItem->enabled())m_pSelectedItem->setEnabled(false);
#endif
			switch(KVI_CANVAS_RTTI_CONTROL_TYPE(m_pSelectedItem))
			{
				case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
					dragRectangle((KviCanvasRectangleItem *)m_pSelectedItem,p);
				break;
				case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
					dragLine((KviCanvasLine *)m_pSelectedItem,p);
				break;
				case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
					dragPolygon((KviCanvasPolygon *)m_pSelectedItem,p);
				break;
			}
		}
	} else {
		// Without buttons
		if(m_state == Idle)
		{
			QCanvasItemList l = canvas()->collisions(p);
			QCanvasItemList::Iterator it = l.begin();
	
			if(it != l.end())
			{
				// Got an item
				QCanvasItem * hit = (QCanvasItem *)*it;
				// Now find the point on that we have clicked it
				if(hit == m_pSelectedItem)
				{
					switch(KVI_CANVAS_RTTI_CONTROL_TYPE(m_pSelectedItem))
					{
						case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
							beginDragRectangle((KviCanvasRectangleItem *)m_pSelectedItem,p);
						break;
						case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
							beginDragLine((KviCanvasLine *)m_pSelectedItem,p);
						break;
						case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
							beginDragPolygon((KviCanvasPolygon *)m_pSelectedItem,p);
						break;
					}
				}
				else if(m_dragMode != None)setCursor(arrowCursor);
			} else {
				if(m_dragMode != None)setCursor(arrowCursor);
			}
		}
	}
}

void KviCanvasView::contentsMouseReleaseEvent(QMouseEvent *e)
{
	if(m_dragMode != None)
	{
		// Was just dragging a rectangle
		m_dragMode = None;
		setCursor(arrowCursor);
		if(m_pSelectedItem)
		{
			m_pSelectedItem->setEnabled(true);
			canvas()->update();
		}
	}
}

void KviCanvasView::contentsMousePressEvent(QMouseEvent *e)
{
	if(e->button() & Qt::LeftButton)
	{
//	    QPoint p = inverseWorldMatrix().map(e->pos());
		QPoint p = e->pos();

		switch(m_state)
		{
			case SelectOrigin:
				clearSelection();
				setCursor(arrowCursor);
				m_state = Idle;
				insertObjectAt(p,m_objectToInsert);
				canvas()->update();
			break;

			case Idle:
			{
				QCanvasItemList l = canvas()->collisions(p);
				QCanvasItemList::Iterator it = l.begin();

				if(it != l.end())
				{
					// Got an item
					QCanvasItem * hit = *it;
					if(hit != m_pSelectedItem)
					{
						// Was not selected yet
						setItemSelected(hit);
						canvas()->update();
					}
					// Now find the point on that we have clicked it
					switch(KVI_CANVAS_RTTI_CONTROL_TYPE(hit))
					{
						case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
							beginDragRectangle(((KviCanvasRectangleItem *)hit),p);
						break;
						case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
							beginDragLine(((KviCanvasLine *)hit),p);
						break;
						case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
							beginDragPolygon(((KviCanvasPolygon *)hit),p,e->state() & Qt::ShiftButton,e->state() & Qt::ControlButton);
						break;
					}
				} else {
					// No item
					clearSelection();
					canvas()->update();
				}
			}
			break;
		}
	}
}


void KviCanvasView::propertyChanged(const QString &s,const QVariant &v)
{
	if(!m_pSelectedItem)return;


	switch(KVI_CANVAS_RTTI_CONTROL_TYPE(m_pSelectedItem))
	{
		case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
			((KviCanvasRectangleItem *)m_pSelectedItem)->setProperty(s,v);
		break;
		case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
			((KviCanvasLine *)m_pSelectedItem)->setProperty(s,v);
		break;
		case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
			((KviCanvasPolygon *)m_pSelectedItem)->setProperty(s,v);
		break;
	}

	canvas()->update();
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviVariantTableItem
//



KviVariantTableItem::KviVariantTableItem(QTable * t,const QVariant & property)
: QTableItem(t,QTableItem::WhenCurrent,QString::null)
{
	m_property = property;
}

KviVariantTableItem::~KviVariantTableItem()
{
}


QWidget * KviVariantTableItem::createEditor() const
{
	switch(m_property.type())
	{
		case QVariant::String:
		{
			QLineEdit * e = new QLineEdit(table()->viewport());
			e->setText(m_property.toString());
			return e;
		}
		break;
		case QVariant::Int:
		{
			QLineEdit * e = new QLineEdit(table()->viewport());
			QString sz;
			sz.setNum(m_property.toInt());
			e->setText(sz);
			e->setValidator(new QIntValidator(e));
			return e;
		}
		break;
		case QVariant::UInt:
		{
			QLineEdit * e = new QLineEdit(table()->viewport());
			QString sz;
			sz.setNum(m_property.toInt());
			e->setText(sz);
			QIntValidator * i = new QIntValidator(e);
			i->setBottom(0);
			e->setValidator(i);
			return e;
		}
		break;
		case QVariant::Bool:
		{
			QComboBox * b = new QComboBox(false,table()->viewport());
			b->insertItem("FALSE");
			b->insertItem("TRUE");
			b->setCurrentItem(m_property.toBool() ? 1 : 0);
			return b;
		}
		break;
		case QVariant::Color:
		{
			QLineEdit * e = new QLineEdit(table()->viewport());
			e->setText(m_property.toColor().name());
			return e;
		}
		break;
		case QVariant::Font:
		{
			QComboBox * b = new QComboBox(true,table()->viewport());

			QString tmp;
			QString tmpDefault;
			QFont f = QFont();
			f.setStyleHint(QFont::SansSerif);
			tmpDefault = f.family();
			f.setStyleHint(QFont::TypeWriter);
			tmp.setNum(m_property.toFont().pointSize());
			tmp.prepend(", ");
			tmp.prepend(m_property.toFont().family());
			b->insertItem(tmp);
			b->insertItem(tmpDefault + ", 8");
			b->insertItem(tmpDefault + ", 10");
			b->insertItem(tmpDefault + ", 12");
			b->insertItem(tmpDefault + ", 14");
			b->insertItem(tmpDefault + ", 16");
			b->insertItem(tmpDefault + ", 18");
			b->insertItem(tmpDefault + ", 20");
			b->insertItem(tmpDefault + ", 24");
			b->insertItem(tmpDefault + ", 28");
			b->insertItem(tmpDefault + ", 32");
			b->insertItem(tmpDefault + ", 40");
			b->insertItem(tmpDefault + ", 48");
			b->insertItem(f.family() + ", 12");
			b->setCurrentItem(0); 

			b->setCurrentItem(m_property.toBool() ? 1 : 0);
			return b;
		}
		break;
		default:
		break;
	}
	return 0;
}

void KviVariantTableItem::setContentFromEditor(QWidget * w)
{
	switch(m_property.type())
	{
		case QVariant::String:
			m_property = QVariant(((QLineEdit *)w)->text());
		break;
		case QVariant::Int:
			m_property = QVariant(((QLineEdit *)w)->text().toInt());
		break;
		case QVariant::UInt:
			m_property = QVariant(((QLineEdit *)w)->text().toUInt());
		break;
		case QVariant::Bool:
			m_property = QVariant(((QComboBox *)w)->currentItem(),1);
		break;
		case QVariant::Color:
			m_property.asColor().setNamedColor(((QLineEdit *)w)->text());
		break;
		case QVariant::Font:
		{
			KviStr txt = ((QComboBox *)w)->currentText();
			if(txt.hasData())
			{
				KviStr fam = txt;
				fam.cutFromFirst(',',true);
				fam.stripWhiteSpace();
				KviStr psz = txt;
				psz.cutToFirst(',',true);
				psz.stripWhiteSpace();
				bool bOk;
				unsigned int uSize = psz.toUInt(&bOk);
				if(!bOk)uSize = 12;
				m_property = QVariant(QFont(fam.ptr(),uSize));
			}
			
		}
		break;
		default:
		break;
	}
}


void KviVariantTableItem::paint(QPainter *p,const QColorGroup &cg,const QRect &cr,bool)
{
	p->fillRect(0,0,cr.width(),cr.height(),cg.base());

	if(m_property.type() == QVariant::Color)
	{
		p->fillRect(3,3,cr.width() - 6,cr.height() - 6,m_property.asColor());
	} else {
		QString sz;
		switch(m_property.type())
		{
			case QVariant::String:
				sz = m_property.toString();
			break;
			case QVariant::Bool:
				sz = m_property.toBool() ? "TRUE" : "FALSE";
			break;
			case QVariant::Font:
				sz.setNum(m_property.toFont().pointSize());
				sz.prepend(", ");
				sz.prepend(m_property.toFont().family());
			break;
			case QVariant::Int:
				sz.setNum(m_property.toInt());
			break;
			case QVariant::UInt:
				sz.setNum(m_property.toUInt());
			break;
			default:
			break;
		}
		p->setPen(cg.text());
		p->drawText(0,0,cr.width(),cr.height(),Qt::AlignLeft | Qt::AlignTop,sz);
	}
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasItemPropertiesWidget
//


KviCanvasItemPropertiesWidget::KviCanvasItemPropertiesWidget(QWidget * par)
: QTable(par)
{
	setSelectionMode(QTable::NoSelection);
	setColumnMovingEnabled(false);
	setRowMovingEnabled(false);
	setShowGrid(true);
	setNumCols(2);
	setSorting(false);
	setLeftMargin(0);
	verticalHeader()->hide();
	connect(this,SIGNAL(valueChanged(int,int)),this,SLOT(cellEdited(int,int)));
}

KviCanvasItemPropertiesWidget::~KviCanvasItemPropertiesWidget()
{
}

void KviCanvasItemPropertiesWidget::cellEdited(int row,int)
{
	QTableItem * it = item(row,0);
	if(!it)return;
	QString szName = it->text();
	it = item(row,1);
	if(!it)return;
	emit propertyChanged(szName,((KviVariantTableItem *)it)->property());
}

void KviCanvasItemPropertiesWidget::editItem(QCanvasItem * it)
{
	if(!it)
	{
		for(int i=0;i<numRows();i++)
		{
			clearCell(i,0);
			clearCell(i,1);
			clearCellWidget(i,1);
		}
		setNumRows(0);
		return;
	}

	QMap<QString,QVariant> * m = 0;

	switch(KVI_CANVAS_RTTI_CONTROL_TYPE(it))
	{
		case KVI_CANVAS_RTTI_CONTROL_TYPE_RECTANGLE:
			m = ((KviCanvasRectangleItem *)it)->properties();
		break;
		case KVI_CANVAS_RTTI_CONTROL_TYPE_LINE:
			m = ((KviCanvasLine *)it)->properties();
		break;
		case KVI_CANVAS_RTTI_CONTROL_TYPE_POLYGON:
			m = ((KviCanvasPolygon *)it)->properties();
		break;
	}

	if(!m)
	{
		editItem(0);
		return;
	}

	for(int i=0;i<numRows();i++)
	{
		clearCell(i,0);
		clearCell(i,1);
		clearCellWidget(i,1);
	}

	setNumRows(m->count());

	QTableItem * item;

	int idx = 0;

	for(QMap<QString,QVariant>::ConstIterator iter = m->begin();iter != m->end();++iter)
	{
		item = new QTableItem(this,QTableItem::Never,iter.key().utf8().data());
		setItem(idx,0,item);
		item = new KviVariantTableItem(this,iter.data());
		setItem(idx,1,item);
		idx++;
	}

}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// KviCanvasWidget
//

KviCanvasWidget::KviCanvasWidget(QWidget * par)
: QWidget(par,"canvas_widget")
{
	m_pCanvas = new QCanvas(this);
//#warning "Make this size as parameter of Dcc ?"
	m_pCanvas->resize(648,480);
	m_pMenuBar = new QMenuBar(this);
	m_pSplitter = new QSplitter(QSplitter::Horizontal,this);
	m_pCanvasView = new KviCanvasView(m_pCanvas,this,m_pSplitter);
	m_pStatusLabel = new QLabel(this);
	m_pPropertiesWidget = new KviCanvasItemPropertiesWidget(m_pSplitter);
	QValueList<int> l;
	l.append(80);
	l.append(20);
	m_pSplitter->setSizes(l);

	connect(m_pPropertiesWidget,SIGNAL(propertyChanged(const QString &,const QVariant &)),m_pCanvasView,SLOT(propertyChanged(const QString &,const QVariant &)));

	KviTalPopupMenu * add = new KviTalPopupMenu(m_pMenuBar);
	KviTalPopupMenu * shapes = new KviTalPopupMenu(add);
	KviTalPopupMenu * polygons = new KviTalPopupMenu(add);
	KviTalPopupMenu * items = new KviTalPopupMenu(add);
	shapes->insertItem(__tr2qs_ctx("&Line","dcc"),m_pCanvasView,SLOT(insertLine()));
	shapes->insertItem(__tr2qs_ctx("&Rectangle","dcc"),m_pCanvasView,SLOT(insertRectangle()));
	shapes->insertItem(__tr2qs_ctx("&Ellipse","dcc"),m_pCanvasView,SLOT(insertEllipse()));
	shapes->insertItem(__tr2qs_ctx("&Pie","dcc"),m_pCanvasView,SLOT(insertPie()));
	shapes->insertItem(__tr2qs_ctx("&Chord","dcc"),m_pCanvasView,SLOT(insertChord()));

	items->insertItem(__tr2qs_ctx("&Rich text (html)","dcc"),m_pCanvasView,SLOT(insertRichText()));

	polygons->insertItem(__tr2qs_ctx("&Triangle","dcc"),m_pCanvasView,SLOT(insertPolygonTriangle()));
	polygons->insertItem(__tr2qs_ctx("&Rectangle","dcc"),m_pCanvasView,SLOT(insertPolygonRectangle()));
	polygons->insertItem(__tr2qs_ctx("&Pentagon","dcc"),m_pCanvasView,SLOT(insertPolygonPentagon()));
	polygons->insertItem(__tr2qs_ctx("&Hexagon","dcc"),m_pCanvasView,SLOT(insertPolygonHexagon()));

	add->insertItem(__tr2qs_ctx("&Shape","dcc"),shapes);
	add->insertItem(__tr2qs_ctx("&Item","dcc"),items);
	add->insertItem(__tr2qs_ctx("&Polygons","dcc"),polygons);

	m_pMenuBar->insertItem(__tr2qs_ctx("&Insert","dcc"),add);
}

KviCanvasWidget::~KviCanvasWidget()
{
}



void KviCanvasWidget::resizeEvent(QResizeEvent *)
{
	int h = m_pMenuBar->sizeHint().height();
	m_pMenuBar->setGeometry(0,0,width(),h);
	int h2 = m_pStatusLabel->sizeHint().height();
	m_pStatusLabel->setGeometry(0,height() - h2,width(),h2);
	m_pSplitter->setGeometry(0,h,width(),height() - (h + h2));
}


#include "m_canvaswidget.moc"

#endif
