//////////////////////////////////////////////////////////////////////
//																	//
//		LINEEDIT CTRL, modification by Ratah						//
//		lova.tahina@hotmail.fr										//
//		Phone +33626984844     										//
//		Febr 2011, FRANCE											//
//																	//
//////////////////////////////////////////////////////////////////////


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

using namespace Upp;

#define LLOG(x) //  LOG(x)

AdvLineEdit::AdvLineEdit() {
	isdrag = false;
	nohbar = false;
	showtabs = false;
	tabsize = 4;
	font = Courier(16);
	SetFrame(ViewFrame());
	sb.NoBox();
	AddFrame(sb);
	sb.WhenScroll = THISBACK(Scroll);
	cutline = true;
	bordercolumn = -1;
	bordercolor = Null;
	overwrite = false;
	filter = NULL;
	
	isBlockActive = true;
}

AdvLineEdit::~AdvLineEdit() {}

void AdvLineEdit::MouseWheel(Point, int zdelta, dword) {
	sb.WheelY(zdelta);
}

void AdvLineEdit::Clear() {
	gcolumn = 0;
	TextCtrl::Clear();
	sb.SetTotal(0, 0);
	sb.Set(0, 0);
	NewScrollPos();
	PlaceCaret(0, false);
}

AdvLineEdit& AdvLineEdit::TabSize(int n) {
	tabsize = n;
	PlaceCaret0(GetColumnLine(cursor));
	Refresh();
	return *this;
}

AdvLineEdit& AdvLineEdit::BorderColumn(int col, Color c)
{
	bordercolumn = col;
	bordercolor = c;
	Refresh();
	return *this;
}

AdvLineEdit& AdvLineEdit::SetFont(Font f) {
	font = f;
	Layout();
	TabSize(tabsize);
	SetSb();
	return *this;
}

Size AdvLineEdit::GetFontSize() const {
	FontInfo fi = font.Info();
	return Size(max(fi['M'], fi['W']), fi.GetHeight());
}

void AdvLineEdit::alimNbCarParLigne()
{
	int nbcar;
	
	m_vNbCarParLigne.Clear();
	String stxt;
	String crlf;
	
	for(int i=0; i<line.GetCount(); i++)
	{
		nbcar = line[i].GetLength() + 1; // Pour compenser le \n non pris en compte dans GetLength
		m_vNbCarParLigne.Add(nbcar);
	}
}

void AdvLineEdit::PaintSelection(Draw& w, int y, int wcaract, int hcaract, int r1, int r2, int c1, int c2, int scx, int scy, Color cfond, Color cborder)
{
	if( y > (hcaract*(r1-2) - scy) && y < (hcaract*(r2-1) - scy) )
		w.DrawRect(wcaract*(c1-1) - scx, y, wcaract*(c2 - c1 + 1), hcaract, cfond);
	
	// Hztal
	w.DrawLine(wcaract*(c1-1) - scx, hcaract*(r1-1)  - scy, wcaract*c2 - scx, hcaract*(r1-1)  - scy, 1, cborder);
	w.DrawLine(wcaract*(c1-1) - scx, hcaract*(r2-1) -1  - scy, wcaract*c2 - scx, hcaract*(r2-1) -1  - scy, 1, cborder);
	
	// Vtcal
	w.DrawLine(wcaract*(c1-1) - scx, hcaract*(r1-1) +1  - scy, wcaract*(c1-1) - scx, hcaract*(r2-1) -1  - scy, 1, cborder);									
	w.DrawLine(wcaract*c2 - scx, hcaract*(r1-1) +1  - scy, wcaract*c2 - scx, hcaract*(r2-1) -1  - scy, 1, cborder);
		
}

void AdvLineEdit::AddSelection(int r1, int r2, int c1, int c2, Color& cfond, Color& cborder, bool isShowen)
{
	selectionSpeciale ss;
	ss.r1 = r1;	
	ss.r2 = r2;
	ss.c1 = c1;
	ss.c2 = c2;
	ss.cfond = cfond;
	ss.cborder = cborder;
	ss.show = isShowen;
	
	m_vss.Add(ss);
}

