/*	XGEditText.cpp
 *
 *		This handles the standard editing control.
 */

/*  YAAF - Yet another application framework
 *  Copyright (C) 1997, 1998 William Edward Woody
 *  
 *  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 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 MERCHANTABIILITY 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.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <XConfig.h>			// must be first to optimize compile on MSVC++
#include <string.h>
#include <ctype.h>
#include <XError.h>
#include <XTextControls.h>
#include <XDataUtil.h>
#include <XDraw.h>
#include <XCursorUtil.h>
#include <XMessages.h>
#include <XAppMenu.h>
#include <XHandle.h>

#if OPT_XWIN
#include <time.h>
#include <sys/times.h>
#endif

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*	@F"XGEditText::XGEditText" @I
 *
 *		Create this edit text box with the specified location
 */

XGEditText::XGEditText(XGOwner *p, XGArgStream &s) : XGView(p,s)
{
	short font,max;
	XGECharType t;
	bool password;
	
	char buffer[256];
	s.ReadString(buffer);
	
	font = s.ReadInteger();
	max  = s.ReadInteger();
	t = (XGECharType)(s.ReadInteger());
	password = s.ReadBoolean();
	
	Init(buffer,font,max,t,password);
}

XGEditText::XGEditText(XGOwner *p, XGSEditInitRecord &s) : XGView(p,s.v.v)
{
	Init(s.v.text,s.v.font,s.maxchars,s.chartype,s.password);
}

/*	@F"XGEditText::~XGEditText" @I
 *
 *		No longer edit the text in here
 */

XGEditText::~XGEditText()
{
}

/************************************************************************/
/*																		*/
/*	Standard Text Events												*/
/*																		*/
/************************************************************************/

/*	@F"XGEditText:DoDrawView" @I
 *
 *		Draw this thing
 */

void XGEditText::DoDrawView(Rect)
{
	XGDraw draw(this);
	Rect r;
	
	r = GetContentRect();
	DrawTextFrame(draw,r);
	::InsetRect(&r,3,3);
	DrawContents(draw);
}

/*	@F"XGEditText::DoActivate" @I
 *
 *		Handle activation/deactivation (if this thing has the focus)
 */

void XGEditText::DoActivate(bool)
{
	InvalView();
}

/*	@F"XGEditText::DoMouseDown" @I
 *
 *		Handle clicks in this thing
 */

bool XGEditText::DoMouseDown(Point where, short flags)
{
	if (!SetFocus()) return false;

	XGDraw draw(this);
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	
	fSelectStart = HitDetect(draw,where.h);
	
	if (flags & KKeyShift) {
		if (fSelectStart < fStart) {
			fStart = fSelectStart;
			fSelectStart = fEnd;
		} else {
			fEnd = fSelectStart;
			fSelectStart = fStart;
		}
	} else {
		fStart = fSelectStart;
		fEnd = fSelectStart;
	}
	DrawContents(draw);
	return true;
}

/*	@F"XGEditText::DoMouseMove" @I
 *
 *		Handle the mouse move event
 */

void XGEditText::DoMouseMove(Point where, short flags)
{
	if (IsActive() && IsEnabled()) {
		SetCursorID(KIBeamCursor);
	} else {
		SetCursorID(KNormalCursor);
	}
	if (flags & KCapturedMouse) {
		short pos;
		XGDraw draw(this);
		if (fFontPtr) {
			fFontPtr->Attach();
			draw.SetFont(fFontPtr);
		}

		short s,e;
		Rect r;
		long tmp;
		short delta;
		bool flag = false;
		
		// hit detect selection range
		pos = HitDetect(draw,where.h);
		if (fSelectStart > pos) {
			s = pos;
			e = fSelectStart;
		} else {
			s = fSelectStart;
			e = pos;
		}
		
		// autoscroll
		r = GetContentRect();
		tmp = fScroll;
		if (where.h < r.left) {
			delta = (r.left - where.h) / 3;
			if (delta > 8) delta = 8;
			fScroll -= delta;
			if (fScroll < 0) fScroll = 0;
		} else if (where.h > r.right) {
			delta = (where.h - r.right) / 3;
			if (delta > 8) delta = 8;
			fScroll += delta;
			if (fScroll > fMaxScroll) fScroll = fMaxScroll;
		}

		if ((fStart != s) || (fEnd != e)) {
			fStart = s;
			fEnd = e;
			flag = true;
		}
		
		if (flag || (tmp != fScroll)) {
			DrawContents(draw);
		}
	}
}

