/* * Copyright (C) 2012 Jason Gedge <http://www.gedge.ca> * * This file is part of the OpGraph project. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ca.gedge.opgraph.app.components; import java.awt.Color; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.InputEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import javax.swing.text.Highlighter; import javax.swing.text.JTextComponent; /** * A text field that begins editing on double-click. When the ENTER key is * pressed, editing finishes and the text is updated. When the ESCAPE key is * pressed, editing finishes and the text reverts to its state before editing * began. */ public class DoubleClickableTextField { /** The property name for the editing property */ public static final String EDITING_PROPERTY = "editingValue"; /** The property name for the text property */ public static final String TEXT_PROPERTY = "textValue"; /** Property change support */ private final PropertyChangeSupport propertyChangeSupport; /** The text component we have attached to */ private final JTextComponent textComponent; /** The currently set highlighter */ private Highlighter highlighter; /** The currently set bg color */ private Color bgColor; /** The text before editing began */ private String oldText; /** Whether or not we are currently editing */ private boolean editing; /** * Constructs a double-clickable text field. * * @param textComponent the text component to attach to */ public DoubleClickableTextField(JTextComponent textComponent) { this.editing = false; this.propertyChangeSupport = new PropertyChangeSupport(this); this.textComponent = textComponent; this.textComponent.setFocusable(false); this.textComponent.setOpaque(false); this.textComponent.setDragEnabled(false); this.textComponent.addMouseListener(mouseAdapter); this.textComponent.addKeyListener(keyAdapter); this.textComponent.addFocusListener(focusAdapter); this.highlighter = textComponent.getHighlighter(); this.bgColor = textComponent.getBackground(); } /** * Gets whether or not we are currently editing. * * @return <code>true</code> if currently editing, <code>false</code> otherwise */ public boolean isEditing() { return editing; } /** * Sets whether or not we are editing the name. * * @param editing <code>true</code> if editing, <code>false</code> otherwise */ public void setEditing(boolean editing) { if(textComponent.isEditable() && this.editing != editing) { this.editing = editing; if(editing) oldText = textComponent.getText(); textComponent.setFocusable(editing); textComponent.setOpaque(editing); textComponent.setBackground(editing ? bgColor : null); textComponent.setHighlighter(editing ? highlighter : null); if(editing) { textComponent.requestFocusInWindow(); textComponent.selectAll(); } else { propertyChangeSupport.firePropertyChange(TEXT_PROPERTY, oldText, textComponent.getText()); } propertyChangeSupport.firePropertyChange(EDITING_PROPERTY, !editing, editing); } } // // MouseAdapter // private final MouseAdapter mouseAdapter = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { if(e.getClickCount() > 1 && !e.isPopupTrigger()) setEditing(true); } }; // // KeyAdapter // private final KeyAdapter keyAdapter = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_ENTER) { if((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != InputEvent.SHIFT_DOWN_MASK) { // Force an update of text //textComponent.setText(textComponent.getText()); setEditing(false); } } else if(e.getKeyCode() == KeyEvent.VK_ESCAPE) { // Reset to text before editing textComponent.setText(oldText); setEditing(false); } } }; // // FocusAdapter // private final FocusAdapter focusAdapter = new FocusAdapter() { @Override public void focusLost(FocusEvent e) { textComponent.setText(textComponent.getText()); setEditing(false); } }; // // PropertyChange listener support // /** * Adds a property change listener to this component. * * @param listener the listener to add */ public void addPropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(listener); } /** * Adds a property change listener for a specific property to this component. * * @param propertyName the property name * @param listener the listener to add */ public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { propertyChangeSupport.addPropertyChangeListener(propertyName, listener); } /** * Removes a property change listener from this component. * * @param listener the listener to remove */ public void removePropertyChangeListener(PropertyChangeListener listener) { propertyChangeSupport.removePropertyChangeListener(listener); } /** * Removes a property change listener for a specific property from this component. * * @param propertyName the property name * @param listener the listener to remove */ public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { propertyChangeSupport.removePropertyChangeListener(propertyName, listener); } }