#ifndef CODE_TEXTBOX_CXX
#define CODE_TEXTBOX_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: textbox.cxx,v 1.13 2003/02/26 14:07:46 schmitzj Exp $)
 * 
 * Copyright (C) 1998 Jürgen Schmitz
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *-----------------------------------------------------------------------
 */

//  UNIX Key bindings:
//         Ctrl-A - Move to beginning of line
//         Ctrl-E - Move to end of line
//         Ctrl-B - Move backwards
//         Ctrl-F - Move forwards
//         Ctrl-U - Erase line
//         Ctrl-D - Delete character
//         Ctrl-C - Copy to X clipboard
//         Ctrl-V - Paste from X clipboard


#include <time.h>
#include <string.h>
#include <ctype.h>
#include "types.h"
#include "../include/extra_keysym.h"
#include <Xclasses/gadget.h>
#include <Xclasses/Xcolors.h>
#include <Xclasses/Ddraw.h>
#include <Xclasses/tools.h>
#include <Xclasses/popup.h>
#include <Xclasses/textbox.h>
#include "include/textbox_p.h"

#define min(a,b)	(a<b?a:b)
#define max(a,b)	(a>b?a:b)

textbox::textbox(): gadget(), Ddraw(this)
{
	while (!(p=new ptextbox))
	{
		outOfMemory("textbox");
	}
	p->o=this;
	BorderSize(2);
	KeyBorder();
	Font(FONT_FIXED);
	Name("textbox");

	p->hscroll.ParentClass(this); p->hscroll.Name("hscroller");
	p->vscroll.ParentClass(this); p->hscroll.Name("vscroller");
	p->innen.ParentClass(this); p->innen.Name("body");
	p->glock.ParentClass(this);
	p->edit_menu.ParentClass(this);
	p->vscroll.Arrows();
	p->hscroll.Arrows();
}
textbox::~textbox()
{
	if (win) Free();
	if (p) delete p;
}
ptextbox::ptextbox() : hscroll(), vscroll(), innen(), glock(), edit_menu()
{
	readonly=0;
	bottom=0;
	m_up=0;
	m_down=0;
	m_left=0;
	m_right=0;
	justactive=0;
	vscrollersize=20;
	hscrollersize=18;
	top=0;
	left=0;
	lines=0;
	cols=0;
	minrows=1;
	mincols=1;
	maxlen=0;
	charw=8;
	charh=8;
	curx=0;
	cury=0;
	curm=0;
	mcurx=0;
	mcury=0;
	mcurm=0;
	mstart=-1;
	mend=-1;
	cache_line=0;
	cache_char=NULL;
	size=MEMBLOCKSIZE;
	if (text=(char*)malloc(size))
		*text=0;
	else
		size=0;

	used=0;
	total=0;
	largest=0;

	hscroll.specialflags|=SPF_NOKEYBORDER;
	vscroll.specialflags|=SPF_NOKEYBORDER;
}
ptextbox::~ptextbox()
{
	if (text)
	{
		used=0;
		size=0;
		::free(text);
	}
}
void ptextbox::RemoveAll(void)
{
	if (text)
	{
		*text=0; used=0;
		if (size>MEMBLOCKSIZE)
		{
			if (text=(char*)realloc(text,MEMBLOCKSIZE))
				size=MEMBLOCKSIZE;
			else
				size=0;
		}
	}
}
void textbox::ReadOnly(void)
{
	p->readonly=1;
}
void textbox::AlwaysShowLast(void)
{
	p->bottom=1;
}
void textbox::MinRows(int m)
{
	p->minrows=m;
}
void textbox::MinCols(int m)
{
	p->mincols=m;
}

void textbox::MaxLineLength(int l)
{
	p->maxlen=l;
}