/*	@F"XGEditText::DoMouseUp" @I
 *
 *		Handle mouse up
 */

void XGEditText::DoMouseUp(Point, short)
{
}

/*	@F"XGEditText:GainFocus" @I
 *
 *		Gain the focus. This enables this thing
 */

void XGEditText::GainFocus()
{
	SetSelect(0,32767);				// select the entire line
}

/*	@F"XGEditText::LoseFocus" @I
 *
 *		Lose the focus. This disables this thing
 */

void XGEditText::LoseFocus()
{
	InvalView();					// redraw my contents to deselect
}

/************************************************************************/
/*																		*/
/*	Dispatched events													*/
/*																		*/
/************************************************************************/

BEGINDISPATCH(XGEditText,XGView)
	DISPATCHTABLE(KXGAppPeriodic,XGEditText::DoIdle)
	DISPATCHTABLE(KXGKeyEvent,XGEditText::DoKey)
	DISPATCHTABLE(KXGGetMenuStatus,XGEditText::DoGetMenuStatus)
	DISPATCHTABLE(KXGDoMenuCommand,XGEditText::DoMenuCommand)
ENDDISPATCH

/*	XGEditText::DoIdle
 *
 *		Handle idle event by blinking the cursor
 */

long XGEditText::DoIdle(long, void *)
{
	long t;
	
	if (fStart != fEnd) return -1;
	if (IsActive() && IsEnabled() && HasFocus()) {

#if OPT_MACOS
		t = TickCount();
#endif

#if OPT_WINOS
		t = GetTickCount();
#endif

#if OPT_XWIN
		struct tms buf;
		t = times(&buf);
#endif

		if (fTime < t) {
#if OPT_MACOS
			fTime = t + GetCaretTime();
#endif

#if OPT_WINOS
			fTime = t + 500;
#endif

#if OPT_XWIN
			fTime = t + CLK_TCK/2;
#endif
			
			XGDraw draw(this);
			Rect r,s;
			if (fFontPtr) {
				fFontPtr->Attach();
				draw.SetFont(fFontPtr);
			}
			
			r = GetContentRect();
			s = r;
			::InsetRect(&r,3,3);
			if (fStart) {
				if (fPassword) {
					r.left += draw.StringWidth("*") * fStart + 2 - fScroll;
				} else {
					r.left += draw.StringWidth(fText,fStart) + 2 - fScroll;
				}
			} else {
				r.left += 2 - fScroll;
			}
			r.right = r.left + 1;
			if (::SectRect(&r,&s,&r)) {
				draw.InvertRect(r);
			}
		}
	}

	return -1;
}

/*	XGEditText::DoGetMenuStatus
 *
 *		Handle getting the menu status
 */

long XGEditText::DoGetMenuStatus(long arg, void *parg)
{
	XGSMenuStatus *m = (XGSMenuStatus *)parg;
	switch (arg) {
		case KXGEditCut:
		case KXGEditCopy:
			if (fPassword) {
				m->enable = false;
				return 0;
			}
			/* Fall through to... */
		case KXGEditClear:
			m->enable = (fStart < fEnd);		// when something is selected.
			return 0;
		case KXGEditPaste:
			m->enable = !fPassword;
			return 0;
	}
	return -1;
}

/*	XGEditText::DoMenuCommand
 *
 *		Do the menu command
 */

