// Copyright 1996, Marimba Inc. All Rights Reserved.


// @(#)TextBoxWidget.java, 1.90, 12/19/96





package marimba.gui;





import java.awt.*;


import marimba.util.*;


import marimba.persist.*;





/**


 * A single line text widget.


 *


 * @author	Arthur van Hoff


 * @author	Klaas Waslander


 * @version 	1.90, 12/19/96


 */


public class TextBoxWidget extends TextWidget implements TimerClient {


    /**


     * Text can be aligned to the left, right or can be centered.


     * @see #getAlign


     * @see #setAlign


     * @see #alignOptions


     */


    public int  align = LEFT;





    /**


     * The possible options for the align.


     * @see #getAlignOptions()


     * @see #align


     */


    public static Options alignOptions = new Options();


    static {


	alignOptions.add("left", LEFT);


	alignOptions.add("right", RIGHT);


	alignOptions.add("center", CENTER);


    }





    /**


     * A textbox can be plain, boxed, filled and underlined.


     * @see #getStyle


     * @see #setStyle


     * @see #styleOptions


     */


    public int  style = PLAIN;





    /**


     * The possible options for the style.


     * @see #getStyleOptions


     * @see #style


     */


    public static Options styleOptions = new Options();


    static {


	styleOptions.add("plain", PLAIN);


	styleOptions.add("boxed", BOXED);


	styleOptions.add("filled", FILLED);


	styleOptions.add("underline", UNDERLINE);


    }





    /**


     * Used to let the caret blink: when caretVisible is true


     * paint() paints the caret, otherwise it doesn't.


     */


    public boolean  caretVisible;





    /**


     * The textboxwidget can retrieve its content in a future


     * session or start blank again.


     * @see #getSavetext


     * @see #setSavetext


     */


    public boolean  savetext = true;





    /**


     * The color for the shadow of the characters.


     * @see #getShadowColor


     * @see #setShadowColor


     */


    public Color  shadowColor;





    /**


     * The length of the text.


     * @see #text


     * @see #getLength


     */


    public int  len;





    /**


     * The content of the textbox. The size of this array is increased


     * by four in setText whenever necessary.


     * @see #len


     * @see #getText


     * @see #getTextChars


     * @see #setText


     */


    public char  text[] = new char[4];





    /**


     * The starting position of the selection.


     * @see #getSelStart


     */


    public int  selStart;





    /**


     * The ending position of the selection.


     * @see #getSelEnd


     */


    public int  selEnd;





    /**


     * The current position of the caret.


     */


    public int  caretPos;





    /**


     * The echo character for this TextBox, which is useful when the


     * user input shouldn't be echoed to the screen, as in the case


     * of a TextBox that represents a password. For every character


     * in the input this character is being echoed.


     * @see #getEchoCharacter


     * @see #setEchoCharacter


     */


    public Character  echoCharacter;





    /**


     * The color in which the text is painted,


     * by default always the foreground color;


     * but in subclasses one can set this color


     * to something else and draw just the text


     * in a different color.


     */


    protected Color  textColor = foreground;





    int scrollx;


    int selDepth;	// the number of mouseclicks


    int selPos;





    /**


     * Default constructor.


     */


    public TextBoxWidget() {


	setEditable(true);


	setStyle(FILLED);


	setValue("TextBox");


    }





    /**


     * Constructor with value.


     */


    public TextBoxWidget(String text) {


	setValue(text);


	editable = false;


    }





    /**


     * Constructor with value for editing.


     */


    public TextBoxWidget(String text, boolean editable, int style) {


	setValue(text);


	this.editable = editable;


	this.style = style;


    }





    /**


     * Get the properties of this widget.


     */


    public void getProperties(PropertyList list) {


	super.getProperties(list);


	list.setBoolean("savetext", savetext, true);


	list.setColor("shadowcolor", shadowColor, null);


	list.setOption("style", styleOptions, style, PLAIN);


	list.setOption("align", alignOptions, align, LEFT);


	list.setString("text", savetext ? getText() : null, null);


	String  echoString = (echoCharacter != null) ? echoCharacter.toString() : null;


	list.setString("echoChar", echoString, null);


    }





