/*	XGEditCore.cpp
 *
 *		This contains the implementation of the XGEditCore object, which
 *	implements basic text editing
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#include <XConfig.h>
#include <XError.h>
#include <XAutoPtr.h>
#include <XView.h>
#include <XDraw.h>
#include <XEditCore.h>
#include <ctype.h>
#include <string.h>
#include <XMisc.h>

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

static bool GEndHit;

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

/*$	XGEditCore::XGEditCore
 *
 *		Create one of these things, and associate this with the specified
 *	view. The reason why this isn't a view is because on rare circumstances,
 *	I want to have two of these per view...
 */

XGEditCore::XGEditCore(XGView *view, XGFont *font, XRect &r)
{
	fDrawFlag = false;
	fCurView = view;
	fCurFont = font;
	if (font) font->Attach();
	fSize = r;
	
	fText = NULL;
	
	fTickTime = XGGetTime();
	fTabStop = 8;
	fWordWrap = false;
	fAutoIndent = false;
	fCursorState = false;
	fPrevLine = false;
	
	fScrollPixels = true;
	fCursorVis = true;
	fCursorPos.h = -1;
	
	fXScroll = 0;
	fYScroll = 0;
	fSelStart = 0;
	fSelEnd = 0;
	fPixelPos = -1;
	
	fFieldEditor = false;
	fPassword = false;
	fFocus = false;
	
	fXOffset = 2;
	
	fForeColor = KXGColorBlack;
	fBackColor = KXGColorWhite;
	
	/*
	 *	Get sizes
	 */
	
	XGDraw draw(view);
	if (font) {
		font->Attach();
		draw.SetFont(font);
	}
	fFontHeight = draw.GetFontHeight();
	fFontAscent = draw.GetFontAscent();
	fPixelStep = fFontHeight;
}

/*$	XGEditCore::XGEditCore
 *
 *		Create one of these things temporarly; this is used for drawing
 *	text inside of a square...
 */

XGEditCore::XGEditCore(XGDraw *draw, XRect &r)
{
	Assert(draw != NULL);
	
	fDrawFlag = true;
	fCurDraw = draw;
	fCurFont = NULL;
	fSize = r;
	
	fText = NULL;
	
	fTickTime = XGGetTime();
	fTabStop = 8;
	fWordWrap = false;
	fAutoIndent = false;
	fCursorState = false;
	fPrevLine = false;
	
	fScrollPixels = true;
	fCursorVis = true;
	fCursorPos.h = -1;
	
	fXScroll = 0;
	fYScroll = 0;
	fSelStart = 0;
	fSelEnd = 0;
	fPixelPos = -1;
	
	fFieldEditor = false;
	fPassword = false;
	fFocus = false;
	
	fXOffset = 2;
	
	fForeColor = KXGColorBlack;
	fBackColor = KXGColorWhite;
	
	/*
	 *	Get sizes
	 */
	
	fFontHeight = fCurDraw->GetFontHeight();
	fFontAscent = fCurDraw->GetFontAscent();
	fPixelStep = fFontHeight;
}

/*$	XGEditCore::~XGEditCore
 *
 *		Delete this
 */

XGEditCore::~XGEditCore()
{
}

/************************************************************************/
/*																		*/
/*	Text Access															*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::SetText
 *
 *		Set the text edit handle
 */

void XGEditCore::SetText(XGHandle *text)
{
	fText = text;					// set the text handle
	RecalcLineStarts();				// recalculate line starts
	if (!fDrawFlag && fCurView) {
		fCurView->InvalView();		// and force a redraw
	}
}

/************************************************************************/
/*																		*/
/*	Text Access															*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::DoIdle
 *
 *		Handle idle event. This basically inverts the cursor.
 */

void XGEditCore::DoIdle(void)
{
	unsigned long t;
	
	t = XGGetTime();
	if (t - fTickTime >= XGGetCursorBlink()) {
		fTickTime = t;
		fCursorState = !fCursorState;

		if (!fCursorVis) return;		// cursor not visible
		
		if (!fDrawFlag && fCurView) {
			XGDraw draw(fCurView);
			if (fCurFont) {
				fCurFont->Attach();
				draw.SetFont(fCurFont);
			}
			InvertCursor(draw);
		}
	}
}

/*$	XGEditCore::DoMouseDown
 *
 *		Handle mouse down event
 */

bool XGEditCore::DoMouseDown(XPoint where, short flags)
{
	long hit;
	
	/*
	 *	Get the focus if I can
	 */
	
	if (fDrawFlag) return false;
	fCurView->SetFocus();
	
	/*
	 *	Calculate the hit range
	 */
	
	fDragWord = false;
	hit = GetOffset(where);
	fPrevLine = GEndHit;			// cursor on previous line?
	if (flags & KDoubleClick) {
		fDragWord = true;
		fDragFrom = hit;
	} else if (flags & KKeyShift) {
		if (hit < fSelStart) {
			fDragFrom = fSelEnd;
		} else {
			fDragFrom = fSelStart;
		}
	} else {
		fDragFrom = hit;
	}
	fDragTo = hit;
	
	/*
	 *	Clear the cursor if it's currently visible. By doing this
	 *	here, I sidestep an interesting problem.
	 */
	
	ClearCursor();
	
	/*
	 *	Update selection
	 */
	
	SetDragSelect(fDragFrom,fDragTo);
	
	/*
	 *	And calculate the maximum X scroll for dragging
	 */
	
	fMaxXVal = (GetMaxWidth() - fSize.right + fSize.left + fPixelStep - 1) / fPixelStep;
	return true;
}

/*$	XGEditCore::DoMouseMove
 *
 *		Handle moving the mouse around
 */

void XGEditCore::DoMouseMove(XPoint where, short flags)
{
	long hit;
	
	if (!(flags & KCapturedMouse)) return;
	
	hit = GetOffset(where);
	if (hit == fDragTo) return;
	fDragTo = hit;
	
	SetDragSelect(fDragFrom,fDragTo);
	
	/*
	 *	Drag autoscroll
	 */

	if (where.h < fSize.left) {
		if (fXScroll > 0) SetScrollValue(fXScroll-1,fYScroll);
	}
	if (where.h > fSize.right) {
		if (fXScroll < fMaxXVal) SetScrollValue(fXScroll+1,fYScroll);
	}
	if (where.v < fSize.top) {
		if (fYScroll > 0) SetScrollValue(fXScroll,fYScroll-1);
	}
	if (where.v > fSize.bottom) {
		long h;
		
		h = (GetMaxHeight() - fSize.bottom + fSize.top + fPixelStep - 1)/fPixelStep;
		if (fYScroll < h) SetScrollValue(fXScroll,fYScroll+1);
	}
}

/*$	XGEditCore::DoMouseUp
 *
 *		End selection drag
 */

