#ifndef CODE_OUTPUT_CXX
#define CODE_OUTPUT_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: output.cxx,v 1.6 2001/05/13 15:20:33 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.
 *-----------------------------------------------------------------------
 */

#include <time.h>
#include <string.h>
#include "types.h"
#include <Xclasses/gadget.h>
#include <Xclasses/Xcolors.h>
#include <Xclasses/Ddraw.h>
#include <Xclasses/output.h>
#include "include/output_p.h"

output::output(): gadget(), Ddraw(this)
{
	while (!(p=new poutput))
	{
		outOfMemory("output");
	}
	p->o=this;
	Name("output"); p->tgg.Name("outputfield");
   	p->tgg.ParentClass(this);
}
output::~output()
{
	if (win) Free();
	if (p) delete p;
}
poutput::poutput() : tgg(), a1(), a2(), ar1(ARROW_LEFT), ar2(ARROW_RIGHT)
{
	text=NULL;
	textwidth=0;
	data=NULL;
	mpos=0;
	mend=-1;
	prop[0]=0;
	tolong=0;
	tolongstart=0;
	tolongcount=0;
	a1.Name("leftbutton");
	a1.Object(ar1);
	a1.SendTicks();
	a2.Name("rightbutton");
	a2.Object(ar2);
	a2.SendTicks();
}
poutput::~poutput()
{
	if (text) free(text);
	text=NULL;
	if (data) free(data);
	data=NULL;
}
void output::Text(char *t)
{
	if (p->text)
		free(p->text);
	p->text=NULL;
    p->text=strdup(t);
}
void output::Output(char *t)
{
	if (p->data) free(p->data);
	p->data=NULL;
	if (t) p->data=strdup(t);
	p->mend=-1;
	p->tolongstart=0;
	if (win) p->taus(0);
}
void output::Free(void)
{
	if (XGetSelectionOwner(display(),XA_PRIMARY)==win)
	{
		XSetSelectionOwner(display(),XA_PRIMARY,None,time(NULL));
	}
	p->tgg.Free();
	p->a1.Free();
	p->a2.Free();
	gadget::Free();
}
int output::Create(void)
{ 
	Background(col_background());
	BorderSize(1);
	
    if (gadget::Create())
    {
		if (!setlimit)
		{
			p->textwidth=textlength(this,p->text);
			if (p->text) before_x=p->textwidth+5+2;
		}
    	Mode(GM_ONEEXPOSE|GM_ALLEVENTS);
    	if (p->text)
    		p->tgg.Dimensions(win,before_x+2+RealSize(),1+RealSize(),width-before_x-RealSize()-5-RealSize(),height-RealSize()-RealSize()-1);
    	else
	    	p->tgg.Dimensions(win,2+RealSize(),1+RealSize(),width-before_x-3-RealSize(),height-RealSize()-RealSize()-1);

		int dummy=p->tgg.col_background(); // warum geht das nicht direkt?!
    	p->tgg.Background(dummy);
    	p->tgg.CopyFont(this);
    	if (p->tgg.Create())
    	{
        	p->tgg.Mode(GM_BUTTON1|GM_MOUSEMOVE|GM_ONEEXPOSE|GM_MINIMALMOVE|GM_ACTIVETICK);
        	p->tgg.SendEventTo(this);
        	p->prop[0]=XInternAtom(display(),"Xclasses_output_property",False);
//			SetEvents(PropertyChangeMask);

			p->a1.ParentClass(this); p->ar1.ParentClass(p->a1);
			p->a2.ParentClass(this); p->ar2.ParentClass(p->a2);
			p->a1.Dimensions(win,width-height/2-RealSize(),RealSize(),height/2,(height-RealSize())/2);
			p->a2.Dimensions(win,width-height/2-RealSize(),RealSize()+(height-RealSize())/2,height/2,(height-RealSize())/2);
			p->a1.SendEventTo(this);
			p->a2.SendEventTo(this);
			p->a1.specialflags|=SPF_NOKEYBORDER;
			p->a2.specialflags|=SPF_NOKEYBORDER;
			p->a1.Create();
			p->a2.Create();
			XUnmapWindow(display(),p->a1.gg_win());
			XUnmapWindow(display(),p->a2.gg_win());

	        return 1;
		}
		else
		{
			gadget::Free();
		}
    } 
    return 0;
}
void output::Resize(int w,int h)
{
	gadget::Resize(w,h);
	if (p->text)
		p->tgg.Resize(w-before_x-5-RealSize()-RealSize(),h-RealSize()-RealSize()-1);
	else
		p->tgg.Resize(w-5-RealSize()-RealSize(),h-RealSize()-RealSize()-1);

	p->a1.Move(width-height/2-RealSize(),RealSize());
	p->a2.Move(width-height/2-RealSize(),RealSize()+(height-RealSize())/2);
}
void output::Lock(void)
{
	gadget::Lock();
	p->tgg.Lock();
}
void output::Unlock(void)
{
	gadget::Unlock();
	p->tgg.Unlock();
}
void output::Block(int b)
{
	gadget::Block(b);
	p->tgg.Block(b);
}
void output::Unblock(int b)
{
	gadget::Unblock(b);
	p->tgg.Unblock(b);
}
void poutput::taus(int f)
{
// f: 0 - Clear, alles neu

	int dhint,fasc,fdec;
	XCharStruct cs;

	if (!f)
	{
		XClearWindow(o->display(),tgg._win());
	}
	if (!data) return;

	if (XTextWidth(tgg.gg_font(),data,strlen(data))>tgg._width())
		tolong=1;
	else
	{
		tolong=0;
		tolongstart=0;
	}

	XTextExtents(tgg.gg_font(),data+tolongstart,strlen(data+tolongstart),&dhint,&fasc,&fdec,&cs);
	XSetForeground(o->display(),tgg._gc(),tgg.col_text());
	XDrawString(o->display(),tgg._win(),tgg._gc(),0,fasc+(tgg._height()-fasc-fdec)/2,data+tolongstart,strlen(data+tolongstart));

	if (mend!=-1 && mpos!=mend)
	{
		int s,e;
		int ws=0; //,we=0;
		if (mpos>mend)
		{
			s=mend;
			e=mpos;
		}
		else
		{
			s=mpos;
			e=mend;
		}
		if (s-tolongstart<0)
		{
			s+=(tolongstart-s);
		}
		if (s<e)
		{
			if (s-tolongstart>0) ws=XTextWidth(tgg.gg_font(),data+tolongstart,s-tolongstart);
			XSetBackground(o->display(),tgg._gc(),tgg.col_text());
			XSetForeground(o->display(),tgg._gc(),tgg.col_background());
			XDrawImageString(o->display(),tgg._win(),tgg._gc(),ws,fasc+(tgg._height()-fasc-fdec)/2,&data[s],e-s);
			XSetBackground(o->display(),tgg._gc(),tgg.col_background());
		}
	}
	if (tolong)
	{
		XMapWindow(o->display(),a1.gg_win());
		XMapWindow(o->display(),a2.gg_win());
	}
	else
	{
		XUnmapWindow(o->display(),a1.gg_win());
		XUnmapWindow(o->display(),a2.gg_win());
	}
}
void poutput::cutpaste(XEvent *event)
{
	if (mend!=-1)
	{
		if (mpos!=mend)
		{
			int s,e;
			Window w;
			if (mend<mpos)
			{
				s=mend;
				e=mpos;
			}
			else
			{
				s=mpos;
				e=mend;
			}
			w=XGetSelectionOwner(o->display(),XA_PRIMARY);
			if (w!=None && w!=o->win)
			{
				if (FindGG(w,GT_ANY))
				{
					XEvent ev;
					ev.type						= SelectionClear;
					ev.xselectionclear.window	= w;
					ev.xselectionclear.selection= XA_PRIMARY;
					ev.xselection.time			= time(NULL);
					XSendEvent(o->display(),w,False,0,&ev);
				}
			}
			XSetSelectionOwner(o->display(),XA_PRIMARY,o->win,event->xbutton.time);

			mpos=s;
			mend=e;
		}
		else
		{
			mend=-1;
		}
	}
}
void output::GExpose(XEvent *event)
{
	if (wasfor)
	{
		if (wasfor==&p->tgg)
		{
			p->taus(0);
			if (locked)
			{
				borderlocked(&p->tgg,0,0,p->tgg._width(),p->tgg._height(),LOCK_NOBORDER);
//				borderlocked(this,before_x,0,width-before_x,height);
			}
		}
		else
			wasfor->GExpose(event);
	}
	else
	{
		XSetForeground(display(),gc,p->tgg.col_background());
		if (p->text)
		{
			XFillRectangle(display(),win,gc,before_x+RealSize(),RealSize(),width-before_x-RealSize(),height-RealSize()/*-3*/);
			borderDown(this,before_x,0,width-before_x,height);
			bordertext(this,0,0,before_x-TEXTDISTANCE,height,p->text,ADJUST_RIGHT|ADJUST_MIDDLE);
		}
		else
		{
			XFillRectangle(display(),win,gc,RealSize(),RealSize(),width-before_x-RealSize(),height-RealSize()/*-3*/);
			borderDown(this,0,0,width,height);
		}
	}
}
int output::GActivate(XEvent *event,int why)
{
	if (wasfor && wasfor!=&p->tgg)
	{
		int r=wasfor->GActivate(event,why);
		if (r && wasfor==&p->a1)
		{
			switch (why)
			{
				case GA_BUTTONPRESS:
					p->tolongcount=0;
					p->tolongstart-=1;
					if (p->tolongstart<0) p->tolongstart=0;
					p->taus(0);
					break;
				case GA_TICK:
					p->tolongstart-=p->tolongcount++;
					if (p->tolongstart<0) p->tolongstart=0;
					p->taus(0);
					break;
			}
		}
		if (r && wasfor==&p->a2)
		{
			switch (why)
			{
				case GA_BUTTONPRESS:
					p->tolongcount=0;
					p->tolongstart+=1;
					if (p->tolongstart>=strlen(p->data)) p->tolongstart=strlen(p->data)-1;
					p->taus(0);
					break;
				case GA_TICK:
					p->tolongstart+=p->tolongcount++;
					if (p->tolongstart>=strlen(p->data)) p->tolongstart=strlen(p->data)-1;
					p->taus(0);
					break;
			}
		}
		return r;
	}
	
	if (why==GA_EVENT)
	{
		switch (event->type)
		{
			case SelectionClear:
				if (event->xselectionclear.window==win && event->xselectionclear.selection==XA_PRIMARY)
				{
					p->mend=-1;
					p->taus(0);
				}
				break;
			case SelectionRequest:
				XEvent ev;
				
				ev.type					= SelectionNotify;
				ev.xselection.requestor	= event->xselectionrequest.requestor;
				ev.xselection.selection	= event->xselectionrequest.selection;
				ev.xselection.target	= event->xselectionrequest.target;
				ev.xselection.time		= event->xselectionrequest.time;
				ev.xselection.property	= event->xselectionrequest.property;
				if (event->xselectionrequest.selection==XA_PRIMARY &&
				    event->xselectionrequest.target==XA_STRING)
				{
					XChangeProperty(display(),
					                ev.xselection.requestor,
					                ev.xselection.property,
					                ev.xselection.target,
					                8,
					                PropModeReplace,
					                (unsigned char*)(p->mend==-1?" \0":&p->data[p->mpos]),
					                (p->mend==-1?1:p->mend-p->mpos));
				}
				else
				{
					ev.xselection.property=None;
				}
				XSendEvent(display(),ev.xselection.requestor,False,0,&ev);
				break;
		}

		return GAR_ACTIVE;
	}
	if (why==GA_BUTTONPRESS && p->data)
	{
		p->mx=0;
		p->mpos=p->tolongstart;
		p->l=strlen(p->data);
		while (p->mpos<p->l && XTextWidth(p->tgg.gg_font(),p->data+p->tolongstart,p->mpos-p->tolongstart+1)<event->xbutton.x) p->mpos++;

		p->mend=-1;
		p->taus(0);
		return GAR_ACTIVE;
	}
	if (why==GA_TICK && (p->mx<0 || p->mx>p->tgg._width()))
	{
		int mx=p->mx;
		if (mx<0 && p->tolong)
		{
			p->tolongstart--;
			if (p->tolongstart<0) p->tolongstart=0;
		}
		if (mx>p->tgg._width() && p->tolong)
		{
			p->tolongstart++;
			if (p->tolongstart>=strlen(p->data)) p->tolongstart=strlen(p->data)-1;
		}
		p->mend=p->tolongstart;
		if (mx<0) mx=0;
		if (mx>width) mx=width;
		p->l=strlen(p->data);
		while (p->mend<p->l && XTextWidth(p->tgg.gg_font(),p->data+p->tolongstart,p->mend-p->tolongstart+1)<mx) p->mend++;
		p->taus(0);
	}
	return 0;
}
int output::GSelected(XEvent *event,int why,int wasabove)
{
	if (wasfor && wasfor!=&p->tgg)
	{
		return wasfor->GSelected(event,why,wasabove);
	}

	if (why==GA_BUTTONPRESS)
	{
		if (p->tgg._active()&GA_DCLICK)
		{
			p->mpos=0;
			p->mend=p->l;
		}

		p->cutpaste(event);
		
		if (p->tgg._active()&GA_DCLICK)
		{
			p->taus(0);
		}
	}
	return 0;
}
void output::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (wasfor && wasfor!=&p->tgg)
	{
		wasfor->GActionPress(mx,my,rx,ry,state,button,reason);
	}
}
void output::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
	if (wasfor && wasfor!=&p->tgg)
	{
		wasfor->GActionRelease(mx,my,rx,ry,state,button,reason);
	}
}
int output::GMouseMove(int mx,int my,int rx,int ry,unsigned int state,int button)
{
	if (wasfor && wasfor!=&p->tgg)
	{
		return wasfor->GMouseMove(mx,my,rx,ry,state,button);
	}

	if (button==1)
	{
		if (mx<0 && p->tolong)
		{
			p->tolongstart--;
			if (p->tolongstart<0) p->tolongstart=0;
		}
		if (mx>p->tgg._width() && p->tolong)
		{
			p->tolongstart++;
			if (p->tolongstart>=strlen(p->data)) p->tolongstart=strlen(p->data)-1;
		}
		p->mx=mx;
		p->mend=p->tolongstart;
		if (mx<0) mx=0;
		if (mx>width) mx=width;
		p->l=strlen(p->data);
		while (p->mend<p->l && XTextWidth(p->tgg.gg_font(),p->data+p->tolongstart,p->mend-p->tolongstart+1)<mx) p->mend++;
		p->taus(0);
	}
	return 0;
}
void output::GSetLimits(void)
{
	p->textwidth=textlength(this,p->text);
	minwidth=p->textwidth+TEXTDISTANCE+8+(p->text && *p->text?p->textwidth/strlen(p->text)*5:textlength(this,"MM")/2*5);
	minheight=textheight(this,p->text)+8+RealSize()*2;
	maxheight=minheight;
	if (p->text) before_x=p->textwidth+5+TEXTDISTANCE;
	setlimit=1;
}

#endif
