#include "Sauklaue.h"
#include <PdfDraw/PdfDraw.h>
#include <plugin/png/png.h>
#define TFILE <Sauklaue/Sauklaue.t>
#include <Core/t.h>
#define TOPICFILE <Sauklaue/sauklaue.tpp/all.i>
#include <Core/topic_group.h>

static const char * binext = "*.skd";
static const Image papers[]={SauklaueImg::Paper1,SauklaueImg::Paper2,SauklaueImg::Paper3,SauklaueImg::Paper4,SauklaueImg::Paper5};

int gracomp(const Graphem& a,const Graphem& b)
{
	return (a.q < b.q);
}

Sauklaue::Sauklaue()
{
	TopMargin = 20;
	LeftMargin = 20;
	Textheight = 100;
	height = 120;
	Interline = 10;
	m_default = 0.0;
	ShowDots = false;
	ShowFrames = true;
	A4Limit = true;
	Baselines = true;
	BasedGraphems = true;
	Paper = 0;
	m_default = 0.0;
	
	doc.voptions.Clear();
	struct options op;
	doc.voptions.Add(op);	
	AddFrame(menu);
	menu.Set(THISBACK(MainBar));
	AddFrame(TopSeparatorFrame());
	AddFrame(toolbar);
	AddFrame(status);
	AddFrame(scroll);
	Sizeable().Zoomable();
	WhenClose = THISBACK(Exit);
	menu.WhenHelp = toolbar.WhenHelp = status;
	SetBar();
	scroll.WhenScroll = THISBACK(Scroll);

	LoadFromFile(*this);
	Icon(SauklaueImg::SKL_Icon());
	if (FileExists(filename))
	{
		LoadFromFile(doc,filename);
//		DLOG("Document Load : " << doc);
		name = GetFileTitle(filename);
		Title(t_("SWord - ") + name);
		if ((doc.TopMargin >= 0) && (doc.TopMargin <= 1000))
		{
			TopMargin  = doc.TopMargin;	
			LeftMargin = doc.LeftMargin;	
			Textheight = doc.Textheight;
			Interline  = doc.Interline;
			m_default  = doc.m_default;
			Paper      = doc.Paper;
			if ((Paper < 0) || (Paper >10)) Paper = 0;
		}
	}
	else New_Doc();
	
	IsModified = false;	
	schrift_modified = false;	
	text_modified = false;	
	pschrifted = (Schrift_Editor * )0;
	ptext = (TextEditor * )0;
	phelp = (HelpWindow *)0;
	int totlength = doc.schrift.vgraphems.GetCount();
	for (int i = 0; i < totlength; i++)
	{
		WString s = doc.schrift.vgraphems[i].text.ToWString();
		doc.schrift.vgraphems[i].q = s.GetLength();
	}
	Sort(doc.schrift.vgraphems,gracomp);
	lastGap = lastmousemode = mousemode = 0;
	undoIndex = -1;
	undoPossible = redoPossible = 0;
	DrawingSize();
	Ctrl::SetFrameRect(1,1,view_size.cx,view_size.cy);
}

void Sauklaue::Paint(Draw& w)
{
	Point pos = scroll;
	Size wins = GetSize();
  	int xlimit = 3968 / 6 - pos.x;
	int ylimit = 6074 / 6 - pos.y;
	
	Do_Drawing(w,wins,true);

	if (A4Limit)
	{
		if (wins.cx > xlimit)
		{
			w.DrawLine(xlimit,0,xlimit,wins.cy,1,LtGray());
			int x1 = xlimit;
			int y1 = 0;
			int x2 = xlimit;
			int y2 = 0;
			while(y2 <= wins.cy )
			{
				y1 += 20;
	 			x2 += 20;
	 			if (x2 > wins.cx)
	 			{
	 				y2 += x2 - wins.cx;
	 				x2 = wins.cx;
	 			}
				if (y1 > wins.cy)
				{
					x1 += y1 - wins.cy;
					y1 = wins.cy;
				}
				w.DrawLine(x1,y1,x2,y2,1,LtGray());
			}
		}
		if (wins.cy > ylimit)
		{
			w.DrawLine(0,ylimit,xlimit,ylimit,1,LtGray());
			int x1 = 0;
			int y1 = ylimit;
			int x2 = 0;
			int y2 = ylimit;
			while(x1 <= xlimit )
			{
				y1 += 20;
	 			x2 += 20;
	 			if (x2 > xlimit)
	 			{
	 				y2 += x2 - xlimit;
	 				x2 = xlimit;
	 			}
				if (y1 > wins.cy)
				{
					x1 += y1 - wins.cy;
					y1 = wins.cy;
				}
				w.DrawLine(x1,y1,x2,y2,1,LtGray());
			}
		}
	}
}

Painting Sauklaue::DrawGraphemes( const Rect& r,Vector<Vector<Grapoint> >& vgrap, struct options& opt, bool isgui)
{
	Vector<Grapoint> extgrap;
	Vector<Grapoint> extg;
	Vector<Pointf> ctrlind; 
	for (int gi = 0; gi < vgrap.GetCount(); gi++)
	{
		extg.Clear();
		ctrlind = ControlIndications(vgrap[gi],extg);
		extgrap.Append(extg);
	}
	PaintingPainter pn(r.GetSize());
//	pn.Opacity(0.75);
	double magnify = Textheight; //r.Height() * 0.8;
	if (!isgui) magnify *= 6;
	int l = 0;
	int n1 = extgrap.GetCount();
	int curstatus =0;
	Pointf p1,p2,pto;
	bool movewas = false;
	
	if (bool align_baseline = false)
	{
		int dy = (r.Height() - magnify*height/100)*doc.schrift.baseline;
		pn.Translate(0,dy);
	}
	if (BasedGraphems && isgui)
	{
		double y = magnify*doc.schrift.baseline * (100 + opt.Trans.stretchy)/100.0;
//		pn.DrawLine(0,y,r.Width(),y,1,LtGray());
		pn.Move(0,y);
		pn.Line(r.Width(),y);
		pn.Stroke(isgui ? 1 : 6,LtGray());
	}
	if (n1)
	{
		for (int i = 0; i < n1; i++)
		{
//			DLOG("Punkt "<<i <<" status " << extgrap[i].status << " : " <<	magnify * extgrap[i].p);
			if (extgrap[i].status & STATUS_CTRL)
			{
				if (l == 0) p1 = magnify * extgrap[i].p;
				else p2 = magnify * extgrap[i].p;
				l++;
			}
			else 
			{
				pto = magnify * extgrap[i].p;
				curstatus = extgrap[i].status & STATUS_CONNECT;
				switch (curstatus)
				{
				case STATUS_MOVETO:
					pn.Move(pto);
					movewas = true;
					break;
				case STATUS_LINETO:
					if (!movewas) 
					{
						pn.Move(pto);
						movewas = true;
					}
					else pn.Line(pto);
					break;
				case STATUS_QUADRB:
					if (!movewas)
					{
						pn.Move(pto);
						movewas = true;
						break;
					}
					if (l) pn.Quadratic(p1,pto);
					else pn.Quadratic(pto);
					break;
				case STATUS_CUBICB:
					if (!movewas)
					{
						pn.Move(pto);
						movewas = true;
						break;
					}
					if (l>1) 
						pn.Cubic(p1,p2,pto);
					else if (l>0)
						pn.Cubic(p1,pto);
					else
					{
						 pn.Move(pto);
						 movewas = true;
					}
					break;
				default:
					pn.Move(pto);
					movewas = true;
				}
				l=0;
			}
		}
		if (opt.writer == 0)
		{	
			pn.LineCap(LINECAP_ROUND);
			pn.Stroke(isgui ? opt.Penwidth : 6*opt.Penwidth,opt.Pencolor);
		}
		else
		{
			pn.LineCap(LINECAP_BUTT);
			Pointf dis;
			dis.x = magnify * opt.Featherwidth/100.0 * cos(opt.Featherturn*PI/180.0);
			dis.y = magnify * opt.Featherwidth/100.0 * sin(opt.Featherturn*PI/180.0);
			int num = sqrt(dis.x*dis.x + dis.y * dis.y);
			pn.Stroke(1,num > 0 ? White():opt.Feathercolor);
			pn.Translate(-dis/2);
			for (int i = 0; i < num;i++)
			{
				pn.Stroke(1,opt.Feathercolor);
				pn.Translate(dis/num);
			}
			pn.Translate(-dis/2);
		}
	}
	return (pn.GetResult());
}
void Sauklaue::DrawGraphemes( Draw&w ,const Rect& r,Vector<Vector<Grapoint> >& vgrap, struct options& opt)
{
	Painting paint = DrawGraphemes( r, vgrap, opt, true);
	w.DrawPainting(r,paint);

}

#define minx 0.05
void Sauklaue::DoTransformation(Rect& r,Vector<Vector<Grapoint> >& vgrap, Trans_t trans)
{
	double shift, xmin = minx, xmax = 0, ymax = 0;
	int n1 = vgrap.GetCount();
	if (n1)
	{
		for (int gi = 0; gi < n1; gi++)
		{
			Vector<Grapoint>& vg = vgrap[gi];
			for (int i = 0; i < vg.GetCount(); i++)
			{
				shift = (doc.schrift.baseline - vg[i].p.y)*trans.slant/100.0;
				shift += trans.shift/100.0;	
				vg[i].p.x += shift;
				vg[i].p.x *= (100 + trans.stretchx)/100.0;
				if (vg[i].p.x < xmin) xmin = vg[i].p.x;
				if (vg[i].p.x > xmax) xmax = vg[i].p.x;						
				double y = vg[i].p.y;
//				y -= doc.schrift.baseline;
				y *= (100 + trans.stretchy)/100.0;
//				y += doc.schrift.baseline;
				vg[i].p.y = y;
				if (y > ymax) ymax = y;
			}
		}
		if (xmin < minx)
		{
			shift = minx -xmin;
			for (int gi = 0; gi < vgrap.GetCount(); gi++)
			{
				Vector<Grapoint>& grap = vgrap[gi];
				for (int i = 0; i < grap.GetCount(); i++)
					grap[i].p.x += shift;
			}
			xmax += shift;
		}
		int needx = 100 * xmax;
		int needy = 100 * ymax;
		if ( needx > r.Width()) r.right = r.left + needx;
		if ( needy > r.Height()) r.bottom = r.top + needy;
	}
}