void textbox::AppendText(char *t)
{
	if (!t) return;

	if (!*t)
	{
		if (p->innen.gg_win())
			p->textaus(TA_EXPOSE);
		return;
	}

	unsigned int l=strlen(t);

	int worstlen=1;
	for (int i = 0; i < l; i++)
	{
		if (t[i]=='\t')
			worstlen+=4;	// worst case!
		else
			worstlen++;
	}

	while (p->used+worstlen>=p->size)
	{
		p->text=(char*)realloc(p->text,p->size+MEMBLOCKSIZE*(worstlen/MEMBLOCKSIZE+1));
		if (!p->text) 
		{
			p->size=0;
			p->used=0;
			return;
		}
		p->size+=MEMBLOCKSIZE*(worstlen/MEMBLOCKSIZE+1);
	}
	char *bt=t,*rt=p->text;
	int x=0;
	while (*rt)
	{
		if (*rt=='\n') x=0;
		rt++; x++;
	}
	while (*bt)
	{
		switch (*bt)
		{
			case '\t':
				{
					int extra=0;
					if (x%4==0) extra=1;
					while (x%4>0 || extra)
					{
						*rt=' ';
						rt++;
						p->used++;
						x++;
						extra=0;
					}
				}
				break;
			default:
				*rt=*bt;
				rt++;
				p->used++;
				break;
		}
		x++;
		if (*bt=='\n') x=0;
		bt++;
	}
	*rt=0;
	p->cache_line=0;
	p->cache_char=NULL;

	int ol=p->total-1;
	p->setvars();
	if (p->bottom)
	{
		p->cury=p->total-1;
		p->curx=0;
		p->setXY2M(p->curx,p->cury);
		if (p->innen.gg_win())
			p->textaus(TA_EXPOSEFROM,ol);
		p->checkVisible();
	}
	else
	{
		if (p->innen.gg_win())
			p->textaus(TA_EXPOSEFROM,ol);
	}
}
void textbox::ReplaceText(char *t)
{
	if (p->text)
	{
		p->used=0;
		*p->text=0;
	}
	if (display() && win) // && XGetSelectionOwner(display(),XA_PRIMARY)==win)
	{
//		XSetSelectionOwner(display(),XA_PRIMARY,None,time(NULL));
		p->edit_menu.LockItem(1);
		p->edit_menu.LockItem(2);
		p->mstart=-1;
		p->mend=-1;
	}
	p->cache_line=0;
	p->cache_char=NULL;

	p->setvars();
	p->curx=0;
	p->cury=0;
	p->curm=0;
	p->checkVisible();

	AppendText(t);
	p->checkMem();
}
void textbox::InsertText(char *t,int x,int y)
{
	if (!t || !*t) return;

	int oldm=p->curm;
	unsigned int l=strlen(t);

	p->setXY2M(x,y);
	if (p->curm<oldm) oldm+=l;
	if (p->mstart!=-1)
	{
		p->edit_menu.LockItem(1);
		p->edit_menu.LockItem(2);
		p->mstart=-1;
		p->mend=-1;
	}

	int worstlen=1;
	for (int i = 0; i < l; i++)
	{
		if (t[i]=='\t')
			worstlen+=4;	// worst case!
		else
			worstlen++;
	}

	// calculate new size (rb)
	int newsize=p->size;
	while (p->used+worstlen>newsize)
		newsize+=MEMBLOCKSIZE*(worstlen/MEMBLOCKSIZE+1);

	if (newsize>p->size)
	{
		p->text=(char*)realloc(p->text,newsize);
		if (!p->text) 
		{
			p->size=0;
			p->used=0;
			return;
		}
		p->size=newsize;
	}

	if (p->text[p->used]!=0)
	{
		fprintf(stderr,"Xclasses internal warning!\n");
		fprintf(stderr,"textbox::InsertChar() got textarray (%d) which has wrong counter value (%d)\n",strlen(p->text),p->used);
		p->text[p->used]=0;
	}

	for(int i=p->used;i>=p->curm;i--)
	{
		p->text[l+i]=p->text[i];
	}
// 	for(int i=0;i<l;i++)
// 	{
// 		p->text[p->used+i]='%';
// 	}

	if (p->text[p->used+l]!=0)
	{
		fprintf(stderr,"Xclasses internal warning!\n");
		fprintf(stderr,"textbox::InsertChar() copied textarray (%d) which has wrong counter value (%d)\n",strlen(p->text),p->used);
		p->text[p->used+l]=0;
	}

	int xx=p->curx;
	char *c=&p->text[p->curm];
	for(int i=0;i<l;i++)
	{
		switch (t[i])
		{
			case '\t':
				{
					int d=4-(xx%4);
					for (char *cc=p->text+p->used+l-i;cc>c;cc--)
						*(cc+d-1)=*cc;

					*c=' ';
					p->curm+=d;
					p->used+=d;
					while (d-->0)
					{
						c++;
						*c=' ';
						xx++;
					}
					c--; xx--;
				}
				break;
			default:
				*c=t[i];
				p->curm++;
				p->used++;
				break;
		}
		xx++;
		if (t[i]=='\n') xx=0;
		c++;
	}
	p->cache_line=0;
	p->cache_char=NULL;

	p->setvars();
	if (p->bottom)
	{
		p->cury=p->total-1;
		p->curx=0;
		p->setXY2M(p->curx,p->cury);
		if (p->innen.gg_win())
			p->textaus(TA_EXPOSEFROM,y-1);
		p->checkVisible();
	}
	else
	{
		p->setM2XY(oldm);
		if (p->innen.gg_win())
			p->textaus(TA_EXPOSEFROM,y-1);
//		ShowLine(p->cury);
	}
	if (p->text[p->used]!=0)
	{
		fprintf(stderr,"Xclasses internal warning!\n");
		fprintf(stderr,"textbox::InsertChar() used textarray (%d:%c) which has wrong counter value (%d)\n",strlen(p->text),p->text[p->used],p->used);
		p->text[p->used]=0;
	}
	p->checkMem();
}
void ptextbox::checkMem(void)
{
	if (size-used>(MEMBLOCKSIZE+MEMBLOCKSIZE/2))
	{
		int f=(used/MEMBLOCKSIZE+1)*MEMBLOCKSIZE;

		text=(char*)realloc(text,f);
		if (!text) 
		{
			size=0;
			used=0;
			return;
		}
		size=f;
		cache_line=0;
		cache_char=NULL;
	}
}
int textbox::TextSize(void)
{
	return p->used;
}
void textbox::CopyText(char *m,int maxsize)
{
	strncpy(m,p->text,maxsize);
	m[maxsize-1]=0;
}
void textbox::Cursor(int &x,int &y)
{
	x=p->curx;
	y=p->cury;
}
void textbox::CursorTo(int x,int y)
{
	p->setXY2M(x,y);
	p->checkVisible();
}
void textbox::ShowLine(int l)
{
	if (l>=p->total) l=p->total-1;
	if (l>=0 && l<p->total)
	{
		if (l<p->cury)
			p->vscroll.Position(l);
		if (l>p->cury+p->lines)
			p->vscroll.Position(l-p->lines);
		p->textaus(TA_SCROLL);
	}
}
void textbox::TopLine(int l)
{
	if (l>=p->total) l=p->total-1;
	if (l>=0 && l<p->total)
	{
		p->vscroll.Position(l);
		p->textaus(TA_SCROLL);
	}
}
int textbox::TopLine(void)
{
	return p->top;
}
int textbox::TotalLines(void)
{
	return p->total;
}
void textbox::MarkTo(int x,int y)
{
	p->mstart=p->curm;
	CursorTo(x,y);
	p->mend=p->curm;
	p->cutpaste(NULL);
	p->textaus(TA_EXPOSE);
}
int textbox::MarkedTextSize(void)
{
	int r=0;
	if (p->mstart!=-1)
	{
		r=max(p->mstart,p->mend)-min(p->mstart,p->mend);
	}
	return r;
}
void textbox::CopyMarkedText(char *m,int maxsize)
{
	if (p->mstart!=-1)
	{
		int l=min((maxsize-1),MarkedTextSize());
		strncpy(m,&p->text[min(p->mstart,p->mend)],l);
		m[l]=0;
	}
	else
		m[0]=0;
}
void textbox::ReplaceMarkedText(char *txt)
{
	if (p->mstart!=-1)
	{
		p->uncursor();
		int oro=p->readonly;
		p->readonly=0;
		p->inschar(txt,strlen(txt));
		p->readonly=oro;
		p->checkVisible();
		p->cursor(1);
	}
}

int textbox::visible_rows(void)
{
	return p->lines;
}
int textbox::visible_cols(void)
{
	return p->cols;
}