void XGEditCore::DoMouseUp(XPoint where, short flags)
{
	long hit;
	
	if (!(flags & KCapturedMouse)) return;
	
	hit = GetOffset(where);
	if (hit == fDragTo) return;
	fDragTo = hit;
	
	SetDragSelect(fDragFrom,fDragTo);
}

/*$	XGEditCore::DoActivate
 *
 *		Handle activation/deactivation. It's like gaining and losing the
 *	focus.
 */

void XGEditCore::DoActivate(bool flag)
{
	if (fCurView) {
		if (!fCurView->HasFocus()) return;		// don't have focus...
	}
	if (!flag) {
		DoLoseFocus();
	} else {
		fFocus = true;							// activation is like
		if (fCurView) fCurView->InvalView();	// gaining focus.
	}
}


/************************************************************************/
/*																		*/
/*	Text Editing Events													*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::DoKey
 *
 *		Insert this key stroke
 */

void XGEditCore::DoKey(char c)
{
	if (c == 0x08) {
		/*
		 *	Delete
		 */
		
		if (fSelStart == fSelEnd) {
			if (fSelStart <= 0) return;
			fSelStart--;
		}
		DoDelete();
	} else {
		/*
		 *	Insert any character
		 */
		
		if (c == '\r') c = '\n';		// adjust for correct cr
		DoInsert(1,&c);
		if ((c == '\n') && (fAutoIndent) && (!fWordWrap)) {
			DoAutoIndent();
		}
	}
}

/*$	XGEditCore::DoInsert
 *
 *		Handle inserting a string of characters at this location
 */

void XGEditCore::DoInsert(long len, char *array)
{
	long lstart;
	long lend;
	long nend;
	long offset,length;
	long onlines,nnlines;
	long prevlen;
	
	if (fText == NULL) return;
	fPixelPos = -1;				// Reset this for arrow processing
	
	/*
	 *	Record the selection start, end ranges
	 */
	
	lstart = FindLine(fSelStart);
	lend = FindLine(fSelEnd);
	onlines = GetNumLines();
	if (lstart > 0) {
		prevlen = GetLineLength(lstart-1);
	} else {
		prevlen = 0;
	}
	
	/*
	 *	Blow the text around
	 */
	
	try {
		if (fSelEnd != fSelStart) fText->Delete(fSelStart,fSelEnd-fSelStart);
		if (len) fText->Insert(fSelStart,len,(void *)array);
	}
	catch (...) {
		RecalcLineStarts();
		throw;
	}
	RecalcStarts(fSelStart,fSelEnd,len);
	fPrevLine = false;
	
	/*
	 *	Determine if the length of the previous line was changed because
	 *	of word wrapping. If so, expand the selection range so I redraw it.
	 */
	
	if (fWordWrap && (lstart > 0)) {
		if (prevlen != GetLineLength(lstart-1)) lstart--;
	}
		
	/*
	 *	Find the new end of this range
	 */
	
	fSelStart = fSelStart + len;
	fSelEnd = fSelStart;
	nend = FindLine(fSelStart);
	nnlines = GetNumLines();
	if ((nend != lend) || (onlines != nnlines)) {
		/*
		 *	Redraw everything from the old start down
		 */
		
		if (nend < lstart) lstart = nend;
		DrawLines(lstart);
		ScrollInRange();
		return;
	}
	
	/*
	 *	The new bottom is equal to the old bottom. Find the end of this
	 *	paragraph (if we're in word wrap mode) and draw that
	 */
	
	if (fWordWrap) {
		char *ptr;
		
		offset = fSelStart;
		length = fText->GetSize();
		ptr = (char *)**fText;
		while (offset < length) {
			if (ptr[offset] == '\n') break;
			offset++;
		}
		lend = FindLine(offset);
	}
	
	DrawLines(lstart,lend);
	ScrollInRange();
}

/*$	XGEditCore::DoCut
 *
 *		Handle cut operation
 */

void XGEditCore::DoCut(void)
{
	if (fPassword) return;
	DoCopy();
	DoDelete();
}

/*$	XGEditCore::CanCut
 *
 *		Can I perform the cut operation?
 */

bool XGEditCore::CanCut(void)
{
	if (fPassword) return false;
	if (fSelStart != fSelEnd) return true;
	return false;
}

/*$	XGEditCore::DoCopy
 *
 *		Handle copy operation: copy the range to the clipboard
 */

void XGEditCore::DoCopy(void)
{
	XGHandle clip;
	
	if (fPassword) return;				// don't allow copying password out
	
	/*
	 *	Create the clip handle and copy the contents
	 */
	
	clip.SetSize(fSelEnd-fSelStart);
	if (fSelEnd > fSelStart) {
		memcpy(*clip,(void *)(fSelStart + (char *)**fText),fSelEnd - fSelStart);
	}
	ToClipboard(clip);
}

/*$	XGEditCore::DoPaste
 *
 *		Handle paste operation: paste the range from the clipboard
 */

void XGEditCore::DoPaste(void)
{
	XGHandle clip;
	
	FromClipboard(clip);
	XGHandleLock lock(clip);
	DoInsert(clip.GetSize(),(char *)*clip);
}

/*$	XGEditCore::DoSelectAll
 *
 *		Handle select all operation: paste the range from the clipboard
 */

void XGEditCore::DoSelectAll(void)
{
	SetSelect(0,fText->GetSize());
}

/************************************************************************/
/*																		*/
/*	Scroll Routines														*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::DoUpArrow
 *
 *		Handle up arrow. Extend range if shift key down
 */

void XGEditCore::DoUpArrow(bool shift)
{
	long line;
	long s,e;
	
	if (fText == NULL) return;
	
	if (!shift && (fSelEnd != fSelStart)) {
		e = fSelStart;
		s = fSelStart;
	} else {
		if (fPixelPos == -1) fPixelPos = FindPixel(fSelStart);
		line = FindLine(fSelStart)-1;
		if (line < 0) {
			s = 0;
			fPixelPos = -1;
		} else {
			s = FindOffset(line,fPixelPos);
		}
		if (!shift) e = s;
		else e = fSelEnd;
	}
	
	fPrevLine = false;
	SetSelectInternal(s,e);
	ScrollInRange(s);
}

/*$	XGEditCore::DoDownArrow
 *
 *		Handle down arrow. Extend range if shift key down
 */

void XGEditCore::DoDownArrow(bool shift)
{
	long line;
	long s,e;
	
	if (fText == NULL) return;
	
	if (!shift && (fSelEnd != fSelStart)) {
		e = fSelEnd;
		s = fSelEnd;
	} else {
		if (fPixelPos == -1) fPixelPos = FindPixel(fSelEnd);
		line = FindLine(fSelEnd)+1;
		if (line >= GetNumLines()) {
			e = fText->GetSize();
			fPixelPos = -1;
		} else {
			e = FindOffset(line,fPixelPos);
		}
		if (!shift) s = e;
		else s = fSelStart;
	}
	
	fPrevLine = false;
	SetSelectInternal(s,e);
	ScrollInRange(s);
}