void Sauklaue::ScaleWordRect(Rect& r, double scale)
{
	Point pos = scroll;
	
	int left = r.left * scale + LeftMargin - pos.x;
	int top  = r.top * scale + TopMargin - pos.y;
	int right = r.right * scale + LeftMargin - pos.x;
	int bottom = r.bottom * scale + TopMargin - pos.y;
	r.Set(left,top,right,bottom);
}


Painting Sauklaue::MakePainting(Size ims)
{
	PaintingPainter pn(ims);

	if (Paper == 0) 
		pn.DrawRect(ims, SColorPaper());
	else 
		pn.Rectangle(0, 0, ims.cx,ims.cy)
	  	.Fill(papers[Paper - 1], 0, 0, 100, 0, FILL_REPEAT);
	if (Baselines)
	{
		int leftx = LeftMargin;
		if (leftx < 0) leftx = 0;
		int rightx = ims.cx - LeftMargin;
		double lining = (height + Interline)*Textheight/100.0;
		for (int i = 0; ; i++)
		{
			int iy = TopMargin+Textheight*doc.schrift.baseline + i * lining;
			int jy =  iy;
			if (jy > ims.cy) break;
			if (jy > 0)
				pn.DrawLine(leftx,jy,rightx,jy, 1, LtGray());
		}
	}

	for (int i = 0; i < doc.vwordrect.GetCount(); i++)
	{
		Rect r = doc.vwordrect[i].r;
		Vector<Vector<Grapoint> > vgrap;
		vgrap <<= doc.vwordrect[i].vgrap;
		struct options& opt = doc.voptions[doc.vwordrect[i].option_index];
		DoTransformation( r,  vgrap, opt.Trans);
		ScaleWordRect(r, (double) Textheight/100.0);
		DrawGraphemes(pn,r,vgrap,opt);
	}
	return pn.GetResult();
}

void Sauklaue::Do_FramesDots(Draw & w, Rect& r, Vector<Vector<Grapoint> >& vgrap)
{
	
	if (ShowFrames)
	{
		Vector<Point> ps;
		ps << r.TopLeft() << r.TopRight() <<r.BottomRight() << r.BottomLeft() << r.TopLeft();
        w.DrawPolyline(ps , 2,  Blue());
	}
	if (ShowDots)
	{
		Point q;
		double magnify = Textheight;
		for (int gi = 0; gi < vgrap.GetCount(); gi++)
		{
			Vector<Grapoint>& grap = vgrap[gi];
			for (int i = 0; i < grap.GetCount(); i++)
			{
				q.x = r.left + (int)(grap[i].p.x * magnify);
				q.y = r.top + (int)(grap[i].p.y * magnify);
				Rect pr(q,q);
				pr.Inflate(2,2);
				Color col = LtRed();
				if (grap[i].status & STATUS_CTRL) col = LtGreen();
				w.DrawEllipse(pr,col);
			}
		}
	}
}

void Sauklaue::Do_Drawing(Draw & w, Size drs,bool isgui)
{
	w.DrawRect(drs, SColorPaper());
   	PaintingPainter pn(drs);
	int xlimit = 3968;
	int ylimit = 6074;
	

if (isgui)
{
	if ((Paper > 0) && (Paper <6))
		pn.Rectangle(0, 0, xlimit/6, ylimit/6)
	 	 .Fill(papers[Paper -1], 0, 0, 100, 0, FILL_REPEAT);
	else pn.Clear(White());
	if (Baselines)
	{
		xlimit /=6;
		ylimit /=6;
		Point pos = scroll;
		int leftx = LeftMargin - pos.x;
		if (leftx < 0) leftx = 0;
		int rightx = xlimit - LeftMargin;
		double lining = (height + Interline)*Textheight/100.0;
		for (int i = 0; i < 10000 ; i++)
		{
			int iy = TopMargin+Textheight*doc.schrift.baseline + i * lining;
			int jy =  iy - pos.y;
			if (jy > drs.cy) break;
			if (jy > (ylimit - TopMargin))break;
			if (jy > 0)
//				pn.DrawLine(leftx,jy,rightx,jy, 0.1, LtGray());
			{
				pn.Move(leftx,jy).Line(rightx,jy).Stroke(0.5,LtGray());
			}
		}
	}

	w.DrawPainting(0,0,drs.cx,drs.cy,pn);
}
else 
{	
	if (Paper > 0)
		pn.Rectangle(0, 0, xlimit, ylimit)
	 	 .Fill(papers[Paper -1], 0, 0, 100, 0, FILL_REPEAT);
	if (Baselines)
	{
		int leftx = 6*LeftMargin;
		int rightx = drs.cx - 6*LeftMargin;
		double lining = (height + Interline)*Textheight/100.0;
		for (int i = 0; ; i++)
		{
			int iy = (TopMargin + 3 + Textheight*doc.schrift.baseline + i * lining)*6;
			if (iy > drs.cy) break;
			pn.DrawLine(leftx,iy,rightx,iy, 6, LtGray());
		}
	}
}

	Painting paint;
	
	paint.Clear();
	
	for (int i = 0; i < doc.vwordrect.GetCount(); i++)
	{
		Rect r = doc.vwordrect[i].r;
		Vector<Vector<Grapoint> >vgrap;
		vgrap <<= doc.vwordrect[i].vgrap;
		struct options& opt = doc.voptions[doc.vwordrect[i].option_index];
		DoTransformation( r,  vgrap, opt.Trans);
		if (isgui)
		{
			ScaleWordRect(r,double(Textheight)/100.0);
			for ( int j = 0; j < vselrect.GetCount();j++)
				if ( vselrect[j] == i)
				{
					w.DrawRect(r,Yellow());
					break;
				}
		}
		else ScaleWordRect(r,double(6 * Textheight)/100.0);

//		
		if (isgui)
		{
			DrawGraphemes(w,r,vgrap,opt);
			Do_FramesDots(w,r,vgrap);
		}
		else 
		{
			Point ptl(6*LeftMargin,6*TopMargin);
			paint = DrawGraphemes(r,vgrap,opt,isgui);
			pn.Opacity(1.0);
			pn.Translate(r.TopLeft()+ptl);
			pn.Paint(paint);
			pn.Translate(-(r.TopLeft()+ptl));
		}
	}
	if(!isgui)
	{
		w.DrawPainting(0,0,drs.cx,drs.cy,pn);
	}
}

#define SEARCH_WINDOW 10
#define OPTION_SEARCH_WINDOW 10

