#include <CtrlLib/CtrlLib.h>
#include "Controls4U.h"

using namespace Upp;

#include <plugin/jpg/jpg.h>

#define TFILE <Controls4U/Controls4U.t>
#include <Core/t.h>

#define IMAGECLASS Controls4UImgC
#define IMAGEFILE <Controls4U/Controls4U.iml>
#include <Draw/iml.h>

String GetValueStringXml(String str, const char* var);
Color GetColorXml(String text);
void ParseG(GraphElemList &elems, XmlParser &xp, Svg2DTransform transf, SvgStyle style, const char *svgFolder);

GraphElem::GraphElem(const GraphElem &graph) {
	transf = graph.transf;
	style = graph.style;
	limits = graph.limits;
}
	
void GraphElemList::Paint(Painter &sw, Svg2DTransform _transf, SvgStyle _style, bool firstLayer) {
	_transf += transf;
	_transf.Apply(sw);			
	_style += style;	
	_style.Apply(sw);	
	for (int i = 0; i < elems.GetCount(); ++i) 
		elems[i].Paint(sw, _transf, _style, false);
	if (firstLayer)
		PaintLimits(sw);
}

void GraphElemList::Clear() {
	elems.Clear();
}

void SvgStyle::Set(String str) {
	String value;
	Color color;
	
	color = GetColorXml(GetValueStringXml(str, "stroke:"));
	if (!color.IsNullInstance())
		strokeColor = color;
	
	value = GetValueStringXml(str, "stroke-width:");
	if (value != "")
		strokeWidth = atoi(value);
	
	color = GetColorXml(GetValueStringXml(str, "fill:"));
	if (!color.IsNullInstance())
		fill = color;
	
	value = GetValueStringXml(str, "opacity:");
	if (value != "")
		opacity = atof(value);
}

void SvgStyle::Apply(Painter &sw) {
	sw.Opacity(opacity);	
	if (!fill.IsNullInstance())	
		sw.Fill(fill);				
	sw.Stroke(strokeWidth, strokeColor);
}

SvgStyle &SvgStyle::operator=(const SvgStyle &style) {
	if (this == &style)      // Same object?
  		return *this; 
	strokeColor = style.strokeColor;
	fill = style.fill;
	opacity = style.opacity;
	strokeWidth = style.strokeWidth;
	return *this;
}

SvgStyle &SvgStyle::operator+=(const SvgStyle &style) {
	if (this == &style)      // Same object?
  		return *this;
  	if (!IsNull(style.strokeColor))
		strokeColor = style.strokeColor;
	if (!IsNull(style.fill))
		fill = style.fill;
	if (!IsNull(style.opacity))
		opacity = style.opacity;
	if (!IsNull(style.strokeWidth))
		strokeWidth = style.strokeWidth;
	return *this;
}
	
String GetValueStringXml(String str, const char* var) {
	int pos, endpos;
	
	if ((pos = str.Find(var)) >= 0) {
		pos += int(strlen(var));
		endpos = str.Find(';', pos+1);
		if (endpos == -1)
			endpos = str.GetCount();
		return NormalizeSpaces(str.Mid(pos, endpos-pos));
	} else
		return "";
}

Color HtmlToColor(const char *str) {
	int col;
	sscanf(str+1, "%x", (unsigned int *)&col);
	return Color(col >> 16, (col >> 8) & 255, col & 255);
}

Color GetColorXml(String text) {
	text = ToLower(text);
	if (text == "none" || text.IsEmpty())
		return Null;
	else if (text[0] == '#') 
		return HtmlToColor(text);
	else
		return GetSvgColor(text);
}

Array<double> GetTransformArgs(String str, const char *command) {
	Array<double> args;
	int pos, endpos;

	if ((pos = str.Find(command)) < 0) 
		return args;
	
	pos += int(strlen(command));
	pos = 1 + str.Find('(', pos);
	
	while (true) {
		if ((endpos = str.Find(',', pos)) < 0) 
			break;
		double &d = args.Add();
		d = atof(str.Mid(pos, endpos-pos));
		pos = endpos+1;
	} 
	if (!args.IsEmpty()) {
		if ((endpos = str.Find(')', pos)) >= 0) {
			double &d = args.Add();
			d = atof(str.Mid(pos, endpos-pos));
		}
	}
	return args;
}