long XGEditText::DoMenuCommand(long arg, void *)
{
	switch (arg) {
		case KXGEditCut:
			DoCut();
			return 0;
		case KXGEditCopy:
			DoCopy();
			return 0;
		case KXGEditPaste:
			DoPaste();
			return 0;
		case KXGEditClear:
			DoClear();
			return 0;
	}
	return -1;
}

/*	XGEditText::DoKey
 *
 *		Handle keyboard command
 */

long XGEditText::DoKey(long arg, void *)
{
	short c;
	short mods;
	
	mods = GETHIWORD(arg);
	c = GETLOWORD(arg);
	if (mods & KSpecialKey) {
		if (c == KKeyDelete) {
			DeleteChar();
		} else if (c == KKeyHome) {
			SetSelect(0,0);
			return 0;
		} else if (c == KKeyEnd) {
			SetSelect(fLength,fLength);
			return 0;
		} else if (c == KKeyLeftCursor) {
			short i;
			
			i = fStart - 1;
			if (i < 0) i = 0;
			SetSelect(i,i);
			return 0;
		} else if (c == KKeyRightCursor) {
			short i;
			
			i = fEnd + 1;
			if (i > fLength) i = fLength;
			SetSelect(i,i);
			return 0;
		}
	} else {
#if OPT_MACOS
		/* Screen out control keys */
		if (mods & KKeyControl) return -1;
#endif

		if (IsSpecialKey(c)) return -1;				// pass keys upwards

		if (c == 0x08) {
			DeleteChar();
		} else {
			InsertChar(c);
		}
		return 0;
	}
	
	return -1;
}

/************************************************************************/
/*																		*/
/*	Miscellaneous Support												*/
/*																		*/
/************************************************************************/

/*	@F"XGView::SetSelect" @I
 *
 *		Set the current selection
 */

void XGEditText::SetSelect(short s, short e)
{
	if (s <= e) {
		fStart = s;
		fEnd = e;
	} else {
		fStart = e;
		fEnd = s;
	}
	if (fStart < 0) fStart = 0;
	if (fEnd < 0) fEnd = 0;
	if (fStart > fLength) fStart = fLength;
	if (fEnd > fLength) fEnd = fLength;
	ScrollInRange();
	
	XGDraw draw(this);
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	DrawContents(draw);
}

/*	@F"XGView::GetSelect" @I
 *
 *		Get the current selection range
 */

void XGEditText::GetSelect(short *s, short *e)
{
	*s = fStart;
	*e = fEnd;
}

/************************************************************************/
/*																		*/
/*	Text manipulation													*/
/*																		*/
/************************************************************************/

/*	@F"XGEditText::SetTitle" @I
 *
 *		Set the text in this thing
 */

void XGEditText::SetTitle(const char *text)
{
	strcpy(fText,text);
	fLength = strlen(fText);
	fStart = 0;
	fEnd = fLength;
	InvalView();
}

/*	@F"XGEditText::GetTitle" @I
 *
 *		Get the text from this thing
 */

void XGEditText::GetTitle(char *text) const
{
	memcpy(text,fText,fLength);
	text[fLength] = 0;
}

/************************************************************************/
/*																		*/
/*	Miscellaneous Hooks													*/
/*																		*/
/************************************************************************/

/*	@F"XGEditText::IsSpecialKey" @I
 *
 *		This determines the 'special keys'--those keys which are passed
 *	up to the containing view. By default, these are the enter key and
 *	the tab key
 */

bool XGEditText::IsSpecialKey(char c)
{
	if (c == 0x0D) return true;
	if (c == 0x09) return true;
	return false;
}

/*	@F"XGEditText::DrawTextFrame" @I
 *
 *		This draws the frame around the edited text
 */

void XGEditText::DrawTextFrame(XGDraw &draw, Rect r)
{
	if (IsEnabled() && IsActive()) {
		draw.Draw3DRect(r,KXGEInset);
		::InsetRect(&r,1,1);
		draw.Draw3DRect(r,KXGEFrame);
		::InsetRect(&r,1,1);
	} else {
		draw.Draw3DRect(r,KXGEGrayFrame);
		::InsetRect(&r,1,1);
	}
	draw.EraseRect(r);
}