void Sauklaue::ReAnalyseText()
{
	struct dsect
	{
		int ipl_start;
		int ipl_end;
		int inl_start;
		int inl_end;
	} linediff;
	struct dsect worddiff;
	struct dsect diffwords[100];
	struct dsect difflines[100];
	Vector<wordrect>& vwr = doc.vwordrect;
	int vwri = 0,newwri = 0,lineshift = 0;
	int numwr = vwr.GetCount();
	Vector<wordrect> newwr;
	
	Vector<String> vnlines = Split(doc.text_buf,'\n',false);
	Vector<String> vplines = Split(prev_text,'\n',false);
	int nnl = vnlines.GetCount();
	int npl = vplines.GetCount();
	int inl,ipl, ognl,ogpl;
	int ilsect = 0;
	int spl,snl;
	WString miss;
	double xoff;

	for (int i = 0; i < nnl; i++)
		vnlines[i] = UPP::TrimBoth(vnlines[i]);
	for (int i = 0; i < npl; i++)
		vplines[i] = UPP::TrimBoth(vplines[i]);
	
	for (inl=ipl=0; (inl < nnl) && (ipl < npl); inl++,ipl++)
	{
		DLOG ("Comparing 0 \"" << vnlines[inl] << "\" with \"" <<vplines[ipl]<<"\"");
		if ( vnlines[inl] != vplines[ipl])
		{
			linediff.ipl_start = ipl;
			linediff.ipl_end = ipl;
			linediff.inl_start = inl;
			linediff.inl_end = inl;
			difflines[ilsect] = linediff;
			ognl = inl + SEARCH_WINDOW;
			if (ognl > nnl) ognl = nnl;
			ogpl = ipl + SEARCH_WINDOW;
			if (ogpl > npl) ogpl = npl;
			bool found = false;
			
			for ( spl = ipl; spl < ogpl;spl++)
			{
				snl = inl;
				if (IsEmpty(vplines[spl]))continue;
				for (; snl < ognl; snl++)
				{
					if (IsEmpty(vnlines[snl]))continue;
					DLOG ("Comparing 1 \"" << vnlines[snl] << "\" with \"" <<vplines[spl]<<"\"");
					if (vnlines[snl] == vplines[spl])
					{
						linediff.ipl_end = spl-1;
						linediff.inl_end = snl-1;
						difflines[ilsect] = linediff;
						if (++ilsect >= 100)ilsect--;
						found = true;
						break;
					}
				}
				if (found) break;
			}
			if (!found)
			{
				linediff.ipl_end = ogpl-1;
				linediff.inl_end = ognl-1;
				difflines[ilsect] = linediff;
				if (++ilsect >= 100)ilsect--;	
				inl = ognl - 1;
				ipl = ogpl - 1;							
			}
			else
			{
				inl = snl;
				ipl = spl;
			}
		}
	}
	if ((inl < nnl) || (ipl < npl))
	{
			linediff.ipl_start = ipl;
			linediff.ipl_end = npl - 1;
			linediff.inl_start = inl;
			linediff.inl_end = nnl - 1;
			difflines[ilsect++] = linediff;
	}
	int traline = 0;
	int outline = 0;
	for (int i = 0; i < ilsect; i++)
	{
		DLOG("Section " <<i <<"*******************");
		linediff = difflines[i];
		int lmax1 = linediff.ipl_end - linediff.ipl_start;
		int lmax2 = linediff.inl_end - linediff.inl_start;
		int lmax = (lmax2 > lmax1) ? lmax2 : lmax1;

		for (; (traline < linediff.ipl_start) && (traline < npl); traline++)
		{
			Vector<String> vw = Split(vplines[traline],' ',true);
			for (int vwi = 0; vwi < vw.GetCount(); vwi++)
			{
				wordrect wore;
				wore.r = vwr[vwri].r;
				wore.s = vwr[vwri].s;
				wore.vgrap <<= vwr[vwri].vgrap;
				wore.option_index = vwr[vwri++].option_index;
				if (TrimRight(wore.s) == vw[vwi])
				{
					if (lineshift)
						wore.r.OffsetVert(lineshift*(height+Interline));
					newwr.Add(wore);
					DLOG("Transfering " << wore.s);
				}
				else 
					DLOG(Format("Out of Sequence!! (Line %d, Word %d) %s <-> %s",traline,vwi,wore.s,vw[vwi]));
			}
			outline++;
		}

		for (int l = 0 ; l <= lmax ; l++)
		{
			String out("-----");
			if (l <= lmax1)
			{
				out.Clear();
				out.Cat(AsString(linediff.ipl_start+l));
				out.Cat(": ");
				out.Cat(vplines[linediff.ipl_start+l]);
			}
			out.Cat("                                                                                                    ",80 - out.GetLength());
			if (l <= lmax2) out.Cat(vnlines[linediff.inl_start+l]);
			else out.Cat("----------");
			DLOG(out);
		}
		for (int l = 0 ; l <= lmax ; l++)
		{	
			String sline= AsString(traline);
			if ((l <= lmax1) && !IsEmpty(vplines[linediff.ipl_start+l]))
			{
				DLOG("TraLine "<<sline<<", DiffLine "<<linediff.ipl_start+l<<" lineshift " << lineshift);
			}
			else
			{
				if (l > lmax1)
				{
					lineshift++;
					sline = Format("%d.%d",traline-lineshift,(l-lmax1));
					DLOG ("Insert New Line  " <<  sline);
				}
				else  //IsEmpty(vplines[linediff.ipl_start+l])
				{
					DLOG("Line "<<sline);
					traline++;
				}
				Vector<String> vw;
				if ((linediff.inl_start+l) <= linediff.inl_end)
					vw = Split(vnlines[linediff.inl_start+l],' ',true);
				int left = 0;
				for (int vwi = 0; vwi < vw.GetCount(); vwi++)
				{
					wordrect wore;
					wore.s = vw[vwi];
					wore.vgrap = Analyse_Word(wore.s, miss, xoff);
					wore.option_index = 0;	//Default
					for (int w = (vwri > OPTION_SEARCH_WINDOW) ? (vwri - OPTION_SEARCH_WINDOW) : 0; 
							 w < ((vwri < (numwr - OPTION_SEARCH_WINDOW)) ? (vwri + OPTION_SEARCH_WINDOW) : numwr);
							 w++)
					{
						if (TrimRight(vwr[w].s) == wore.s)
						{
							wore.option_index = vwr[w].option_index;
							break;
						}
					}
					int rwidth = height*xoff/1.2;
					int rtop = outline * (height + Interline);
					wore.r = Rect(left,rtop,left+rwidth,rtop + height);
					left += rwidth;
					newwr.Add(wore);
					DLOG("Inserting " << wore.s <<" with Option_Index " << wore.option_index);
				}
				outline++;
				continue;
 			}
 			if  ((l > lmax2) || IsEmpty(vnlines[linediff.inl_start+l]))
 			{
				lineshift--;
 				DLOG ("Delete Line " << sline);
 				Vector<String> vw ;
 				if ((linediff.ipl_start+l) <= linediff.ipl_end)
					vw = Split(vplines[linediff.ipl_start +l],' ',true);
				for (int vwi = 0; vwi < vw.GetCount(); vwi++)
				{
					wordrect wore;
					wore.r = vwr[vwri].r;
					wore.s = vwr[vwri].s;
					wore.vgrap <<= vwr[vwri].vgrap;
					wore.option_index = vwr[vwri++].option_index;
					if (TrimRight(wore.s) == vw[vwi])
					{
						DLOG("Deleting " << wore.s);
					}
					else 
						DLOG(Format("Out of Sequence!! (Line %d, Word %d) %s <-> %s",traline,vwi,wore.s,vw[vwi]));
				}
	 			traline++;
	 			continue;	
 			}
 			// Compare lines
	 		Vector<String> vnwords;
	 		if (l <= lmax2) vnwords = Split(vnlines[linediff.inl_start+l],' ',false);
	 		Vector<String> vpwords;
	 		if (l <= lmax1) vpwords = Split(vplines[linediff.ipl_start+l],' ',false);
			int nnw = vnwords.GetCount();
			int npw = vpwords.GetCount();
			DLOG("Comparing prev line "<<linediff.ipl_start+l<<" ("<<npw<<" words) with new line "<<linediff.inl_start+l<<" ("<<nnw<<" words)");
			DLOG("Traline "<<traline);
			int inw,ipw;
			int iwsect = 0;
			int spw,snw;
			for (inw=ipw=0; (inw < nnw) && (ipw < npw); inw++,ipw++)
				if ( vnwords[inw] != vpwords[ipw])
				{
					worddiff.ipl_start = ipw;
					worddiff.ipl_end = ipw;
					worddiff.inl_start = inw;
					worddiff.inl_end = inw;
					diffwords[iwsect] = worddiff;
					for ( spw = ipw; spw < npw;spw++)
					{
						bool found = false;
						for ( snw = inw; snw < nnw; snw++)
						{
							if (vnwords[snw] == vpwords[spw])
							{
								worddiff.ipl_end = spw-1;
								worddiff.inl_end = snw-1;
								diffwords[iwsect] = worddiff;
								if (++iwsect >= 100)iwsect--;
								found = true;
								break;
							}
						}
						if (found) break;
					}
					if ((snw == nnw) && ( spw == npw))
					{
						worddiff.ipl_end = spw-1;
						worddiff.inl_end = snw-1;
						diffwords[iwsect] = worddiff;
						if (++iwsect >= 100)iwsect--;				
					}
					inw = snw;
					ipw = spw;
				}
			if ((inw < nnw) || (ipw < npw))
			{
				worddiff.ipl_start = ipw;
				worddiff.ipl_end = npw - 1;
				worddiff.inl_start = inw;
				worddiff.inl_end = nnw - 1;
				diffwords[iwsect++] = worddiff;				
			}
			DLOG ("In Line " <<sline <<" are " <<iwsect <<" worddiff sections");
			int traword = 0;
			int wordshift = 0;
			int insleft = 0;
			for (int k = 0; k < iwsect; k++)
			{
				worddiff = diffwords[k];
				int wmax1 = worddiff.ipl_end - worddiff.ipl_start;
				int wmax2 = worddiff.inl_end - worddiff.inl_start;
				int wmax = (wmax2 > wmax1) ? wmax2 : wmax1;
				for (; traword < worddiff.ipl_start; traword++)
				{
					wordrect wore;
					wore.r = vwr[vwri].r;
					wore.s = vwr[vwri].s;
					wore.vgrap <<= vwr[vwri].vgrap;
					wore.option_index = vwr[vwri++].option_index;
					if (TrimRight(wore.s) == vpwords[traword])
					{
						if (lineshift)
							wore.r.OffsetVert(lineshift*(height+Interline));
						if (wordshift)
							wore.r.OffsetHorz(wordshift);
						newwr.Add(wore);
						insleft = wore.r.right;
						DLOG("Transfering  in Line " << sline <<" Word "<<traword <<": "<< wore.s <<"Rect " <<vwr[vwri-1].r <<" -> "<<wore.r);
					}
					else 
						DLOG(Format("Out of Sequence!! ( Word %d) %s <-> %s",traword,wore.s,vpwords[traword]));
				}

				for (int jj = 0 ; jj <= wmax ; jj++)
				{
					String out("--");
					if (jj <= wmax1)
					{
						out.Clear();
						out.Cat(vpwords[worddiff.ipl_start+jj]);
					}
					out.Cat("                                                        ",40 - out.GetLength());
					if (jj <= wmax2) out.Cat(vnwords[worddiff.inl_start+jj]);
					else out.Cat("----------");
					DLOG(out);
					if ((jj <= wmax1) && (jj <= wmax2))
					{
						DLOG("Replace in Line " << sline <<" Word "<<traword <<": " <<vpwords[worddiff.ipl_start+jj] << " with " << vnwords[worddiff.inl_start+jj]);
						wordrect wore;
						wore.r = vwr[vwri].r;
						int oldwidth = wore.r.Width();
						wore.s = vnwords[worddiff.inl_start+jj];
						wore.vgrap= Analyse_Word(wore.s, miss, xoff);
						int width = 100*xoff;
						wore.r.right = wore.r.left + width;
						wore.option_index = vwr[vwri++].option_index;
						if (lineshift)
							wore.r.OffsetVert(lineshift*(height+Interline));
						if (wordshift)
							wore.r.OffsetHorz(wordshift);
						
						struct options& opt = doc.voptions[wore.option_index];
						Vector<Vector<Grapoint> >vgrap;
						vgrap <<= wore.vgrap;
						DoTransformation(wore.r, vgrap, opt.Trans);
						
						newwr.Add(wore);
						insleft = wore.r.right;
//						DLOG("insleft : "<<insleft);
						DLOG("Replacing " << wore.s <<" :Rect " << vwr[vwri-1].r <<" -> " << wore.r);
						wordshift += wore.r.Width() - oldwidth;
						traword++;
					}
					else if (jj <= wmax1)
					{
						DLOG("Delete in Line " <<sline <<" Word "<<traword <<": " <<vpwords[worddiff.ipl_start+jj]);
						wordrect wore;
						wore.r = vwr[vwri].r;
						wore.s = vwr[vwri].s;
						wore.vgrap <<= vwr[vwri].vgrap;
						wore.option_index = vwr[vwri++].option_index;
						if (TrimRight(wore.s) == vpwords[worddiff.ipl_start+jj])
						{
							DLOG("Deleting " << wore.s);
							wordshift -= wore.r.Width();
						}
						else 
							DLOG(Format("Out of Sequence!! ( Word %d) %s <-> %s",traword,wore.s,vpwords[worddiff.ipl_start+jj]));
						traword++;
					}
					else 
					{
						DLOG ("Insert in Line " <<sline <<" after Word "<<traword <<": "  <<vnwords[worddiff.inl_start+jj]);
						wordrect wore;
						wore.s = vnwords[worddiff.inl_start+jj];
						wore.vgrap = Analyse_Word(wore.s, miss, xoff);
						wore.option_index = 0;	//Default
						for (int w = (vwri > OPTION_SEARCH_WINDOW) ? (vwri - OPTION_SEARCH_WINDOW) : 0; 
							 w < ((vwri < (numwr - OPTION_SEARCH_WINDOW)) ? (vwri + OPTION_SEARCH_WINDOW) : numwr);
							 w++)
						{
							if (TrimRight(vwr[w].s) == wore.s)
							{
								wore.option_index = vwr[w].option_index;
								break;
							}
						}
						int width = 100*xoff;
						int top = outline * (height + Interline);
						wore.r = Rect(insleft,top,insleft + width,top + height);
						insleft += width;
						newwr.Add(wore);
						DLOG("Inserting " << wore.s);
						wordshift += wore.r.Width();
					}
				}
			}
			for (; traword < vpwords.GetCount(); traword++)
			{
				wordrect wore;
				wore.r = vwr[vwri].r;
				wore.s = vwr[vwri].s;
				wore.vgrap <<= vwr[vwri].vgrap;
				wore.option_index = vwr[vwri++].option_index;
				if (TrimRight(wore.s) == vpwords[traword])
				{
					if (lineshift)
						wore.r.OffsetVert(lineshift*(height+Interline));
					if (wordshift)
						wore.r.OffsetHorz(wordshift);
					newwr.Add(wore);
					insleft = wore.r.right;
					DLOG("Transfering  in Line " << sline <<" Word "<<traword <<": "<< wore.s <<"Rect " <<vwr[vwri-1].r <<" -> "<<wore.r);
				}
				else 
					DLOG(Format("Out of Sequence!! ( Word %d) %s <-> %s",traword,wore.s,vpwords[traword]));

			}
			outline++;
			traline++;
		}
		traline = linediff.ipl_end + 1;
	}
	for (; traline < npl; traline++)
	{
		Vector<String> vw = Split(vplines[traline],' ',true);
		for (int vwi = 0; vwi < vw.GetCount(); vwi++)
		{
			wordrect wore;
			wore.r = vwr[vwri].r;
			wore.s = vwr[vwri].s;
			wore.vgrap <<= vwr[vwri].vgrap;
			wore.option_index = vwr[vwri++].option_index;
			if (TrimRight(wore.s) == vw[vwi])
			{
				if (lineshift)
					wore.r.OffsetVert(lineshift*(height+Interline));
				newwr.Add(wore);
				DLOG("Transfering " << wore.s);
			}
			else 
				DLOG(Format("Out of Sequence!! (Line %d, Word %d) %s <-> %s",traline,vwi,wore.s,vw[vwi]));
		}
	}
	doc.vwordrect.Clear();
	doc.vwordrect = newwr;
}