/*$	XGEditCore::DoLeftArrow
 *
 *		Handle left arrow. Extend range if shift key down
 */

void XGEditCore::DoLeftArrow(bool shift)
{
	long s,e;
	
	if (fText == NULL) return;
	
	fPixelPos = -1;
	fPrevLine = false;
	if (!shift && (fSelEnd != fSelStart)) {
		s = fSelStart;
		e = fSelStart;
	} else {
		s = fSelStart - 1;
		if (s < 0) s = 0;
		if (!shift) e = s;
		else e = fSelEnd;
	}
	SetSelectInternal(s,e);
	ScrollInRange(s);
}

/*$	XGEditCore::DoRightArrow
 *
 *		Handle down arrow. Extend range if shift key down
 */

void XGEditCore::DoRightArrow(bool shift)
{
	long len;
	long s,e;
	
	if (fText == NULL) return;

	fPixelPos = -1;
	fPrevLine = false;
	if (!shift && (fSelEnd != fSelStart)) {
		e = fSelEnd;
		s = fSelEnd;
	} else {
		e = fSelEnd + 1;
		len = fText->GetSize();
		if (e > len) e = len;
		if (!shift) s = e;
		else s = fSelStart;
	}
	SetSelectInternal(s,e);
	ScrollInRange(e);
}

/*$	XGEditCore::GetCurLine
 *
 *		Get the line I'm currently on
 */

long XGEditCore::GetCurLine(void)
{
	return FindLine(fSelStart);
}

/*$	XGEditCore::SetCurLine
 *
 *		Set the current line
 */

void XGEditCore::SetCurLine(long line)
{
	long s,e;
	
	s = GetLineOffset(line);
	e = GetLineOffset(line+1);
	fPrevLine = false;
	SetSelect(s,e);
	ScrollInRange();
}

/************************************************************************/
/*																		*/
/*	Position Support													*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::GetNumLines
 *
 *		Get the number of _drawable_ lines
 */

long XGEditCore::GetNumLines()
{
	return fStarts.GetSize() / sizeof(long) + 1;
}

/*$	XGEditCore::GetOffset
 *
 *		Get the offset given the point. This factors in scrolling
 */

long XGEditCore::GetOffset(XPoint where)
{
	long xpt,ypt;
	long line;
	
	/*
	 *	Convert to global coords
	 */
	
	xpt = where.h + fXScroll * fPixelStep - fSize.left;
	ypt = where.v + fYScroll * fPixelStep - fSize.top;
	
	/*
	 *	Find line
	 */
	
	line = ypt / fFontHeight;
	return FindOffset(line,xpt);
}

/*$	XGEditCore::FindOffset
 *
 *		Find the character offset given the line and pixel offsets.
 *	The pixel offset is unscrolled 
 */

long XGEditCore::FindOffset(long line, long pixel)
{
	long linelen;
	long lineoff;
	long x;
	long sw,ew;
	char *ptr;
	
	lineoff = GetLineOffset(line);
	linelen = GetLineLength(line);
	
	XGHandleLock lock(*fText);
	ptr = (char *)**fText;
	ew = fXOffset;

	if (fDrawFlag) {
		for (x = 0; x < linelen; x++) {
			sw = fXOffset + TextWidth(*fCurDraw,ptr+lineoff,x+1);
			if (pixel < (sw+ew)/2) break;
			ew = sw;
		}
	} else if (fCurView) {
		XGDraw draw(fCurView);
		if (fCurFont) {
			fCurFont->Attach();
			draw.SetFont(fCurFont);
		}
		
		for (x = 0; x < linelen; x++) {
			sw = fXOffset + TextWidth(draw,ptr+lineoff,x+1);
			if (pixel < (sw+ew)/2) break;
			ew = sw;
		}
	}
	GEndHit = (x >= linelen);
	return x + lineoff;
}

/*$	XGEditCore::GetLineOffset
 *
 *		Find the offset to the start of this line
 */

long XGEditCore::GetLineOffset(long line)
{
	long *ptr;
	
	if (line <= 0) return 0;
	if (line >= GetNumLines()) return fText->GetSize();
	ptr = (long *)*fStarts;
	return ptr[line-1] + 1;
}

/*$	XGEditCore::GetLineLength
 *
 *		Find the length of this line, removing the newline if it's there
 */

long XGEditCore::GetLineLength(long line)
{
	long end;
	char *txt;
	
	if (line >= GetNumLines()-1) end = fText->GetSize();
	else {
		end = GetLineOffset(line+1);
		txt = (char *)**fText;
		if (txt[end-1] == '\n') end--;  // my line ends with '\n'? Decrement length
	}
	
	return end - GetLineOffset(line);
}

/*$	XGEditCore::FindLine
 *
 *		Find the line this thing is on
 */

long XGEditCore::FindLine(long offset)
{
	long len;
	long line;
	
	len = GetNumLines();
	for (line = 1; line < len; line++) {
		if (GetLineOffset(line) > offset) break;
	}
	return line-1;
}

/*$	XGEditCore::FindPixel
 *
 *		Find the pixel offset. This isn't the most efficient way in the
 *	world to do this...
 */

long XGEditCore::FindPixel(long offset)
{
	long start;
	long line;
	char *ptr;
	
	line = FindLine(offset);
	start = GetLineOffset(line);
	if (offset == start) return fXOffset;
	
	
	XGHandleLock lock(*fText);
	ptr = (char *)**fText;

	if (fDrawFlag) {
		return TextWidth(*fCurDraw,ptr + start, offset - start) + fXOffset;
	} else if (fCurView) {
		XGDraw draw(fCurView);
		if (fCurFont) {
			fCurFont->Attach();
			draw.SetFont(fCurFont);
		}
		return TextWidth(draw,ptr + start, offset - start) + fXOffset;
	} else {
		return fXOffset;
	}
}

/************************************************************************/
/*																		*/
/*	Scroll Routines														*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::GetMaxWidth
 *
 *		Get the maximum width in pixels.
 */

long XGEditCore::GetMaxWidth()
{
	long max,line;
	long off;
	long width;
	char *ptr;
	long len;
	
	if (fText == NULL) return 0;
	
	/*
	 *	Width
	 */
	
	if (fWordWrap) {
		return fSize.right - fSize.left;
	} else {
		/*
		 *	Find the length
		 */
		
		XGHandleLock lock(*fText);
		ptr = (char *)**fText;
		
		if (fDrawFlag) {
			max = 0;
			for (line = GetNumLines()-1; line >= 0; line--) {
				off = GetLineOffset(line);
				len = GetLineLength(line);
				
				if (len) {
					width = TextWidth(*fCurDraw,ptr+off,len);
					if (width > max) max = width;
				}
			}
		} else if (fCurView) {
			XGDraw draw(fCurView);
			if (fCurFont) {
				fCurFont->Attach();
				draw.SetFont(fCurFont);
			}
		
			max = 0;
			for (line = GetNumLines()-1; line >= 0; line--) {
				off = GetLineOffset(line);
				len = GetLineLength(line);
				
				if (len) {
					width = TextWidth(draw,ptr+off,len);
					if (width > max) max = width;
				}
			}
		}
		return fXOffset + max;
	}
}