/*	@F"XGEditText::ValidateText" @I
 *
 *		Validate the input. Is this an appropriate input value? By default
 *	this transmits a message upwards. A return value of '1' means the text
 *	input is *not* valid.
 */

bool XGEditText::ValidateText(char *text)
{
	char *c;
	short x;
	
	/*
	 *	Check to see if this is fine with the limits defined by the
	 *	resource specification
	 */
	
	switch (fCharType) {
		case KXGEAnyChar:
		case KXGENumeric:
		case KXGEHexDigit:
		case KXGEAlphaNumeric:
			break;
		case KXGENumSign:
		case KXGEFloating:
			/*
			 *	Make sure the only '+' and '-' is at the start
			 */
			
			if (*text) {
				for (c = text+1; *c; c++) {
					if ((*c == '+') || (*c == '-')) return false;
				}
			}
			if (fCharType == KXGENumSign) break;
			
			/*
			 *	For floating, make sure there is only one '.'
			 */
			
			x = 0;
			for (c = text; *c; c++) {
				if (*c == '.') x++;
				if (x > 1) return false;
			}
			break;
	}
	
	/*
	 *	Now see if the parent thinks this is fine.
	 */
	
	if (1 == DoDispatch(KXGKeyClick,GetViewID(),(void *)text)) {
		return false;
	}
	return true;
}

/*	@F"XGEditText::CanInsertKey" @I
 *
 *		Can this key be inserted? By default use the attributes
 *	that were passed to me
 */

bool XGEditText::CanInsertKey(char c)
{
	short len;
	
	switch (fCharType) {
		case KXGEAnyChar:
			break;
		case KXGENumeric:
			if (!isdigit(c)) return false;
			break;
		case KXGENumSign:
			if (!isdigit(c) && (c != '+') && (c != '-')) return false;
			break;
		case KXGEFloating:
			if (!isdigit(c) && (c != '.') && (c != '+') && (c != '-')) return false;
			break;
		case KXGEHexDigit:
			if (!isxdigit(c)) return false;
			break;
		case KXGEAlphaNumeric:
			if (!isalnum(c)) return false;
			break;
	}
	
	if (fMaxChars == 0) return true;
	len = fLength - (fEnd - fStart);
	if (len >= fMaxChars) return false;
	return true;
}

/*	@F"XGEditText::HitDetect" @I
 *
 *		Handle hit detection
 */

short XGEditText::HitDetect(XGDraw &draw, short x)
{
	short i;
	short l1,l2;
	short l;
	
	x += fScroll;
	l1 = 5;
	for (i = 0; i < fLength; i++) {
		if (fPassword) {
			l2 = draw.StringWidth("*") * (i + 1) + 5;
		} else {
			l2 = draw.StringWidth(fText,i+1) + 5;
		}
		
		l = (l1 + l2)/2;
		if (x < l) return i;
		l1 = l2;
	}
	return fLength;
}

/*	@F"XGEditText::DrawContents" @I
 *
 *		Draw the contents
 */