void SvgGet_Rect(GraphElemList &elems, XmlParser &xp) {
	RectElem &elem = static_cast<RectElem&>(elems.elems.Add(new RectElem()));
	
	for (int i = 0; i < xp.GetAttrCount(); ++i) {
		String attr = xp.GetAttr(i);
		if (attr == "x") 
			elem.x = atoi(xp[i]);
		else if (attr == "y") 
			elem.y = atoi(xp[i]);
		else if (attr == "width") 
			elem.width = atoi(xp[i]);
		else if (attr == "height") 
			elem.height = atoi(xp[i]);
		else if (attr == "style") 
			elem.style.Set(xp[i]);
		else if (attr == "transform") {
			Array<double> args;
			args = GetTransformArgs(xp[i], "translate");
			if (!args.IsEmpty()) 
				elem.transf.Translate(args[0], args[1]);
			args = GetTransformArgs(xp[i], "scale");
			if (!args.IsEmpty()) 
				elem.transf.Scale(args[0], args[1]);			
		}
	}	
	elem.SetLimits();			
}

void SvgGet_Image(GraphElemList &elems, XmlParser &xp, const char *svgFolder) {
	ImageElem &elem = static_cast<ImageElem&>(elems.elems.Add(new ImageElem()));
	
	for (int i = 0; i < xp.GetAttrCount(); ++i) {
		String attr = xp.GetAttr(i);
		if (attr == "x") 
			elem.x = atoi(xp[i]);
		else if (attr == "y") 
			elem.y = atoi(xp[i]);
		else if (attr == "width") 
			elem.width = atoi(xp[i]);
		else if (attr == "height") 
			elem.height = atoi(xp[i]);
		else if (attr == "xlink:href") {
			elem.fileName = xp[i];
			if (!FileExists(elem.fileName))
				elem.fileName = AppendFileName(svgFolder, elem.fileName);
		} else if (attr == "style") 
			elem.style.Set(xp[i]);
		else if (attr == "transform") {
			Array<double> args;
			args = GetTransformArgs(xp[i], "translate");
			if (!args.IsEmpty()) 
				elem.transf.Translate(args[0], args[1]);
			args = GetTransformArgs(xp[i], "scale");
			if (!args.IsEmpty()) 
				elem.transf.Scale(args[0], args[1]);			
		}
	}	
	elem.SetLimits();			
}

void SvgGet_Ellipse(GraphElemList &elems, XmlParser &xp) {
	EllipseElem &elem = static_cast<EllipseElem&>(elems.elems.Add(new EllipseElem()));
	
	for (int i = 0; i < xp.GetAttrCount(); ++i) {
		String attr = xp.GetAttr(i);
		if (attr == "cx") 
			elem.x = atoi(xp[i]);
		else if (attr == "cy") 
			elem.y = atoi(xp[i]);
		else if (attr == "rx") 
			elem.width = atoi(xp[i]);
		else if (attr == "ry") 
			elem.height = atoi(xp[i]);
		else if (attr == "style") 
			elem.style.Set(xp[i]);
		else if (attr == "transform") {
			Array<double> args;
			args = GetTransformArgs(xp[i], "translate");
			if (!args.IsEmpty()) 
				elem.transf.Translate(args[0], args[1]);
			args = GetTransformArgs(xp[i], "scale");
			if (!args.IsEmpty()) 
				elem.transf.Scale(args[0], args[1]);			
		}
	}	
	elem.SetLimits();						
}

void SvgGet_Line(GraphElemList &elems, XmlParser &xp) {
	LineElem &elem = dynamic_cast<LineElem&>(elems.elems.Add(new LineElem()));
	
	for (int i = 0; i < xp.GetAttrCount(); ++i) {
		String attr = xp.GetAttr(i);
		if (attr == "x1") 
			elem.x1 = atoi(xp[i]);
		else if (attr == "y1") 
			elem.y1 = atoi(xp[i]);
		else if (attr == "x2") 
			elem.x2 = atoi(xp[i]);
		else if (attr == "y2") 
			elem.y2 = atoi(xp[i]);
		else if (attr == "style") 
			elem.style.Set(xp[i]);
		else if (attr == "transform") {
			Array<double> args;
			args = GetTransformArgs(xp[i], "translate");
			if (!args.IsEmpty()) 
				elem.transf.Translate(args[0], args[1]);
			args = GetTransformArgs(xp[i], "scale");
			if (!args.IsEmpty()) 
				elem.transf.Scale(args[0], args[1]);			
		}
	}	
	elem.SetLimits();			
}

