text: #include <CtrlLib/CtrlLib.h>
  #include "Controls4U.h"
  
  #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>
  
  
  namespace Upp {
  	
  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) {
  	unsigned col;
  	sscanf(str+1, "%x", &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);
  }
  
  static 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 , SvgStyle , 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 , dword ) {
  	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");
  	
  
  }
  
  }