    /**


     * Set the properties of this widget.


     */


    public void setProperties(PropertyList list) {


	super.setProperties(list);


	savetext = list.getBoolean("savetext", true);


	style = list.getOption("style", styleOptions, PLAIN);





	shadowColor = list.getColor("shadowcolor", null);


	align = list.getOption("align", alignOptions, LEFT);





	textColor = foreground;


	String str = list.getString("text", "");


	text = new char[len = str.length()];


	str.getChars(0, len, text, 0);


	selStart = selEnd = len;





	String  echoString = list.getString("echoChar", null);


	if (echoString != null && echoString.length() > 0) {


	    echoCharacter = new Character(echoString.charAt(0));


	} else {


	    echoCharacter = null;


	}


	transparent = (style != FILLED);


    }





    /**


     * Get the possible options for align.


     * @see #alignOptions


     */


    public Options getAlignOptions() {


	return alignOptions;


    }





    /**


     * Get the possible options for the style.


     */


    public Options getStyleOptions() {


	return styleOptions;


    }





    /**


     * Get the editor for this widget.


     */


    public String getEditor() {


	return "marimba.builder.TextBoxWidgetEditor";


    }





    /**


     * Get the length of the text.


     * @see #len


     */


    public int getLength() {


	return len;


    }





    /**


     * Return the start of the selection.


     * @see #selStart


     */


    public int getSelStart() {


	return selStart;


    }





    /**


     * Return the end of the selection.


     * @see #selEnd


     */


    public int getSelEnd() {


	return selEnd;


    }





    /**


     * Get the character that is echoed to the screen


     * for every character in the user input.


     * Returns null if it is not set.


     * @see #echoCharacter


     */


    public Character getEchoCharacter() {


	if (echoCharacter != null) {


	    return echoCharacter;


	}


	return null;


    }





    /**


     * Set the character that is echoed to the screen


     * for every character in the user input.


     * Specify null if you want to disable this.


     * @see #echoCharacter


     */


    public void setEchoCharacter(Character echoCharacter) {


	if (this.echoCharacter != echoCharacter) {


	    this.echoCharacter = echoCharacter;


	    repaint();


	}


    }





    /**


     * Get the value of the text.


     * @see #text


     */


    public String getText() {


	return new String(text, 0, len);


    }





    /**


     * Return text characters that are being displayed,


     * overriding this method changes the characters


     * that are being displayed.


     * @see #text


     */


    public char[] getTextChars() {


	if (echoCharacter == null) {


	    return text;


	} else {


	    char[]  result = new char[len];


	    for (int i = 0; i < len; i++) {


		result[i] = echoCharacter.charValue();


	    }


	    return result;


	}


    }





    /**


     * Set the value of the text.


     * @see #text


     */


    public void setText(String text) {


	if (text == null) {


	    text = "";


	}


	String old = getText();


	if (!text.equals(old)) {


	    this.text = new char[len = text.length()];


	    text.getChars(0, len, this.text, 0);


	    selStart = selEnd = len;


	    repaint();


	} else {


	    select(len);


	}


	focus(0);


    }





    /**


     * Get the style of this widget.


     * @see #style


     */


    public int getStyle() {


	return style;


    }





    /**


     * Set the style of this widget.


     * @see #style


     */


    public void setStyle(int style) {


	if (this.style != style) {


	    this.style = style;


	    transparent = (style != FILLED);


	    repaint();


	}


    }





    /**


     * Get the alignment of this widget.


     * @see #align


     */


    public int getAlign() {


	return align;


    }





    /**


     * Set the style of this widget.


     * @see #align


     */


    public void setAlign(int align) {


	if (align==LEFT || align==RIGHT || align==CENTER ) {


	    if (this.align != align) {


		this.align = align;


		repaint();


	    }


	}


    }





    /**


     * Check whether the text is being saved.


     * @see #savetext


     */


    public boolean getSavetext() {


	return savetext;


    }





    /**


     * Let the text be saved or not.


     * @see #savetext


     */