Array<Pointf> GetPolygonPointsXml(String &strpoints) {
	Array<Pointf> points;
	int newpos, pos;
	char separator;
	
	if (strpoints.Find(',') >= 0)
		separator = ',';
	else
		separator = ' ';
	
	pos = newpos = 0;
	while (newpos != strpoints.GetCount()) {
		int x, y;
		if ((newpos = strpoints.Find(separator, pos)) < 0)
			break;
		x = atoi(strpoints.Mid(pos, newpos+1));
		pos = newpos+1;
		if ((newpos = strpoints.Find(' ', pos+1 < strpoints.GetCount()? pos+1: pos)) < 0)	// Jump possible spaces 
			newpos = strpoints.GetCount();
		y = atoi(strpoints.Mid(pos, newpos+1));
		pos = newpos+1;
		Pointf &point = points.Add();
		point.x = x;
		point.y = y;
	}
	return points;
}

void SvgGet_Polygon(GraphElemList &elems, XmlParser &xp) {
	PolygonElem &elem = static_cast<PolygonElem&>(elems.elems.Add(new PolygonElem()));
	
	for (int i = 0; i < xp.GetAttrCount(); ++i) {
		String attr = xp.GetAttr(i);
		if (attr == "points") {
			String strpoints;
			strpoints = NormalizeSpaces(xp[i]);
			elem.points = GetPolygonPointsXml(strpoints);
		} else if (attr == "style") 
			elem.style.Set(xp[i]);
		else if (attr == "transform") {
			Array<double> args;
			args = GetTransformArgs(xp[i], "translate");
			if (!args.IsEmpty()) 
				elem.transf.Translate(args[0], args[1]);
			args = GetTransformArgs(xp[i], "scale");
			if (!args.IsEmpty()) 
				elem.transf.Scale(args[0], args[1]);			
		}
	}	
	elem.SetLimits();		
}

void SvgGet_Title(GraphElemList &elems, XmlParser &xp) {
	elems.title = xp.ReadText();
}

void SvgGet_Text(GraphElemList &elems, XmlParser &xp) {
	TextElem &elem = static_cast<TextElem&>(elems.elems.Add(new TextElem()));
	
	elem.text = xp.ReadText();
	elem.text.Replace("\n", " ");
	elem.text.Replace("\r", "");
	
	for (int i = 0; i < xp.GetAttrCount(); ++i) {
		String attr = xp.GetAttr(i);
		if (attr == "x") 
			elem.x = atoi(xp[i]);
		else if (attr == "y") 
			elem.y = atoi(xp[i]);
		else if (attr == "style") { 
			elem.style.Set(xp[i]);
			
			if (elem.style.strokeColor.IsNullInstance())
				elem.style.strokeColor = Black();
			if (elem.style.fill.IsNullInstance())
				elem.style.fill = elem.style.strokeColor;
			String fontText;
			fontText = GetValueStringXml(xp[i], "font-family:");
			if (fontText.Find("Roman") >= 0)
				elem.f.Face(Font::ROMAN);
			else if (fontText.Find("Arial") >= 0)
				elem.f.Face(Font::ARIAL);			
			else if (fontText.Find("Courier") >= 0)
				elem.f.Face(Font::COURIER);		
			elem.f.Height(atoi(GetValueStringXml(xp[i], "font-size:"))*elem.factor);
			if (elem.f.GetHeight() == 0)
				elem.f.Height(12*elem.factor);
		} else if (attr == "transform") {
			Array<double> args;
			args = GetTransformArgs(xp[i], "translate");
			if (!args.IsEmpty()) 
				elem.transf.Translate(args[0], args[1]);
			args = GetTransformArgs(xp[i], "scale");
			if (!args.IsEmpty()) 
				elem.transf.Scale(args[0], args[1]);			
		}
	}
	elem.SetLimits();
}

void SvgGet_Path(GraphElemList &elems, XmlParser &xp) {
	PathElem &elem = static_cast<PathElem&>(elems.elems.Add(new PathElem()));
	
	for (int i = 0; i < xp.GetAttrCount(); ++i) {
		String attr = xp.GetAttr(i);
		if (attr == "d") 
			elem.path = xp[i];
		else if (attr == "style") 
			elem.style.Set(xp[i]);
		else if (attr == "transform") {
			Array<double> args;
			args = GetTransformArgs(xp[i], "translate");
			if (!args.IsEmpty()) 
				elem.transf.Translate(args[0], args[1]);
			args = GetTransformArgs(xp[i], "scale");
			if (!args.IsEmpty()) 
				elem.transf.Scale(args[0], args[1]);			
		}
	}	
	elem.SetLimits();	
}