/*$	XGEditCore::GetMaxHeight
 *
 *		Calculate the size of this thing in pixels
 */

long XGEditCore::GetMaxHeight()
{
	if (fText == NULL) return 0;
	return GetNumLines() * fFontHeight;
}

/*$	XGEditCore::SetScrollValue
 *
 *		Set the scrollbar value, scrolling the pixels by the amount
 *	specified
 */

void XGEditCore::SetScrollValue(long x, long y)
{
	if ((x == fXScroll) && (y == fYScroll)) return;

	/*
	 *	Force cursor to be erased if it's visible.
	 */
	
	ClearCursor();
	if (!fDrawFlag && fCurView && fScrollPixels) {
		fCurView->ScrollView(fPixelStep * (fXScroll-x),
							 fPixelStep * (fYScroll-y),
							 &fSize);
	}
	fXScroll = x;
	fYScroll = y;
}

/*$	XGEditCore::ScrollInRange
 *
 *		Scroll this thing into range.
 */

void XGEditCore::ScrollInRange(long sel)
{
	long sline,eline;
	long x,y;
	long xmin,xmax;
	long delta;
	
	if (sel == -1) {
		/*
		 *	Scroll entire range into view
		 */
		
		sline = FindLine(fSelStart);
		eline = FindLine(fSelEnd);
		if (sline != eline) {
			x = 0;
		} else {
			/*
			 *	Update scroll
			 */
			 
			xmin = FindPixel(fSelStart) - fXScroll * fPixelStep + fSize.top;
			xmax = FindPixel(fSelEnd) - fXScroll * fPixelStep + fSize.top;
			x = fXScroll;
			if (xmax > fSize.right) {
				delta = (fPixelStep - 1 + xmax - fSize.right) / fPixelStep;
				x += delta;
				xmin -= delta * fPixelStep;
				xmax -= delta * fPixelStep;
			}
			
			if (xmin < fSize.left) {
				delta = (fPixelStep - 1 + fSize.left - xmin) / fPixelStep;
				x -= delta;
			}
		}
		eline = (1 + eline) * fFontHeight - fYScroll * fPixelStep + fSize.top;
		sline = sline * fFontHeight - fYScroll * fPixelStep + fSize.top;
	} else {
		/*
		 *	Scroll just one thing
		 */
		
		sline = FindLine(fSelStart);
		sline = sline * fFontHeight - fYScroll * fPixelStep + fSize.top;
		eline = sline + fFontHeight;
		
		xmin = FindPixel(sel) - fXScroll * fPixelStep + fSize.top;
		x = fXScroll;
		if (xmin > fSize.right) {
			delta = (fPixelStep - 1 + xmin - fSize.right) / fPixelStep;
			x += delta;
		} else if (xmin < fSize.left) {
			delta = (fPixelStep - 1 + fSize.left - xmin) / fPixelStep;
			x -= delta;
		}
	}
	
	/*
	 *	X has the right value, but y doesn't.
	 */
	
	y = fYScroll;
	if (eline > fSize.bottom) {
		delta = (fPixelStep - 1 + eline - fSize.bottom) / fPixelStep;
		y += delta;
		eline -= delta * fPixelStep;
		sline -= delta * fPixelStep;
	}
	
	if (sline < fSize.top) {
		delta = (fPixelStep - 1 + fSize.top - sline) / fPixelStep;
		y -= delta;
	}
	
	/*
	 *	We have the new scroll values.
	 */
	
	if (fWordWrap) x = 0;
	SetScrollValue(x,y);
}

/************************************************************************/
/*																		*/
/*	Display Control														*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::DoDraw
 *
 *		Handle drawing the view
 */

void XGEditCore::DoDraw(XGDraw &draw, XRect r)
{
	long minline,maxline;
	long len;
	
	if (fCurFont) {
		fCurFont->Attach();
		draw.SetFont(fCurFont);
	}
	
	minline = (fYScroll * fPixelStep - fSize.top + r.top) / fFontHeight;
	maxline = (fYScroll * fPixelStep - fSize.top + r.bottom + fFontHeight - 1) / fFontHeight;
	len = GetNumLines();
	if (len > maxline) len = maxline;
	
	/*
	 *	Draw the lines
	 */
	
	draw.ClipRect(&fSize);
	while (minline < len) DrawLine(draw,minline++);
	
	/*
	 *	And erase the bottom of the screen
	 */
	
	if (minline < maxline) {
		r.top = minline * fFontHeight - fYScroll * fPixelStep + fSize.top;
		if (r.top < r.bottom) {
			draw.SetBackColor(fBackColor);
			draw.EraseRect(r);
		}
	}
	draw.ClipRect();
}

/*$	XGEditCore::DoGainFocus
 *
 *		Gain focus
 */

void XGEditCore::DoGainFocus(bool invalview)
{
	fFocus = true;
	if (fFieldEditor) SetSelect(0,0x7FFFFFFF);		// select all
	if (fCurView && invalview) fCurView->InvalView();
}

/*$	XGEditCore::DoLoseFocus
 *
 *		Lose focus. Basically erase the flashing cursor.
 */

void XGEditCore::DoLoseFocus(bool invalview)
{
//	if (!fDrawFlag && fCurView && fCursorState) {
//		XGDraw draw(fCurView);
//		if (fCurFont) {
//			fCurFont->Attach();
//			draw.SetFont(fCurFont);
//		}
//		InvertCursor(draw);
//		fCursorState = false;
//	}
	
	fFocus = false;
	if (fCurView && invalview) fCurView->InvalView();
}


/************************************************************************/
/*																		*/
/*	Recalculate Line Starts												*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::RecalcLineStarts
 *
 *		Recalculate all of the line starts
 */