    public void setSavetext(boolean savetext) {


	this.savetext = savetext;


    }





    /**


     * Check the shadowColor of this textBox.


     * @see #shadowColor


     */


    public Color getShadowColor() {


	return shadowColor;


    }





    /**


     * Set the shadowColor of this textBox.


     * @see #shadowColor


     */


    public void setShadowColor(Color shadowColor) {


	if (shadowColor == null || !shadowColor.equals(this.shadowColor)) {


	    this.shadowColor = shadowColor;


	    repaint();


	}


    }





    /**


     * Clear the current selection.


     */


    public void delete() {


	if (selStart < selEnd) {


	    System.arraycopy(text, selEnd, text, selStart, len - selEnd);


	    len -= (selEnd - selStart);


	    selEnd = selStart;


	    focus(selStart);


	    repaint();


	}


    }





    /**


     * Insert a character. This will first clear the selecton,


     * before inserting the character.


     */


    public void insert(char ch) {


	delete();


	if (len + 1 >= text.length) {


	    char newtext[] = new char[Math.max(len*2, 4)];


	    System.arraycopy(text, 0, newtext, 0, len);


	    text = newtext;


	}


	System.arraycopy(text, selStart, text, selStart+1, len - selStart);


	text[selStart++] = ch;


	len++;


	selEnd = selStart;


	focus(selStart);


	repaint();


    }





    /**


     * Insert a string at the current caret position.


     */


    public void insert(String str) {


	delete();





	int strlen = str.length();


	if (strlen > 0) {


	    if (len + strlen >= text.length) {


		char newtext[] = new char[Math.max(len*2, len + strlen)];


		System.arraycopy(text, 0, newtext, 0, len);


		text = newtext;


	    }


	    System.arraycopy(text, selStart, text, selStart+strlen, len - selStart);


	    str.getChars(0, strlen, text, selStart);


	    selStart += strlen;


	    selEnd = selStart;


	    len += strlen;


	    repaint();


	}


	focus(selStart);


    }





    /**


     * Get the selected part of the text.


     * @see #select


     */


    public String getSelected() {


	return new String(text, selStart, selEnd - selStart);


    }





    /**


     * Select a portion of the text. Depth controls character (1),


     * word (2), and line selection (3).


     * @see #getSelected


     */


    public void select(int selStart, int selEnd, int depth) {


	if (selStart > selEnd) {


	    selStart ^= selEnd;


	    selEnd ^= selStart;


	    selStart ^= selEnd;


	}


	if (selStart < 0) {


	    selStart = 0;


	} else if (selStart > len) {


	    selStart = len;


	}


	if (selEnd < selStart) {


	    selEnd = selStart;


	} else if (selEnd > len) {


	    selEnd = len;


	}


	switch (depth) {


	  case 0:


	  case 1:


	    break;


	  case 2:


	    for (; (selStart > 0) && isWord(text[selStart-1]) ; selStart--);


	    for (; (selEnd < len) && isWord(text[selEnd]) ; selEnd++);


	    break;


	  default:


	    selStart = 0;


	    selEnd = len;


	    break;


	}


	if ((selStart != this.selStart) || (selEnd != this.selEnd)) {


	    this.selStart = selStart;


	    this.selEnd = selEnd;


	    repaint();


	}


    }





    /**


     * Blink the caret.


     */


    public long tick(long tm, Object arg) {


	caretVisible = !caretVisible;


	repaint();


	return tm + 500;


    }





    /**


     * Left margin.


     * @see #rightMargin


     */


    public int leftMargin() {


	return 0;


    }





    /**


     * Right margin.


     * @see #leftMargin


     */


    public int rightMargin() {


	return 0;


    }





    /**


     * Focus on a given position. This will scroll the position into view.


     * It will also change the caretPosition to the focused position.


     */