void AdvLineEdit::PaintSelection(Draw& w, int y, int wcaract, int hcaract, int scx, int scy, Vector<selectionSpeciale>& vss)
{
	for(int i=0; i<	vss.GetCount() && vss[i].show; i++)
	{
		selectionSpeciale vsstmp = vss[i];
		
		
		if( y > (hcaract*(vsstmp.r1-2) - scy) && y < (hcaract*(vsstmp.r2-1) - scy) )
		{
			w.DrawRect(wcaract*(vsstmp.c1-1) - scx, y, wcaract*(vsstmp.c2 - vsstmp.c1 + 1), hcaract, vsstmp.cfond);
		}
		
		// Hztal
		w.DrawLine(wcaract*(vsstmp.c1-1) +1 - scx, hcaract*(vsstmp.r1-1)  - scy, wcaract*vsstmp.c2 - scx, hcaract*(vsstmp.r1-1)  - scy, 1, vsstmp.cborder);
		w.DrawLine(wcaract*(vsstmp.c1-1) +1 - scx, hcaract*(vsstmp.r2-1) -1  - scy, wcaract*vsstmp.c2 - scx, hcaract*(vsstmp.r2-1) -1  - scy, 1, vsstmp.cborder);
		
		// Vtcal
		w.DrawLine(wcaract*(vsstmp.c1-1) - scx, hcaract*(vsstmp.r1-1) +1  - scy, wcaract*(vsstmp.c1-1) - scx, hcaract*(vsstmp.r2-1) -1  - scy, 1, vsstmp.cborder);									
		w.DrawLine(wcaract*vsstmp.c2 - scx, hcaract*(vsstmp.r1-1) +1  - scy, wcaract*vsstmp.c2 - scx, hcaract*(vsstmp.r2-1) -1  - scy, 1, vsstmp.cborder);
	}
}

void AdvLineEdit::Paint0(Draw& w) 
{
	int sell, selh;
	GetSelection(sell, selh);
	
	if(!IsEnabled())
		sell = selh = 0;
	
	Size sz = GetSize();
	Size fsz = GetFontSize();
	Point sc = sb;  // ScrollBar !!
	
	int ll = min(line.GetCount(), sz.cy / fsz.cy + sc.y + 1);
	int  y = 0;
	cpos = GetPos(sc.y);
	cline = sc.y;
	sell -= cpos;
	selh -= cpos;
	int pos = cpos;
	
	Vector<int> dx, dx2;
	int fascent = font.Info().GetAscent();

	for(int i = sc.y; i < ll; i++) 
	{
		WString tx = line[i];
		int len = tx.GetLength();
		
		if(w.IsPainting(0, y, sz.cx, fsz.cy)) 
		{
			Highlight ih;			
			ih.ink = color[IsShowEnabled() ? INK_NORMAL : INK_DISABLED];
			ih.paper = color[IsReadOnly() || !IsShowEnabled() ? PAPER_READONLY : PAPER_NORMAL];
			if(nobg)
				ih.paper = Null;
			ih.font = font;
			ih.chr = 0;
			
			
		// Partie sélection
		
			Vector<Highlight> hl;
			hl.SetCount(len + 1, ih);
			
			for(int q = 0; q < tx.GetCount(); q++)
				hl[q].chr = tx[q];
			
			HighlightLine(i, hl, pos);
			
			int ln = hl.GetCount() - 1;
			int l = max(sell, 0);
			int h = selh > len ? len : selh;
			
			if(l < h)
				for(int i = l; i < h; i++) 
				{
					//hl[i].paper = color[PAPER_SELECTED];
					//hl[i].ink = color[INK_SELECTED];
					
					// Couleur de la partie selectionnée, occupée par des caractères
					hl[i].paper = Blue();
					hl[i].ink = color[INK_SELECTED];
				}
				
			if(sell <= len && selh > len)
				for(int i = len; i < hl.GetCount(); i++) 
				{
					//hl[i].paper = color[PAPER_SELECTED];
					
					// Couleur de la partie droite selectionnée mais non occupée par des caractères
					hl[i].paper = Color(220, 201, 230);  
					hl[i].ink = color[INK_SELECTED];
				}
				
				
// ln est le nbre de caractères

			Buffer<wchar> txt(ln);
			for(int i = 0; i < ln; i++)
				txt[i] = hl[i].chr;
			
			
			for(int pass = 0; pass < 2; pass++) 
			{
				int gp = 0;
				int scx = fsz.cx * sc.x;
				int scy = fsz.cy * sc.y;
				
				if(ln >= 0) 
				{
					int q = 0;
					while(q < ln) 
					{
						Highlight& h = hl[q];
						if(txt[q] == '\t') 
						{
							int ngp = (gp + tabsize) / tabsize * tabsize;
							int l = ngp - gp;
							LLOG("Highlight -> tab[" << q << "] paper = " << h.paper);
							if(pass == 0) 
							{
								w.DrawRect(gp * fsz.cx - scx, y, fsz.cx * l, fsz.cy, h.paper);
								
								
								if(showtabs && h.paper != SColorHighlight && q < tx.GetLength()) 
								{
									Color c = Blend(SColorLight, SColorHighlight);
									
									w.DrawRect(gp * fsz.cx - scx + 2, y + fsz.cy / 2,
									           l * fsz.cx - 4, 1, c);
									w.DrawRect(ngp * fsz.cx - scx - 3, y + 3,
									           1, fsz.cy - 6, c);
									
								}
								if(bordercolumn > 0 && bordercolumn >= gp && bordercolumn < gp + l)
								{
									w.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, bordercolor);
									//w.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, Yellow());
								}
							}
							q++;
							gp = ngp;
						}
						else 
						{
							bool cjk = IsCJKIdeograph(txt[q]);
							int p = q + 1;
							while(p < len && h == hl[p] && txt[p] != '\t' && IsCJKIdeograph(txt[p]) == cjk && p - q < 128)
								p++;
							int l = p - q;
							int ll = cjk ? 2 * l : l;
							LLOG("Highlight -> paper[" << q << "] = " << h.paper);
							int x = gp * fsz.cx - scx;
							int xx = x + (gp + ll) * fsz.cx;
							
							if(max(x, 0) < min(xx, sz.cx))
								if(pass == 0) 
								{
									
								// Couleur de la zone où il y a des lettres 								
									w.DrawRect(x, y, fsz.cx * ll, fsz.cy, h.paper);
									
								// Appliquer la sélection									

									//PaintSelection(w, y, fsz.cx, fsz.cy, r1, r2, c1, c2, scx, scy, Color(230, 235, 200), Color(130, 135, 100));
									//PaintSelection(w, y, fsz.cx, fsz.cy, r3, r4, c3, c4, scx, scy, Color(230, 235, 255), Color(130, 135, 155));
									
									PaintSelection(w, y, fsz.cx, fsz.cy, scx, scy, m_vss);									
									if(bordercolumn > 0 && bordercolumn >= gp && bordercolumn < gp + ll)
										w.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, bordercolor);
								}
								else 
								{
									if(cjk)
										dx2.At(l, 2 * fsz.cx);
									else
										dx.At(l, fsz.cx);
									
									
									
									w.DrawText(x,
									           y + fascent - h.font.Info().GetAscent(),
									           ~txt + q, h.font, h.ink, l, cjk ? dx2 : dx);
								}
							q = p;
							gp += ll;
							if(x > sz.cx)
								break;
						}
					}
				}
				if(pass == 0) 
				{
					int gpx = gp * fsz.cx - scx;
					
					
				// Ligne actuellement occupée
					// w.DrawRect(gpx, y, sz.cx - gpx, fsz.cy, Yellow());   
					
					w.DrawRect(gpx, y, sz.cx - gpx, fsz.cy, hl.Top().paper);

					if(bordercolumn > 0 && bordercolumn >= gp)
						w.DrawRect((bordercolumn - sc.x) * fsz.cx, y, 1, fsz.cy, bordercolor);
				}
			}
		}
		y += fsz.cy;
		sell -= len + 1;
		selh -= len + 1;
		pos += len + 1;
	}
	
	// Couleur du reste de la zone
		// w.DrawRect(0, y, sz.cx, sz.cy - y, Blue());
		w.DrawRect(0, y, sz.cx, sz.cy - y, color[IsReadOnly() || !IsShowEnabled() ? PAPER_READONLY : PAPER_NORMAL]);
	
	DrawTiles(w, DropCaret(), CtrlImg::checkers());
	
}