int textbox::Create(void)
{ 
	Background(col_background());
	
    if (gadget::Create())
    {
    	Mode(GM_ONEEXPOSE|GM_KEY|GM_KEYTICK|GM_CUTPASTE);

		if (p->readonly)
			p->bgcol=p->innen.col_background();
		else
			p->bgcol=p->innen.col_shinebackground();
		p->innen.Dimensions(win,RealSize()+2,RealSize()+1,width-p->vscrollersize-RealSize()*2-4,height-p->hscrollersize-RealSize()*2-2);
    	p->innen.Background(p->bgcol);
    	p->innen.CopyFont(this);
    	if (!p->innen.Create())
    	{
    		gadget::Free();
    		return 0;
    	}
    	p->innen.MCursor(XC_xterm);
       	p->innen.SendEventTo(this);
       	p->innen.Mode(GM_BUTTON1|GM_BUTTON2|GM_BUTTON3|GM_MOUSEMOVE|GM_ONEEXPOSE|GM_KEYTICK|GM_ACTIVETICK|GM_KEY|GM_MINIMALMOVE);

		p->vscroll.Dimensions(win,width-p->vscrollersize,0,p->hscrollersize,height-p->vscrollersize);
    	p->vscroll.CopyFont(this);
    	if (!p->vscroll.Create())
    	{
    		p->innen.Free();
    		gadget::Free();
    		return 0;
    	}
       	p->vscroll.SendEventTo(this);

		p->hscroll.Dimensions(win,0,height-p->hscrollersize,width-p->vscrollersize,p->hscrollersize);
    	p->hscroll.CopyFont(this);
    	p->hscroll.Horiz();
    	if (!p->hscroll.Create())
    	{
    		p->vscroll.Free();
    		p->innen.Free();
    		gadget::Free();
    		return 0;
    	}
       	p->hscroll.SendEventTo(this);

		p->edit_menu.AddHeadline("Edit");
		if (!p->readonly)
		{
			p->edit_menu.Add("Cut","Ctrl+X",1);
			p->edit_menu.LockItem(1);
		}
		p->edit_menu.Add("Copy","Ctrl+C",2);
		p->edit_menu.LockItem(2);
		if (!p->readonly)
		{
			p->edit_menu.Add("Paste","Ctrl+V",3);
		}
		p->edit_menu.AddRuler();
		p->edit_menu.Add("Select all",NULL,4);
		if (!p->readonly)
		{
			p->edit_menu.AddRuler();
			p->edit_menu.Add("Clear",NULL,5);
		}
		p->edit_menu.SendEventTo(this);
		if (!p->edit_menu.Create())
		{
    		p->hscroll.Free();
    		p->vscroll.Free();
    		p->innen.Free();
    		gadget::Free();
    		return 0;
		}
		p->charh=textheight(this,"o");
		p->charw=textlength(this,"o");
		p->setvars();
		return 1;
    } 
    return 0;
}
void textbox::Free(void)
{
	p->edit_menu.Free();
	p->hscroll.Free();
	p->vscroll.Free();
	p->innen.Free();
	gadget::Free();
}

