package talk.awtui;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

/**
   <P>TalkEdit is an Editor widget with a specially limited set of
   editing commands.  It is designed for use in a talk client, where
   every key the user types in is sent to a remote user, therefore,
   characters can only be appended, or deleted from the end of the
   buffer, not in the middle.  The user can move the cursor anywhere,
   in order to scroll, but any time a key is typed which would modify
   the buffer, the cursor is first moved to the end.<P>
*/
public class TalkEdit extends TextArea implements ITalkEditor {
  private Vector talkListeners = new Vector();
  private static final int SPACE = 32;
  private static final int DELETE = 127;

  /**
     Creates a TalkEdit widget with visible size <code>rows</code> and
     <code>cols</code>.  There is only a vertical scrollbar, so lines
     are wrapped.
  */
  public TalkEdit(int rows, int cols) {
    super("", rows, cols, TextArea.SCROLLBARS_VERTICAL_ONLY);
    enableEvents(AWTEvent.KEY_EVENT_MASK);
  }


  public synchronized void backspace() {
    setCaretPosition(Integer.MAX_VALUE);
    int pos = getCaretPosition();
    replaceRange("", pos - 1, pos);
  }

  public synchronized void clear() {
    setText("");
  }
  
  /**
     Filters key events, so that any keys which change text are
     processed after moving the cursor to the end.  Also, only ascii
     characters are allowed to be entered.  Also calls
     processTalkEvent if the buffer was modified.
  */
  protected void processKeyEvent(KeyEvent e) {
    if (!isEditable()) {
      super.processKeyEvent(e);
      return;
    }
    
    char[] ch = {e.getKeyChar()};
    int cc = e.getKeyCode();
    int mods = e.getModifiers();

    // if it's a printing character call processTalkEvent
    if (e.getID() == KeyEvent.KEY_PRESSED &&
	(!Character.isISOControl(ch[0]) ||
	 cc == KeyEvent.VK_BACK_SPACE ||
	 cc == KeyEvent.VK_ENTER ||
	 cc == KeyEvent.VK_TAB ||
	 cc == KeyEvent.VK_DELETE)) {
      setCaretPosition(Integer.MAX_VALUE);
      if (cc == KeyEvent.VK_DELETE) { // make it the same as backspace
	ch[0] = '\b';
	e.setKeyChar('\b');
	e.setKeyCode(KeyEvent.VK_BACK_SPACE);
      }
			 
      processTalkEvent(new String(ch));
    } else {
      switch (cc) {
      case KeyEvent.VK_UP:
      case KeyEvent.VK_DOWN:
      case KeyEvent.VK_LEFT:
      case KeyEvent.VK_RIGHT:
      case KeyEvent.VK_HOME:
      case KeyEvent.VK_END:
      case KeyEvent.VK_PAGE_UP:
      case KeyEvent.VK_PAGE_DOWN:
	break;
      default:			// ignore every other key
	e.consume();
      }
    }
    
    super.processKeyEvent(e);
  }    

  /**
     Calls any TalkListeners registered with addTalkListener.
  */
  protected synchronized void processTalkEvent(String s) {
    Vector remove = new Vector();
    for (Enumeration e = talkListeners.elements(); e.hasMoreElements(); ) {
      TalkListener tl = (TalkListener)e.nextElement();
      if (!tl.textAppended(s))
	remove.addElement(tl);
    }
    for (Enumeration e = remove.elements(); e.hasMoreElements();)
      removeTalkListener((TalkListener)e.nextElement());
  }

  /**
     Adds a TalkListener to listen for events which change the edit
     buffer. Really it is only called when keystrokes are pressed,
     though I should catch pastes too.
  */
  public synchronized void addTalkListener(TalkListener tl) {
    talkListeners.addElement(tl);
  }

  /**
     Removes <code>tl</code> from the listener list.
  */
  public synchronized void removeTalkListener(TalkListener tl) {
    talkListeners.removeElement(tl);
  }

  public synchronized void removeTalkListeners() {
    talkListeners.clear();
  }

  public static void main(String[] args) {
    Frame frame = new Frame("TalkEdit");
    TalkEdit edit = new TalkEdit(10, 80);
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
	System.exit(0);
      }
    });

    class EditTalk implements TalkListener {
      public boolean textAppended(String s) {
	System.out.println("Got event '"+s+"'");
	return true;
      }
    }
    edit.addTalkListener(new EditTalk());
    frame.add(edit, "Center");
    frame.pack();
    frame.setVisible(true);
  }
}