void AdvLineEdit::Paint(Draw& w)
{
	Paint0(w);
	scroller.Set(sb);
}

struct AdvLineEdit::RefreshDraw : public NilDraw {
	Ctrl  *ctrl;
	bool (*chars)(int c);
	Size   fsz;
	virtual void DrawTextOp(int x, int y, int angle, const wchar *text, Font,
	                        Color, int n, const int *dx) {
		if(dx)
			while(n > 0) {
				if((*chars)(*text))
					ctrl->Refresh(x, y, fsz.cx, fsz.cy);
				text++;
				x += *dx++;
				n--;
			}
	}
	bool IsPaintingOp(const Rect& r) const {
		return true;
	}
};

void AdvLineEdit::RefreshChars(bool (*chars)(int c))
{
	RefreshDraw rw;
	rw.ctrl = this;
	rw.fsz = GetFontSize();
	rw.chars = chars;
	Paint(rw);
}

void   AdvLineEdit::Layout() {
	Size sz = sb.GetReducedViewSize();
	if(nohbar || isdrag)
		sz.cy = GetSize().cy;
	sb.SetPage(sz / GetFontSize());
	SetHBar();
}

int   AdvLineEdit::GetGPos(int ln, int cl) const {
	ln = minmax(ln, 0, line.GetCount() - 1);
	WString txt = line[ln];
	const wchar *b = txt;
	const wchar *e = txt.End();
	const wchar *s = b;
	int gl = 0;
	while(s < e) {
		if(*s == '\t')
			gl = (gl + tabsize) / tabsize * tabsize;
		else
			gl += 1 + IsCJKIdeograph(*s);
		if(cl < gl) break;
		s++;
	}
	return GetPos(ln, int(s - b));
}

Point AdvLineEdit::GetColumnLine(int pos) const {
	Point p;
	if(pos > total) pos = total;
	p.y = GetLinePos(pos);
	p.x = 0;
	WString txt = line[p.y];
	const wchar *s = txt;
	while(pos--) {
		if(*s == '\t')
			p.x = (p.x + tabsize) / tabsize * tabsize;
		else
			p.x += 1 + IsCJKIdeograph(*s);
		s++;
	}
	return p;
}

Point AdvLineEdit::GetIndexLine(int pos) const
{
	Point p;
	if(pos > total) pos = total;
	p.y = GetLinePos(pos);
	p.x = minmax(pos, 0, line[p.y].GetLength());
	return p;
}