void XGEditCore::RecalcLineStarts(void)
{
	bool wrap;
	char *text;
	long width,len;
	long linewidth;
	long ptr;
	long startline;
	long x,y;
	autoptr<XGDraw> drawptr;
	XGDraw *draw = NULL;
	
	if (fText == NULL) return;

	/*
	 *	Initialize drawing context. This is done in a wierd way, so
	 *	that I set this up only when word wrap is enabled.
	 */
	
	wrap = false;
	if (fWordWrap) {
		if (fDrawFlag) wrap = true;
		else if (fCurView) wrap = true;
	}

	if (wrap) {
		if (fDrawFlag) {
			draw = fCurDraw;
		} else {
			drawptr = new XGDraw(fCurView);
			if (fCurFont) {
				fCurFont->Attach();
				drawptr->SetFont(fCurFont);
			}
			draw = drawptr.Ptr();
		}
	}

	/*
	 *	Initialize to calculate line starts
	 */
	
	fStarts.SetSize(0);
	XGHandleLock textlock(*fText);
	text = (char *)**fText;
	
	width = fSize.right - fSize.left - 4;
	if (width < 20) width = 20;
	len = fText->GetSize();
	
	
	/*
	 *	Scan the text
	 */
	
	ptr = 0;
	startline = 0;
	while (ptr < len) {
		if (text[ptr] == '\n') {
			fStarts.Append(sizeof(ptr),(void *)&ptr);
			startline = ptr+1;
		} else if (wrap) {
			/*
			 *	Determine if the width of this thing has overflowed
			 */
			
			x = ptr+1;
			while ((x > startline) && isspace(text[x-1])) x--;
			linewidth = TextWidth(*draw, text + startline, x - startline);
			if (linewidth > width) {
				/*
				 *	Time to word wrap. Scan backwards until I find
				 *	the first white space before this text
				 */
				
				y = x-1;
				while ((y >= startline) && !isspace(text[y])) y--;
				if (y <= startline) {
					y = x-1;
					if (y <= startline) y = startline+1;
				}
				
				/*
				 *	Break at 'y'
				 */
				
				fStarts.Append(sizeof(y),(void *)&y);
				startline = y+1;
			}
		}
		
		ptr++;
	}
	
	/*
	 *	Now make sure the selection range is within limits
	 */
	
	x = fSelStart;
	if (x < 0) x = 0;
	y = fSelEnd;
	if (y > (long)fText->GetSize()) y = fText->GetSize();
	if (x > y) x = y;
	
	if ((x != fSelStart) || (y != fSelEnd)) {
		SetSelect(x,y);
	}
}


/************************************************************************/
/*																		*/
/*	Customization														*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::SetFont
 *
 *		Set the word wrap flag
 */

void XGEditCore::SetFont(XGFont *font)
{
	/* Update the pointers */
	if (font == fCurFont) return;
	if (fCurFont) fCurFont->Detach();
	if (font) font->Attach();
	fCurFont = font;
	
	/* Update the height */
	if (fDrawFlag) {
		fCurFont->Attach();
		fCurDraw->SetFont(fCurFont);
		fFontHeight = fCurDraw->GetFontHeight();
		fFontAscent = fCurDraw->GetFontAscent();
	} else if (fCurView) {
		XGDraw draw(fCurView);
		if (fCurFont) {
			fCurFont->Attach();
			draw.SetFont(fCurFont);
		}
		fFontHeight = draw.GetFontHeight();
		fFontAscent = draw.GetFontAscent();
		fCurView->InvalView();
	}

	if (fWordWrap) RecalcLineStarts();
}

/*$	XGEditCore::SetWordWrap
 *
 *		Set the word wrap flag
 */

void XGEditCore::SetWordWrap(bool flag)
{
	fWordWrap = flag;
	ClearCursor();
	RecalcLineStarts();
	if (!fDrawFlag && fCurView) fCurView->InvalView();
}

/*$	XGEditCore::SetTabStop
 *
 *		Set the tab stop value
 */

void XGEditCore::SetTabStop(long tab)
{
	fTabStop = tab;
	ClearCursor();
	if (fWordWrap) RecalcLineStarts();
	if (!fDrawFlag && fCurView) fCurView->InvalView();
}

/*$	XGEditCore::IsWordBreak
 *
 *		Is this a word break? The default behavior for double-click
 *	support.
 */

bool XGEditCore::IsWordBreak(long offset, char *str)
{
	bool p,c;
	
	p = isalnum(str[offset-1]);
	c = isalnum(str[offset]);
	
	if (p && !c) return true;
	if (!p && c) return true;
	return false;
}


/************************************************************************/
/*																		*/
/*	Internal cursor routines											*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::ClearCursor
 *
 *		Clear the cursor. Also clears the cursor cache.
 */

void XGEditCore::ClearCursor()
{
	if (fCursorState && fCursorVis && (fCursorPos.h != -1) &&
			!fDrawFlag && fCurView) {
		XGDraw draw(fCurView);
		InvertCursor(draw);
		fCursorState = false;
	}
	fCursorVis = true;
	fCursorPos.h = -1;
}

/*$	XGEditCore::InvertCursor
 *
 *		Invert the cursor at the location specified.
 */

void XGEditCore::InvertCursor(XGDraw &draw)
{
	long xpos,ypos;
	long line;
	long off;
	bool prev;
	XRect r;
	
	if (!fCursorVis) return;
	if (fDrawFlag) return;			// no cursors on view-only edit boxes
	if (!fFocus) return;			// focus disabled?
	
	/*
	 *	Recalculate the cursor location if it was cleared by the
	 *	internal 'HideCursor' routine
	 */
	
	if (fCursorPos.h == -1) {
		if (fSelStart == fSelEnd) {
			line = FindLine(fSelStart);
			prev = false;
			if ((fPrevLine == true) && (line > 0) && fWordWrap) {
				if (GetLineOffset(line) == fSelStart) {
					XGHandleLock lock(*fText);
					char *ptr = (char *)**fText;
					if (ptr[fSelStart-1] != '\n') {
						/*
						 *	The cursor should be drawn on the *previous* line.
						 *	Calculate this by hand because FindPixel won't
						 *	do this right in this case.
						 */
						
						prev = true;
						line--;
						ypos = fFontHeight * line - fYScroll * fPixelStep + fSize.top;
						off = GetLineOffset(line);
						xpos = GetLineLength(line);
						xpos = TextWidth(draw,ptr + off,xpos) + fXOffset;
						xpos -= fXScroll * fPixelStep - fSize.top;
					}
				}
			}
				
			if (!prev) {
				/*
				 *	If we didn't have to do the previous thing,...
				 */
				
				ypos = fFontHeight * line - fYScroll * fPixelStep + fSize.top;
				xpos = FindPixel(fSelStart) - fXScroll * fPixelStep + fSize.left;
			}
			if (fWordWrap && (xpos > fSize.right-2)) xpos = fSize.right-2;
			
			if ((xpos >= fSize.left) && (xpos < fSize.right) &&
					(ypos + fFontHeight >= fSize.top) && (ypos < fSize.bottom)) {
				
				fCursorPos.h = xpos;
				fCursorPos.v = ypos;
			} else {
				fCursorVis = false;
				return;
			}
		} else {
			fCursorVis = false;
			return;
		}
	}
	
	/*
	 *	Draw the cursor
	 */
	
	
	r.left = fCursorPos.h - 1;
	r.right = fCursorPos.h;
	r.top = fCursorPos.v;
	r.bottom = fCursorPos.v + fFontHeight;
	draw.InvertRect(r);
}