Vector<Vector<Grapoint> >  Sauklaue::Analyse_Word(String& ins, WString& missing, double& xoff)
{
	WString s (ins);
	s.Cat(" ");
	Vector<Grapoint> grap;
	Vector<Vector<Grapoint> > vgrap;
	xoff = 0.0;
	Grapoint glast;
	Grapoint gcont;
	int lasti;
	double steigung;
//			DLOG("Wort " <<j <<" = " <<s);
	bool join_out = false;
	bool join_in = false;
	bool join = false;
	int letterscan;
	for (int k = 0; k < s.GetLength(); k++)
	{
		double xmax = xoff;
		WString c;
		int l = doc.schrift.vgraphems.GetCount();
		for (--l; l >= 0 ; l--)
		{
			int n = doc.schrift.vgraphems[l].q;
			String txt = doc.schrift.vgraphems[l].text;
			char fich = txt[0];
			if ((k == 0) && (n > 1) && (fich == ' '))
			{
				c = " ";
				c.Cat(s.Mid(k,--n));
				letterscan = IsLetter(c[1]) ? 1 : -1;
			}						
			else
			{
				c = s.Mid(k,n);
				letterscan = IsLetter(c[0]) ? 0 : -1;
			}
			if (letterscan >= 0) 
				for (int ci = letterscan; ci < c.GetLength(); ci++)
					if (!IsLetter (c[ci])) 
					{
						c.Set(ci,wchar(' '));
						n--;
						break;
					}
//					DLOG ("Searching for: " <<c.ToString());
			if (c == txt.ToWString())
			{
				int i1=0,i2=0;
//						DLOG("Match " <<c <<" - " << txt);
				int points = doc.schrift.vgraphems[l].form1.GetCount();
				Vector<Grapoint> vg;
				vg <<= doc.schrift.vgraphems[l].form1;
				if (join_out)
				{
					join_in = false;
					glast = grap[lasti];
					double d1 = 0;
					for(i1 = 0; i1 < doc.schrift.vgraphems[l].form1.GetCount();i1++)
					{
						if (doc.schrift.vgraphems[l].form1[i1].status & STATUS_INJOIN)
						{
							d1 = doc.schrift.vgraphems[l].form1[i1].p.y - glast.p.y;
							join_in = true;
							break;
						}
					}
					if (d1 < 0) d1 = -d1;
					double d2 = 1;
					for(i2 = 0; i2 < doc.schrift.vgraphems[l].form2.GetCount();i2++)
					{
						if (doc.schrift.vgraphems[l].form2[i2].status & STATUS_INJOIN)
						{
							d2 = doc.schrift.vgraphems[l].form2[i2].p.y - glast.p.y;
							join_in = true;
							break;
						}
					}
					if (d2 < 0) d2 = -d2;
//							DLOG("Text " <<c <<" d1 = " <<d1 << " d2 = " << d2);
					if (d2 < d1)
					{
						points = doc.schrift.vgraphems[l].form2.GetCount();
						vg <<= doc.schrift.vgraphems[l].form2;
						i1 = i2;
					}
					if (i1 >= vg.GetCount()) join_in = false;
					if (join_in)
					{
						double m_out = 0.0;
						double m_in = 0.0;
						double d = 0.0;
						double dx = (vg[i1].p.x + xoff - glast.p.x); 
						if (!( glast.status & STATUS_MOVETO))
						{
							m_out = (glast.p.y - grap[lasti-1].p.y) / (glast.p.x - grap[lasti-1].p.x);
						}
						if (!(vg[i1+1].status & STATUS_MOVETO))
						{
							m_in = (vg[i1+1].p.y - vg[i1].p.y) / (vg[i1+1].p.x - vg[i1].p.x);
						}
						if (m_out)
						{
							d = dx - (vg[i1].p.y - glast.p.y)/m_out;
						}
						else if (m_in)
						{
							d = dx - (vg[i1].p.y - glast.p.y)/m_in;
						}
						else if (m_default > 0)
						{
							d = dx + (vg[i1].p.y - glast.p.y)/m_default;
						}
						else d = dx;
						if (d > dx) 
						{
							d = dx;
							vg[i1].p.y = glast.p.y;
						}
						xoff -= d;
						if(lasti < grap.GetCount()-1)
						{
							glast.status = STATUS_MOVETO;
							grap.Add(glast);
						}
						if (vg[i1].status & STATUS_MOVETO)
						{
							vg[i1].status &= ~STATUS_CONNECT;
							vg[i1].status |= STATUS_LINETO;
						}
						else
						{
							Grapoint gp = vg[i1];
							gp.status = STATUS_LINETO;
							gp.p.x += xoff;
							grap.Add(gp);
						}
					}
					else 
					{
						vgrap.Add(grap);
						grap.Clear();
						i1 = 0;
					}
						
				}
				join_out = false;
				for (int m = 0/*i1*/; m < points; m++)
				{
					Grapoint gp = vg[m];
					gp.p.x += xoff;
					if (gp.status & STATUS_OUTJOIN)
					{
						join_out = true;
						lasti = grap.GetCount();
					}
					int stat = gp.status;
					gp.status &= ~(STATUS_INJOIN | STATUS_OUTJOIN);
					grap.Add(gp);
					if ((gp.p.x > xmax) && !(gp.status & STATUS_CTRL))  
						xmax = gp.p.x;
				}
				if (n <= 0)
				{
					Exclamation ("Analyse: Internal ERROR ");
				}
				else k += --n;
				break;
			}
		}
		if (l < 0)
		{
			int num = doc.schrift.vgraphems.GetCount();
			if (num > 0)
			{
				int code = c[0];
				if (missing.Find(code) < 0)
					missing.Cat(code);
				DLOG ("No Match for " <<c << "("<<code << ") in "<<num <<" Graphems");
			}
			else
			{
				missing = "Alle !!!";
				break;
			}
			join_out = false;
		}
		xoff = xmax;
		if (!join_out)
		{
			vgrap.Add(grap);
			grap.Clear();
		}
	}
	if (grap.GetCount() > 0) vgrap.Add(grap);
	return (vgrap);
}

void Sauklaue::Analyse()
{
	Point rtl(0,0);
	Size rs (height,height);
	Rect r (rtl,rtl);
	double xmaxall = 0.0;
	double xoff = 0.0;

	WString missing;
	Save4undo();
	doc.vwordrect.Clear();
	doc.voptions.Trim(1);
	if (doc.text_buf.IsEmpty()) return;
	
	Vector<String> vlines = Split(doc.text_buf,'\n',false);
	for (int i = 0; i < vlines.GetCount(); i++)
	{
		vlines[i].Replace("\r","");
 		Vector<String> vwords = Split(vlines[i],' ',false);
 		for (int j = 0; j < vwords.GetCount(); j++)
 		{
 			if (IsEmpty(vwords[j])) continue;
			Vector<Vector<Grapoint> > vgrap = Analyse_Word(vwords[j],missing,xoff);

			rs.cx = height*xoff/1.2;
			r.SetSize(rs);
			struct wordrect wr;
			wr.r = r;
			wr.s = vwords[j];
			wr.vgrap = vgrap;
			wr.option_index = 0;
//			DLOG ("Wordrect \""<<wr.s <<"\" : "<<wr.r << " #Grapheme " << wr.vgrap.GetCount());
			doc.vwordrect.Add(wr);
			r.OffsetHorz(rs.cx);
			if(r.left > xmaxall) xmaxall = r.left;

 		}
		rtl.y += height + Interline;
 		r.Set(rtl,rtl);
	}
	drawing_size.cy = rtl.y;
	drawing_size.cx = xmaxall;
	DLOG("Analyse Drawing Size " <<drawing_size);
	if (missing.GetCount() > 0) 
	{
		WString excl(t_("Missing Characters: "));
		excl.Cat(missing);
		Exclamation( excl.ToString());
	}
	IsModified = true;
}

void Sauklaue::Popup(Bar& bar) 
{
	if (vselrect.GetCount())
	{
		bar.Add(t_("Moving"), SauklaueImg::MoveRect, THISBACK(MoveRect));
		bar.Add(t_("Sizing"), SauklaueImg::SizeRect, THISBACK(SizeRect));
		bar.Add(t_("Options"), SauklaueImg::Options, THISBACK(Options));
		bar.Add(t_("Extend Selection"), SauklaueImg::Options, THISBACK(ExtSel));
		bar.Add(t_("Reset"),SauklaueImg::Normal, THISBACK(Normalize));
		bar.Add(t_("Append words"), SauklaueImg::AppendWord, THISBACK(AppWord));
	}
	else
		bar.Add(t_("Help"), THISBACK(AboutMenu));
}