int AdvLineEdit::GetIndexLinePos(Point pos) const
{
	if(pos.y < 0)
		return 0;
	if(pos.y >= GetLineCount())
		return total;
	return GetPos(pos.y, minmax(pos.x, 0, line[pos.y].GetLength()));
}

void AdvLineEdit::RefreshLine(int i) {
	Size sz = GetSize();
	int fcy = GetFontSize().cy;
	Refresh(0, (i - sb.Get().y) * fcy, sz.cx, fcy);
}

Rect AdvLineEdit::GetLineScreenRect(int line) const {
	int fcy = GetFontSize().cy;
	Rect r = RectC(0, (line - sb.Get().y) * fcy, GetSize().cx, fcy);
	r.Offset(GetScreenView().TopLeft());
	return r;
}

void AdvLineEdit::SetSb() {
	sb.SetTotalY(line.GetCount());
	SetHBar();
}

void AdvLineEdit::NewScrollPos() {}
void AdvLineEdit::HighlightLine(int line, Vector<Highlight>& h, int pos) {}

void AdvLineEdit::AlignChar() {
	int c = GetCursor();
	if(c == 0)
		return;
	Point pos = GetColumnLine(c);
	if(pos.x == 0)
		return;
	for(int d = 1; d <= pos.y && d < 100; d++) {
		int lny = pos.y - d;
		WString above = GetWLine(lny);
		int offset = GetGPos(lny, pos.x) - GetPos(lny);
		int end = offset;
		char ch = GetChar(c - 1);
		if(ch == ' ')
		{
			offset++;
			while(end < above.GetLength() && above[end] != ' ')
				end++;
			while(end < above.GetLength() && above[end] == ' ')
				end++;
		}
		else
			while(end < above.GetLength() && above[end] != ch)
				end++;
		if(end < above.GetLength()) {
			int count = end - offset + 1;
			WString s(' ', count);
			Insert(c - 1, s, true);
			SetCursor(c + count);
			return;
		}
	}
}

void AdvLineEdit::PlaceCaret0(Point p) {
	Size fsz = GetFontSize();
	p -= sb;
	caretpos = Point(p.x * fsz.cx, p.y * fsz.cy);
	if(overwrite)
		SetCaret(caretpos.x, caretpos.y + fsz.cy - 2, fsz.cx, 2);
	else
		SetCaret(caretpos.x, caretpos.y, 2, fsz.cy);
}

void AdvLineEdit::PlaceCaretBlock(Point p) {
	Size fsz = GetFontSize();
	p -= sb;
	caretpos = Point(p.x * fsz.cx, p.y * fsz.cy);
	if(overwrite)
		SetCaret(caretpos.x, caretpos.y + fsz.cy - 5, fsz.cx, 5);
	else
	{
		SetCaret(caretpos.x, caretpos.y, 1, fsz.cy*hcursor);
	}
}

// TODO, add hauteur Caret

int AdvLineEdit::PlaceCaretNoGBlock(int newcursor, bool sel) {
	
	if(newcursor > total) newcursor = total;
	Point p = GetColumnLine(newcursor);
	if(sel) {
		if(anchor < 0) {
			anchor = cursor;
		}
		RefreshLines(p.y, GetLine(cursor));
	}
	else
		if(anchor >= 0) {
			RefreshLines(GetLine(cursor), GetLine(anchor));
			anchor = -1;
		}
	cursor = newcursor;
	ScrollIntoCursor();
	PlaceCaretBlock(p);
	SelectionChanged();
	WhenSel();
	
	//anchor = 2;
	if(IsSelection())
	{
		//PromptOK("C'est une selection");
		//SetSelection();
	}
	return p.x;
}

bool AdvLineEdit::IsSelection()
{ 
	if(isBlockActive)
		return true;
	else
		return anchor >= 0; 
}
	
int AdvLineEdit::PlaceCaretNoG(int newcursor, bool sel) {
	if(newcursor > total) newcursor = total;
	Point p = GetColumnLine(newcursor);
	if(sel) {
		if(anchor < 0) {
			anchor = cursor;
		}
		RefreshLines(p.y, GetLine(cursor));
	}
	else
		if(anchor >= 0) {
			RefreshLines(GetLine(cursor), GetLine(anchor));
			anchor = -1;
		}
	cursor = newcursor;
	ScrollIntoCursor();
	PlaceCaret0(p);
	SelectionChanged();
	WhenSel();
	if(IsSelection())
	{
		//PromptOK("C'est une selection");
		SetSelectionSource(ClipFmtsText());
	}
	return p.x;
}

void AdvLineEdit::PlaceCaret(int newcursor, bool sel) {
	gcolumn = PlaceCaretNoG(newcursor, sel);
}

void AdvLineEdit::TopCursor(int lines)
{
	sb.SetY(max(0, GetLine(cursor) - lines));
}