    public void focus(int pos) {


	if (align == LEFT) {


	    char text[] = getTextChars();


	    FontMetrics fm = getFontMetrics(font);


	    int leftMar = leftMargin();


	    int rightMar = rightMargin();





	    if ((editor != null) && !editable) {


		int w = leftMar + 5 + fm.charsWidth(text, 0, len) + 5 + rightMar;


		w = (w / 5) * 5 + 5;


		if (width < w) {


		    resize(w, height);


		    Widget parent = this.parent;


		    if (parent != null) {


			parent.repaint(x-4, y-4, width+8, height+8);


		    }


		}


	    } else {


		int lx = leftMar + 5 + scrollx + fm.charsWidth(text, 0, pos);


		if (lx < leftMar + 5) {


		    scrollx = Math.min(0, scrollx + 5 + (width/3) - lx);


		    repaint();


		} else if (lx > width - (rightMar+5)) {


		    scrollx += Math.max(leftMar + 5, width*2/3)-lx;


		    repaint();


		}


	    }


	} else if (scrollx != 0) {


	    scrollx = 0;


	    repaint();


	}


	caretPos = pos;


    }





    /**


     * The starting, position of the previous word.


     * @see #nextWordPos


     */


    public int previousWordPos(int pos) {


	int p = pos-1;


	while (p>=0 && (text[p]==' ' || text[p]=='\t')) {


	    p--;


	}


	// below comparing to a collection whitespaces in the future


	while (p>=0 && isWord(text[p])) {


	    p--;


	}


	while (p>=0 && (text[p] == ',' || text[p] == '.')) {


	    p--;


	}


	return (p == (pos-1)) ? p : p+1;


    }





    /**


     * The starting position of the next word.


     * @see #previousWordPos


     */


    public int nextWordPos(int pos) {


	// below comparing to a collection whitespaces in the future


	int  p = pos+1;


	while (p<len && isWord(text[p])) {


	    p++;


	}


	if (p < len && (text[p-1] == ',' || text[p-1] == '.')) {


	    while (p<len && (text[p] == ',' || text[p] == '.')) {


		p++;


	    }


	}


	if (p<len && text[p] == ' ') {


	    p++;


	}


	return p;


    }





    /**


     * Paint the text widget.


     */


    public void paintBackground(Graphics g) {


	FontMetrics fm = g.getFontMetrics();


	int leftMar = leftMargin();


	int rightMar = rightMargin();


	int bothMar = leftMar + rightMar;


	int ly = (height - fm.getHeight())/2 + fm.getAscent();





	g.setColor(background);


	switch (style) {


	  case PLAIN:


	    break;





	  case UNDERLINE:


	    Bevel.drawRect(g, leftMar+1, ly, width - (bothMar+2), 2, true, 1);


	    break;





	  case BOXED:


	    if (disabled) {


		Bevel.drawRect(g, 0, 0, width, height, false, 1);


	    } else {


		Bevel.drawFieldBorder(g, 0, 0, width, height);


	    }


	    break;





	  case FILLED:


	    if (disabled) {


		Bevel.drawRect(g, 0, 0, width, height, false, 1);


		g.setColor(hilite);


		g.fillRect(leftMar + 1, 1, width - (bothMar+2), height-2);


	    } else {


		Bevel.drawFieldBorder(g, 0, 0, width, height);


		g.setColor(hilite);


		g.fillRect(leftMar + 2, 2, width - (bothMar+4), height-4);


	    }


	    break;


	}


    }





    /**


     * Paint the text widget.


     */