void Sauklaue::RightDown(Point p, dword keyflags)
{
	if ((keyflags & K_CTRL) && (lastmousemode !=0) && (vselrect.GetCount() > 0))
	{
		mousemode = lastmousemode;
		if (mousemode & MM_RESTRICTSNAP)
			SnapRect();
	}
	else if (keyflags & K_ALT)
	{
		for (int i = 0; i< vselrect.GetCount();i++)
			DLOG("RECT " << vselrect[i] << doc.vwordrect[vselrect[i]] );
	}
	else MenuBar::Execute(THISBACK(Popup));
}

void Sauklaue::RestrictRect(Rect& r)
{
//	DLOG("RestrictRect " <<r);
	if (mousemode & MM_RESTRICTVERT)
	{
		r.left = rstart.left;
		r.right = rstart.right;
	}
	if (mousemode & MM_RESTRICTHORZ)
	{
		r.top = rstart.top;
		r.bottom = rstart.bottom;
	}
	
}

void Sauklaue::LeftDown(Point p, dword keyflags)
{
//	click = p;
	if (mousemode & (MM_MOVINGRECT | MM_SIZINGRECT) )
	{
		Save4undo();
		double scalf = double(Textheight)/100.0;
		RectTracker tr(*this);
		Rect start = doc.vwordrect[vselrect[0]].r;
		for (int i = 1; i < vselrect.GetCount(); i++)
			start.Union(doc.vwordrect[vselrect[i]].r);
		ScaleWordRect(start,scalf);
		if (mousemode & MM_MOVINGRECT)
		{
			tr.SetCursorImage(SauklaueImg::MoveRect);
			tr.round = THISBACK(RestrictRect);
			rstart = start;
			Rect target;
			
			target = tr.Track(start,ALIGN_CENTER,ALIGN_CENTER);
			Pointf dis = (target.TopRight() - start.TopRight())/scalf;
			for (int i = 0; i < vselrect.GetCount(); i++)
				doc.vwordrect[vselrect[i]].r += dis;
		}
		else if (mousemode & MM_SIZINGRECT)
		{
			Point pos = scroll;
			if (mousemode & MM_SIZINGSCHRIFT)tr.SetCursorImage(SauklaueImg::SizeRectS);
			else tr.SetCursorImage(SauklaueImg::SizeRect);
			Rect target;
			if (mousemode & MM_RESTRICTHORZ)
				target = tr.Track(start,ALIGN_RIGHT,ALIGN_CENTER);
			else if (mousemode & MM_RESTRICTVERT)
				target = tr.Track(start,ALIGN_CENTER,ALIGN_TOP);
			else
				target = tr.Track(start,ALIGN_RIGHT,ALIGN_TOP);
			double fx = double(target.Width())/double(start.Width());
			double fy = double(target.Height())/double(start.Height());
			int opisave = 0;
			DLOG ("Sizing Rect from "<<start <<" to "<<target);
			Vector<int> vsels;
			int line = -1;
			start.Offset(-LeftMargin,-TopMargin);
			target.Offset(-LeftMargin,-TopMargin);
			for (int i = 0; i < vselrect.GetCount(); i++)
			{
				DLOG ("Sizing Rect, vselrect : "<<i);
				Rect r = doc.vwordrect[vselrect[i]].r;
				int left = r.left * scalf - pos.x;
				int top  = r.top * scalf - pos.y;
				int right = r.right * scalf - pos.x;
				int bottom = r.bottom * scalf - pos.y;
				if (mousemode & MM_SIZINGBLOCKED)
				{
					int ilin = r.bottom /(height+Interline);
					if (line != ilin)
					{
						int maxright = 0;
						vsels.Clear();
						for (int ii = i; ii < vselrect.GetCount(); ii++)
						{
							Rect rr = doc.vwordrect[vselrect[ii]].r;
							if (ilin != rr.bottom /(height+Interline)) break;
							vsels.Add(vselrect[ii]);
							DLOG ("Sizing Rect, vsels added : "<<vselrect[ii]);
							if (rr.right > maxright) maxright = rr.right;
						}
						start.right = maxright * scalf - pos.x;
						fx = double(target.Width())/double(start.Width());
						line = ilin;
					}
				}
				top = target.top + (top - start.top)*fy;
				bottom = target.bottom + (bottom - start.bottom)*fy;
				left = target.left + (left - start.left)*fx;
				right = target.right + (right - start.right)*fx;
				r.top = (top + pos.y) / scalf;
				r.bottom = (bottom + pos.y)/ scalf;
				r.left = (left + pos.x)/ scalf;
				r.right = (right + pos.x)/ scalf;
				doc.vwordrect[vselrect[i]].r = r;
				if (mousemode & MM_SIZINGSCHRIFT)
				{
					int opi = Handle_Optionsets(mousemode & MM_SIZINGBLOCKED ? vsels : vselrect);

					if (opi != opisave)
					{
						DLOG("Sizing Rect: Changing Option Set " << opi);
						int strx = doc.voptions[opi].Trans.stretchx;
						int stry = doc.voptions[opi].Trans.stretchy;
						double fstrx= (100 + strx)/100.0;
						double fstry= (100 + stry)/100.0;
						fstrx *= fx;
						fstry *= fy;
						strx = fstrx * 100 - 100;
						stry = fstry * 100 - 100;
						doc.voptions[opi].Trans.stretchx = strx;
						doc.voptions[opi].Trans.stretchy = stry;

						opisave = opi;
					}
				}
			}
		}
		Refresh();
		IsModified = true;
		mousemode &= MM_SELECTRECT;
		return;
	}
	if ((keyflags & K_SHIFT_CTRL ) == 0)
		vselrect.Clear();
	for (int i = 0; i < doc.vwordrect.GetCount(); i++)
	{
		Rect ri = doc.vwordrect[i].r;
		Rect r = ri;
		ScaleWordRect(r,double(Textheight)/100.0);
		if (r.Contains(p))
		{
			for (int j = 0; j < vselrect.GetCount();j++)
				if (vselrect[j] == i)
				{
					if (keyflags & K_CTRL)vselrect.Remove(j);
					else if (keyflags & K_SHIFT)vselrect.Trim(j);
					Refresh();
					return;
				}
			if ( keyflags & K_SHIFT)
			{
				int j = vselrect.GetCount();
				if ( j > 0)
				{
					int k = vselrect[0];
					if ( i >= k)
						for (++k;k <= i; k++)
							vselrect.Add(k);
					else
					{
						 vselrect.Clear();
						 for (int l = i; l <= k; l++)
						     vselrect.Add(l);
					}
				}
			}
			else vselrect.Add(i);
			Refresh();
			return;
		}
	}
	vselrect.Clear();
	Refresh();
}
#define NUM_UNDO 10

String UndoFilename(int index)
{
	String tempPath = GetTempPath();
	String filename("SKL_UNDO");
	filename << index <<".tmp";
	return AppendFileName(tempPath,filename);
}

void Sauklaue::Save4undo()
{
	undoIndex++;
	if (undoIndex >= NUM_UNDO) undoIndex -= NUM_UNDO;
	undoPossible++;
	if (undoPossible > NUM_UNDO) undoPossible = NUM_UNDO;
	String filename = UndoFilename(undoIndex);
	StoreToFile(doc,filename);
	DLOG ("Save4undo to file " << filename);
	redoPossible = -1;
}

void Sauklaue::EditUndo()
{
	if (undoPossible > 0)
	{
		if(redoPossible < 0)
		{
		 	Save4undo();
		 	redoPossible = 0;
		 	undoPossible--;
		}
		undoPossible--;
		undoIndex--;
		if (undoIndex < 0) undoIndex += NUM_UNDO;
		String filename = UndoFilename(undoIndex);
		if (FileExists(filename))
		{
			DLOG ("UNDO from file " << filename);
			doc.schrift.Clear();
			doc.text_buf.Clear();
			doc.vwordrect.Clear();
			doc.voptions.Clear();
			LoadFromFile(doc,filename);
			redoPossible++;
			Refresh();
		}
		else DLOG ("UNDO: Nonexisting File " << filename);
	}	
}

void Sauklaue::EditRedo()
{
	if (redoPossible > 0)
	{
		undoPossible++;
		undoIndex++;
		if (undoIndex >= NUM_UNDO) undoIndex -= NUM_UNDO;
		redoPossible--;
		String filename = UndoFilename(undoIndex);
		if (FileExists(filename))
		{
			DLOG ("ReDo from file " << filename);
			doc.schrift.Clear();
			doc.text_buf.Clear();
			doc.vwordrect.Clear();
			doc.voptions.Clear();
			LoadFromFile(doc,filename);
			Refresh();
		}
		else DLOG ("ReDo: Nonexisting File " << filename);
	}
}

void Sauklaue::EditCut()
{
	EditCopy();
	Sort(vselrect);
	doc.vwordrect.Remove(vselrect);
	vselrect.Clear();
	Refresh();
}
static const char *pclipboardformat= "VWORDRECT";

void Sauklaue::EditCopy()
{
	Vector<wordrect> vwordr;
	for (int i = 0; i < vselrect.GetCount(); i++)
	{
		DLOG ("EditCopy, vselrect : "<<i);
		vwordr.Add(doc.vwordrect[vselrect[i]]);
	}
	String s;
	s = StoreAsString(vwordr);
	WriteClipboard(pclipboardformat, s);
}

void Sauklaue::EditPaste()
{
	Vector<wordrect> vwordr;
	String s;
	if(IsClipboardAvailable(pclipboardformat))
	{
		s = ReadClipboard(pclipboardformat);
		LoadFromString(vwordr,s);
		for (int i = 0; i < vwordr.GetCount(); i++)
			vwordr[i].r.Offset(5,5);
	}
	int ins, i = vselrect.GetCount();
	if (i > 0)
		ins = vselrect[0];
	else 
		ins = doc.vwordrect.GetCount();
	doc.vwordrect.Insert(ins,vwordr);
	vselrect.Clear();
	Refresh();	 
}

void Sauklaue::New_Doc()
{
	name = t_("Unknown");
	Title(t_("SWord - ") + name);
	doc.schrift.Clear();
	doc.text_buf.Clear();
	doc.vwordrect.Clear();
	doc.voptions.Clear();
	struct options op;
	doc.voptions.Add(op);	
	undoIndex = -1;
	Refresh();
}

