package abbot.editor.widgets; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; import java.util.*; import abbot.Log; /** A better text area that fires when focus leaves the component, and also selects all the contents when the action is fired to indicate the contents were accepted. */ // FIXME extend to have a "commit" on enter or focus change, where ESC will // revert public class TextArea extends JTextArea { public static final String ACTION_FOCUS_LOST = "focus-lost"; public static final String ACTION_TEXT_CHANGED = "text-changed"; public static final String ACTION_TEXT_INSERTED = "text-changed"; public static final String ACTION_TEXT_REMOVED = "text-changed"; private boolean continuousFire = true; private boolean fieldChanging = false; private ArrayList listeners = new ArrayList(); public TextArea(String value) { super(value); addFocusListener(new java.awt.event.FocusAdapter() { public void focusLost(java.awt.event.FocusEvent ev) { if (!ev.isTemporary() && !isLocalMenuActive()) { Log.debug("Firing on focus loss"); fireActionPerformed(ACTION_FOCUS_LOST); } } }); getDocument().addDocumentListener(new DocumentListener() { public void changedUpdate(DocumentEvent ev) { if (!fieldChanging && continuousFire) fireActionPerformed(ACTION_TEXT_CHANGED); } public void insertUpdate(DocumentEvent ev) { if (!fieldChanging && continuousFire) fireActionPerformed(ACTION_TEXT_INSERTED); } public void removeUpdate(DocumentEvent ev) { if (!fieldChanging && continuousFire) fireActionPerformed(ACTION_TEXT_REMOVED); } }); } /** Don't fire events when text is set directly (to conform to regular JTextArea behavior). */ public void setText(String text) { fieldChanging = true; super.setText(text); fieldChanging = false; } /** Detect temporary focus loss due to menu activation. */ private boolean isLocalMenuActive() { boolean active = false; Window window = SwingUtilities.getWindowAncestor(TextArea.this); while (window != null && !active) { window = SwingUtilities.getWindowAncestor(window); if (window instanceof JFrame) { Component comp = window.getFocusOwner(); Log.debug("Focus is in " + abbot.tester.Robot.toString(comp)); active = comp != null && (comp instanceof JMenuItem); } } return active; } protected void fireActionPerformed(String actionCommand) { fireActionPerformed(actionCommand, false); } protected void fireActionPerformed() { fireActionPerformed(getText(), true); } /** On normal fire (enter) select all text. */ protected void fireActionPerformed(String cmd, boolean select) { if (select) selectAll(); ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, cmd); Iterator iter = listeners.iterator(); while (iter.hasNext()) { ((ActionListener)iter.next()).actionPerformed(e); } } public void addActionListener(ActionListener l) { listeners.add(l); } public void removeActionListener(ActionListener l) { listeners.remove(l); } }