void SvgGet_G(GraphElemList &elems, XmlParser &xp, const char *svgFolder) {
	GraphElemList &elem = static_cast<GraphElemList&>(elems.elems.Add(new GraphElemList()));
	
	for (int i = 0; i < xp.GetAttrCount(); ++i) {
		String attr = xp.GetAttr(i);
		if (attr == "style") 
			elem.style.Set(xp[i]);
		else if (attr == "transform") {
			Array<double> args;
			args = GetTransformArgs(xp[i], "translate");
			if (!args.IsEmpty()) 
				elem.transf.Translate(args[0], args[1]);
			args = GetTransformArgs(xp[i], "scale");
			if (!args.IsEmpty()) 
				elem.transf.Scale(args[0], args[1]);
		}
	}	
	while(!xp.End()) 
		ParseG(elem, xp, elem.transf, elem.style, svgFolder);	
	elem.SetLimits();
}

void ParseG(GraphElemList &elems, XmlParser &xp, Svg2DTransform transf, SvgStyle style, const char *svgFolder) {
	if(xp.TagE("rect"))  
		SvgGet_Rect(elems, xp);
	else if(xp.TagE("ellipse"))
		SvgGet_Ellipse(elems, xp);				
	else if(xp.TagE("line"))
		SvgGet_Line(elems, xp);				
	else if(xp.TagE("polygon"))
		SvgGet_Polygon(elems, xp);				
	else if(xp.TagE("polyline"))
		SvgGet_Polygon(elems, xp);	
	else if(xp.TagE("path"))
		SvgGet_Path(elems, xp);	
	else if(xp.TagE("image"))
		SvgGet_Image(elems, xp, svgFolder);		
	else if(xp.Tag("text")) {
		SvgGet_Text(elems, xp);
		xp.PassEnd();	
	} else if(xp.Tag("g")) 
		SvgGet_G(elems, xp, svgFolder);
	else if(xp.Tag("title")) {
		SvgGet_Title(elems, xp);
		xp.PassEnd();	
	} else
		xp.Skip();
}