/************************************************************************/
/*																		*/
/*	Internal drag selection support										*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::SetDragSelect
 *
 *		Set the selection range, but handle dragging and selection of
 *	words
 */

void XGEditCore::SetDragSelect(long s, long e)
{
	long tmp;
	
	if (s > e) {
		tmp = s;
		s = e;
		e = tmp;
	}
	
	if (fDragWord) GetWordSelect(&s,&e);
	SetSelect(s,e);
}

/*$	XGEditCore::GetWordSelect
 *
 *		Get the word selection range
 */

void XGEditCore::GetWordSelect(long *s, long *e)
{
	XGHandleLock lock(*fText);
	char *ptr = (char *)**fText;
	long len = fText->GetSize();
	
	while (*s > 0) {
		if (IsWordBreak(*s,ptr)) break;
		(*s)--;
	}
	
	while (*e < len) {
		if (IsWordBreak(*e,ptr)) break;
		(*e)++;
	}
}

/*$	XGEditCore::SetSelectInternal
 *
 *		Set the selection range to the range specified
 */

void XGEditCore::SetSelectInternal(long start, long end)
{
	long tmp;
	long lstart,lend;
	long ostart,oend;
	long min,max;
	bool dflag;
	long istart,iend;
	
	/*
	 *	Update the parameters to legal ranges
	 */
	
	if (start > end) {
		tmp = end;
		end = start;
		start = tmp;
	}
	
	if (fText) tmp = fText->GetSize();
	else tmp = 0;

	if (start < 0) start = 0;
	if (end > tmp) end = tmp;
	if (end < start) start = 0, end = 0;
	
	/*
	 *	Did the selection range change? If not, skip this process
	 */
	
	if ((fSelStart == start) && (fSelEnd == end)) return;
	if (fDrawFlag || (fCurView == NULL)) {
		fSelStart = start;
		fSelEnd = end;
		return;
	}
	
	/*
	 *	Selection range changed. Find the range modified and change it
	 */
	
	XGDraw draw(fCurView);
	if (fCurFont) {
		fCurFont->Attach();
		draw.SetFont(fCurFont);
	}
	draw.ClipRect(&fSize);
	
	HideCursor(draw);					// hide the cursor.

	lstart = FindLine(start);
	lend = FindLine(end);
	ostart = FindLine(fSelStart);
	oend = FindLine(fSelEnd);
	
	min = (lstart < ostart) ? lstart : ostart;
	max = (lend > oend) ? lend : oend;
	
	istart = fSelStart;
	iend = fSelEnd;
	fSelStart = start;
	fSelEnd = end;

	for (tmp = min; tmp <= max; tmp++) {
		dflag = false;

		/*
		 *	Handle the boundary conditions
		 */
		
		if ((tmp == lstart) || (tmp == ostart)) {
			if ((tmp == lend) || (tmp == oend)) dflag = true;
			else dflag |= (start != istart);
		}
		if ((tmp == lend) || (tmp == oend)) {
			if ((tmp == lstart) || (tmp == ostart)) dflag = true;
			else dflag |= (end != iend);
		}
		
		/*
		 *	Determine if I'm in the middle of
		 *	the selection ranges
		 */
		
		if (!dflag) {
			if ((lstart <= tmp) && (tmp <= lend)) {
				dflag = true;
			}
			if ((ostart <= tmp) && (tmp <= oend)) {
				dflag = (dflag) ? false : true;	// work around compiler bug
			}
		}
		
		/*
		 *	Can I draw this?
		 */
		
		if (dflag) DrawLine(draw,tmp);
	}
	
	ShowCursor(draw);				// force the cursor to be shown.
	draw.ClipRect();
}


/************************************************************************/
/*																		*/
/*	Auto Indent Support													*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::DoAutoIndent
 *
 *		Handle autoindenting right after inserting a new line
 */

void XGEditCore::DoAutoIndent(void)
{
	long line;
	char buffer[256];
	long start,len;
	long pos;
	char *ptr;
	
	line = FindLine(fSelStart)-1;
	if (line < 0) return;
	
	start = GetLineOffset(line);
	len = GetLineLength(line);
	if (len > 256) len = 256;
	
	/*
	 *	Copy the white space from the previous line
	 */
	
	fText->Lock();
	ptr = start + (char *)**fText;
	for (pos = 0; pos < len; pos++) {
		if (isspace(ptr[pos])) buffer[pos] = ptr[pos];
		else break;
	}
	fText->Unlock();
	
	/*
	 *	And insert it at my current position
	 */
	
	if (pos > 0) DoInsert(pos,buffer);
}

/************************************************************************/
/*																		*/
/*	Line Drawing														*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::DrawLine
 *
 *		Draw the specified line, including selection.
 */

void XGEditCore::DrawLine(XGDraw &draw, long line)
{
	XRect r;
	bool sel;
	XRect invert;
	char *ptr;
	long start,end;
	long pos;
	long ypos;
	
	/*
	 *	Is the line less than zero? If so, don't draw anything.
	 *	This allows me to stack editing items without erasing below
	 *	the bottom.
	 */
	
	if (line < 0) return;
	
	/*
	 *	Find where this is drawn
	 */
	
	ypos = line * fFontHeight - fYScroll * fPixelStep + fSize.top;
	if (ypos > fSize.bottom) return;
	if (ypos + fFontHeight < fSize.top) return;		// not visible
	
	r.top = ypos;
	r.bottom = ypos + fFontHeight;
	ypos += fFontAscent;
	
	/*
	 *	Erase the line to make sure it's correctly erased.
	 */
	
	r.left = fSize.left;
	r.right = fSize.right;
	draw.SetBackColor(fBackColor);
	draw.EraseRect(r);
	
	/*
	 *	Get the text
	 */
	
	XGHandleLock lock(*fText);
	ptr = (char *)**fText;
	start = GetLineOffset(line);
	end = start + GetLineLength(line);
	
	/*
	 *	Calculate the selection range in the rectangle
	 */
	
	invert = r;
	if (fDrawFlag) sel = false;
	else if (!fFocus) sel = false;			// I don't have focus
	else if (fSelEnd == fSelStart) sel = false;
	else if ((fSelEnd <= start) || (end < fSelStart)) sel = false;
	else {
		sel = true;
		if (fSelStart <= start) {
			invert.left = r.left;
		} else {
			pos = r.left + 
				  fXOffset + 
				  TextWidth(draw,ptr+start,fSelStart-start) -
				  fXScroll * fPixelStep;
			if (pos < r.left) invert.left = r.left;
			else invert.left = pos;
		}
		
		if (fSelEnd > end) {
			invert.right = r.right;
		} else {
			pos = r.left + 
				  fXOffset + 
				  TextWidth(draw,ptr+start,fSelEnd-start) -
				  fXScroll * fPixelStep;
			if (pos > r.right) invert.right = r.right;
			else invert.right = pos;
		}
		
		if (invert.right <= invert.left) {
			sel = false;
		} else {
			sel = true;
		}
	}
	
	/*
	 *	Okay, now we're here. Change end selection if there is
	 *	one line, and we're in 'form' mode
	 */
	
	if (fFieldEditor) {
		if ((fSelStart != fSelEnd) && (GetNumLines() == 1)) {
			if (fSelEnd == (long)fText->GetSize()) {
				/*
				 *	Selection runs to end.
				 */
				
				invert.right = r.right;
			}
		}
	}
		
	/*
	 *	Draw the entire line
	 */
	
	pos = r.left + fXOffset - fXScroll * fPixelStep;
	draw.SetForeColor(fForeColor);
	draw.SetBackColor(fBackColor);
	if (end > start) {
		draw.MoveTo(pos, ypos);
		DrawText(draw,ptr + start, 0, end - start);
	}
	
	/*
	 *	Now draw the hilite range if any
	 */
	
	if (sel) {
		draw.ClipRect(&invert);
		draw.SetForeColor(KXGColorHiliteText);
		draw.SetBackColor(KXGColorHilite);
		draw.EraseRect(invert);
		if (end > start) {
			draw.MoveTo(pos, ypos);
			DrawText(draw,ptr + start, 0, end - start);
		}
		draw.ClipRect();
		draw.SetForeColor(fForeColor);
		draw.SetBackColor(fBackColor);
	}
}