void Sauklaue::Load()
{
	undoIndex = -1;
	TopMargin  = doc.TopMargin;	
	LeftMargin = doc.LeftMargin;	
	Textheight = doc.Textheight;
	Interline  = doc.Interline;
	Paper = doc.Paper;
	m_default = doc.m_default;
	DrawingSize();
	Layout();
	Refresh();
}

void Sauklaue::Open_Doc()
{
	if(IsModified) {
		switch(PromptYesNoCancel(t_("Save the changes?"))) {
		case 1:
			Save_Doc();
			break;
		case -1:
			return;
		}
	}
	FileSel fs;
	fs.Type(t_("Handwriting file"),binext);
	if (!docdir.IsEmpty()) fs.ActiveDir(docdir);
	if(fs.ExecuteOpen())
	{
		String filename_save = filename;
		filename = fs;
		docdir = GetFileDirectory(filename);
		if (LoadFromFile(doc,filename))
		{
			name = GetFileTitle(filename);
			Title(t_("SWord - ") + name);
			Load();
		}
		else 
		{
			filename.Replace("\\","\\\\");
			String excl(t_("Cannot read "));
			excl.Cat(filename);
			Exclamation (excl);
			filename = filename_save;
		}
	}
}

void Sauklaue::Save_Doc()
{
	if(!IsModified)
	{
		if ((PromptYesNo(t_("Not modified. Save anyway ?"))) != 1)
			return;
	}
	if(filename.IsEmpty())
		SaveAs_Doc();
	else
	{
		if (stricmp(GetFileExtPos(filename), binext+1))
		{
			filename = ForceExt(filename, binext+1);
			if (PromptOKCancel(t_("Save file as: ") + filename)<=0)
				return;
		}
		doc.TopMargin  = TopMargin;	
		doc.LeftMargin = LeftMargin;	
		doc.Textheight = Textheight;
		doc.Interline  = Interline;
		doc.Paper      = Paper;
		doc.m_default  = m_default;

		if(StoreToFile(doc,filename)) {
			IsModified = schrift_modified = text_modified = false;
			undoIndex = -1;
		}
		else
			Exclamation(t_("Error saving the file: ") + filename);
	}
}
void Sauklaue::SaveAs_Doc()
{
	FileSel fs;
	fs.Type(t_("Handwriting file"),binext);
	if (!docdir.IsEmpty()) fs.ActiveDir(docdir);
	if(fs.ExecuteSaveAs()) {
		filename = fs;
		docdir = GetFileDirectory(filename);
		name = GetFileTitle(filename);
		Title(t_("SWord - ") + name);
		IsModified = true; //for save_doc to store in any case
		Save_Doc();
	}
}

void Sauklaue::OpenXML_Doc()
{
	if(IsModified) {
		switch(PromptYesNoCancel(t_("Save the changes?"))) {
		case 1:
			Save_Doc();
			break;
		case -1:
			return;
		}
	}
	FileSel fs;
	fs.Type(t_("XML Handwriting File"),"*.xml");
	if (!Xmldocdir.IsEmpty()) fs.ActiveDir(Xmldocdir);
	
	if(fs.ExecuteOpen())
	{
		filename = fs;
		Xmldocdir = GetFileDirectory(filename);
		LoadFromXMLFile(doc,filename);
		name = GetFileTitle(filename);
		Title(t_("SWord - ") + name);
		
		Load();
		filename = docdir + name;
	}
}

void Sauklaue::SaveAsXML_Doc()
{
	FileSel fs;
	fs.Type(t_("XML Handwriting File"),"*.xml");
	if (!Xmldocdir.IsEmpty()) fs.ActiveDir(Xmldocdir);
	fs.Set(GetFileTitle(filename));
	if(fs.ExecuteSaveAs()) {
		filename = fs;
		Xmldocdir = GetFileDirectory(filename);
		filename = ForceExt(filename, ".xml");
		doc.TopMargin  = TopMargin;	
		doc.LeftMargin = LeftMargin;	
		doc.Textheight = Textheight;
		doc.Interline  = Interline;
		doc.Paper      = Paper;
		doc.m_default  = m_default;
		StoreAsXMLFile(doc,name,filename);
		IsModified = false;
	}
}

void Sauklaue::Launch(Bar& bar)
{
	bar.Add(t_("Script Editor"),THISBACK(Launch_Schrift_Editor));
	bar.Add(t_("Text Editor"),THISBACK(Launch_TextEditor));
	bar.Add(t_("Help"),THISBACK(Launch_HelpWindow));
}

void Sauklaue::ProgParams()
{
	WithProgParamLayout<TopWindow> dlg;
	CtrlLayoutOKCancel(dlg, t_("Program Parameters"));
	dlg.TopMargin <<= TopMargin;
	dlg.LeftMargin <<= LeftMargin;
	dlg.theight <<= Textheight;
	dlg.interline <<= Interline;
	dlg.M_Default <<= m_default;
	dlg.WithDots <<= ShowDots;
	dlg.WithFrames <<= ShowFrames;
	dlg.A4Limit <<= A4Limit;
	dlg.Baselines <<= Baselines;
	dlg.BasedGraphems <<= BasedGraphems;
	dlg.SPaper <<= Paper;
	if(dlg.Execute() != IDOK)
		return;
	TopMargin = ~dlg.TopMargin;
	LeftMargin = ~dlg.LeftMargin;
	ShowDots   = ~dlg.WithDots;
	ShowFrames = ~dlg.WithFrames;
	A4Limit = ~dlg.A4Limit;
	Baselines = ~dlg.Baselines;
	BasedGraphems = ~dlg.BasedGraphems;
	Paper = ~dlg.SPaper;
	m_default = ~dlg.M_Default;
	int teheight = ~dlg.theight;
	if (teheight != Textheight)
	{
		Textheight = teheight;
		Layout();
	}
	int tegap = ~dlg.interline;
	if (tegap != Interline)
	{
		int yoff = 0;
		int py =0;
		for (int i = 1; i < doc.vwordrect.GetCount(); i++)
		{
			Rect& r = doc.vwordrect[i].r;

			while ((r.top - py) >= (height + Interline)/2)
			{
				yoff -= Interline - tegap;
				py += height + Interline;
			}
			py = r.top;
			if (yoff) r.OffsetVert(yoff);
		}
		Interline = tegap;
		DrawingSize();
		Layout();
	}
	Refresh();
}
int Sauklaue::Handle_Optionsets(Vector<int>& vseli)
{
	int j,n = vseli.GetCount();
	int opi = 0;
	bool newopts = false;
	
	if (n)
	{
		opi = doc.vwordrect[vseli[0]].option_index;
		if (opi == 0)
			newopts = true;
		else 
		{
			for (j = 0; j < n; j++)
			{
				if ( opi != doc.vwordrect[vseli[j]].option_index)
				{
					newopts = true;
					break; 
				}
			}
			if (!newopts) for (int i = 0; i < doc.vwordrect.GetCount(); i++)
			{
				int oli = doc.vwordrect[i].option_index;
				if ((oli > 0) && (oli == opi)) //not default and points to the same
				{
					for (j = 0; j < n; j++)
						if ( i == vseli[j]) break; // is in selection
					if (j >= n) //not found
					{
						newopts = true;
						break;
					}
				}
			}
		}
	}
	struct options& op = doc.voptions[opi];
	if (newopts)
	{
		for (j = 0; j < doc.voptions.GetCount(); j++)
			if (doc.voptions[j].writer < 0)
			{
				doc.voptions[j] = op;
				break;
			}
		if (j >= doc.voptions.GetCount())
			doc.voptions.Add(op);
		for (int i = 0; i < n; i++)
		{
			int si = vseli[i];
			DLOG("Handle_Optionsets: Changing wordrect ["<<si<<"] optionindex from " <<doc.vwordrect[si].option_index <<" to " << j);
			doc.vwordrect[si].option_index = j;
		}
		opi = j;
		int nop = doc.voptions.GetCount();
		DLOG ("Optionsets " << nop);
		int nwr = doc.vwordrect.GetCount();
		int nfree = 0;
		for (int i = 1; i < nop; i++)
		{
			for( j = 0; j < nwr; j++)
				if ( doc.vwordrect[j].option_index == i) break;
			if (j >= nwr)
			{
				nfree++;
				doc.voptions[i].writer = -1;
			}
		}
		DLOG ("Therefrom free Optionsets " << nfree);
	}

	return opi;
}

void Sauklaue::Options()
{
	WithOptionsLayout<TopWindow> dlg;
	CtrlLayoutOKCancel(dlg, t_("Options"));
	int	opi = 0;
	bool selonly = false;
	if (vselrect.GetCount() > 0)
	{
		opi = doc.vwordrect[vselrect[0]].option_index;
		selonly = true;
	}
	struct options op = doc.voptions[opi];
	dlg.fwidth <<= op.Featherwidth;
	dlg.fturn <<= op.Featherturn;
	dlg.swriter <<= op.writer;
	dlg.penwidth <<= op.Penwidth;
	dlg.ColorPen <<= op.Pencolor;
	dlg.ColorFeather <<= op.Feathercolor;
	dlg.slant <<= op.Trans.slant;
	dlg.shift <<= op.Trans.shift;
	dlg.stretchx <<= op.Trans.stretchx;
	dlg.stretchy <<= op.Trans.stretchy;

	if(dlg.Execute() != IDOK)
		return;
	op.Featherwidth    = ~dlg.fwidth;
	op.Featherturn     = ~dlg.fturn;
	op.writer		   = ~dlg.swriter;
	op.Penwidth		   = ~dlg.penwidth;
	op.Pencolor		   = ~dlg.ColorPen;
	op.Feathercolor    = ~dlg.ColorFeather;
	op.Trans.slant     = ~dlg.slant;
	op.Trans.shift     = ~dlg.shift;
	op.Trans.stretchx  = ~dlg.stretchx;
	op.Trans.stretchy  = ~dlg.stretchy;
	Save4undo();
	if (selonly)
		opi = Handle_Optionsets(vselrect);
	doc.voptions[opi] = op;
	IsModified = true;
}
void Sauklaue::ExtSel()
{
	int n = vselrect.GetCount();
	if (n <= 0)Exclamation("No Selection to extend !");
	else
	{
		int	opi = doc.vwordrect[vselrect[0]].option_index;
		vselrect.Clear();
		for (int i = 0; i < doc.vwordrect.GetCount(); i++)
			if (doc.vwordrect[i].option_index == opi)
				vselrect.Add(i);
		String is(t_("Set "));
		is << opi;
		status.Set(is);
		Refresh();
	}
}