/*

bool ParseSVG(DrawingCanvas &canvas, const char *svgFile, const char *svgFolder) {
	XmlParser xp(svgFile);
	while(!xp.IsTag())
		xp.Skip();
	xp.PassTag("svg");
	Svg2DTransform transf;
	SvgStyle style;
	style.SetFill(Black()).SetOpacity(1);
	//p.Begin();
	while(!xp.End()) {
		transf.Init();
		style.Init();
		ParseG(canvas.elemList, xp, transf, style, svgFolder);
	}
	//p.End();
	return true;
}


DrawingCanvas::DrawingCanvas() {
	transparent = false;
	translateX = translateY = 0;
	rotate = 0;
	scale = 1;
	
	quality = MODE_ANTIALIASED;	// 1 or 2 MODE_NOAA        = 1, MODE_SUBPIXEL    = 2,
	linejoin = LINEJOIN_MITER;
	linecap = LINECAP_BUTT;
	transparent = false;
	opacity = 1;
	scaleFactor = 1.2;

	focusMove.focusMoving = false;
	selectionWindow.isSelected = false;
}
		
void DrawingCanvas::Paint(Draw& w) {
	Size sz = GetSize();
	if(transparent) {
		for(int y = 0; y + 32 < sz.cy; y += 32)
			for(int x = 0; x + 32 < sz.cx; x += 32)
				w.DrawRect(x, y, 32, 32, (x ^ y) & 32 ? Color(254, 172, 120) : Color(124, 135, 253));
	}
	ImageBuffer ib(sz);
	BufferPainter sw(ib, quality);
	DoPaint(sw);
	if (selectionWindow.isSelected) { 
		sw.Rectangle(selectionWindow.selected.left, selectionWindow.selected.top, 
			selectionWindow.selected.GetWidth(), selectionWindow.selected.GetHeight())
			.Opacity(0.4).Dash("1").Stroke(1, Black()).Fill(LtGray());
		//sw.RectPath(selectionWindow.selected).Dash("1").Stroke(0.5, Black());
	}
	w.DrawImage(0, 0, ib);
}

void DrawingCanvas::DoPaint(Painter& sw) {
	sw.Translate(translateX, translateY);
	sw.Rotate(rotate);
	sw.Scale(scale);
	sw.Opacity(opacity);
	sw.LineCap(linecap);
	sw.LineJoin(linejoin);
	{ PAINTER_TIMING("FILL");
		if(transparent)
			sw.Clear(RGBAZero());
		else
			sw.Clear(White());
	}
	Svg2DTransform trans;
	SvgStyle style;
	style.SetFill(Null).SetStrokeColor(Black()).SetOpacity(1).SetStrokeWidth(1);
	
	sw.Begin();
	elemList.Paint(sw, trans, style, true);
	sw.End();
}


bool LoadSvg(DrawingCanvas &canvas, String fileName) {
	if (!FileExists(fileName))
		return false;

	return ParseSVG(canvas, LoadFileBOM(fileName), GetFileDirectory(fileName));
}

void DrawingCanvas::MouseWheel(Point p, int zdelta, dword keyflags) {
	Size sz = GetSize();
	double factor;
	if(zdelta > 0)
		factor = scaleFactor;
	else
		factor = 1/scaleFactor;
	
	scale *= factor;
	translateX = sz.cx*(1-factor)/2. + translateX*factor;
	translateY = sz.cy*(1-factor)/2. + translateY*factor;
	
	Refresh();
}

void DrawingCanvas::MiddleDown(Point p, dword keyflags) {
	focusMove.lastFocusPoint = p;
    focusMove.focusMoving = true;
}

void DrawingCanvas::MiddleUp(Point p, dword keyflags) {
    focusMove.focusMoving = false;
}

void DrawingCanvas::MouseLeave() {
    focusMove.focusMoving = false;
}

void DrawingCanvas::MouseMove(Point p, dword keyflags) {
    if (focusMove.focusMoving) {
     	translateX -= focusMove.lastFocusPoint.x - p.x;
      	translateY -= focusMove.lastFocusPoint.y - p.y;
      	focusMove.lastFocusPoint = p;

        Refresh();
    }
    if (selectionWindow.isSelected) {
      	selectionWindow.selected.right = p.x;
		selectionWindow.selected.bottom = p.y;  
		
		Refresh();
    }
}

void DrawingCanvas::LeftDown(Point p, dword keyflags) {
	selectionWindow.isSelected = true;
	selectionWindow.selected.left = selectionWindow.selected.right = p.x;
	selectionWindow.selected.top = selectionWindow.selected.bottom = p.y;
}

void DrawingCanvas::LeftUp(Point p, dword keyflags) {
	if (selectionWindow.isSelected) {
		Refresh();
		selectionWindow.isSelected = false;
	}
}
*/


PainterCanvas::PainterCanvas() {
	rotate = 0;
	mouseHandling = true;
	NoTransparent();
	/*translateX = translateY = 0;
	scale = 1;*/
	
	mode = MODE_ANTIALIASED;	// MODE_NOAA, MODE_SUBPIXEL
	linejoin = LINEJOIN_MITER;
	linecap = LINECAP_BUTT;
	opacity = 1;
	scaleFactor = 1.2;
	backColor = Null;
	backImage = Null;
	colorUnderBackgroundImage = false;
	alwaysFitInCanvas = true;
	Layout();
	showWindow = true;
	cursorImage = Image::Arrow();
	
	focusMove.focusMoving = false;
	
	legendFont = StdFont().Height(12);
	legendShowFilename = false;
	legendShowXY = false;
}

Image PainterCanvas::GetImage(Size sz)
{
	if (IsNull(sz))
		sz = GetSize(); 
	ImageBuffer ib(sz);	
	BufferPainter sw(ib, mode);
	
	sw.Translate(translateX, translateY);
	sw.Rotate(rotate);
	sw.Scale(scale);
	sw.Opacity(opacity);
	sw.LineCap(linecap);
	sw.LineJoin(linejoin);

	if (backColor)
		sw.Clear(backColor);
	else
		sw.Clear(SColorFace());
//sw.Move(0, 0).Line(300, 300).Stroke(2, Black());
	if (canvasSize.cx > 0 && canvasSize.cy > 0) {
		if (!IsNull(backImage)) 
			sw.Rectangle(0, 0, canvasSize.cx, canvasSize.cy).Fill(backImage, 0, 0, canvasSize.cx, 0, FILL_FAST).Stroke(0, Black());
		DoPaint(sw);
		WhenPaint(sw);
//		sw.Move(0, 0).Line(300, 300).Stroke(2, Black());
	}	
	return ib;
}
		