void textbox::Resize(int w,int h)
{
	p->innen.Resize(w-p->vscrollersize-RealSize()*2-4,h-p->hscrollersize-RealSize()*2-2);
	p->vscroll.Move(w-p->vscrollersize,0);
	p->vscroll.Resize(p->vscrollersize,h-p->hscrollersize);
	p->hscroll.Move(0,h-p->hscrollersize);
	p->hscroll.Resize(w-p->vscrollersize,p->hscrollersize);
	gadget::Resize(w,h);
	p->setvars();
	p->checkVisible();
}
void ptextbox::setvars(void)
{ // set all scrollers
	if (!innen.gg_win()) return;
	char *r=text;
	int z=1;
	lines=innen.gg_height()/charh;
	cols=innen.gg_width()/charw;

	if (r)
	{
		while (*r && r<(text+used))
		{
			if (*r=='\n') z++;
			r++;
		}
	}
	total=z;
	vscroll.Visible(lines);
	vscroll.Items(total);
	vscroll.Position(top);
	hscroll.Visible(cols);
	hscroll.Items(cols);
	hscroll.Position(left);
	checkwidth();
}
void ptextbox::checkwidth(void)
{ // set horiz scroller
	char *r=findline(top);
	int z=top,m=0,l;

	while (r && *r && z<(top+lines))
	{
		l=linelength(r);
		if (l>m) m=l;
		z++;
		r=findline(z);
	}
	largest=m;
	hscroll.Items(m);
}
void textbox::Lock(void)
{
	gadget::Lock();
	p->innen.Lock();
	p->hscroll.Lock();
	p->vscroll.Lock();
}
void textbox::Unlock(void)
{
	gadget::Unlock();
	p->innen.Unlock();
	p->hscroll.Unlock();
	p->vscroll.Unlock();
}
void textbox::Block(int b)
{
	gadget::Block(b);
	p->innen.Block(b);
	p->hscroll.Block(b);
	p->vscroll.Block(b);
}
void textbox::Unblock(int b)
{
	gadget::Unblock(b);
	p->innen.Unblock(b);
	p->hscroll.Unblock(b);
	p->vscroll.Unblock(b);
}
void ptextbox::inschar(char *t,int len)
{
	if (readonly) return;

	if (mstart!=-1) delchar(0);
	if (!len) len=strlen(t);

	// calculate worst case size of (char*)t (rb)
	int worstlen=1;
	for (int i = 0; i < len; i++)
	{
		if (t[i]=='\t')
			worstlen+=4;	// worst case!
		else
			worstlen++;
	}

	// calculate new size (rb)
	int newsize=size;
	while (used+worstlen>=newsize)
		newsize+=MEMBLOCKSIZE*(worstlen/MEMBLOCKSIZE+1);

	if (newsize>size)
	{
		text=(char*)realloc(text,newsize);
		if (!text) 
		{
			size=0;
			used=0;
			return;
		}
		size=newsize;      
	}
	if (text[used]!=0)
	{
		fprintf(stderr,"Xclasses internal warning!\n");
		fprintf(stderr,"textbox::inschar() got textarray (%d) which has wrong counter value (%d)\n",strlen(text),used);
		text[used]=0;
	}

	cache_line=0;
	cache_char=NULL;

	char *tx=text+used,*t2=t;
	int l=len,lf=0;
	for(int i=used;i>=curm;i--)
	{
		*(tx+l)=*tx; tx--;
	}
	tx=text+curm;
	for(int i=0;i<l;i++)
	{
		if (*t2)
		{
			*tx=*t2;
			used++;
			curm++;
			curx++;
			if (*t2=='\t')
			{
				*tx=' ';

				// new tab calculation (rb)
				//
				int d=3-(curx-1)%4;
				for (char *cc=text+used+l-i;cc>tx;cc--)
					*(cc+d)=*cc;
				while (d-->0)
				{
					tx++;
					*tx=' ';
					curm++;
					curx++;
					used++;
				}
			}
			else if (*t2=='\n')
			{
				curx=0;
				cury++;
				lf++;
				total++;
				vscroll.Items(total);
			}
			while (maxlen && curx>maxlen)
			{
				int x=curx;
				char *t3=tx;
				while (x>0)
				{
					if (*t3==' ' && x<=maxlen)
					{
						*t3='\n';
						curx=curx-x;
						cury++;
						lf=2;
						hscroll.Position(0);
						break;
					}
					x--; t3--;
				}
				if (!x) break;     // line contains no spaces(!) -> do nothing
			}
			tx++;
		}
		else
		{
			l--; i--;
		}
		t2++;
	}
	if (lf>0 && t[0]!='\n')
	{
		setvars();
//		checkwidth();
		textaus(TA_EXPOSE);
	}
	else
	{
		if (t[0]=='\n')
		{
			checkwidth();
			textaus(TA_EXPOSE);
		}
		else
		{
			t2=findline(cury);
			l=linelength(t2);
			if (l>largest)
			{
				largest=l;
				hscroll.Items(l);
			}
			textaus(TA_CURSOR);
		}
	}
	checkVisible();
//	cursor(1);
	mcurx=curx;
	mcury=cury;
	mcurm=curm;
	if (text[used]!=0)
	{
		fprintf(stderr,"Xclasses internal warning!\n");
		fprintf(stderr,"textbox::inschar() used textarray (%d) which has wrong counter value (%d)\n",strlen(text),used);
		text[used]=0;
	}
}
void ptextbox::checkVisible(void)
{
	int s=0;
	if (hscroll.Position()!=left)
	{
		s=1;
	}
	if (curx<left)
	{
		hscroll.Position(curx);
		s=1;
	}
	if (curx>=left+cols)
	{
		hscroll.Position(curx-cols+1);
		s=1;
	}

	if (cury<top)
	{
		vscroll.Position(cury);
		s=1;
	}
	if (cury>=top+lines)
	{
		vscroll.Position(cury-lines+1);
		s=1;
	}
	if (s || vscroll.Position()!=top)
	{
		if (innen.gg_win())
			textaus(TA_SCROLL);
	}
}
void ptextbox::delchar(int w)
{
	int p=curm-w;
	if (readonly) return;

	if (mstart!=-1)
	{
		char *tx1=text+min(mstart,mend),*tx2=text+max(mstart,mend);
		cache_line=0;
		cache_char=NULL;

		for(int i=max(mstart,mend);i<=used;i++)
		{
			*tx1=*tx2; tx1++; tx2++;
		}
		used-=(max(mstart,mend)-min(mstart,mend));
		setvars();
		setM2XY(min(mstart,mend));
		mstart=-1;
		mend=-1;
		textaus(TA_EXPOSE);
		checkVisible();
//		cursor(1);
		checkMem();
		edit_menu.LockItem(1);
		edit_menu.LockItem(2);
		return;
	}

	if (p<0 || p>=used)
	{
		XBell(o->display(),-75);
		return;
	}
	cache_line=0;
	cache_char=NULL;

	char *tx=text+p,deleted=text[p];
	int l=0;
	if (w==1 && deleted=='\n') l=linelength(findline(cury-1));

	for(int i=p;i<used;i++)
	{
		*tx=*(tx+1); tx++;
	}
	used-=1;

	if (w==1)
	{
		curm--;
		curx--;
	}

	if (deleted=='\n')
	{
		total--;
		vscroll.Items(total);
		if (w==1)
		{
			cury--;
			curx=l;
		}
		tx=findline(cury);
		l=linelength(tx);
		if (l>largest)
		{
			largest=l;
			hscroll.Items(l);
		}
		textaus(TA_EXPOSE);
	}
	else
		textaus(TA_CURSOR);

	checkVisible();
//	cursor(1);
	checkMem();

	mcurx=curx;
	mcury=cury;
	mcurm=curm;
}
void ptextbox::setXY2M(int x,int y)
{
	char *r=text;
	int cx=0,cy=0;

	if (r)
	{
		while (*r && r<(text+used))
		{
			if (cx==x && cy==y)
			{
				curx=cx;
				cury=cy;
				curm=r-text;

				mcurx=curx;
				mcury=cury;
				mcurm=curm;
				return;
			}
			if (*r=='\n')
			{
				if (y==cy)
				{
					curx=cx;
					cury=cy;
					curm=r-text;

					mcurx=curx;
					mcury=cury;
					mcurm=curm;
					return;
				}
				cy++;
				cx=0;
			}
			else
			{
				cx++;
			}
			r++;
		}
		if (y>=cy)
		{
			curx=cx;
			cury=cy;
			curm=used;
		}
	}
	mcurx=curx;
	mcury=cury;
	mcurm=curm;
}
void ptextbox::setM2XY(int m)
{
	char *r=text;
	int cx=0,cy=0,mm=0;

	if (r)
	{
		while (*r && r<(text+used))
		{
			if (mm==m)
			{
				curx=cx;
				cury=cy;
				curm=mm;

				mcurx=curx;
				mcury=cury;
				mcurm=curm;
				return;
			}
			if (*r=='\n')
			{
				cy++;
				cx=0;
			}
			else
			{
				cx++;
			}
			r++; mm++;
		}
		if (m>=mm)
		{
			curx=cx;
			cury=cy;
			curm=mm;
		}
	}
	mcurx=curx;
	mcury=cury;
	mcurm=curm;
}
void ptextbox::cutpaste(XEvent *event)
{ // event no longer required
	if (mstart!=-1)
	{
		if (mstart!=mend)
		{
			int s,e;
			s=min(mstart,mend);
			e=max(mstart,mend);

			o->ClipTo(text+s,e-s);

//			mstart=s;
//			mend=e;
			edit_menu.UnlockItem(1);
			edit_menu.UnlockItem(2);
		}
		else
		{
			mstart=-1;
			mend=-1;
			edit_menu.LockItem(1);
			edit_menu.LockItem(2);
		}
	}
}
void ptextbox::remmark(XEvent *event)
{
	if (event->xkey.state & ShiftMask)
	{
		if (abs(curx-mcurx)<=1 || abs(cury-mcury)<=1)
		{ // vermutlich Ok.
			if (mstart==-1)
			{
				mstart=mcurm;
				mend=curm;
			}
			else
			{
				mend=curm;
			}
			cutpaste(event);
			if (mcury!=cury)
			{
				for(int i=min(mcury,cury);i<=max(mcury,cury);i++)
				{
					zeileaus(i-top);
				}
			}
			else
				textaus(TA_CURSOR);
		}
	}
	else
	{
		if (mstart!=-1)
		{
			mstart=-1;
			mend=-1;
			textaus(TA_EXPOSE);
		}
	}
	mcurx=curx;
	mcury=cury;
	mcurm=curm;
}