    public void paint(Graphics g) {


	char  text[] = getTextChars();


	FontMetrics  fm = g.getFontMetrics(font);


	int  leftMar = leftMargin();


	int  rightMar = rightMargin();


	int  bothMar = leftMar + rightMar;


	int  vertMar = 0;


	int  caretY = (height - fm.getHeight()) / 2;





	int  lx = leftMar + 5;


	int  ly = (height - fm.getHeight())/2 + fm.getAscent();


	int  strw = fm.charsWidth(text, 0, len);





	paintBackground(g);





	switch (style) {


	  case PLAIN:


	    g.clipRect(leftMar, 0, width - bothMar, height);


	    break;





	  case UNDERLINE:


	    g.clipRect(leftMar+1, 0, width - (bothMar+2), height);


	    break;





	  default:


	    vertMar = 3;


	    g.clipRect(leftMar+2, 2, width - (bothMar+4), height-4);


	    break;


	}


	


	switch (align) {


	  case RIGHT:


	     lx = (width - rightMar) - (strw + 5);


	     break;


	  case CENTER:


	     lx = leftMar + ((width - bothMar) - strw) / 2;


	     break;


	  case LEFT:


	     if (editable) {


		 lx += scrollx;


	     } else {


		 scrollx = 0;


	     }


	     break;


	}





	boolean  textNotDrawn = true;


	if (((editable && !disabled) || (editor != null)) && hasFocus()) {


	    int  selx = lx + fm.charsWidth(text, 0, selStart);


	    int  selw = fm.charsWidth(text, selStart, selEnd - selStart);


	    int  caretx = (selStart==caretPos) ? selx : selx+selw;


	    Color  caretColor = background.equals(Color.black) ? Color.white : Color.black;





	    if (caretVisible) {


		// draw caret and text


		if (shadowColor != null) {


		    int off = (fm.getHeight() > 18) ? 2 : 1;


		    g.setColor(shadowColor);


		    g.drawChars(text, 0, len, lx+off, ly+off);


		}


		g.setColor(caretColor);


		g.fillRect(caretx, caretY, 2, height - caretY * 2);


		g.setColor(textColor);


		g.drawChars(text, 0, len, lx, ly);


		textNotDrawn = false;





		// draw text which is covered by caret in selection background color.


		Graphics g2 = g.create();


		try {


		    g2.clipRect(caretx, caretY, 2, height - caretY * 2);


		    g2.setColor(getSelForeground());


		    g2.drawChars(text, 0, len, lx, ly);


		} finally {


		    g2.dispose();


		}


	    }





	    if (selStart != selEnd) {


		int  rectHeight = height - caretY * 2;


		g.setColor(getSelBackground());


		if (caretVisible && selStart == caretPos) {


		    g.fillRect(selx+2, caretY, selw-2, rectHeight);


		} else {


		    g.fillRect(selx, caretY, selw, rectHeight);


		}





		// draw entire text


		int shadowOffset = (fm.getHeight() > 18) ? 2 : 1;


		if (shadowColor != null) {


		    g.setColor(shadowColor);


		    g.drawChars(text, 0, len, lx+shadowOffset, ly+shadowOffset);


		}


		textNotDrawn = false;


		g.setColor(textColor);


		g.drawChars(text, 0, len, lx, ly);





		// draw selected text again in different color


		Graphics  g2 = g.create();


		try {


		    if (!caretVisible) {


			if (selStart == caretPos) {


			    Color invert = new Color(255,255,100);


			    g2.setColor((hilite.equals(invert)) ? Color.red : invert);


			    g2.fillRect(caretx, caretY, 2, rectHeight);


			    g2.clipRect(caretx+2, caretY, selw-2, rectHeight);


		        } else {


			    g2.clipRect(selx, caretY, selw, rectHeight);


		        }


		    } else {


			if (selStart == caretPos) {


			    g2.clipRect(selx+2, caretY, selw-2, rectHeight);


		        } else {


			    g2.clipRect(selx, caretY, selw, rectHeight);


		        }


		    }


		    g2.setColor(getSelBackground());


		    g2.drawChars(text, 0, len, lx+shadowOffset, ly+shadowOffset);


		    g2.setColor(getSelForeground());


		    g2.drawChars(text, 0, len, lx, ly);


		} finally {


		    g2.dispose();


		}


		


		// remove shadowed text behind caret


		if (caretVisible) {


		    Graphics  g3 = g.create();


		    try {


			g3.clipRect(caretx, caretY, 2, rectHeight);


			g3.setColor(caretColor);


			g3.drawChars(text, 0, len, lx+shadowOffset, ly+shadowOffset);


			g3.setColor(getSelForeground());


			g3.drawChars(text, 0, len, lx, ly);


		    } finally {


			g3.dispose();


		    }


		}


	    }


	}





	// draw entire text


	if (textNotDrawn) {


	    if (shadowColor != null) {


		int off = (fm.getHeight() > 18) ? 2 : 1;


		g.setColor(shadowColor);


		g.drawChars(text, 0, len, lx+off, ly+off);


	    }


	    g.setColor(textColor);


	    g.drawChars(text, 0, len, lx, ly);


	}


    }