void XGEditText::DrawContents(XGDraw &draw)
{
	Rect r;
	int y;
	int i;
	
	/*
	 *	Recalculate the scroll
	 */
	 
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	RecalcScroll();
	 
	/*
	 *	Initialize stuff
	 */
	
	r = GetContentRect();
	::InsetRect(&r,3,3);
	draw.ClipRect(&r);
	draw.EraseRect(r);
	
	/*
	 *	Draw the text
	 */
	
	y = (r.bottom + r.top - draw.GetFontHeight())/2 + draw.GetFontAscent();
	draw.MoveTo(r.left + 2 - fScroll, y);
	if (fLength) {
		if (fPassword) {
			for (i = 0; i < fLength; i++) {
				draw.DrawString("*");
			}
		} else {
			draw.DrawString(fText,fLength);
		}
	}
	
	/*
	 *	Draw the hilite region if active
	 */
	
	if (IsActive() && IsEnabled() && HasFocus()) {
		Rect s;
		if (fStart != fEnd) {
			s = r;
			if (fStart == 0) s.left = r.left;
			else {
				if (fPassword) {
					s.left = 2 + r.left - fScroll;
					s.left += draw.StringWidth("*") * fStart;
				} else {
					s.left = 2 + r.left + draw.StringWidth(fText,fStart) - fScroll;
				}
			}
			if (fEnd >= fLength) s.right = r.right;
			else {
				if (fPassword) {
					s.right = 2 + r.left - fScroll;
					s.right += draw.StringWidth("*") * fEnd;
				} else {
					s.right = 2 + r.left + draw.StringWidth(fText,fEnd) - fScroll;
				}
			}
			::SectRect(&r,&s,&s);
			draw.FillRect(s,KXGColorHilite);
			
			/*
			 *	Clip the hilite area and draw the text again in the
			 *	text hilite color
			 */
			
			draw.ClipRect(&s);
			draw.MoveTo(r.left + 2 - fScroll, y);
			if (fLength) {
				draw.SetForeColor(KXGColorHiliteText);
				if (fPassword) {
					for (i = 0; i < fLength; i++) {
						draw.DrawString("*");
					}
				} else {
					draw.DrawString(fText,fLength);
				}
				draw.SetForeColor(KXGColorBlack);
			}
			draw.ClipRect();
		}
	}
}

/************************************************************************/
/*																		*/
/*	Edit Core															*/
/*																		*/
/************************************************************************/

/*	@F"XGEditText::InsertChar" @I
 *
 *		Insert a character
 */

void XGEditText::InsertChar(char c)
{
	short x;
	short d;
	char buffer[256];
	short s,e,l;
	
	if (!CanInsertKey(c)) return;
	
	memcpy(buffer,fText,fLength);				// make backup of buffer
	s = fStart;
	e = fEnd;
	l = fLength;
	
	if (fStart != fEnd) {						// edit the buffers
		d = fEnd - fStart;
		for (x = fEnd; x != fLength; x++) {
			fText[x - d] = fText[x];
		}
		fLength -= d;
		fEnd = fStart;
	}
	
	if (fLength >= 255) return;
	
	for (x = fLength-1; x >= fStart; x--) {
		fText[x+1] = fText[x];
	}
	fText[fStart] = c;
	fStart++;
	fEnd++;
	fLength++;
	
	fText[fLength] = 0;
	if (!ValidateText(fText)) {
		memcpy(fText,buffer,l);
		fStart = s;
		fEnd = e;
		fLength = l;
		return;
	}
	
	ScrollInRange();
	
	XGDraw draw(this);
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	DrawContents(draw);
}

/*	@F"XGEditText::DeleteChar" @I
 *
 *		Delete a character
 */

void XGEditText::DeleteChar()
{
	short x;
	short d;
	char buffer[256];
	short s,e,l;
	
	memcpy(buffer,fText,fLength);				// make backup of buffer
	s = fStart;
	e = fEnd;
	l = fLength;
	
	if ((fStart == 0) && (fEnd == 0)) return;
	if (fStart == fEnd) fStart--;
	
	d = fEnd - fStart;
	for (x = fEnd; x != fLength; x++) {
		fText[x - d] = fText[x];
	}
	fLength -= d;
	fEnd = fStart;
	
	fText[fLength] = 0;							// send click message
	if (!ValidateText(fText)) {
		memcpy(fText,buffer,l);
		fStart = s;
		fEnd = e;
		fLength = l;
		return;
	}
	
	ScrollInRange();
	
	XGDraw draw(this);
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	DrawContents(draw);
}
	

/************************************************************************/
/*																		*/
/*	Edit Menu Support													*/
/*																		*/
/************************************************************************/

#if OPT_XWIN
XGHandle		GClipboard;
#endif