int textbox::GClipHandler(int why,char *data,int size)
{
	int r=0;
	switch (why)
	{
		case CH_DESELECT:
			p->edit_menu.LockItem(1);
			p->edit_menu.LockItem(2);
			p->mstart=-1;
			p->mend=-1;
			p->textaus(TA_EXPOSE);
			break;
		case CH_REQUEST:
			r=GCH_MESSAGE;
			p->uncursor();
			p->inschar(data,size);
			p->checkVisible();
			p->cursor(1);
			break;
	}
	return r;
}
void textbox::GExpose(XEvent *event)
{
	if (wasfor && wasfor!=&p->innen)
	{
		wasfor->GExpose(event);
	}
	else
	{
		if (wasfor)
		{
			if (event && (event->type==NoExpose || event->type==GraphicsExpose))
			{
				p->textaus(TA_EVENT,event);
			}
			else
			{
				if (!event && !locked)
				{
					XClearWindow(display(),p->innen.gg_win());
				}
				p->textaus(TA_EXPOSE);
				p->cursor(1);
			}
		}
		else
		{
			XSetForeground(display(),gc,p->bgcol);
			XFillRectangle(display(),win,gc,0,0,width-p->vscrollersize,height-p->hscrollersize);
			borderDown(this,0,0,width-p->vscrollersize,height-p->hscrollersize);
			if (active & GA_KEY) bordermark(this,0,0,width-p->vscrollersize,height-p->hscrollersize); else borderunmark(this,0,0,width-p->vscrollersize,height-p->hscrollersize);
		}
	}
}
static int moved=0,gmx=0,gmy=0;
int textbox::GActivate(XEvent *event,int why)
{
	if (p->edit_menu.PopupOpen())
	{
		return p->edit_menu.GActivate(event,why) & GAR_ACTIVE;
	}
	int r=0;

	if (wasfor && why!=GA_KEY && wasfor!=&p->innen)
	{
		r=wasfor->GActivate(event,why) & GAR_ACTIVE;
		
		if (p->vscroll.Position()!=p->top)
		{
			p->textaus(TA_SCROLL);
			if (p->readonly)
			{
				p->cury=p->top;
				p->setXY2M(p->curx,p->cury);
			}
		}
		if (p->hscroll.Position()!=p->left)
		{
			p->textaus(TA_SCROLL);
		}
		return r;
	}
	if (why==GA_KEY)
	{
		int c;
		char taste[50];
		KeySym key;
		int status;
		c=LookupString(event,taste,sizeof(taste),key,status);

		p->uncursor();

		switch (key)
		{
			case XK_Return:
			case XK_KP_Enter:
				p->inschar("\n");
				break;
			case XK_Delete:
			case XK_KP_Delete:
				p->delchar(0);
				break;
			case XK_BackSpace:
				p->delchar(1);
				break;
			case XK_Left:
			case XK_KP_Left:
				p->curm--;
				if (p->curm<0)
				{
					XBell(display(),-75);
					p->curm=0;
				}
				else
				{
					p->curx--;
					if (p->curx<0)
					{
						char *t;
						p->cury--; if (p->cury<0) p->cury=0;
						t=p->findline(p->cury);
						p->curx=p->linelength(t);
					}
					p->remmark(event);
					p->checkVisible();
				}
				break;
			case XK_Right:
			case XK_KP_Right:
				p->curm++;
				if (p->curm>p->used)
				{
					XBell(display(),-75);
					p->curm=p->used;
				}
				else
				{
					char *t;
					t=p->findline(p->cury);
					p->curx++;
					if (p->curx>p->linelength(t))
					{
						p->curx=0;
						p->cury++;
						if (p->cury>=p->total) p->cury=p->total-1;
					}
					p->remmark(event);
					p->checkVisible();
				}
				break;
			case XK_Up:
			case XK_KP_Up:
				p->cury--;
				if (p->cury<0)
				{
					XBell(display(),-75);
					p->cury=0;
				}
				else
				{
					char *t=p->findline(p->cury);
					int l=p->linelength(t);

					p->curm-=p->curx;
					if (l<p->curx) p->curx=l;
					p->curm-=(l-p->curx+1);
					p->remmark(event);
					p->checkVisible();
				}
				break;
			case XK_Down:
			case XK_KP_Down:
				p->cury++;
				if (p->cury>=p->total)
				{
					XBell(display(),-75);
					p->cury=p->total-1;
				}
				else
				{
					char *t=p->findline(p->cury-1);
					int l=p->linelength(t)+1;

					p->curm+=l-p->curx;

					t=p->findline(p->cury);
					l=p->linelength(t);
					if (l<p->curx) p->curx=l;
					p->curm+=p->curx;
					p->remmark(event);
					p->checkVisible();
				}
				break;
			case XK_Home:
			case XK_KP_Home:
				p->curm-=p->curx;
				p->curx=0;
				p->remmark(event);
				p->checkVisible();
				break;
			case XK_End:
			case XK_KP_End:
				{
					char *t=p->findline(p->cury);
					int l=p->linelength(t);
					p->curm+=l-p->curx;
					p->curx=l;
					p->remmark(event);
					p->checkVisible();
				}
				break;
			case XK_Page_Up:
			case XK_KP_Page_Up:
				{
					int mcurx=p->mcurx,mcury=p->mcury,mcurm=p->mcurm;
					p->cury-=p->lines;
					if (p->cury<0) p->cury=0;
					p->setXY2M(p->curx,p->cury);
					p->mcurx=mcurx;
					p->mcury=mcury;
					p->mcurm=mcurm;
					p->remmark(event);
					p->checkVisible();
				}
				break;
			case XK_Page_Down:
			case XK_KP_Page_Down:
				{
					int mcurx=p->mcurx,mcury=p->mcury,mcurm=p->mcurm;
					p->cury+=p->lines;
					if (p->cury>=p->total) p->cury=p->total-1;
					p->setXY2M(p->curx,p->cury);
					p->mcurx=mcurx;
					p->mcury=mcury;
					p->mcurm=mcurm;
					p->remmark(event);
					p->checkVisible();
				}
				break;
			case XK_Tab:
				if (event->xkey.state & Mod1Mask || p->readonly)
				{ // KeyHandler
					event->xkey.state&=~Mod1Mask;
					DefaultKeyHandler(event);					
				}
				else
				{ // tab
					p->inschar("\t");
				}
				break;
			case Sun_XK_Cut: // cut
			case 0xffd1:
				if (p->mstart!=-1) p->delchar(0);
				break;
			case Sun_XK_Copy: // copy
			case 0xffcd:
				break;
			case Sun_XK_Paste: // paste
			case 0xffcf:
				ClipRequest();
				break;

			default:
				{
					int done=0;
					if (!p->readonly && c==1 && taste[0] && !(event->xkey.state & ControlMask)) // && !(event->xkey.state & Mod1Mask))
					{
						taste[1]=0;
						p->inschar(taste);
						done=1;
					}
					if (!p->readonly && (event->xkey.state & ControlMask) && !(event->xkey.state & Mod1Mask))
					{
						switch (key)
						{
							case XK_x: // cut
								if (p->mstart!=-1) p->delchar(0);
								done=1;
								break;
							case XK_c: // copy
								done=1;
								break;
							case XK_v: // paste
								ClipRequest();
								done=1;
								break;
							case XK_a:
								p->curm-=p->curx;
								p->curx=0;
								p->remmark(event);
								p->checkVisible();
								break;
							case XK_e:
								{
									char *t=p->findline(p->cury);
									int l=p->linelength(t);
									p->curm+=l-p->curx;
									p->curx=l;
									p->remmark(event);
									p->checkVisible();
								}
								break;
						}
					}
					if (!p->readonly && (event->xkey.state & ShiftMask))
					{
						switch (key)
						{
							case XK_Insert: // paste
								ClipRequest();
								done=1;
								break;
						}
					}

					if (!done)
					{
						if (c==1) event->xkey.state&=~Mod1Mask;
						DefaultKeyHandler(event);
					}
				}
				break;
		}
		p->cursor(1);
		return r; // ==0
	}

	if (why==GA_BUTTONPRESS)
	{
		if (event->xbutton.button==3)
		{
			int x,y;
			ActivateKey();
//			p->innen.ActivateKey();
			GetPositionOnRoot(display(),win,x,y);
			x+=event->xbutton.x;
			y+=event->xbutton.y;
			p->edit_menu.Open(x,y);
			return GAR_ACTIVE;
		}

		if (event->xbutton.button==2)
		{
			ActivateKey();
//			p->innen.ActivateKey();
			ClipRequest();
			int gy=event->xbutton.y/(p->charh)+p->top;
			int gx=event->xbutton.x/(p->charw)+p->left;
			p->uncursor();
			p->setXY2M(gx,gy);
			if (p->mstart!=-1)
			{
				p->mstart=-1;
				p->mend=-1;
				p->textaus(TA_EXPOSE);
			}
			p->cursor(1);
			return 0;
		}

		if (!(active&GA_KEY))
		{
			moved=0;
			ActivateKey();
//			p->innen.ActivateKey();
			p->justactive=1;
//			return 0;
		}

		int gy=event->xbutton.y/(p->charh)+p->top;
		int gx=event->xbutton.x/(p->charw)+p->left;
		p->uncursor();
		p->setXY2M(gx,gy);

		r=1;
		moved=1;
		p->m_up=0;
		p->m_down=0;
		p->m_left=0;
		p->m_right=0;
		if (active & GA_DCLICK && !p->justactive)
		{
			int p1,p2;
			
			p1=p->curm;
			p2=p->curm;

			if (p->mstart<=p->curm && p->curm<=p->mend)
			{
				while (p1>=0 && *(p->text+p1)!='\n') p1--;
				while (p2<p->used && *(p->text+p2)!='\n') p2++;
			}
			else
			{
				while (p1>=0 && isalnum(*(p->text+p1))) p1--;
				while (p2<p->used && isalnum(*(p->text+p2))) p2++;
			}
			p->mstart=p1+1;
			p->mend=p2;
			p->curm=p2;
			p->setM2XY(p2);
			p->textaus(TA_EXPOSE);
			p->cursor(1);
		}
		else
		{
			if (p->mstart!=-1)
			{
				p->mstart=-1;
				p->mend=-1;
				p->textaus(TA_EXPOSE);
			}
			p->cursor(1);
			p->mstart=p->curm;
			p->mend=p->mstart;
		}
		p->mcurx=p->curx;
		p->mcury=p->cury;
		p->mcurm=p->curm;
		p->justactive=0;
		return GAR_ACTIVE;
	}
	if (why==GA_TICK)
	{
		r=GAR_ACTIVE;
		p->cursor();

		if (p->m_up)
		{
//			p->top--;
//			if (p->top<0) p->top=0;
			p->vscroll.Position(p->top-1);
			p->textaus(TA_SCROLL);
		}
		if (p->m_down)
		{
//			p->top++;
//			if (p->top>p->total-p->lines) p->top=p->total-p->lines;
			p->vscroll.Position(p->top+1);
			p->textaus(TA_SCROLL);
		}
		if (p->m_left)
		{
//			p->left--;
//			if (p->left<0) p->left=0;
			p->hscroll.Position(p->left-1);
			p->textaus(TA_SCROLL);
		}
		if (p->m_right)
		{
//			p->left++;
//			if (p->left>p->total-p->lines) p->left=p->total-p->lines;
			p->hscroll.Position(p->left+1);
			p->textaus(TA_SCROLL);
		}
		if (p->m_left || p->m_right || p->m_up || p->m_down)
		{
			int bx,by,l,cy;

			by=gmy/p->charh+p->top;
			if (by>p->total) by=p->total;
			if (by<0) by=0;
			bx=gmx/p->charw+p->left;
			l=p->linelength(p->findline(by));
			if (bx>l) bx=l;
			if (bx<0) bx=0;
			cy=p->cury;
			p->setXY2M(bx,by);
			if (p->mend!=p->curm)
			{
				p->mend=p->curm;
				if (cy!=p->cury)
				{
					for(int i=min(cy,p->cury);i<=max(cy,p->cury);i++)
					{
						p->zeileaus(i-p->top);
					}
				}
				else
					p->textaus(TA_CURSOR);
			}
		}

		return r;
	}

	return 0;
}
int textbox::GSelected(XEvent *event,int why,int wasabove)
{
	if (p->edit_menu.PopupOpen())
	{
		if (p->edit_menu.GSelected(event,why,wasabove) & GSR_SELECTION)
		{
			switch (p->edit_menu.PID())
			{
				case 1: // Cut
					p->delchar(0);
					break;
				case 2: // Copy
					break;
				case 3: // Paste
					ClipRequest();
					break;
				case 4: // Select all
					p->mstart=0;
					p->mend=p->used;
					p->textaus(TA_EXPOSE);
					p->cutpaste(event);
					break;
				case 5: // Clear all
					ReplaceText("");
					break;
			}
		}
		return GSR_MESSAGE;
	}
	if (wasfor && why!=GA_KEY && wasfor!=&p->innen)
	{
		wasfor->GSelected(event,why,wasabove);
		
		if (p->vscroll.Position()!=p->top)
		{
			p->textaus(TA_SCROLL);
			if (p->readonly)
			{
				p->cury=p->top;
				p->setXY2M(p->curx,p->cury);
			}
		}
		if (p->hscroll.Position()!=p->left)
		{
			p->textaus(TA_SCROLL);
		}
		ActivateKey();
//		p->innen.ActivateKey();
		return 0;
	}

	if (why==GA_BUTTONPRESS)
	{
		p->m_up=0;
		p->m_down=0;
		p->m_left=0;
		p->m_right=0;
		moved=0;
		if (p->mstart!=-1 && p->mend!=-1 && p->mstart!=p->mend)
		{
			p->cutpaste(event);
		}
		else
		{
			p->mstart=-1;
			p->mend=-1;
			p->edit_menu.LockItem(1);
			p->edit_menu.LockItem(2);
		}
	}
	return 0;
}
void textbox::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (p->edit_menu.PopupOpen())
	{
		p->edit_menu.GActionPress(mx,my,rx,ry,state,button,reason);
		return;
	}
	if (wasfor && reason!=GB_KEY && wasfor!=&p->innen)
	{
		wasfor->GActionPress(mx,my,rx,ry,state,button,reason);
	}
	else if (reason==GB_KEY)
	{
		bordermark(this,0,0,width-p->vscrollersize,height-p->hscrollersize);
		p->cursor(1);
	}
}
void textbox::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (p->edit_menu.PopupOpen())
	{
		p->edit_menu.GActionRelease(mx,my,rx,ry,state,button,reason);
		return;
	}
	if (wasfor && reason!=GB_KEY && wasfor!=&p->innen)
	{
		wasfor->GActionRelease(mx,my,rx,ry,state,button,reason);
	}
	else if (reason==GB_KEY)
	{
		borderunmark(this,0,0,width-p->vscrollersize,height-p->hscrollersize);
		p->cursor(1);
	}
}