void AdvLineEdit::CenterCursor() {
	int cy = sb.GetPage().cy;
	if(cy > 4)
		sb.SetY(max(min(GetLine(cursor) - cy / 2, line.GetCount() - cy), 0));
}

void AdvLineEdit::Scroll() {
	
	if(isBlockActive)
		PlaceCaretBlock(GetColumnLine(cursor));
	else	
		PlaceCaret0(GetColumnLine(cursor));
	
	scroller.Scroll(*this, GetSize(), sb.Get(), GetFontSize());
	SetHBar();
	NewScrollPos();
}

int AdvLineEdit::GetMousePos(Point p) const {
	Size fsz = GetFontSize();
	p = (p + fsz.cx / 2 + fsz * (Size)sb.Get()) / fsz;
	return GetGPos(p.y, p.x);
}

void AdvLineEdit::LeftDown(Point p, dword flags) {
	mpos = GetMousePos(p);
	int l, h;
	if(GetSelection(l, h) && mpos >= l && mpos < h) 
	{
		selclick = true;
		return;
	}
	
	if((flags & K_ALT))
	{
		if(!isBlockSelection)
		{
			isBlockSelection = true;

			Size fsz = GetFontSize();
			p = (p + fsz.cx / 2 + fsz * (Size)sb.Get()) / fsz;
			
			m_CursorTL.x = p.x;
			m_CursorTL.y = p.y;
		}
	}
	
	PlaceCaret(mpos, flags & K_SHIFT);
	SetWantFocus();
	SetCapture();
	
	m_vss.Clear();
	Refresh();
}

void AdvLineEdit::LeftUp(Point p, dword flags)
{
	if(!HasCapture() && selclick) {
		mpos = GetMousePos(p);
		PlaceCaret(mpos, flags & K_SHIFT);
		SetWantFocus();
	}
	selclick = false;
	
	if(isBlockSelection)
	{	
		isBlockSelection = false;		
		PlaceBlockCaret(p);
	}
}

void AdvLineEdit::RightDown(Point p, dword flags)
{
	mpos = GetMousePos(p);
	SetWantFocus();
	int l, h;
	
	if(!isBlockActive)
	{
		if(!GetSelection(l, h) || mpos < l || mpos >= h)
			PlaceCaret(mpos, false);
	}
	else
	{
		// TODO: Besoin de récupérer les données selectionnées et activer les items copy, insert, ... de WhenBar
	}
	
	MenuBar::Execute(WhenBar);
}

void AdvLineEdit::LeftDouble(Point, dword)
{
	int l, h;
	if(GetWordSelection(cursor, l, h))
		SetSelection(l, h);
	
	// Le texte selectionné
	
	int r_curs = GetCursorLine();
	WString txt = line[r_curs];
	
	//PromptOK( "ligne = " + AsString(r_curs) + ", l (caract avant) = " + AsString(l) +  ", h (caract apres) = " + AsString(h) + ", cursor (caract apres) = " + AsString(cursor) );
	//PromptOK( "line[r_curs] = " + AsString(txt) );
	
	int relative_l;
	int navant = 0;
	
	alimNbCarParLigne();
	for(int i=0; i<r_curs; i++)
	{
		navant += m_vNbCarParLigne[i];
	}
	
	relative_l = l-navant;	
	txt = txt.Mid(relative_l, h-l);
	
	String selectTxt = txt.ToString();
	
	//PromptOK("navant = " + AsString(navant) + ",& l = " + AsString(l) + ",& relative_l = l-navant = " + AsString(relative_l) + "& => " + AsString(selectTxt) );
		
	
	// Trouver les autres textes dans le vecteur
	
	if(txt.GetLength()>0)
	{
	
		VectorMap<int, int> posSelect;
		WString wactuel;
		String sactuel, msg;
		int pos;
		int TOTAL = line.GetCount();
		
		Color fond(221, 223, 200);
		Color bord(181, 183, 180);
		
		m_vss.Clear();
		Vector<int> vitmp;
		VectorMap<int, Vector<int> > vi;
		
		for(int i=0; i<TOTAL; i++)
		{
			wactuel = line[i];
			sactuel = line[i].text;
			vitmp = FindWordOccurence(wactuel, txt);
			
			vi.Add(i, vitmp);		
		}
		
		for(int i=0; i<vi.GetCount(); i++)
		{
			int key = vi.GetKey(i);
			Vector<int> vint = vi.Get(key);
			
			for(int j=0; j<vint.GetCount(); j++)
			{
				int val = vint[j];
				AddSelection(key+1, key+2, val+1, val+h-l, fond, bord, true);		
			}
		}
		
	}
	
	Refresh();
}

void AdvLineEdit::LeftTriple(Point, dword)
{
	int q = cursor;
	int i = GetLinePos(q);
	q = cursor - q;
	SetSelection(q, q + GetLineLength(i) + 1);
}