/*	@F"XGEditText::DoCut" @I
 *
 *		Handle cut operation
 */

void XGEditText::DoCut()
{
	if (fPassword) return;			// don't allow cleartext copy

	DoCopy();
	DoClear();
}

/*	@F"XGEditText::DoCopy" @I
 *
 *		Handle copy operation
 */

void XGEditText::DoCopy()
{
	if (fPassword) return;			// don't allow cleartext copy
	
#if OPT_MACOS
	::ZeroScrap();
	if (fEnd > fStart) {
		::PutScrap(fEnd-fStart,'TEXT',fText+fStart);
	}
#endif

#if OPT_WINOS
	HANDLE h;
	char *ptr;
	
	h = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,fEnd-fStart+1);
	if (!h) throw XGPostError(KXGOutMemory);
	ptr = (char *)::GlobalLock(h);
	memcpy(ptr,fText+fStart,fEnd-fStart);
	ptr[fEnd-fStart] = 0;
	::GlobalUnlock(h);
	
	::OpenClipboard(NULL);
	::EmptyClipboard();
	::SetClipboardData(CF_TEXT,h);
	::CloseClipboard();
#endif

#if OPT_XWIN
	GClipboard.SetSize(fEnd - fStart);
	memcpy(*GClipboard,fText+fStart,fEnd-fStart);
#endif
}

/*	@F"XGEditText::DoPaste" @I
 *
 *		Handle paste operation
 */

void XGEditText::DoPaste()
{
	long maxpaste;
	short x;
	char buffer[256];
	short s,e,l;
	char insbuf[256];
	short inslen;
	
	/*
	 *	Don't allow cleartext paste on password
	 */
	
	if (fPassword) return;
	
	/*
	 *	Backup copy of buffer
	 */
	
	memcpy(buffer,fText,fLength);				// make backup of buffer
	s = fStart;
	e = fEnd;
	l = fLength;
	
	maxpaste = fLength - (fEnd - fStart);
	maxpaste = 255 - maxpaste;
	if (maxpaste <= 0) return;					// can't paste at all...
	
	/*
	 *	Get from clipboard
	 */
	
#if OPT_MACOS
	Handle h;
	long res;
	long offset;
	
	h = ::NewHandle(0);
	res = ::GetScrap(h,'TEXT',&offset);
	if (res < 0) {
		inslen = 0;
	} else {
		inslen = ::GetHandleSize(h);
		if (inslen > maxpaste) inslen = maxpaste;
		memcpy(insbuf,*h,inslen);
		::DisposeHandle(h);
	}
#endif

#if OPT_WINOS
	HANDLE h;
	char *ptr;
	
	::OpenClipboard(NULL);
	h = ::GetClipboardData(CF_TEXT);
	if (!h) {
		inslen = 0;
	} else {
		ptr = (char *)::GlobalLock(h);
		inslen = strlen(ptr);
		if (inslen > maxpaste) inslen = maxpaste;
		memcpy(insbuf,ptr,inslen);
		::GlobalUnlock(h);
	}
	::CloseClipboard();
#endif

#if OPT_XWIN
	inslen = GClipboard.GetSize();
	if (inslen > maxpaste) inslen = maxpaste;
	memcpy(insbuf,*GClipboard,inslen);
#endif

	if (inslen <= 0) return;					// nothing to paste

	/*
	 *	Move data into my buffer
	 */
	
	for (x = fLength-1; x >= fStart; x--) {
		fText[x+inslen] = fText[x];
	}
	fLength += inslen;
	memcpy(fText+fStart,insbuf,inslen);
	fStart = fStart + inslen;
	fEnd = fStart;
	
	/*
	 *	Verify I can paste this
	 */
	
	fText[fLength] = 0;							// send click message
	if (!ValidateText(fText)) {
		memcpy(fText,buffer,l);
		fStart = s;
		fEnd = e;
		fLength = l;
		return;
	}
	
	/*
	 *	Reformat and redraw
	 */
	
	ScrollInRange();
	XGDraw draw(this);
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	DrawContents(draw);
}