/*$	XGEditCore::DrawLines
 *
 *		Draw the lines specified.
 */

void XGEditCore::DrawLines(long start, long end)
{
	long minline,maxline;
	long len;
	XGDraw *draw = NULL;
	autoptr<XGDraw> drawptr;
	
	minline = (fYScroll * fPixelStep - fSize.top) / fFontHeight;
	maxline = (fYScroll * fPixelStep + fSize.bottom - fSize.top + fFontHeight - 1) / fFontHeight;
	if (minline < start) minline = start;
	if ((maxline >= end) && (end != -1)) maxline = end+1;
	
	len = GetNumLines();
	if (len > maxline) len = maxline;
	
	/*
	 *	Draw the lines
	 */
	
	if (fDrawFlag) {
		draw = fCurDraw;
	} else if (fCurView) {
		drawptr = new XGDraw(fCurView);
		if (fCurFont) {
			fCurFont->Attach();
			drawptr->SetFont(fCurFont);
		}
		draw = drawptr.Ptr();
	} else {
		return;
	}
	draw->ClipRect(&fSize);
	HideCursor(*draw);			// hide the cursor

	while (minline < len) DrawLine(*draw,minline++);
	
	/*
	 *	And erase the bottom of the screen
	 */
	
	if ((end == -1) && (minline < maxline)) {
		XRect r = fSize;
		
		r.top = minline * fFontHeight - fYScroll * fPixelStep + fSize.top;
		if (r.top < r.bottom) {
			draw->SetBackColor(fBackColor);
			draw->EraseRect(r);
		}
	}
	
	/*
	 *	Since I'm only called while I'm entering information,
	 *	make sure the cursor is shown
	 */
	
	ShowCursor(*draw);			// shows the cursor if it's not
	draw->ClipRect();
}


/************************************************************************/
/*																		*/
/*	Line Recalculation Core												*/
/*																		*/
/************************************************************************/

/*$	XGEditCore::RecalcStarts
 *
 *		Recalculate the line starts. This calculates the line starts of
 *	the text pointer, and then wedges this into the existing line starts
 *	array.
 */

void XGEditCore::RecalcStarts(long start, long end, long len)
{
	XGHandle newstarts;
	bool wrap;
	long width;
	long linewidth;
	long ptr;
	long startline;
	long x,y;
	autoptr<XGDraw> drawptr;
	XGDraw *draw = NULL;
	long *sindex,*nindex;
	long sval,eval,slen;
	long dlen;
	char *text;
	long tlen;
	long line;
	
	if (fText == NULL) return;
	XGHandleLock lock(*fText);
	text = (char *)**fText;

	/*
	 *	Initialize drawing context. This is done in a wierd way, so
	 *	that I set this up only when word wrap is enabled.
	 */
	
	wrap = false;
	if (fWordWrap) {
		if (fDrawFlag) wrap = true;
		else if (fCurView) wrap = true;
	}

	if (wrap) {
		if (fDrawFlag) {
			draw = fCurDraw;
		} else {
			drawptr = new XGDraw(fCurView);
			if (fCurFont) {
				fCurFont->Attach();
				drawptr->SetFont(fCurFont);
			}
			draw = drawptr.Ptr();
		}
	}

	/*
	 *	Initialize to calculate line starts
	 */
	
	newstarts.SetSize(0);
	width = fSize.right - fSize.left - 10;
	if (width < 20) width = 20;
	
	/*
	 *	Initialize the width of the first line in this inserted text
	 */
	
	line = FindLine(start);
	startline = GetLineOffset(line);
	
	/*
	 *	Scan the text forward until I hit the end or not.
	 */
	
	if (wrap) {
		/*
		 *	Move back to the start of the previous line just in
		 *	case this operation affects the newline.
		 */
		
		len += start - startline;
		start = startline;
		if ((start > 0) && (line > 0) && (text[start-1] != '\n')) {
			startline = GetLineOffset(line-1);
			len += start - startline;
			start = startline;
		}
	}
	
	ptr = start;
	if (wrap) tlen = fText->GetSize();
	else tlen = len + start;

	while (ptr < tlen) {
		if (text[ptr] == '\n') {
			newstarts.Append(sizeof(ptr),(void *)&ptr);
			startline = ptr+1;
		} else if (wrap) {
			/*
			 *	Determine if the width of this thing has overflowed
			 */
			
			x = ptr+1;
			while ((x > startline) && isspace(text[x-1])) x--;
			linewidth = TextWidth(*draw, text + startline, x - startline);
			if (linewidth > width) {
				/*
				 *	Time to word wrap. Scan backwards until I find
				 *	the first white space before this text
				 */
				
				y = x-1;
				while ((y >= startline) && !isspace(text[y])) y--;
				if (y <= startline) {
					y = x-1;
					if (y <= startline) y = startline+1;
				}
				
				/*
				 *	Break at 'y'
				 */
				
				newstarts.Append(sizeof(y),(void *)&y);
				startline = y+1;
			}
		}
		
		if (ptr >= (len + start)) {
			if (text[ptr] == '\n') {
				ptr++;
				break;
			}
		}
		ptr++;
	}
	
	/* Adjust boundaries if this thing overflows because of word wrap */
	/* calculations */
	end += ptr - (len + start);
	len += ptr - (len + start);

	/*
	 *	Now that we've gotten here, 'newstarts' contains the new
	 *	linestart offsets that I need to insert. Find where they
	 *	are inserted, and rearrange the old list
	 */
	
	sindex = (long *)*fStarts;
	slen = fStarts.GetSize() / sizeof(long);
	for (sval = 0; sval < slen; sval++) {		// find start column
		if (sindex[sval] >= start) break;
	}
	for (eval = sval; eval < slen; eval++) {	// find end column
		if (sindex[eval] >= end) break;
	}
	for (x = eval; x < slen; x++) {				// adjust stuff after this
		sindex[x] += len - (end - start);
	}
	
	dlen = (newstarts.GetSize() / sizeof(long)) - (eval - sval);
	if (dlen < 0) {
		// Delete entries
		fStarts.Delete(sval * sizeof(long), - dlen * sizeof(long));
	} else if (dlen > 0) {
		// Insert entries
		fStarts.Insert(sval * sizeof(long), dlen * sizeof(long));
	}
	
	// Copy entries
	sindex = (long *)*fStarts;
	nindex = (long *)*newstarts;
	for (x = newstarts.GetSize()/sizeof(long) - 1; x >= 0; x--) {
		sindex[x + sval] = nindex[x];
	}
}