void AdvLineEdit::MouseMove(Point p, dword flags) {
	if((flags & K_MOUSELEFT) && HasFocus() && HasCapture()) 
	{
		int c = GetMousePos(p);
		PlaceCaret(c, mpos != c || HasCapture());
	}
	
	if((flags & K_MOUSELEFT) && isBlockSelection)
	{
		ClearSelection();
		GetCursorBRPos();		
		SelectBlock();		
	}
}

void AdvLineEdit::LeftRepeat(Point p, dword flags) {
	if(HasCapture()) {
		int c = GetMousePos(p);
		if(mpos != c)
			PlaceCaret(c, true);
	}
}

Image AdvLineEdit::CursorImage(Point, dword) {
	return Image::IBeam();
}

void AdvLineEdit::MoveUpDown(int n, bool sel) {
	int cl = cursor;
	int ln = GetLinePos(cl);
	ln = minmax(ln + n, 0, line.GetCount() - 1);
	PlaceCaretNoG(GetGPos(ln, gcolumn), sel);
}

void AdvLineEdit::MoveLeft(bool sel) {
	if(cursor)
		PlaceCaret(cursor - 1, sel);
}

void AdvLineEdit::MoveRight(bool sel) {
	if(cursor < total)
		PlaceCaret(cursor + 1, sel);
}

void AdvLineEdit::MoveUp(bool sel) {
	MoveUpDown(-1, sel);
}

void AdvLineEdit::MoveDown(bool sel) {
	MoveUpDown(1, sel);
}

void AdvLineEdit::MovePage(int dir, bool sel) {
	int n = dir * max(GetSize().cy / GetFontSize().cy - 2, 2);
	sb.SetY(Point(sb).y + n);
	MoveUpDown(n, sel);
}

void AdvLineEdit::MovePageUp(bool sel) {
	MovePage(-1, sel);
}

void AdvLineEdit::MovePageDown(bool sel) {
	MovePage(1, sel);
}

inline bool sTabSpace(int c) { return c == '\t' || c == ' '; }

void AdvLineEdit::MoveHome(bool sel) {
	int cl = cursor;
	int li = GetLinePos(cl);
	int i = 0;
	WString l = line[li];
	while(sTabSpace(l[i]))
		i++;
	PlaceCaret(GetPos(li, cl == i ? 0 : i), sel);
}

void AdvLineEdit::MoveEnd(bool sel) {
	int i = GetLine(cursor);
	PlaceCaret(GetPos(i, line[i].GetLength()), sel);
}

void AdvLineEdit::MoveTextBegin(bool sel) {
	PlaceCaret(0, sel);
}

void AdvLineEdit::MoveTextEnd(bool sel) {
	PlaceCaret(total, sel);
}

bool AdvLineEdit::InsertChar(dword key, int count, bool canow) {
	if(key == K_TAB && !processtab)
		return false;
	if(filter && key >= 32 && key < 65535)
		key = (*filter)(key);
	if(!IsReadOnly() && (key >= 32 && key < 65536 || key == '\t' || key == '\n' ||
	   key == K_ENTER && processenter || key == K_SHIFT_SPACE)) {
		if(key >= 128 && key < 65536 && charset != CHARSET_UNICODE
		   && FromUnicode((wchar)key, charset) == DEFAULTCHAR)
			return true;
		if(!RemoveSelection() && overwrite && key != '\n' && key != K_ENTER && canow) {
			int q = cursor;
			int i = GetLinePos(q);
			if(q + count - 1 < GetLineLength(i))
				Remove(cursor, count);
		}
		WString text(key == K_ENTER ? '\n' : key == K_SHIFT_SPACE ? ' ' : key, count);
		Insert(cursor, text, true);
		PlaceCaret(cursor + count);
		Action();
		return true;
	}
	return false;
}

void AdvLineEdit::DeleteChar() {
	if(IsReadOnly() || RemoveSelection()) {
		Action();
		return;
	}
	if(cursor < total) {
		Remove(cursor, 1);
		Action();
	}
}

void AdvLineEdit::Backspace() {
	if(IsReadOnly() || RemoveSelection() || cursor == 0) return;
	MoveLeft();
	DeleteChar();
	Action();
}

void AdvLineEdit::DeleteLine()
{
	int b, e;
	if(GetSelection(b, e) && GetLine(b) != GetLine(e)) {
		RemoveSelection();
		return;
	}
	int i = GetLine(cursor);
	int p = GetPos(i);
	Remove(p, line[i].GetLength() + 1);
	PlaceCaret(p);
	Action();
}

void AdvLineEdit::CutLine()
{
	if(IsReadOnly()) return;
	int b, e;
	if(GetSelection(b, e) && GetLine(b) != GetLine(e)) {
		Cut();
		return;
	}
	int i = GetLine(cursor);
	int p = GetPos(i);
	WString txt = Get(p, line[i].GetLength() + 1).ToWString();
	WriteClipboardUnicodeText(txt);
	AppendClipboardText(txt.ToString());
	ClearSelection();
	DeleteLine();
}

void AdvLineEdit::EditPos::Serialize(Stream& s) {
	int version = 0;
	s / version;
	s % sby % cursor;
}