void Sauklaue::MainBar(Bar& bar)
{
	bar.Add(t_("File"), THISBACK(File));
	bar.Add(t_("Edit"), THISBACK(Edit));
	bar.Add(t_("Window"), THISBACK(Launch)); 
	bar.Add(t_("Change"), THISBACK(Change));
	bar.Add(t_("Help"), THISBACK(AboutMenu));
}
void Sauklaue::SetBar()
{
	toolbar.Set(THISBACK(MainBar));
}

void Sauklaue::File(Bar& bar)
{
	bar.Add(t_("New"), CtrlImg::new_doc(), THISBACK(New_Doc))
	   .Key(K_CTRL_N)
	   .Help(t_("Create New Handwriting"));
	bar.Add(t_("Open.."), CtrlImg::open(), THISBACK(Open_Doc))
	   .Key(K_CTRL_O)
	   .Help(t_("Open existing handwriting file"));
	bar.Add( t_("Save"), CtrlImg::save(), THISBACK(Save_Doc))
	   .Key(K_CTRL_S)
	   .Help(t_("Save the changed handwriting"));
	bar.Add(t_("Save As"), CtrlImg::save_as(), THISBACK(SaveAs_Doc))
	   .Help(t_("Save the handwriting file with a new name"));

    bar.Separator();
    bar.Add(t_("Import XML"),THISBACK(OpenXML_Doc))
    	.Help(t_("Load handwriting from XML-file"));
    bar.Add(t_("Export XML"),THISBACK(SaveAsXML_Doc))
    	.Help(t_("Export handwriting to XML-file"));
    bar.Separator();

	bar.Add(t_("Save as PDF"),SauklaueImg::pdf,THISBACK(Pdf)).Help(t_("Create file in PDF Format"));
	bar.Add(t_("Save as XML Painting"),SauklaueImg::spaint,THISBACK(SavePainting)).Help(t_("Create file as XML Painting"));
	bar.Add(t_("Save as PNG"),SauklaueImg::png,THISBACK(SavePNG)).Help(t_("Create file in PNG Format"));
	bar.Add(t_("Exit"), THISBACK(Exit));
	
}
void Sauklaue::Edit(Bar& bar)
{
	bar.Add(t_("Undo"), CtrlImg::undo(), THISBACK(EditUndo))
		.Key(K_CTRL_Z)
		.Help(t_("Undo changes to the document"));
	bar.Add(t_("Redo"), CtrlImg::redo(), THISBACK(EditRedo))
		.Key(K_SHIFT|K_CTRL_Z)
		.Help(t_("Redo undone changes"));
	bar.Separator();

	bar.Add(t_("Cut"), CtrlImg::cut(), THISBACK(EditCut))
		.Key(K_CTRL_X)
		.Help(t_("Cut selection and place it on the system clipboard"));
	bar.Add(t_("Copy"), CtrlImg::copy(), THISBACK(EditCopy))
		.Key(K_CTRL_C)
		.Help(t_("Copy current selection on the system clipboard"));
	bar.Add(t_("Paste"), CtrlImg::paste(), THISBACK(EditPaste))
		.Key(K_CTRL_V)
		.Help(t_("Insert words from clipboard at current location"));
}

void Sauklaue::Change(Bar& bar)
{
	bar.Add(t_("Reset"), SauklaueImg::Refresh, THISBACK(DoRefresh)).Help(t_("Analyse and draw all from new"));
	bar.Add(t_("Text update"), SauklaueImg::TextDif, THISBACK(TextDif)).Help(t_("Update from Text Changes"));
	bar.Add(t_("Script update"), SauklaueImg::SchriftDif, THISBACK(SchriftDif)).Help(t_("Update from Script Changes"));
	bar.Add(t_("Options"), SauklaueImg::Options, THISBACK(Options)).Help(t_("Specify Options for Drawing"));	
	bar.Add(t_("Program Parameters"), SauklaueImg::ProgParams, THISBACK(ProgParams)).Help(t_("Specify Program Parameters"));
}

void Sauklaue::Launch_Schrift_Editor(void)
{
	if (pschrifted)
	{
		pschrifted->SetSchrift(&doc.schrift,schrift_modified);
		if (pschrifted->IsShown()) pschrifted->SetForeground();
		else pschrifted->Show();
	 	return;
	}
	pschrifted = new Schrift_Editor;
	pschrifted->SetSchrift(&doc.schrift,schrift_modified);
	pschrifted->OpenMain();
}
void Sauklaue::Launch_TextEditor(void)
{
	if (ptext)
	{
		ptext->SetText(doc.text_buf,text_modified);
		if (ptext->IsShown()) ptext->SetForeground();
		else ptext->show();
		return;
	}
	ptext = new TextEditor;
	ptext->SetText(doc.text_buf, text_modified);
	ptext->OpenMain();
}

void Sauklaue::Launch_HelpWindow(void)
{
	if (phelp) 
	{
		if (phelp->IsShown())
			phelp->SetForeground();
		else phelp->Show();
		return;
	}
	phelp = new HelpWindow;
	phelp->WhenClose = THISBACK(HideHelp);
	Rect r = GetRect();
	r.OffsetHorz(r.Width());
	phelp->NoCenter();
	phelp->SetRect(r);
	String ts="topic://Sauklaue/sauklaue/Primer$";
	if (GetCurrentLanguage() == LNG_('d','e','D','E'))
		ts.Cat("de-de");
	else ts.Cat("en-us");
	phelp->GoTo(ts);
	phelp->OpenMain();
}

void Sauklaue::SchriftRefresh()
{
	if (pschrifted)
	{
		Schrift * pschred = pschrifted->GetSchrift(schrift_modified);
		for (int i = 0; i < doc.schrift.vgraphems.GetCount(); i++)
		{
			Graphem g = doc.schrift.vgraphems[i];
			g.form1.Clear();
			g.form2.Clear();
		}
		doc.schrift.vgraphems.Clear();
		doc.schrift.name = pschred->name;
		doc.schrift.ascent = pschred->ascent;
		doc.schrift.descent = pschred->descent;
		doc.schrift.baseline = pschred->baseline;
		for (int i = 0; i < pschred->vgraphems.GetCount(); i++)
		{
			Graphem g;
			g.q = pschred->vgraphems[i].text.ToWString().GetLength();
			g.text = pschred->vgraphems[i].text;
			for (int j = 0; j < pschred->vgraphems[i].form1.GetCount(); j++)
			{
				Grapoint gp = pschred->vgraphems[i].form1[j];
				g.form1.Add(gp);
			}
			for (int j = 0; j < pschred->vgraphems[i].form2.GetCount(); j++)
			{
				Grapoint gp = pschred->vgraphems[i].form2[j];
				g.form2.Add(gp);
			}
			doc.schrift.vgraphems.Add(g);
		}
		Sort(doc.schrift.vgraphems,gracomp);		
	}	
}

void Sauklaue::DoRefresh()
{
	SchriftRefresh();
	if (ptext)
	{
		doc.text_buf = ptext->GetText(text_modified);
	}
	IsModified = IsModified || schrift_modified || text_modified;
	Analyse();
	Layout();
	Refresh();
}

void Sauklaue::TextDif()
{
	if (ptext)
	{
		prev_text = doc.text_buf;
		doc.text_buf = ptext->GetText(text_modified);
		ReAnalyseText();
	}
	else Exclamation(t_("No Text Editor up"));
	IsModified = IsModified || text_modified;	
	Layout();
	Refresh();
}

void Sauklaue::SchriftDif()
{
	WString mis;
	double xoff;
	
	SchriftRefresh();
	int numsel = vselrect.GetCount();
	if (numsel <= 0)
	{
		for (int w = 0; w < doc.vwordrect.GetCount(); w++)
		{
			wordrect& wore = doc.vwordrect[w];
			wore.vgrap.Clear();
			wore.vgrap = Analyse_Word(wore.s,mis,xoff);
			wore.r.right = wore.r.left + 100*xoff;
		}
	}
	else 
	{
		for (int i = 0; i < numsel; i++)
		{
			wordrect& wore = doc.vwordrect[vselrect[i]];
			wore.vgrap.Clear();
			wore.vgrap = Analyse_Word(wore.s,mis,xoff);
			wore.r.right = wore.r.left + 100*xoff;
		}
	}
	IsModified = IsModified || schrift_modified;	
	Layout();
	Refresh();
}

void Sauklaue::SnapRect()
{
	if (mousemode & MM_RESTRICTSNAP)
	{
		Save4undo();
		IsModified = true;
		if (mousemode & MM_MOVETOBASELINE)
		{
			int lining = height + Interline;
			Point pos = scroll;
	
			for (int i = 0; i < vselrect.GetCount(); i++)
			{
				Rect r = doc.vwordrect[vselrect[i]].r;
				struct options& opt = doc.voptions[doc.vwordrect[vselrect[i]].option_index];
				int cury = r.top + doc.schrift.baseline* (100 + opt.Trans.stretchy);
				int iline = cury/ lining;
				int curbas = iline * lining + 100*doc.schrift.baseline;
				int ydiff = curbas - cury;
				doc.vwordrect[vselrect[i]].r.OffsetVert(ydiff);
			}
		}
		if(mousemode & MM_MOVELINEUP)
		{
			int lining = height + Interline;
			Rect r0 = doc.vwordrect[vselrect[0]].r;
			int xmin = r0.left;
			for (int i = 1; i < vselrect.GetCount(); i++)
			{
				Rect r = doc.vwordrect[vselrect[i]].r;
				int xoffset = r.left - r0.right - lastGap;
				int yoffset = r.bottom - r0.bottom;
				if (yoffset < lining)  yoffset = 0;
				else
				{
					if (!(mousemode & MM_MOVELINEUP1L))
					{
						yoffset = 0;
						xoffset = r.left - xmin;
					}
				}
				if (r.left < xmin) xmin = r.left;
				doc.vwordrect[vselrect[i]].r.Offset(-xoffset, -yoffset);
				r0 = doc.vwordrect[vselrect[i]].r;
			}
		}
		Refresh();
	}
}