int textbox::GMouseMove(int mx,int my,int rx,int ry,unsigned int state,int button)
{
	if (p->edit_menu.PopupOpen())
	{
		return p->edit_menu.GMouseMove(mx,my,rx,ry,state,button) & GSR_SELECTION;
	}
	if (wasfor && wasfor!=&p->innen)
	{
		wasfor->GMouseMove(mx,my,rx,ry,state,button);
		if (p->vscroll.Position()!=p->top)
		{
			p->textaus(TA_SCROLL);
			if (p->readonly)
			{
				p->cury=p->top;
				p->setXY2M(p->curx,p->cury);
			}
		}
		if (p->hscroll.Position()!=p->left)
		{
			p->textaus(TA_SCROLL);
		}
	}
	else if (moved)
	{
		gmy=my;
		gmx=mx;
		p->m_up=0;
		p->m_down=0;
		p->m_left=0;
		p->m_right=0;
		p->uncursor();
		if (my<0)
		{
			if (p->top>0) p->m_up=1;
//			GActivate(NULL,GA_TICK);
		}
		else if (my>p->lines*p->charh)
		{
			if (p->top+p->lines<p->total) p->m_down=1;
//			GActivate(NULL,GA_TICK);
		}
		if (mx<0)
		{
			if (p->left>0) p->m_left=1;
//			GActivate(NULL,GA_TICK);
		}
		else if (mx>p->cols*p->charw)
		{
			p->m_right=1;
//			GActivate(NULL,GA_TICK);
		}
		if (!(p->m_down && p->m_up && p->m_left && p->m_right))
		{
			int bx,by,l,cy;

			by=my/p->charh+p->top;
			if (by>p->total) by=p->total;
			bx=mx/p->charw+p->left;
			l=p->linelength(p->findline(by));
			if (bx>l) bx=l;
			cy=p->cury;
			p->setXY2M(bx,by);
			if (p->mend!=p->curm)
			{
				p->mend=p->curm;
				if (cy!=p->cury)
				{
					for(int i=min(cy,p->cury);i<=max(cy,p->cury);i++)
					{
						p->zeileaus(i-p->top);
					}
				}
				else
					p->textaus(TA_CURSOR);
			}
		}
	}
	return 0;
}
void textbox::GSetLimits(void)
{
	p->vscroll.GSetLimits();
	p->hscroll.GSetLimits();

	minwidth=p->vscrollersize+max(RealSize()*2+2+p->mincols*textlength(this,"MO")/2,p->vscroll.gg_minheight());
	minheight=p->hscrollersize+max((gg_font()->ascent+gg_font()->descent+2)*p->minrows+RealSize()*2+2,p->vscroll.gg_minheight());
	setlimit=1;
}

