/*
 * Java core library component.
 *
 * Copyright (c) 1997, 1998
 *      Transvirtual Technologies, Inc.  All rights reserved.
 *
 * See the file "license.terms" for information on usage and redistribution
 * of this file.
 */


package java.awt;

import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Enumeration;
import java.util.Vector;

public class TextArea
  extends TextComponent
{
	final public static int SCROLLBARS_VERTICAL_ONLY = 1;
	final public static int SCROLLBARS_HORIZONTAL_ONLY = 2;
	final public static int SCROLLBARS_NONE = 0;
	final public static int SCROLLBARS_BOTH = 3;
	int tabWidth;
	int crows;
	int ccols;
	TextPane tp = new TextPane();

class TextPane
  extends RowCanvas
  implements KeyListener, MouseListener, MouseMotionListener
{
	TextCursor tCursor = new TextCursor();
	Point sSel = new Point();
	Point eSel = new Point();

public TextPane () {
	xOffsInit = 4;
	xOffs = 4;

	setCursor( Cursor.getPredefinedCursor( Cursor.TEXT_CURSOR));
	tCursor.setPos( xOffs, rowHeight + BORDER_WIDTH - 1);
	insertLine( "", 0);
	addKeyListener( this);
	addMouseListener( this);
	addMouseMotionListener( this);
}

void append( String s) {
	cursorTextEnd( false);
	insert( s, true);
}

void backspace() {
	TextBuffer tb = getCursorLine();
	if ( tCursor.index >= 1 ) {
		tb.remove( tCursor.index-1, 1);
		setCursorPos( tCursor.index-1, tCursor.yindex, false, true);
		repaintLine( tCursor.yindex, tCursor.index, tb);
	}
	else if ( cursorLeft(1, false) ){
		TextBuffer tb1 = getCursorLine();
		tb1.append( tb);
		rows.removeElement( tb);
		repaint();
		updateVScroll();
	}
}

void blankCursor( Graphics g) {
	TextBuffer tb;
		
	g.setColor( getBackground() );
	tCursor.blank( g, xOffs, getRowYPos( tCursor.yindex) );
	g.setColor( getForeground() );
	tb = (TextBuffer)rows.elementAt( tCursor.yindex );
	tb.paint( g, xOffs, tCursor.y, rowHeight, tCursor.index, 1);
}

void cursorDown( int steps, boolean extend) {
	int newY = Math.min( tCursor.yindex + steps, rows.size() -1);
	if ( newY != tCursor.yindex) {
		if ( extend )
			updateSel( tCursor.index, newY, true);
		else
			setCursorPos( tCursor.index, newY, true, true);
	}
}

void cursorEnd( boolean extend) {
	TextBuffer tb = getCursorLine();
	if ( extend)
		updateSel( tb.len, tCursor.yindex, true);
	else
		setCursorPos( tb.len, tCursor.yindex, true, true);
}

void cursorHome( boolean extend) {
	if ( extend)
		updateSel( 0, tCursor.yindex, true);
	else
		setCursorPos( 0, tCursor.yindex, true, true);
}

boolean cursorLeft( int steps, boolean extend) {
	int nx = -1, ny = -1;
	
	if ( tCursor.index >= steps) {
		nx = tCursor.index - steps;
		ny = tCursor.yindex;
	}
	else if ( tCursor.yindex > 0 ) {
		nx = ((TextBuffer)rows.elementAt( tCursor.yindex-1)).len;
		ny = tCursor.yindex-1;
	}

	if ( ny > -1 ) {
		if ( extend )
			updateSel( nx, ny, true);
		else
			setCursorPos( nx, ny, true, true);
		return true;
	}
	
	return false;
}

void cursorRight( int steps, boolean extend) {
	int nx = -1, ny = -1;
	TextBuffer tb = (TextBuffer)rows.elementAt( tCursor.yindex);
	
	if ( tb.len >= tCursor.index + steps) {
		nx = tCursor.index + steps;
		ny = tCursor.yindex;
	}
	else if ( tCursor.yindex < rows.size() - 1 ) {
		nx = 0;
		ny = tCursor.yindex+1;
	}

	if ( ny > -1 ) {
		if ( extend)
			updateSel( nx, ny, true);
		else
			setCursorPos( nx, ny, true, true);
	}
}

void cursorTextEnd( boolean extend) {
	int yIdx = rows.size() - 1;
	TextBuffer tb = (TextBuffer)rows.elementAt( yIdx);
	if ( extend)
		updateSel( tb.len, yIdx, true);
	else
		setCursorPos( tb.len, yIdx, true, true);
}

void cursorTextHome( boolean extend) {
	if ( extend)
		updateSel( 0, 0, true);
	else
		setCursorPos( 0, 0, true, true);
}

void cursorUp( int steps, boolean extend) {
	int newY = Math.max( tCursor.yindex - steps, 0);
	if ( newY != tCursor.yindex) {
		if ( extend )
			updateSel( tCursor.index, newY, true);
		else
			setCursorPos( tCursor.index, newY, true, true);
	}
}

void del() {
	TextBuffer tb = getCursorLine();
	if ( tb.len > tCursor.index ) {
		tb.remove( tCursor.index, 1);
		repaintLine( tCursor.yindex, tCursor.index, tb);
	}
	else if ( tCursor.yindex < rows.size() - 1 ){
		TextBuffer tb1 = (TextBuffer)rows.elementAt( tCursor.yindex+1);
		tb.append( tb1);
		rows.removeElement( tb1);
		repaint();
		updateVScroll();
	}
}

void deleteSel() {
	Point ps = getSelStart();
	Point pe = getSelEnd();
	TextBuffer tb = (TextBuffer)rows.elementAt( ps.y);
	int af = 1;
	
	if ( (ps.x == pe.x) && (ps.y == pe.y) )
		return;
		
	if ( ps.y == pe.y ) {
		tb.remove( ps.x, pe.x - ps.x);
		setCursorPos( ps.x, ps.y, true, true);
		return;
	}

	if ( ps.x > 0 )
		tb.remove( ps.x, tb.len - ps.x);
	else
		af = 0;
	tb = (TextBuffer)rows.elementAt( pe.y);
	tb.remove( 0, pe.x);
	
	for ( int i = ps.y + af; i < pe.y; i++)
		rows.removeElementAt( ps.y + af);
		
	setCursorPos( ps.x, ps.y, false, true);
	updateVScroll();
	repaint();
}

public void focusGained( FocusEvent e) {
	Graphics g = getClippedGraphics();
	if ( g != null ) {
		repaintCursor( g);
		g.dispose();
	}
	super.focusGained( e);
}

public void focusLost( FocusEvent e) {
	Graphics g = getClippedGraphics();
	if ( g != null ) {
		paintInactiveCursor( g);
		g.dispose();
	}
	super.focusLost( e);
	
	if ( hasSel() )
		copyToClipboard();
}

int get1D( Point p) {
	int xt = 0;
	
	for ( int i=0; i<p.y; i++) {
		TextBuffer tb = (TextBuffer)rows.elementAt( i);
		xt += tb.len;
	}
	xt += p.x;
	
	return xt;
}

Point get2D( int pos) {
	int rs = rows.size();
	int xt = 0;
	
	for ( int i=0; i<rs; i++) {
		TextBuffer tb = (TextBuffer)rows.elementAt( i);
		xt += tb.len;
		if (xt >= pos)
			return new Point( tb.len - xt + pos, i);
	}
	
	return new Point( -1, -1);
}

int getCol( int row, int x) {
	TextBuffer tb = (TextBuffer) rows.elementAt( row);
	return tb.getIdx( x - xOffs);
}

TextBuffer getCursorLine() {
	return (TextBuffer)rows.elementAt( tCursor.yindex);
}

Point getSelEnd() {
	if ( sSel.y > eSel.y )
		return sSel;
	if ( sSel.y == eSel.y ) {
		if ( sSel.x > eSel.x )
			return sSel;
		return eSel;
	}
	return eSel;
}

Point getSelStart() {
	if ( sSel.y < eSel.y )
		return sSel;
	if ( sSel.y == eSel.y ) {
		if ( sSel.x < eSel.x )
			return sSel;
		return eSel;
	}
	return eSel;
}

boolean hasSel() {
	return ( (sSel.x != eSel.x) || (sSel.y != eSel.y) );
}

boolean hasSel( int row) {
	if ( hasSel() )
		return ( (sSel.y <= row) || (eSel.y >= row) );
	return false;
}

void insert( String s, boolean keepCursor) {

	if ( (s == null) || (s.length() == 0) )
		return;
		
	String[] lns = breakLines( s);
	int lnsi = 0;
	TextBuffer tb = (TextBuffer)rows.elementAt( tCursor.yindex);
	
	if ( lns.length == 0 )
		return;
		
	if ( lns.length == 1 ) {
		int ncp = keepCursor ? tCursor.index : tCursor.index + lns[0].length();
		tb.insert( tCursor.index, lns[0] );
		setCursorPos( ncp, tCursor.yindex, false, false);
		repaintLine( tCursor.yindex, 0, tb);
		return;
	}
	
	String le = tb.getString( tCursor.index, tb.len - tCursor.index);
	tb.remove( tCursor.index, tb.len - tCursor.index);
	tb.append( lns[lnsi++] );
	
	while ( lnsi < lns.length)
		tb = insertLine( lns[lnsi++], tCursor.yindex + lnsi - 1 );
	
	if ( ! keepCursor)	
		setCursorPos( tb.len, tCursor.yindex + lns.length - 1, false, false);
	tb.append( le);

	updateVScroll();
	repaint();
}

void insert( String s, int pos) {

	Point p = get2D( pos);
	setCursorPos( p.x, p.y, true, true);
	
	insert( s, false);
}

void insertChar( char c) {
	boolean app;
	
	TextBuffer tb = (TextBuffer)rows.elementAt( tCursor.yindex);
	app = ( tCursor.index == tb.len);
	tb.insert( tCursor.index, c);
	
	if ( !app){
		//leading problem ( repaint left cursor char also)
		int sIdx = (tCursor.index > 0 ) ? tCursor.index-1 : tCursor.index;
		repaintLine( tCursor.yindex, sIdx, tb);
	}

	cursorRight( 1, false);
}

TextBuffer insertLine( String str, int lIdx) {
	TextBuffer tb = new TextBuffer( str);
	tb.setMetrics( fm, tabWidth);
	rows.insertElementAt( tb, lIdx);
	return tb;
}

public void keyPressed( KeyEvent e) {
	int code = e.getKeyCode();
	int mods = e.getModifiers();
	boolean sh = e.isShiftDown();

	if ( handleClipboard( e) ) {
		e.consume();
		return;
	}

	//do not consume unused key for HotKeyHandler
	if ( (mods != 0) && (mods != e.SHIFT_MASK) )
		return;
		
	switch( code) {
		case e.VK_LEFT:
			cursorLeft( 1, sh);
			break;
		case e.VK_RIGHT:
			cursorRight( 1, sh);
			break;
		case e.VK_UP:
			cursorUp( 1, sh);
			break;
		case e.VK_DOWN:
			cursorDown( 1, sh);
			break;
		case e.VK_TAB:
		  if ( sh )
				return;	//do not consume event for HotKeyHandler
  		insertChar( '\t' );
			break;
		case e.VK_ENTER:
			newline();
			break;
		case e.VK_BACK_SPACE:
			if ( hasSel() )
				replaceSelectionWith("");
			else
				backspace();
			break;
		case e.VK_DELETE:
			if ( hasSel() )
				replaceSelectionWith("");
			else
				del();
			break;
		case e.VK_HOME:
			cursorHome( sh);
			break;
		case e.VK_END:
			cursorEnd( sh);
			break;
		case e.VK_PAGE_UP:
			pageUp( sh);
			break;
		case e.VK_PAGE_DOWN:
			pageDown( sh);
			break;
		case e.VK_ESCAPE:
			resetSel( true);
			break;
		default:
		  return;
	}

  e.consume();
}

public void keyReleased( KeyEvent e) {
}

public void keyTyped( KeyEvent e) {

	if ( ! isEditable || ! isPrintableTyped( e) )
		return;

	char c = e.getKeyChar();

	if ( hasSel() ) {
		Character cc = new Character( c);
		replaceSelectionWith( cc.toString() );
	}
	else
		insertChar( c );
		
	if ( textListener != null) {
		tEvt.setSource( parent);
		processTextEvent( tEvt);
	}
}

public void mouseClicked( MouseEvent e) {
}

public void mouseDragged( MouseEvent e) {
	int y = getRowIdx( e.getY() );
	int x = getCol( y, e.getX() );
	updateSel( x, y, true);
}

public void mouseEntered( MouseEvent e) {
}

public void mouseExited( MouseEvent e) {
}

public void mouseMoved( MouseEvent e) {
}

public void mousePressed( MouseEvent e) {
	int mods = e.getModifiers();
	
	switch ( mods) {
		case InputEvent.BUTTON1_MASK:
			tp.requestFocus();
			resetSel( true);
			int y = getRowIdx( e.getY() );
			int x = getCol( y, e.getX() );
			setCursorPos( x, y, true, true);
			break;
		case InputEvent.BUTTON2_MASK:
			pasteFromClipboard();
			break;
	}
	
}

public void mouseReleased( MouseEvent e) {
}

void newline() {
	String s = "";
	
	TextBuffer tb = getCursorLine();
	if ( tb.len > tCursor.index ) {
		int rl = tb.len - tCursor.index;
		s = tb.getString( tCursor.index, rl);
		tb.remove( tCursor.index, rl);
	}
	
	TextBuffer tbNew = insertLine( s, tCursor.yindex+1);
	tbNew.copyLevelFrom( tb);
	
	updateVScroll();
	
	setCursorPos( tbNew.getLevel(), tCursor.yindex+1, true, true);
	repaintRows( tCursor.yindex-1, getVisibleRows() );

}

void pageDown( boolean extend) {
	int vr = getVisibleRows();
	int newY = Math.min( tCursor.yindex + vr, rows.size() - 1);
	if ( extend)
		updateSel( tCursor.index, newY, true);
	else
		setCursorPos( tCursor.index, newY, true, true);
}

void pageUp( boolean extend) {
	int vr = getVisibleRows();
	int newY = Math.max( tCursor.yindex - vr, 0);
	if ( extend)
		updateSel( tCursor.index, newY, true);
	else
		setCursorPos( tCursor.index, newY, true, true);
}

public void paint( Graphics g) {
	paintBorder( g);

	int d = BORDER_WIDTH;
	g.clipRect( d, d, width -2*d, height -2*d);

	repaintRows( g, first, rows.size()-first );		
}

void paintInactiveCursor( Graphics g) {
	g.setColor( Defaults.TextCursorInactiveClr );
	tCursor.blank( g, xOffs, getRowYPos( tCursor.yindex) );
}

void repaintCursor( Graphics g) {
	if ( AWTEvent.keyTgt == this)
		tCursor.paint( g, xOffs, getRowYPos( tCursor.yindex) );
	else
		paintInactiveCursor( g);
}

void repaintLine( int row, int startX, TextBuffer tb ) {
	Graphics g = getClippedGraphics();

	if ( g != null ) {
		repaintLine( row, startX, tb, g);
		g.dispose();
	}
}

void repaintLine( int row, int startX, TextBuffer tb, Graphics g) {
	int x0, w;
	int d = BORDER_WIDTH;
	
	if ( tb == null )
		tb = (TextBuffer)rows.elementAt( row);
		
	int ss = selXStart( row);
	int se = selXEnd( row, tb);
	int y0 = d + (row - first) * rowHeight;
	int xa = tCursor.width;
	
	if ( ss == se ) {
		x0 = (startX == 0) ? 0 : tb.getPos( startX) + xOffs;
		w = width - x0;
		g.setColor( getBackground() );
		g.fillRect( x0, y0, w-d, rowHeight);
		g.setColor( getForeground() );
		tb.paint( g, xOffs, y0, rowHeight, startX);
	}
	else {
		if ( ss > startX ) {
			x0 = tb.getPos( startX) + xOffs;
			w = tb.getWidth( startX, ss);
			g.setColor( getBackground() );
			g.fillRect( x0, y0, w, rowHeight);
			g.setColor( getForeground() );			
			tb.paint( g, xOffs, y0, rowHeight, startX, ss-startX);
		}
		if ( se > startX ) {
			x0 = tb.getPos( ss) + xOffs;
			w = tb.getWidth( ss, se); 
			g.setColor( Defaults.TextAreaSelBgClr );
			g.fill3DRect( x0, y0, w, rowHeight, true);
			g.setColor( Defaults.TextAreaSelTxtClr );			
			tb.paint( g, xOffs, y0, rowHeight, ss, se-ss);
		}
		x0 = tb.getPos( se) + xOffs;
		w = width - x0;
		g.setColor( getBackground() );
		g.fillRect( x0, y0, w, rowHeight);
		if ( se < tb.len ) {
			g.setColor( getForeground() );			
			tb.paint( g, xOffs, y0, rowHeight, se);
		}
	}

	if ( tCursor.yindex == row )
		repaintCursor( g);
		
}

void repaintRow( Graphics g, int idx) {
	repaintLine( idx, 0, null, g);
}

void replaceRange( String s, int start, int end) {
	sSel = get2D( start);
	eSel = get2D( end);

	if ( (sSel.y > -1) && (eSel.y > -1) )
		deleteSel();
	else
		resetSel( false);
		
	insert( s, true);
}

void replaceSelectionWith( String s) {
	deleteSel();
	insert( s, false);
}

void resetSel( boolean repaint) {
	boolean se = hasSel();
		
	int y0 = Math.min( sSel.y, eSel.y);
	int y1 = Math.max( sSel.y, eSel.y);

	sSel.x = tCursor.index;
	sSel.y = tCursor.yindex;
	eSel.x = sSel.x;
	eSel.y = sSel.y;
		
	if ( se && repaint)
		repaintRows( y0, y1-y0);
}

int selXEnd( int row, TextBuffer tb) {
	Point ps = getSelStart();
	Point pe = getSelEnd();
	
	if ( row > pe.y )
		return -1;
	if ( row < ps.y )
		return -1;
	if ( row == pe.y )
		return pe.x;
		
	return tb.len;
}

int selXStart( int row) {
	Point ps = getSelStart();
	Point pe = getSelEnd();

	if ( row > pe.y )
		return -1;
	if ( row < ps.y )
		return -1;
	if ( row == ps.y )
		return ps.x;
		
	return 0;
}

void setContents( String s) {
	String[] sa = breakLines( s);
	rows.removeAllElements();
	for ( int i=0; i<sa.length; i++)
		insertLine( sa[i], i);
	setCursorPos( 0, 0, false, true);
	updateVScroll();
	repaint();
}

void setCursorPos( int x, int y, boolean repaint, boolean resetSel) {
	TextBuffer tb;
	int xPos;
	Graphics g = null;
	int lastX = tCursor.index;
	
	if ( resetSel )
		resetSel( repaint);
		
	if ( repaint) {
		g = getClippedGraphics();
		if ( g != null)
			blankCursor( g);
	}

	makeVisible( y);
	
	tb = (TextBuffer)rows.elementAt( y);
	tCursor.setYIndex( y, getRowYPos( y) );
	if ( x > tb.len)
		x = tb.len;
	xPos = tb.getPos( x);
	tCursor.setIndex( x, xPos );

	if ( g != null) {
		repaintCursor( g);
		g.dispose();
	}
		
	if ( resetSel)
		resetSel( false);

	if ( width > 0) {

		int dx = 10;
		if ( (x > lastX) && (xPos - xOffs > width -dx) ){
			xOffs = width - xPos - dx;
			hScroll.setValue( -xOffs);
			repaint();
		}
		else if ( xPos + xOffs < xOffsInit ) {
			xOffs = -xPos + xOffsInit;
			hScroll.setValue( -xOffs);
			repaint();
		}
	}
}

public void setFont( Font f) {
	TextBuffer tb;
	int s = rows.size();
	super.setFont( f);
	fm = getFontMetrics( f);
	
	tabWidth = 3*fm.charWidth( 'x');
	rowHeight = 4*fm.getHeight()/3;

	for ( int i=0; i<s; i++) {
		tb = (TextBuffer)rows.elementAt( i);
		tb.setMetrics( fm, tabWidth);
	}
	
	tb = (TextBuffer)rows.elementAt( tCursor.yindex);
	tCursor.setHeight( rowHeight - 1 );
	tCursor.setYIndex( tCursor.yindex, getRowYPos( tCursor.yindex) );
	tCursor.setIndex( tCursor.index, tb.getPos( tCursor.index) );

	if ( isShowing() )
		repaint();
}

boolean updateSel( int x, int y, boolean repaint) {
	if ( (x == eSel.x) && (y == eSel.y) )
		return false;

	int y0 = Math.min( sSel.y, eSel.y );
	int y1 = Math.max( sSel.y, eSel.y );
			
	eSel.x = x;
	eSel.y = y;

	setCursorPos( x, y, false, false);
	if ( repaint)
		repaintRows( y0, y1 - y0 + 1);
		
	return true;
}

void vPosChange( int dy) {
	tCursor.setYIndex( tCursor.yindex, getRowYPos( tCursor.yindex) );
}
}

public TextArea() {
	this( null, 10, 10, SCROLLBARS_BOTH);
}

public TextArea( String text) {
	this( text, 10, 10, SCROLLBARS_BOTH);
}

public TextArea( String text, int rows, int cols) {
	this( text, rows, cols, SCROLLBARS_BOTH);
}

public TextArea( String text, int rows, int cols, int scrolls) {
	crows = rows;
	ccols = cols;

	setLayout( null);
	setFont( Defaults.TextAreaFont);

	if ( (scrolls & SCROLLBARS_VERTICAL_ONLY) > 0 ) {
		tp.vScroll = new Scrollbar( Scrollbar.VERTICAL);
		add( tp.vScroll);
	}
	if ( (scrolls & SCROLLBARS_HORIZONTAL_ONLY) > 0 ){
		tp.hScroll = new Scrollbar( Scrollbar.HORIZONTAL);
		tp.hScroll.setValues( 0, 5 * tabWidth, 0,  100 * tp.fm.charWidth( 'x'));
		tp.hScroll.setUnitIncrement( tp.fm.charWidth( 'x'));
		add( tp.hScroll);
	}

	add( tp);
	tp.setListeners();
	
	setBackground( Defaults.TextAreaBgClr);
	setForeground( Defaults.TextAreaTxtClr);

	if ( text != null )
		append( text);
}

public TextArea( int rows, int cols) {
}

public void add( PopupMenu m) {
	tp.add( m);
}

public synchronized void append( String str) {
	tp.append( str);
}

String[] breakLines( String str) {
	int i;
	int bIdx = 0;
	int lc = 0;
	int numLines = 1;
	String[] sbuf;

	if ( str == null )
		return new String[0];

	char[] buf = str.toCharArray();
	for ( i=0; i<buf.length; i++) {
		if ( buf[i] == '\n' ) numLines++;
	}
	sbuf = new String[numLines];

	for ( i=0; i<buf.length; i++) {
		if ( buf[i] == '\n' ){
			if ( buf[i-1] == '\r' )
				sbuf[lc++] = str.substring( bIdx, i-1);
			else
				sbuf[lc++] = str.substring( bIdx, i);
			bIdx = i+1;
		}
	}

	if ( bIdx < buf.length ){
		sbuf[lc] = str.substring( bIdx, buf.length);
	}

	return sbuf;
}

public void doLayout() {
	tp.innerLayout();
}

public int getCaretPosition() {
	Point p = new Point( tp.tCursor.index, tp.tCursor.yindex);
	return tp.get1D( p);
}

public int getColumns() {
	return ccols;
}

public Dimension getMinimumSize() {
	return getMinimumSize( crows, ccols);
}

public Dimension getMinimumSize( int rows, int cols) {
	return new Dimension( cols*tp.fm.charWidth( 'x'), rows*tp.fm.getHeight() );
}

public Dimension getPreferredSize() {
	return getPreferredSize( crows, ccols);
}

public Dimension getPreferredSize( int rows, int cols) {
	return new Dimension( cols*tp.fm.charWidth( 'x'), rows*tp.fm.getHeight() );
}

public int getRows() {
	return crows;
}

public int getScrollbarVisibility() {
	if ( (tp.hScroll == null) && (tp.vScroll == null) )
		return SCROLLBARS_NONE;
	if ( tp.hScroll == null )
		return SCROLLBARS_VERTICAL_ONLY;
	if ( tp.vScroll == null )
		return SCROLLBARS_HORIZONTAL_ONLY;

	return SCROLLBARS_BOTH;
}

public String getSelectedText() {
	StringBuffer sb = new StringBuffer();
	
	int y0 = Math.min( tp.sSel.y, tp.eSel.y);
	int yMax = Math.max( tp.sSel.y, tp.eSel.y);
	
	for ( int i=y0; i<=yMax; i++) {
		TextBuffer tb = (TextBuffer) tp.rows.elementAt( i);
		int x0 = tp.selXStart( i);
		int x1 = tp.selXEnd( i, tb);
		sb.append( tb.buf, x0, x1-x0 );
		if ( i < yMax )
			sb.append( " ");
	}
	
	return sb.toString();
}

public int getSelectionEnd() {
	int i1 = tp.get1D( tp.sSel);
	int i2 = tp.get1D( tp.eSel);
	
	return Math.max( i1, i2);
}

public int getSelectionStart() {
	int i1 = tp.get1D( tp.sSel);
	int i2 = tp.get1D( tp.eSel);
	
	return Math.min( i1, i2);
}

public String getText() {
	StringBuffer sb = new StringBuffer();
	
	int len = tp.rows.size();
	
	for ( int i = 0; i < len; i++) {
		TextBuffer tb = (TextBuffer) tp.rows.elementAt(i);
		sb.append(tb.buf);
		if ( i < len )
			sb.append( " ");
	}
	
	return sb.toString();
}

void hPosChange() {
	tp.hPosChange();
}

public synchronized void insert( String str, int pos) {
	tp.insert( str, pos);
}

protected String paramString() {
	return super.paramString();
}

void repaintRow( Graphics g, int row) {
	tp.repaintLine( row, 0, null, g);
}

public synchronized void replaceRange( String str, int start, int end) {
	tp.replaceRange( str, start, end);
}

void replaceSelectionWith ( String s ) {
	tp.replaceSelectionWith( s);
}

public void requestFocus() {
	tp.requestFocus();
}

public void select( int start, int end) {
	Point p = tp.get2D( end);
	
	tp.sSel = tp.get2D( start);
	tp.updateSel( p.x, p.y, true);
}

public void selectAll() {
	TextBuffer tb = (TextBuffer) tp.rows.lastElement();
	tp.sSel.x = 0;
	tp.sSel.y = 0;
	tp.eSel.x = 0;
	tp.eSel.y = 0;
	tp.updateSel( tb.len, tp.rows.size()-1, true);
}

public void setBackground( Color clr) {
	tp.setBackground( clr);
}

public void setCaretPosition( int pos) {
	Point p = tp.get2D( pos);
	tp.setCursorPos( p.x, p.y, true, true);
}

public void setColumns( int cols) {
	ccols = cols;
}

public void setFont( Font f) {
	super.setFont( f);
	tp.setFont( f);
}

public void setForeground( Color clr) {
	tp.setForeground( clr);
}

public void setRows( int rows) {
}

public void setSelectionEnd( int end) {
	Point p = tp.get2D( end);
	tp.updateSel( p.x, p.y, true);
}

public void setSelectionStart( int start) {
	Point p = tp.get2D( start);
	tp.updateSel( p.x, p.y, true);
}

public void setText( String text) {
	tp.setContents( text);
}

void vPosChange( int dy) {
	tp.vPosChange( dy);
}
}