void PainterCanvas::Paint(Draw& w) {
/*	Size sz = GetSize();
	ImageBuffer ib(sz);
	BufferPainter sw(ib, mode);
	
	sw.Translate(translateX, translateY);
	sw.Rotate(rotate);
	sw.Scale(scale);
	sw.Opacity(opacity);
	sw.LineCap(linecap);
	sw.LineJoin(linejoin);*/
	//if (/*IsNull(backImage) || */colorUnderBackgroundImage) 
	//	sw.Clear(backColor);
	//else
	//	sw.Clear(RGBAZero());
/*	if (backColor)
		sw.Clear(backColor);
	else
		sw.Clear(SColorFace());*/
	//else {
	//	Color c = Null;
	//	sw.Clear(c);
	//}

/*	if (canvasSize.cx > 0 && canvasSize.cy > 0) {
		if (!IsNull(backImage)) 
			sw.Rectangle(0, 0, canvasSize.cx, canvasSize.cy).Fill(backImage, 0, 0, canvasSize.cx, 0, FILL_FAST).Stroke(0, Black());
		DoPaint(sw);
		WhenPaint(sw);
	}*/
	w.DrawImage(0, 0, GetImage());	
	if (legendShowXY) {
		Size sz = GetTextSize(legendText, legendFont);
		w.DrawRectOp(0, 0, sz.cx, sz.cy, LtGray());
		w.DrawText(0, 0, legendText, legendFont, Black());
	}
	
	if (showWindow && canvasSize.cx > 0 && canvasSize.cy > 0) {
		int twidth = 100;
		int theight = int(twidth*double(canvasSize.cy)/canvasSize.cx); 
		Size sz = GetSize();
		int tx = sz.cx-twidth-20;
		int ty = sz.cy-theight-20;	
		
		ImageBuffer tib(twidth, theight);
		BufferPainter tsw(tib, mode);
		
		tsw.Scale(double(twidth)/canvasSize.cx);
		
		if (IsNull(backImage) || colorUnderBackgroundImage) 
			tsw.Clear(backColor);
		 
		if (!IsNull(backImage)) 
			tsw.Rectangle(0, 0, canvasSize.cx, canvasSize.cy).Fill(backImage, 0, 0, canvasSize.cx, 0, FILL_FAST).Stroke(0, Black());
		
		if (canvasSize.cx > 0 && canvasSize.cy > 0) 
			WhenPaint(tsw);
		
		w.DrawImage(tx, ty, twidth, theight, tib);
		DrawRectLine(w, tx-1, ty-1, twidth+1, theight+1, 1, Black());
		
		double rateRep = double(twidth)/(canvasSize.cy*scale);
		DrawRectLine(w, int(tx-translateX*rateRep-1), int(ty-translateY*rateRep-1), 
					1+int(twidth*sz.cx/((canvasSize.cx - translateX*rateRep)*scale)), 
					1+int(theight*sz.cy/((canvasSize.cy - translateY*rateRep)*scale)), 1, Black());	
	}
	if (HasFocus()) {
		Size sz = GetSize();
		DrawFocus(w, Rect(sz).Deflated(2));
	}
}

void PainterCanvas::Layout() {
	if (alwaysFitInCanvas) 
		FitInCanvas();	
}

void PainterCanvas::Scroll(double factorX, double factorY)
{
    translateX -= GetSize().cx*factorX;
  	translateY -= GetSize().cy*factorY;

	Refresh();      	
}

void PainterCanvas::Zoom(double factor)
{
	scale *= factor;
	
	Size sz = GetSize();
	translateX = sz.cx*(1-factor)/2. + translateX*factor;
	translateY = sz.cy*(1-factor)/2. + translateY*factor;
	
	Refresh();
}

void PainterCanvas::FitInCanvas() {
	Rect r = FitInFrame(GetSize(), GetCanvasSize());

	double scalex = GetSize().cx/double(GetCanvasSize().cx);
	double scaley = GetSize().cy/double(GetCanvasSize().cy);
	
	scale = min(scalex, scaley);
	translateX = r.left;
	translateY = r.top;

	Refresh();
}	

void PainterCanvas::MouseLeave() {
    focusMove.focusMoving = false;
    WhenMouseLeave();
}