/************************************************************************/
/*																		*/
/*	Clipboard															*/
/*																		*/
/************************************************************************/

#if OPT_XWIN
XGHandle	GClipboard;				// defined in YAAF libraries
#endif

#if OPT_WINOS

/*$	CvtToCRLF
 *
 *		CRLF conversion of text handle
 */

static HANDLE CvtToCRLF(XGHandle &in)
{
	unsigned long i,len;
	char *x;
	unsigned long nlen;
	HANDLE h;
	char *ptr;
	char c;

	/*
	 *	Figure out the size needed for the handle
	 */
		
	len = in.GetSize();
	XGHandleLock lock(in);
	x = (char *)*in;
	i = 0;
	nlen = 1;
	while (i < len) {
		if ((x[i] == 0x0D) || (x[i] == 0x0A)) {
			c = x[i];
			i++;
			nlen += 2;
			if (i >= len) break;
			if ((c != x[i]) && ((x[i] == 0x0D) || (x[i] == 0x0A))) {
				i++;
			}
		} else {
			nlen++;
			i++;
		}
	}
	
	/*
	 *	Create a movable handle of the correct size
	 */
	
	h = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE,nlen);
	if (!h) throw XGPostError(KXGOutMemory);
	ptr = (char *)::GlobalLock(h);
	
	/*
	 *	Copy the data into the CF_TEXT format expected by Windows
	 */
	
	nlen = 0;
	i = 0;
	while (i < len) {
		if ((x[i] == 0x0D) || (x[i] == 0x0A)) {
			c = x[i];
			ptr[nlen++] = 0x0D;
			ptr[nlen++] = 0x0A;
			i++;
			if (i >= len) break;
			if ((c != x[i]) && ((x[i] == 0x0D) || (x[i] == 0x0A))) {
				i++;
			}
		} else ptr[nlen++] = x[i++];
	}
	ptr[nlen++] = 0;
	
	/*
	 *	Unlock and return
	 */
	
	::GlobalUnlock(h);
	return h;
}

/*$	CvtFromCRLF
 *
 *		CRLF conversion of text handle
 */

static void CvtFromCRLF(XGHandle &out, HANDLE in)
{
	char *ptr;
	unsigned long hsize;
	unsigned long len;
	unsigned long i;
	char *optr;
	
	/*
	 *	Figure out the size
	 */
	
	hsize = ::GlobalSize(in);
	ptr = (char *)::GlobalLock(in);
	for (len = 0, i = 0; i < hsize; i++) {
		if (ptr[i] == 0) break;
		if (ptr[i] != 0x0A) len++;
	}
	
	/*
	 *	Now set the size and copy
	 */
	
	out.SetSize(len);
	
	XGHandleLock lock(out);
	optr = (char *)*out;
	for (len = 0, i = 0; i < hsize; i++) {
		if (ptr[i] == 0) break;
		if (ptr[i] == 0x0D) {
			optr[len++] = '\n';
		} else if (ptr[i] != 0x0A) {
			optr[len++] = ptr[i];
		}
	}
	::GlobalUnlock(in);
}

#endif // OPT_WINOS

/*$	XGEditCore::ToClipboard
 *
 *		Put this onto the clipboard
 */

void XGEditCore::ToClipboard(XGHandle &text)
{
#if OPT_MACOS
	long i,len;
	char *ptr;
	
	::ZeroScrap();
	if (0 != (len = text.GetSize())) {
		XGHandleLock lock(text);
		
		ptr = (char *)*text;
		for (i = 0; i < len; i++) {
			if (ptr[i] == 0x0A) ptr[i] = 0x0D;
		}
		::PutScrap((long)text.GetSize(),'TEXT',(Ptr)ptr);
	}
#endif

#if OPT_XWIN
	long len;
	
	len = text.GetSize();
	GClipboard.SetSize(len);
	if (len) ::memcpy(*GClipboard,*text,len);
#endif

#if OPT_WINOS
	HANDLE h;
	
	/*
	 *	Copy text
	 */
	
	h = CvtToCRLF(text);
	text.SetSize(0);						// zero out old handle
	
	/*
	 *	Convert to a global handle
	 */
	 
	::OpenClipboard(NULL);
	::EmptyClipboard();
	::SetClipboardData(CF_TEXT,h);
	::CloseClipboard();
#endif
}

/*$	XGEditCore::FromClipboard
 *
 *		Copy this from the clipboard
 */

void XGEditCore::FromClipboard(XGHandle &text)
{
#if OPT_MACOS
	Handle h;
	long res;
	long offset;
	long i,len;
	char *ptr;
	
	h = ::NewHandle(0);
	res = ::GetScrap(h,'TEXT',&offset);
	if (res < 0) {
		text.SetSize(0);
	} else {
		text.SetSize((unsigned long)::GetHandleSize(h));
		memcpy(*text,*h,text.GetSize());
		
		len = text.GetSize();
		ptr = (char *)*text;
		for (i = 0; i < len; i++) {
			if ((ptr[i] == 0x0D) || (ptr[i] == 0x0A)) ptr[i] = '\n';
		}
	}
	::DisposeHandle(h);
#endif

#if OPT_XWIN
	long len;
	
	len = GClipboard.GetSize();
	text.SetSize(len);
	if (len) ::memcpy(*text,*GClipboard,len);
#endif

#if OPT_WINOS
	HANDLE h;
	
	/*
	 *	Get text
	 */
	
	::OpenClipboard(NULL);
	h = ::GetClipboardData(CF_TEXT);
	if (!h) {
		text.SetSize(0);
	} else {
		CvtFromCRLF(text,h);
	}
	::CloseClipboard();
#endif
}

/************************************************************************/
/*																		*/
/*	Tabbed drawing support												*/
/*																		*/
/************************************************************************/