static int blink=0;
void ptextbox::cursor(int rq)
{
	if (rq || blink++==0)
	{
		static char dot[]={1,2};
		int x=(curx-left)*charw,y=(cury-top)*charh;
		if (x<0 || y<0 || x>cols*charw || y>(lines-1)*charh) return;

		if (!(o->active & GA_KEY))
		{
			uncursor();
			XSetDashes(o->display(),innen.gg_gc(),0,dot,2);
			XSetLineAttributes(o->display(),innen.gg_gc(),0,LineOnOffDash,CapButt,JoinRound);
		}

		XSetForeground(o->display(),innen.gg_gc(),o->col_text());
		XDrawLine(o->display(),innen.gg_win(),innen.gg_gc(),x,y+1,x,y+charh);

		if (!(o->active & GA_KEY))
			XSetLineAttributes(o->display(),innen.gg_gc(),0,LineSolid,CapButt,JoinRound);
		blink=1;
	}
	if (blink==6 && !readonly)
	{
		uncursor();
	}
	if (blink>=10) blink=0;
}
void ptextbox::uncursor(void)
{
	textaus(TA_CURSOR);
}

void ptextbox::textaus(int r,int v)
{
	textaus(r,(XEvent*)v);
}
void ptextbox::textaus(int r,XEvent *event)
{ // r = TA_EXPOSE, r= TA_SCROLL, r= TA_CURSOR, r= TA_EVENT
	if (!text && r!=TA_EXPOSE) return;
	switch (r)
	{
		case TA_EVENT:
			if (event->type==GraphicsExpose)
			{
				// Zeilen zwischen event->xgraphicsexpose.y und 
				// event->xgraphicsexpose.height ausgeben

				for(int i=0;i<lines;i++)
				{
					if (i+1>=event->xgraphicsexpose.y/charh&&
					    i<=(event->xgraphicsexpose.y+event->xgraphicsexpose.height)/charh)
					{
						zeileaus(i);
					}
				}
				cursor(1);
			}
			break;
		case TA_EXPOSE:
			{
				for(int i=0;i<lines;i++)
				{
					zeileaus(i);
				}
			}
			break;
		case TA_EXPOSEFROM:
			{
				for(int i=((int)event)-top;i<lines;i++)
				{
					zeileaus(i);
				}
			}
			break;

		case TA_SCROLL:
			{
				int oldtop=top;
				int oldleft=left;
				int x,y;

				top=vscroll.Position();
				left=hscroll.Position();

				y=(oldtop-top)*charh;
				x=(oldleft-left)*charw;
				if (innen.gg_win())
				{
					XCopyArea(o->display(),innen.gg_win(),innen.gg_win(),innen.gg_gc(),-x,-y+1,cols*charw+1,lines*charh,0,1);
					// oldtop > top:
					//               user nach oben
					//               area nach unten
					//               y>0
					// oldtop < top:
					//               user nach unten
					//               area nach oben
					//               y<0
					if (x)
					{ // bei x!=0 alles neu...
						for(int i=0;i<lines;i++)
						{
							zeileaus(i);
						}
					}
					else
					{
						if (y<0)
						{ // area nach oben, unten füllen
							for(int i=0;i<lines;i++)
							{
								if (i>=lines-(top-oldtop)) zeileaus(i);
							}
						}
						else if (y>0)
						{ // area nach unten, oben füllen
							for(int i=0;i<lines;i++)
							{
									if (i<oldtop-top) zeileaus(i);
							}
						}
					}
				}
				checkwidth();
			}
			break;
		case TA_CURSOR:
			{
				zeileaus(cury-top);
			}
			break;
	}
	if (o->locked && innen.gg_win())
	{
		glock.draw_locked(&innen,0,0,innen.gg_width(),innen.gg_height());
	}
}
int ptextbox::lineoffset(int l)
{
	if (findline(l))
		return cache_curm;
	else
		return 0;
}
char *ptextbox::findline(int l)
{
	char *r=NULL;
	int z,cm;

	if (cache_char && l==cache_line)
	{
		r=cache_char;
		cm=cache_curm;
	}
	else if (l==0)
	{ // wegen schneller
		r=text;
		cm=0;
	}
	else if (cache_char && l>cache_line)
	{ // eine folgende Zeile
		z=cache_line;
		r=cache_char;
		cm=cache_curm;
		while (*r && r<(text+used) && l>z)
		{
			if (*r=='\n') z++;
			r++; cm++;
		}
		if (l!=z) r=NULL;
	}
#ifdef KLAPPT_DAS 
	else if (cache_char && l<cache_line)
	{ // eine vorherige Zeile
		z=cache_line;  // Problem: wenn 1. Zeile leer (\n) zeilenweise scroller falsch
		r=cache_char-1;
		cm=cache_curm-1;
		while (*r && r>text && l<=z)
		{
			if (*r=='\n')
			{
				if (z==l)
				{
					r++; cm++;
					break;
				}
				z--;
			}
			r--; cm--;
		}
		if (l!=z) r=NULL;
	}
#endif
	else
	{ // ab Start suchen
		z=0;
		r=text;
		cm=0;
		while (*r && r<(text+used) && l>z)
		{
			if (*r=='\n') z++;
			r++; cm++;
		}
		if (l!=z) r=NULL;
	}

	if (r) // && *r)
	{
		cache_line=l;
		cache_char=r;
		cache_curm=cm;
	}
	return r;
}
int ptextbox::linelength(char *t)
{
	int l=0;
	while (t && *t && *t!='\n') 
	{
		l++;
		t++;
	}
	return l;
}
int ptextbox::linelength2(char *t)
{
	int l=linelength(t);
	if (l>cols) l=cols;
	return l;
}
void ptextbox::zeileclear(int z,int x)
{
	if (!innen.gg_win() || z<0 || z>=lines) return;
	XFillRectangle(o->display(),innen.gg_win(),innen.gg_gc(),x,z*charh+1,charw*cols-x+1,charh);
}
void ptextbox::zeileaus(int l)
{
	if (!innen.gg_win()) return;
	if (l<0 || l>=lines) return;

	int y,p=0,ms=-1,me=-1,cm,ll,tl;
	char *t;

	t=findline(l+top);
	if (!t || t+left>text+used)
	{
		XSetForeground(o->display(),innen.gg_gc(),bgcol);
		zeileclear(l);
		return;
	}

	if (mstart>=0 && mend>=0 && mstart!=mend)
	{
		ms=min(mstart,mend);
		me=max(mstart,mend);
	}

	cm=lineoffset(l+top)+left;
	tl=linelength(t); if (cm-(t-text)>tl) cm=tl+(t-text);
	if (tl-left<=0)
	{
		if (me>cm && ms<=cm)
			XSetForeground(o->display(),innen.gg_gc(),o->col_markbackground());
		else
			XSetForeground(o->display(),innen.gg_gc(),bgcol);

		zeileclear(l);
		return;
	}
	t+=left;
	y=charh*l+o->gg_font()->ascent+1;

// 4 Markierungsfälle:
//  1. ganze Zeile
//  2. Anfang der Zeile
//  3. Ende der Zeile
//  4. Innerhalb der Zeile

	ll=linelength2(t);
	if (me<cm || ms>cm+ll)
	{ // nicht sichtbar
		write(p,y,t,linelength2(t),o->col_text(),bgcol);
		XSetForeground(o->display(),innen.gg_gc(),bgcol);
	}
	else if (ms<=cm && me>=cm+ll)
	{ // Fall 1
		write(p,y,t,linelength2(t),o->col_text(),o->col_markbackground());
		if (tl>0 && me>cm+ll)
			XSetForeground(o->display(),innen.gg_gc(),o->col_markbackground());
		else
			XSetForeground(o->display(),innen.gg_gc(),bgcol);
	}
	else if (ms>cm && me<=cm+ll)
	{ // Fall 4
		write(p,y,t,linelength2(t),o->col_text(),bgcol);
		write(p+(ms-cm)*charw,y,t+(ms-cm),me-ms,o->col_text(),o->col_markbackground());
		XSetForeground(o->display(),innen.gg_gc(),bgcol);
	}
	else if (me<cm+ll)
	{ // Fall 2
		write(p,y,t,me-cm,o->col_text(),o->col_markbackground());
		write(p+(me-cm)*charw,y,t+(me-cm),linelength2(t)-(me-cm),o->col_text(),bgcol);
		XSetForeground(o->display(),innen.gg_gc(),bgcol);
	}
	else if (ms>cm)
	{ // Fall 3
		write(p,y,t,(ms-cm),o->col_text(),bgcol);
		write(p+(ms-cm)*charw,y,t+(ms-cm),linelength2(t)-(ms-cm),o->col_text(),o->col_markbackground());
		XSetForeground(o->display(),innen.gg_gc(),o->col_markbackground());
	}
	zeileclear(l,linelength2(t)*charw);
}
void ptextbox::write(int x,int y,char *t,int l,int tcol,int hcol)
{
	XSetForeground(o->display(),innen.gg_gc(),tcol);
	XSetBackground(o->display(),innen.gg_gc(),hcol);
	XDrawImageString(o->display(),innen.gg_win(),innen.gg_gc(),x,y,t,l);
}

#endif