    /**


     * Convert an x coordinate into a text position.


     */


    public int x2pos(int x) {


	char text[] = getTextChars();


	FontMetrics fm = getFontMetrics(font);


	int widths[] = fm.getWidths();


	int leftMar = leftMargin();


	int rightMar = rightMargin();





	switch (align) {


	  case RIGHT:


	    x -= width - (fm.charsWidth(text, 0, len) + 5 + rightMar);


	    break;


	  case CENTER:


	    x -= leftMar + (width - (fm.charsWidth(text, 0, len) + leftMar + rightMar)) / 2;


	    break;


	  default:


	    x -= leftMar + 5 + scrollx;


	    break;


	}





	for (int i = 0 ; i < len ; i++) {


	    int w = widths[text[i]];


	    if (x < w/2) {


		return i;


	    }


	    x -= w;


	}


	return len;


    }





    /**


     * Handle mouse and character events.


     */


    public boolean handleEvent(Event evt) {


        Object clip;





	if ((editable && !disabled) || (editor != null)) {


	    switch (evt.id) {


	      case Event.MOUSE_DOWN:


		if (!evt.metaDown()) {


		    requestFocus();


		    selDepth = evt.clickCount;


		    if (evt.shiftDown()) {


			int pos = x2pos(evt.x);


			if (pos < selStart) {


			    selPos = selEnd;


			} else {


			    selPos = selStart;


			}


			select(selPos, pos, selDepth);


			focus(pos);


		    } else {


			selPos = x2pos(evt.x);


			select(selPos, selPos, selDepth);


			focus(selPos);


		    }


		}


		return false;





	      case Event.MOUSE_DRAG:


	      case Event.MOUSE_UP:


		if (!evt.metaDown()) {


		    if (evt.y < -5) {


			select(0, selPos, selDepth);


			focus(selStart);


		    } else if (evt.y > height + 5) {


			select(selPos, len, selDepth);


			focus(selEnd);


		    } else {


			int p = x2pos(evt.x);


			select(selPos, p, selDepth);


			focus(p);


		    }


		    return true;


		}


		return false;





	      case Event.GOT_FOCUS:


		selStart = 0;


		selEnd = len;


		selPos = len;


		caretPos = len;


		repaint();


		Timer.master.add(this);


		return true;





	      case Event.LOST_FOCUS:


		Timer.master.remove(this);


		if (caretVisible) {


		    caretVisible = false;


		}


		repaint();


		return true;





	      case Event.KEY_ACTION:


		Timer.master.add(this, System.currentTimeMillis() + 500, null);


		if (!caretVisible) {


		    caretVisible = true;


		    repaint();


		}


		switch (evt.key) {


		  case Event.HOME:


		    if (evt.shiftDown()) {


		      if (selStart!=selEnd && caretPos>selStart) {


			select(0, selStart, 1);


		      } else {


			select(0, selEnd, 1);


		      }


		    } else {


		      select(0);


		    }


		    focus(0);


		    return true;





		  case Event.END:


		    if (evt.shiftDown()) {


		      if (selStart!=selEnd && caretPos<selEnd) {


			select(selEnd, len, 1);


		      } else {


			select(selStart, len, 1);


		      }


		    } else {


		      select(len);


		    }


		    focus(len);


		    return true;





		  case Event.UP:


		  case Event.LEFT:


		    if (evt.shiftDown()) {


		      int  selEnd = getSelEnd();


		      int  selStart = getSelStart();


		      if (caretPos > selStart) {


			if (evt.controlDown()) {


			  selEnd = previousWordPos(selEnd) + 1;


			}


			selEnd = Math.max(0, selEnd - 1);


			focus(selEnd);


		      } else {


			if (evt.controlDown()) {


			  selStart = previousWordPos(selStart) + 1;


			}


			selStart = Math.max(0, selStart-1);


			focus(selStart);


		      }


		      select(selStart, selEnd, 1);


		    } else {


		      if (selStart != selEnd) {


			int  caretPos = this.caretPos;


			if (evt.controlDown()) {


			  caretPos = previousWordPos(caretPos) + 1;


			}


			select(caretPos-1);


			focus(caretPos);


		      } else {


			int  selStart = getSelStart();


			if (evt.controlDown()) {


			  selStart = previousWordPos(selStart) + 1;


			}


			select(selStart-1);


			focus(selStart);


		      }


		    }


		    return true;





		  case Event.DOWN:


		  case Event.RIGHT:


		    if (evt.shiftDown()) {


		      int  selEnd = getSelEnd();


		      int  selStart = getSelStart();


		      if (caretPos < selEnd) {


			if (evt.controlDown()) {


			  selStart = nextWordPos(selStart) - 1;


			}


		        selStart = Math.min(len, selStart+1);


			focus(selStart);


		      } else {


			if (evt.controlDown()) {


			  selEnd = nextWordPos(selEnd) - 1;


			}


		        selEnd = Math.min(len, selEnd+1);


			focus(selEnd);


		      }


		      select(selStart, selEnd, 1);


		    } else {


		      if (selStart != selEnd) {


			int  caretPos = this.caretPos;


			if (evt.controlDown()) {


			  caretPos = nextWordPos(caretPos) - 1;


			}


			select(caretPos+1);


			focus(caretPos);


		      } else {


			int  selEnd = getSelEnd();


			if (evt.controlDown()) {


			  selEnd = nextWordPos(selEnd) - 1;


			}


			select(selEnd+1);


			focus(selEnd);


		      }


		    }


		    return true;


		}


		return true;


		


	      case Event.KEY_PRESS:


		Timer.master.add(this, System.currentTimeMillis() + 500, null);


		if (!caretVisible) {


		    caretVisible = true;


		    repaint();


		}





		switch (evt.key) {


		  case 'c' & 0x1F:


		    Environment.setClipboard(getSelected());


		    return true;


		  case 'v' & 0x1F:


		     clip = Environment.getClipboard();


		     if (clip != null && clip instanceof String) {


		       insert((String)clip);


		     }


		     return true;


		  case 'x' & 0x1F:


		    Environment.setClipboard(getSelected());


		    delete();


		    return true;


		    


		  case 'u' & 0x1F:


		    select(0, getLength());


		    delete();


		    return true;


		  case 'e' & 0x1F:


		    select(getLength());


		    focus(getSelEnd());


		    return true;


		  case 'a' & 0x1F:


		    select(0);


		    focus(0);


		    return true;


		  case 'f' & 0x1F:


		    select(getSelEnd()+1);


		    focus(getSelEnd());


		    return true;


		  case 'k' & 0x1F:


		    select(getSelStart(), getLength());


		    delete();


		    return true;


		  case 'b' & 0x1F:


		    select(getSelStart()-1);


		    focus(getSelStart());


		    return true;


		  case 127:


		  case 'd' & 0x1F:


		    if (getSelStart() == getSelEnd()) {


			select(getSelStart(), getSelEnd()+1);


		    }


		    delete();


		    return true;





		  case '\b':


		    // control+h results in the same, so check for control key


		    if (!evt.controlDown()) {


			if (getSelStart() == getSelEnd()) {


			    select(getSelStart()-1, getSelEnd());


			}


			delete();


			return true;


		    }


		    break;





		  case '\n':


		    action();


		    return true;





		  case '\r':


		    action();


		    return true;





		  case '\t':


		    break;





		  default:


		    if (evt.key >= ' ') {


			insert((char)evt.key);


			return true;


		    }


		    break;


		}


	    }


	}


	return super.handleEvent(evt);


    }





    /**


     * Perform the default action.


     */


    public void action() {


	Widget  defaultButton = getDefaultButton();


	if (defaultButton != null) {


	    defaultButton.requestFocus();


	    defaultButton.action();


	} else {


	    super.action();


	}


    }


}