/*	@F"XGEditText::DoClear" @I
 *
 *		Handle clear operation. This is almost like the DeleteChar()
 *	routine above.
 */

void XGEditText::DoClear()
{
	short x;
	short d;
	char buffer[256];
	short s,e,l;
	
	memcpy(buffer,fText,fLength);				// make backup of buffer
	s = fStart;
	e = fEnd;
	l = fLength;
	
	if (fStart == fEnd) return;
	d = fEnd - fStart;
	for (x = fEnd; x != fLength; x++) {
		fText[x - d] = fText[x];
	}
	fLength -= d;
	fEnd = fStart;
	
	fText[fLength] = 0;							// send click message
	if (!ValidateText(fText)) {
		memcpy(fText,buffer,l);
		fStart = s;
		fEnd = e;
		fLength = l;
		return;
	}
	
	ScrollInRange();
	
	XGDraw draw(this);
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	DrawContents(draw);
}

/************************************************************************/
/*																		*/
/*	Text Scroll															*/
/*																		*/
/************************************************************************/

/*	@F"XGEditText::RecalcScroll" @I
 *
 *		Recalculate the scroll boundaries; this calculates the number of
 *	pixels wide the text is
 */

void XGEditText::RecalcScroll()
{
	Rect r;
	XGDraw draw(this);
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	
	r = GetContentRect();
	::InsetRect(&r,3,3);
	
	if (fPassword) {
		fMaxScroll = draw.StringWidth("*") * fLength - r.right + r.left + 4;
	} else {
		fMaxScroll = draw.StringWidth(fText,fLength) - r.right + r.left + 4;
	}
	if (fMaxScroll < 0) fMaxScroll = 0;
	if (fScroll < 0) fScroll = 0;
	if (fScroll > fMaxScroll) fScroll = fMaxScroll;
}

/*	@F"XGEditText::ScrollInRange" @I
 *
 *		Scroll the selection into range
 */

void XGEditText::ScrollInRange(void)
{
	Rect r;
	short pixl,pixr;
	XGDraw draw(this);
	if (fFontPtr) {
		fFontPtr->Attach();
		draw.SetFont(fFontPtr);
	}
	
	RecalcScroll();				// make sure I've got this right
	
	/*
	 *	Find the pixel location of the scrolls
	 */
	
	r = GetContentRect();
	::InsetRect(&r,5,3);
	pixl = r.left - fScroll;
	pixr = pixl;
	if (fPassword) {
		pixl += draw.StringWidth("*") * fStart;
		pixr += draw.StringWidth("*") * fEnd;
	} else {
		if (fStart) pixl += draw.StringWidth(fText,fStart);
		if (fEnd) pixr += draw.StringWidth(fText,fEnd);
	}
	
	/*
	 *	Now see if I have to scroll things over
	 */
	
	if (pixr > r.right) {
		fScroll += pixr - r.right;
		pixl -= pixr - r.right;
		pixr = r.right;
	}
	if (pixl < r.left) {
		fScroll -= r.left - pixl;
		pixr += r.left - pixl;
		pixl = r.left;
	}
}


/************************************************************************/
/*																		*/
/*	Initialization														*/
/*																		*/
/************************************************************************/

/*	XGEditText::Init
 *
 *		Initialize this thing
 */

void XGEditText::Init(char *buffer, short font, short max, 
					  XGECharType t, bool password)
{
	fScroll = 0;
	
	fFontPtr = XGFont::LoadFont(font);
	strcpy(fText,buffer);
	fLength = strlen(buffer);
	fStart = 0;
	fEnd = fLength;
	fMaxChars = max;
	fCharType = t;
	fPassword = password;

#if OPT_MACOS
	fTime = TickCount() + GetCaretTime();
#endif

#if OPT_WINOS
	fTime = GetTickCount() + 500;
#endif
}