AdvLineEdit::EditPos AdvLineEdit::GetEditPos() const {
	EditPos pos;
	pos.sby = sb.Get().y;
	pos.cursor = cursor;
	return pos;
}

void AdvLineEdit::SetEditPos(const AdvLineEdit::EditPos& pos) {
	sb.SetY(minmax(pos.sby, 0, line.GetCount() - 1));
	SetCursor(pos.cursor);
}

void AdvLineEdit::SetEditPosSb(const AdvLineEdit::EditPos& pos) {
	SetCursor(pos.cursor);
	sb.SetY(minmax(pos.sby, 0, line.GetCount() - 1));
}

void AdvLineEdit::SetHBar()
{
	int mpos = 0;
	if(!nohbar && !isdrag) {
		int m = min(sb.y + sb.GetPage().cy + 2, line.GetCount());
		for(int i = sb.y; i < m; i++) {
			int pos = 0;
			WString l = line[i];
			const wchar *s = l;
			const wchar *e = l.End();
			while(s < e) {
				if(*s == '\t')
					pos = (pos + tabsize) / tabsize * tabsize;
				else
					pos += 1 + IsCJKIdeograph(*s);
				s++;
			}
			mpos = max(mpos, pos);
		}
	}
	sb.SetTotalX(mpos + 1);
}

void AdvLineEdit::ScrollIntoCursor()
{
	Point p = GetColumnLine(GetCursor());
	sb.ScrollInto(p);
	SetHBar();
	sb.ScrollInto(p);
}

bool AdvLineEdit::Key(dword key, int count) {
	NextUndo();
	
	//isBlockSelection = false;
	
	switch(key) {
	case K_CTRL_UP:
		ScrollUp();
		return true;
	case K_CTRL_DOWN:
		ScrollDown();
		return true;
	case K_INSERT:
		OverWriteMode(!IsOverWriteMode());
		break;
	}
	bool sel = key & K_SHIFT;
	switch(key & ~K_SHIFT) {
	case K_CTRL_LEFT:
		{
			PlaceCaret(GetPrevWord(cursor), sel);
			break;
		}
	case K_CTRL_RIGHT:
		{
			PlaceCaret(GetNextWord(cursor), sel);
			break;
		}
	case K_LEFT:
		MoveLeft(sel);
		break;
	case K_RIGHT:
		MoveRight(sel);
		break;
	case K_HOME:
		MoveHome(sel);
		break;
	case K_END:
		MoveEnd(sel);
		break;
	case K_UP:
		MoveUp(sel);
		break;
	case K_DOWN:
		MoveDown(sel);
		break;
	case K_PAGEUP:
		MovePageUp(sel);
		break;
	case K_PAGEDOWN:
		MovePageDown(sel);
		break;
	case K_CTRL_PAGEUP:
	case K_CTRL_HOME:
		MoveTextBegin(sel);
		break;
	case K_CTRL_PAGEDOWN:
	case K_CTRL_END:
		MoveTextEnd(sel);
		break;
	case K_CTRL_C:
	case K_CTRL_INSERT:
		Copy();
		break;
	case K_CTRL_A:
		SelectAll();
		break;
	default:
		if(IsReadOnly())
			return MenuBar::Scan(WhenBar, key);
		switch(key) {
		case K_DELETE:
			DeleteChar();
			break;
		case K_BACKSPACE:
		case K_SHIFT|K_BACKSPACE:
			Backspace();
			break;
	   	case K_SHIFT_TAB:
			AlignChar();
			break;
		case K_CTRL_Y:
		case K_CTRL_L:
			if(cutline) {
				CutLine();
				break;
			}
		default:
			if(InsertChar(key, count, true))
				return true;
			return MenuBar::Scan(WhenBar, key);
		}
		return true;
	}
	Sync();
	return true;
}

void AdvLineEdit::DragAndDrop(Point p, PasteClip& d)
{
	if(IsReadOnly()) return;
	int c = GetMousePos(p);
	if(AcceptText(d)) {
		NextUndo();
		int a = sb.y;
		int sell, selh;
		if(GetSelection(sell, selh)) {
			if(c >= sell && c < selh) {
				RemoveSelection();
				if(IsDragAndDropSource())
					d.SetAction(DND_COPY);
				c = sell;
			}
			else
			if(d.GetAction() == DND_MOVE && IsDragAndDropSource()) {
				if(c > sell)
					c -= selh - sell;
				RemoveSelection();
				d.SetAction(DND_COPY);
			}
		}
		int count = Insert(c, GetWString(d));
		sb.y = a;
		SetFocus();
		SetSelection(c, c + count);
		Action();
		return;
	}
	if(!d.IsAccepted()) return;
	if(!isdrag) {
		isdrag = true;
		ScrollIntoCursor();
	}
	Point dc = Null;
	if(c >= 0)
		dc = GetColumnLine(c);
	if(dc != dropcaret) {
		RefreshDropCaret();
		dropcaret = dc;
		RefreshDropCaret();
	}
}