void PainterCanvas::MouseMove(Point p, dword keyflags) {
    if (mouseHandling && focusMove.focusMoving) {
     	translateX -= focusMove.lastFocusPoint.x - p.x;
      	translateY -= focusMove.lastFocusPoint.y - p.y;
      	focusMove.lastFocusPoint = p;

        Refresh();
    }
    Pointf pf;
    if(WhenMouseMove || legendShowXY) {
       	pf.x = p.x - translateX;
        pf.y = p.y - translateY;
        pf.x = pf.x / scale;
        pf.y = pf.y / scale;
    }
    if(legendShowXY) {
    	legendText = Format(t_("Pos: %d:%d. Real pos: %.2f:%.2f"), p.x, p.y, pf.x, pf.y);
    	Size sz = GetTextSize(legendText, legendFont);
    	Refresh(0, 0, legendLastSize.cx, legendLastSize.cy);
    	legendLastSize = sz;
    }
    if(WhenMouseMove) {
        if (pf.x >= 0 && pf.x < canvasSize.cx && pf.y >= 0 && pf.y < canvasSize.cy)
    		WhenMouseMove(p, pf, keyflags);
    }
}

Pointf PainterCanvas::GetPf(Point &p) {
    Pointf pf;
   	pf.x = p.x - translateX;
    pf.y = p.y - translateY;
    pf.x = pf.x / scale;
    pf.y = pf.y / scale;
    if (pf.x >= 0 && pf.x < canvasSize.cx && pf.y >= 0 && pf.y < canvasSize.cy)	
        return pf;
    return Null;
}

void PainterCanvas::MouseWheel(Point p, int zdelta, dword keyflags) {
	if (mouseHandling) {
		double factor;
		if(zdelta > 0)
			factor = scaleFactor;
		else
			factor = 1/scaleFactor;
		
		Zoom(factor);
	}
	if (WhenMouseLeftDown) {
        Pointf pf = GetPf(p);
        if (!IsNull(pf))
    		WhenMouseWheel(p, pf, zdelta, keyflags);
	}
}

void PainterCanvas::LeftDown(Point p, dword keyflags) {
	SetWantFocus();
	if (WhenMouseLeftDown) {
        Pointf pf = GetPf(p);
        if (!IsNull(pf))
    		WhenMouseLeftDown(p, pf, keyflags);
	}
}

void PainterCanvas::LeftUp(Point p, dword keyflags) {
	if (WhenMouseLeftUp) {
        Pointf pf = GetPf(p);
        if (!IsNull(pf))
    		WhenMouseLeftUp(p, pf, keyflags);
	}
}

void PainterCanvas::MiddleDown(Point p, dword keyflags) {
	if (mouseHandling) {
		focusMove.lastFocusPoint = p;
	    focusMove.focusMoving = true;
	}
	if (WhenMouseMiddleDown) {
        Pointf pf = GetPf(p);
        if (!IsNull(pf))
    		WhenMouseMiddleDown(p, pf, keyflags);
	}
}

void PainterCanvas::MiddleUp(Point p, dword keyflags) {
	if (mouseHandling) 
		 focusMove.focusMoving = false;
		 
	if (WhenMouseMiddleUp) {
        Pointf pf = GetPf(p);
        if (!IsNull(pf))
    		WhenMouseMiddleUp(p, pf, keyflags);
	}
}

void PainterCanvas::RightDown(Point p, dword keyflags) {
	if (mouseHandling) 
		MenuBar::Execute(THISBACK(ContextMenu));
	
	if (WhenMouseRightDown) {
        Pointf pf = GetPf(p);
        if (!IsNull(pf))
    		WhenMouseRightDown(p, pf, keyflags);
	}
}

void PainterCanvas::RightUp(Point p, dword keyflags) {
	if (WhenMouseRightDown) {
        Pointf pf = GetPf(p);
        if (!IsNull(pf))
    		WhenMouseRightUp(p, pf, keyflags);
	}
}

bool PainterCanvas::Key(dword key, int repeat) {
	WhenKey(key, repeat);
	return false;
}

void PainterCanvas::GotFocus() {
	Refresh();
}

void PainterCanvas::LostFocus() {
	Refresh();
}
	
PainterCanvas &PainterCanvas::SetBackground(const Image image)	{
	backImage = image; 
	if (backImage.GetSize() != canvasSize) {
		SetCanvasSize(backImage.GetSize()); 
		Layout();
	} 
	Refresh();
	return *this;
}

PainterCanvas &PainterCanvas::SetBackground(const String &imageFilename)	{
	return SetBackground(StreamRaster::LoadFileAny(~imageFilename));
}                      
           