void Sauklaue::MoveRect()
{
	
	WithMovingOptionsLayout<TopWindow> dlg;
	CtrlLayoutOKCancel(dlg, t_("Options for Moving"));
	int optval = 0;
	
	if (mousemode & MM_RESTRICTHORZ) optval = 1;
	else if (mousemode & MM_RESTRICTVERT) optval = 2;
	else if (mousemode & MM_RESTRICTSNAP) optval = 3;
	else optval = 0;
	dlg.Restrict <<= optval;
	
	optval = (mousemode & MM_MOVETOBASELINE) ? 1:0;
	dlg.ToBaseline <<= optval;
	optval = (mousemode & MM_MOVELINEUP) ? 1:0;
	dlg.LineUp <<= optval;
	dlg.Gap <<= lastGap;
	optval = (mousemode & MM_MOVELINEUP1L) ? 1:0;
	dlg.OneLine <<= optval;

	if(dlg.Execute() != IDOK)
		return;
	
	mousemode = 0;
	optval =  ~dlg.Restrict;
	if (optval < 3)
	{
		mousemode |= MM_MOVINGRECT;

		if (optval == 1)
				mousemode |= MM_RESTRICTHORZ;
		if (optval == 2)
				mousemode |= MM_RESTRICTVERT;
	}
	else
	{
		mousemode |= MM_RESTRICTSNAP;
		optval = ~dlg.ToBaseline;
		if ( optval) 
			mousemode |= MM_MOVETOBASELINE;
		optval = ~dlg.LineUp;
		if ( optval) 
		{
			mousemode |= MM_MOVELINEUP;
			optval = ~dlg.OneLine;
			if (optval) mousemode |= MM_MOVELINEUP1L;
			lastGap = ~dlg.Gap;
		}
		SnapRect();
	}
	lastmousemode = mousemode;
}

void Sauklaue::SizeRect()
{
	WithSizingOptionsLayout<TopWindow> dlg;
	CtrlLayoutOKCancel(dlg, t_("Options for Sizing"));
	int optval = 0;
	optval = (mousemode & MM_SIZINGSCHRIFT) ? 1:0;
	dlg.WithSchrift <<= optval;
	optval = (mousemode & MM_SIZINGBLOCKED) ? 1:0;
	dlg.Blocked <<= optval;
	if (mousemode & MM_RESTRICTHORZ) optval = 1;
	else if (mousemode & MM_RESTRICTVERT) optval = 2;
	else optval = 0;
	dlg.Restrict <<= optval;

	if(dlg.Execute() != IDOK)
		return;
	
	mousemode = 0;
	optval = ~dlg.WithSchrift;
	if ( optval) mousemode |= MM_SIZINGSCHRIFT;
	optval = ~dlg.Blocked;
	if ( optval) mousemode |= MM_SIZINGBLOCKED;
	optval = ~dlg.Restrict;
	if (optval == 1)
			mousemode |= MM_RESTRICTHORZ;
	if (optval == 2)
			mousemode |= MM_RESTRICTVERT;

	mousemode |= MM_SIZINGRECT;	
	lastmousemode = mousemode;
}

void Sauklaue::Normalize()
{
	Save4undo();
	Vector<wordrect>& vwr = doc.vwordrect;
	for (int i = 0; i < vselrect.GetCount(); i++)
	{
		int wri = vselrect[i];
		Rect& r1=vwr[wri].r;
		r1.bottom = r1.top + height;
		vwr[wri].option_index = 0;
		Vector <Vector <Grapoint> >& vg = vwr[wri].vgrap;
		int li = vg.GetCount() - 1;
		int lj = vg[li].GetCount() - 1;
		double xmax = vg[li][lj].p.x;
		xmax *= 100;
		r1.right = r1.left + xmax;
		if (wri > 0) 
		{
			Rect& r0 = vwr[wri - 1].r;
			if(abs(r1.top - r0.top) < height)
			{
				int xoff = r1.left - r0.right;
				r1.OffsetHorz(-xoff);
			}
		}
	}
	IsModified = true;
	Refresh();
}

void Sauklaue::AppWord()
{
	int added = 0;
	for (int i = 0; i < vselrect.GetCount(); i++)
	{
		int j = vselrect[i];
		String s = doc.vwordrect[j].s;
		WString ws = s.ToWString();
		for (int ci = 0; ci < ws.GetCount(); ci++)
		{
			if (!IsLetter(ws[ci])) ws.Trim(ci);
		}
		s = ws.ToString();
		int l = doc.schrift.vgraphems.GetCount();
		for (--l; l >= 0 ; l--)
		{
			if (s == doc.schrift.vgraphems[l].text)
			{
				DLOG("Match " <<s <<" - " << doc.schrift.vgraphems[l].text);
				break;
			}
		}
		if (l>=0) continue;	//Already in
		added++;
		Graphem g;
		g.text = s;
		g.q = s.GetLength();
		for (int gi = 0; gi < doc.vwordrect[j].vgrap.GetCount();gi++)
			g.form1.Append(doc.vwordrect[j].vgrap[gi]) ;
		doc.schrift.vgraphems.Add(g);
	}
	if (added <= 0)
	{
		PromptOK(t_("No new word selected"));
	 	return;
	}
	String feedback = AsString(added);
	if (added == 1) feedback.Cat(t_(" word"));
	else feedback.Cat(t_(" words"));
	feedback.Cat(t_(" entered."));
	PromptOK(feedback);
	Sort(doc.schrift.vgraphems,gracomp);
	schrift_modified = true;
	if (pschrifted)
		pschrifted->SetSchrift(&doc.schrift,schrift_modified);
}

void Sauklaue::Pdf()
{
	FileSel fs;
	fs.Type("PDF","*.pdf");
	if (!pdfdir.IsEmpty()) fs.ActiveDir(pdfdir);
	fs.Set(name);
	if(!fs.ExecuteSaveAs(t_("Save as PDF Format")))
		return;
	pdfdir = GetFileDirectory(~fs);
	Size page = Size(3968, 6074);
	PdfDraw pdf(page);
	Do_Drawing(pdf,page);
/*	DrawingSize();
	scroll.Set(0,0);
	Size s = drawing_size * Textheight / 100; 
	Painting myp = MakePainting(s);
	pdf.DrawPainting(LeftMargin,TopMargin,s.cx*6,s.cy*6,myp);
*/
	WaitCursor waitcursor;
	SaveFile(~fs, pdf.Finish());
}

void Sauklaue::SavePainting()
{
	FileSel fs;
	fs.Type(t_("XML Painting"),"*.xmlp");
	if (!pntdir.IsEmpty()) fs.ActiveDir(pntdir);
	fs.Set(name);
	if(!fs.ExecuteSaveAs(t_("Save as XML Painting file")))
		return;
	String filename = fs;
	pntdir = GetFileDirectory(filename);
	filename = ForceExt(filename, ".xmlp");
	DrawingSize();
	scroll.Set(0,0);
	Size s = drawing_size * Textheight / 100; 
	s.cx += 2*LeftMargin;
	s.cy += 2*TopMargin;
	Painting myp = MakePainting(s);
	StoreAsXMLFile(myp,name,filename);
}

void Sauklaue::SavePNG()
{
	FileSel fs;
	fs.Type("PNG","*.png");
	if (!pngdir.IsEmpty()) fs.ActiveDir(pngdir);
	fs.Set(name);
	if(!fs.ExecuteSaveAs(t_("Save as PNG Format")))
		return;
	pngdir = GetFileDirectory(~fs);
	DrawingSize();
	scroll.Set(0,0);
	Size s = drawing_size * Textheight / 100; 
	s.cx += 2*LeftMargin;
	s.cy += 2*TopMargin;
	ImageDraw w(s);
	w.DrawPainting(0,0,s.cx,s.cy,MakePainting(s));
	PNGEncoder().SaveFile(~fs, w);
}
void Sauklaue::Serialize(Stream& s)
{
	view_size = GetSize();
	s
		% filename % name
		% TopMargin % LeftMargin
		% ShowDots % ShowFrames
		% A4Limit % Baselines % BasedGraphems % Paper
		% Textheight % Interline % m_default
		% view_size
		% docdir % Xmldocdir
		% pdfdir % pngdir % pntdir
	;
}

void Sauklaue::Exit()
{
	if(PromptOKCancel(t_("Exit SWord ?")))
	{
		if(IsModified) 
		{
			if (PromptYesNo(t_("Save the changes?"))> 0) 
				Save_Doc();
		}
		StoreToFile(*this);
		for (int i = 0; i < NUM_UNDO; i++)
		{
			String fn = UndoFilename(i);
			FileDelete(fn);
		}
		if (pschrifted)
		{
			pschrifted->ExitCB();	//To save configuration
			delete pschrifted;
		}
		if (ptext) 
		{
			ptext->Close();	//To save configuration
			delete ptext;
		}
		if (phelp)
			delete phelp;
		Close();
	}
}

void Sauklaue::AboutMenu(Bar& bar)
{
	bar.Add(t_("Primer.."), SauklaueImg::About, THISBACK(Launch_HelpWindow));
	bar.Add("RITA", SauklaueImg::RITA, THISBACK(Rita));
	bar.Add(t_("About.."), SauklaueImg::Help, THISBACK(About));
}

void Sauklaue::HideHelp()
{
	phelp->Hide();
	if (phelp)
		delete phelp;
	phelp = (HelpWindow *) 0;
}

void Sauklaue::Rita()
{
	if (!phelp) Launch_HelpWindow();
	else
	{
		if (phelp->IsShown())
			phelp->SetForeground();
		else phelp->Show();
	}

	String ts="topic://Sauklaue/sauklaue/RITA$";
	if (GetCurrentLanguage() == LNG_('d','e','D','E'))
		ts.Cat("de-de");
	else ts.Cat("en-us");
	phelp->GoTo(ts);
}

void Sauklaue::About()
{
	Prompt(Ctrl::GetAppName(),SauklaueImg::SKL_Icon,t_("[R5 SWord] [= &[* Handwriting simulated] & &Edition MMXIV &by &Manfred Herr]"),t_("OK"));
}

void Sauklaue::DrawingSize()
{
	Rect tot = Null;
	drawing_size = Size(0,0);
	for (int i = 0; i < doc.vwordrect.GetCount(); i++)
	{
		tot.Union(doc.vwordrect[i].r);
	}
	drawing_size = tot.Size();
}

void Sauklaue::Layout()
{
	Size s = drawing_size * Textheight/ 100;
	s.cx += 2*LeftMargin;
	s.cy += 2*TopMargin;
	scroll.SetPage(GetSize());
	scroll.SetTotal(s);
	DLOG("Layout Total = "<<s);
}

void Sauklaue::Scroll()
{
	SetFocus();
	Refresh();
}

GUI_APP_MAIN
{
//	SetLanguage(LNG_('D','E','D','E'));
	(new Sauklaue)->OpenMain();
	Ctrl::SetAppName(t_("SWord"));

    Ctrl::EventLoop();
	
}