Rect AdvLineEdit::DropCaret()
{
	if(IsNull(dropcaret))
		return Rect(0, 0, 0, 0);
	Size fsz = GetFontSize();
	Point p = dropcaret - sb;
	p = Point(p.x * fsz.cx, p.y * fsz.cy);
	return RectC(p.x, p.y, 1, fsz.cy);
}

void AdvLineEdit::RefreshDropCaret()
{
	Refresh(DropCaret());
}

void AdvLineEdit::DragRepeat(Point p)
{
	sb.y = (int)sb.y + GetDragScroll(this, p, 1).y;
}

void AdvLineEdit::DragLeave()
{
	RefreshDropCaret();
	dropcaret = Null;
	isdrag = false;
	Layout();
}

void AdvLineEdit::LeftDrag(Point p, dword flags)
{
	int c = GetMousePos(p);
	int l, h;
	if(!HasCapture() && GetSelection(l, h) && c >= l && c < h) {
		WString sample = GetW(l, min(h - l, 3000));
		Size sz = StdSampleSize();
		ImageDraw iw(sz);
		iw.DrawRect(sz, Black());
		iw.Alpha().DrawRect(sz, Black());
		DrawTLText(iw.Alpha(), 0, 0, 9999, sample, Courier(10), White());
		NextUndo();
		if(DoDragAndDrop(ClipFmtsText(), iw) == DND_MOVE) {
			RemoveSelection();
			Action();
		}
	}
}

void AdvLineEdit::PlaceBlockCaret(Point p)
{			
	int xav = m_CursorTL.x+1;
	int xap = m_CursorBR.x;
	
	if(xav > xap)
	{
		int i = xap;
		xap = xav;
		xav = i;

		mpos -= xap - xav - 1;
	}
	else
		mpos += xap - xav + 1;
	
	int yav = m_CursorTL.y+1;
	int yap = m_CursorBR.y+1;
	
	if(yav > yap)
	{
		int i = yap;
		yap = yav;
		yav = i;
		
		mpos = GetMousePos(p);		
	}

	hcursor = yap - yav;	
	PlaceCaretNoGBlock(mpos);
}

void AdvLineEdit::SelectBlock()
{
	m_vss.Clear();		
		
	Color fond(211, 213, 225);
	Color bord(181, 183, 198);
		
	int xav = m_CursorTL.x+1;
	int xap = m_CursorBR.x;
	
	if(xav > xap)
	{
		int i = xap;
		xap = xav;
		xav = i+1;
	}
		
	int yav = m_CursorTL.y+1;
	int yap = m_CursorBR.y+1;
	
	if(yav >= yap)
	{
		int i = yap;
		yap = yav+1;
		yav = i-1;

		m_CursorBR.y -=2;
	}
		
	AddSelection(yav, yap, xav, xap, fond, bord, true);
	
	Refresh();
}

void AdvLineEdit::GetCursorTLPos()
{
	int c_curs = GetCursor();	
	int r_curs = GetCursorLine();
	
	m_CursorTL.x = c_curs;
	m_CursorTL.y = r_curs;

		String s;
	
		s << "TL x : " << m_CursorTL.x << "&";	
		s << "TL y : " << m_CursorTL.y << "&";
		
		s << "BR x : " << m_CursorBR.x << "&";	
		s << "BR y : " << m_CursorBR.y << "&";
		
		PromptOK(s);	
}

void AdvLineEdit::GetCursorBRPos()
{
	int c_curs = GetCursor();	
	int r_curs = GetCursorLine();
	
	int navant = 0;
	
	alimNbCarParLigne();
	for(int i=0; i<r_curs; i++)
	{
		navant += m_vNbCarParLigne[i];
	}
	
	int relative_c = c_curs-navant;	
	
	m_CursorBR.x = relative_c;
	m_CursorBR.y = r_curs+1;	

}

/****************************** String Manipulations *****************************************************/

Vector<int> AdvLineEdit::FindWordOccurence(WString& line, WString& wordselect)
{
	Vector<int> vpos;
	Vector<WString> vs;
	
	int L = line.GetLength();
	int t = wordselect.GetLength();
	WString lineremaining = line;
	WString stmp;
	WString portion;
	
	int posactuel = 0;
	
	int l = L;
	//String s;
	
	for(; l>0;)
	{
		portion = lineremaining.Mid(0, t);
		//s << AsString(posactuel) + " -> " + portion.ToString() << "&";
				
		if(portion == wordselect)
		{
			//PromptOK(AsString(posactuel) + " ->  Trouvé : " + portion.ToString());
			
			vpos.Add(posactuel);
			vs.Add(portion);
			posactuel += t;
		}
		else
		{
			posactuel++;
		}
		
		lineremaining = line.Mid(posactuel, L);	
		l = lineremaining.GetLength();
	}
	
	/*
		//s << "&  &";
		
		for(int i=0; i<vs.GetCount(); i++)
		{
			s << vpos[i] << " - " << vs[i] << "&";
		}
		
		PromptOK(s);
	*/
	
	
	
	return vpos;	
}