void PainterCanvas::ContextMenu(Bar& bar) {	
	bar.Add(t_("Fit to size"), 	Controls4UImgC::ShapeHandles(), THISBACK(FitInCanvas));
	bar.Add(t_("Zoom +"), 		Controls4UImgC::ZoomPlus(), 	THISBACK1(Zoom, 1.2));
	bar.Add(t_("Zoom -"), 		Controls4UImgC::ZoomMinus(), 	THISBACK1(Zoom, 1/1.2));
	bar.Add(t_("Scroll Left"), 	Controls4UImgC::LeftArrow(), 	THISBACK2(Scroll, 0.2, 0));
	bar.Add(t_("Scroll Right"), Controls4UImgC::RightArrow(), 	THISBACK2(Scroll, -0.2, 0));
	bar.Add(t_("Scroll Up"), 	Controls4UImgC::UpArrow(), 		THISBACK2(Scroll, 0, -0.2));
	bar.Add(t_("Scroll Down"), 	Controls4UImgC::DownArrow(), 	THISBACK2(Scroll, 0, 0.2));			
	bar.Separator();
	bar.Add(t_("Copy"), Controls4UImgC::Copy(), 			THISBACK(SaveToClipboard)).Key(K_CTRL_C);
	bar.Add(t_("Save background"), Controls4UImgC::Save(), 	THISBACK1(SaveToFile, Null));
	bar.Add(t_("Load background"), Controls4UImgC::Save(), 	THISBACK1(Load, Null));
}

void PainterCanvas::SaveToClipboard()  {
	GuiLock __;
	Image img = GetBackground();
	WriteClipboardImage(img);
}

void PainterCanvas::Load(String fileName) {
	GuiLock __;
	if (IsNull(fileName)) {
		FileSel fs;
		
		fs.Type("Image files", "*.png; *.jpg; *.tif; *.bmp");
		fs.Type("All files", "*.*");
	    if(!fs.ExecuteOpen(t_("Loading background image"))) {
	        Exclamation(t_("Image has not been loaded"));
	        return;
	    }
        fileName = fs;
	} 
	if (!FileExists(fileName)) {
		Exclamation(Format(t_("File \"%s\" not found"), DeQtf(fileName)));
		return;
	}
	SetBackground(fileName);
	if (IsNull(backImage)) {
		Exclamation(Format(t_("File format \"%s\" not found"), GetFileExt(fileName)));
		return;
	}
}     

void PainterCanvas::SaveToFile(String fileName) {
	GuiLock __;
	if (IsNull(fileName)) {
		FileSel fs;
		
		fs.Type("PNG file", "*.png");
		fs.Type("JPEG file", "*.jpg");
	    if(!fs.ExecuteSaveAs(t_("Saving background image to PNG or JPEG file"))) {
	        Exclamation(t_("Image has not been saved"));
	        return;
	    }
        fileName = fs;
	} 
	if (FileExists(fileName)) {
		if (!PromptOKCancel(Format(t_("File \"%s\" found.&Do you want to overwrite it?"), DeQtf(fileName))))
			return;
	}
	
	String ext = GetFileExt(fileName);
	if (ext == ".png") {
		PNGEncoder encoder;
		encoder.SaveFile(fileName, GetBackground());
	} else if (ext == ".jpg") {	
		JPGEncoder encoder(90);
		encoder.SaveFile(fileName, GetBackground());		
	} else
		Exclamation(Format(t_("File format \"%s\" not found"), ext));
}      
                          
Image PainterCanvas::CursorImage(Point p, dword keyflags) {
	return cursorImage;
}

void PainterCanvas::DoPaint(Painter& sw) {
	//sw.Translate(translateX, translateY);
	//sw.Rotate(rotate);
	//sw.Scale(scale);
	//sw.Opacity(opacity);
	//sw.LineCap(linecap);
	//sw.LineJoin(linejoin);
	/*{ PAINTER_TIMING("FILL");
		if(transparent)
			sw.Clear(RGBAZero());
		else
			sw.Clear(White());
	}*/
	Svg2DTransform trans;
	SvgStyle style;
	style.SetFill(Null).SetStrokeColor(Black()).SetOpacity(1).SetStrokeWidth(1);
	
	sw.Begin();
	elemList.Paint(sw, trans, style, true);
	sw.End();
}

PainterCanvas &PainterCanvas::SetLegend(bool _legendShowXY, Font _legendFont) {
	legendShowXY = _legendShowXY;
	legendFont = _legendFont;
	return *this;
}

INITBLOCK{
	GraphElem::Register<GraphElem>("GraphElem");
	GraphElem::Register<LineElem>("LineElem");
	GraphElem::Register<RectElem>("RectElem");
	
	// Falta el resto	
}