001 /*
002 * Copyright (c) 1995-2011, The University of Sheffield. See the file
003 * COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004 *
005 * This file is part of GATE (see http://gate.ac.uk/), and is free
006 * software, licenced under the GNU Library General Public License,
007 * Version 2, June 1991 (in the distribution as file licence.html,
008 * and also available at http://gate.ac.uk/gate/licence.html).
009
010 * Valentin Tablan, 15 Apr 2011
011 *
012 * $Id: JNullableTextField.java 13679 2011-04-15 13:18:56Z valyt $
013 */
014 package gate.gui.annedit;
015
016 import gate.gui.MainFrame;
017
018 import java.awt.Color;
019 import java.awt.event.ActionEvent;
020 import java.beans.PropertyChangeEvent;
021 import java.beans.PropertyChangeListener;
022 import java.util.Collections;
023 import java.util.HashSet;
024 import java.util.Set;
025
026 import javax.swing.AbstractAction;
027 import javax.swing.Box;
028 import javax.swing.BoxLayout;
029 import javax.swing.JButton;
030 import javax.swing.JPanel;
031 import javax.swing.JTextField;
032 import javax.swing.event.DocumentEvent;
033 import javax.swing.event.DocumentListener;
034
035 /**
036 * An encapsulation of {@link JTextField} and a {@link JButton} that allows
037 * the text value to be set to null by pressing the button. Provides the minimal
038 * API required for the needs of {@link SchemaFeaturesEditor}.
039 */
040 public class JNullableTextField extends JPanel {
041 private static final long serialVersionUID = -1530694436281692216L;
042
043 protected class NullifyTextAction extends AbstractAction {
044 private static final long serialVersionUID = -7807829141939910776L;
045
046 public NullifyTextAction() {
047 super(null, MainFrame.getIcon("delete"));
048 putValue(SHORT_DESCRIPTION, "Removes this feature completely");
049 }
050
051 public void actionPerformed(ActionEvent e) {
052 textField.setText(null);
053 text = null;
054 fireRemoveUpdate(null);
055 }
056 }
057
058 /**
059 * The button used to clear (nullify) the textual value.
060 */
061 protected JButton nullifyButton;
062
063 /**
064 * The text field used for editing the textual value.
065 */
066 protected JTextField textField;
067
068 /**
069 * The normal background colour for the text field.
070 */
071 protected Color normalBgColor;
072
073 /**
074 * The colour used for the text field's background when the value is null.
075 */
076 protected Color nullBgColor = new Color(200, 250, 255);
077
078 /**
079 * My document listeners.
080 */
081 protected Set<DocumentListener> documentListeners;
082
083 /**
084 * The text value, which can be null
085 */
086 protected String text = null;
087
088 /**
089 * Creates a new {@link JNullableTextField} widget.
090 */
091 public JNullableTextField() {
092 initGui();
093 initListeners();
094 }
095
096 /**
097 * Sets the value edited by this component. Will cause an insertUpdate
098 * notification to all {@link DocumentListener}s associated with this
099 * component (see {@link #addDocumentListener(DocumentListener)}.
100 * @param text
101 */
102 public void setText(String text) {
103 textField.setText(text);
104 this.text = text;
105 fireInsertUpdate(null);
106 }
107
108 /**
109 * Gets the value currently being edited. Unlike {@link JTextField}, this
110 * value may be null (if {@link #setText(String)} was called previously with
111 * a <code>null</code> value, of the delete button was pressed by the user).
112 * @return
113 */
114 public String getText() {
115 return text;
116 }
117
118 /**
119 * Sets the number of columns for the included {@link JTextField}, see
120 * {@link JTextField#setColumns(int)}.
121 * @param cols
122 */
123 public void setColumns(int cols) {
124 textField.setColumns(cols);
125 }
126
127 protected void initGui() {
128 setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
129
130 textField = new JTextField();
131 add(textField);
132 add(Box.createHorizontalStrut(2));
133 nullifyButton = new JButton(new NullifyTextAction());
134 add(nullifyButton);
135
136 normalBgColor = textField.getBackground();
137 }
138
139 protected void initListeners() {
140 documentListeners = Collections.synchronizedSet(
141 new HashSet<DocumentListener>());
142
143 final DocumentListener tfDocumentListener = new DocumentListener() {
144 public void removeUpdate(DocumentEvent e) {
145 text = textField.getText();
146 fireRemoveUpdate(e);
147 }
148
149 public void insertUpdate(DocumentEvent e) {
150 text = textField.getText();
151 fireInsertUpdate(e);
152 }
153
154 public void changedUpdate(DocumentEvent e) {
155 fireChangedUpdate(e);
156 }
157 };
158
159 textField.getDocument().addDocumentListener(tfDocumentListener);
160
161 textField.addPropertyChangeListener("document", new PropertyChangeListener() {
162 public void propertyChange(PropertyChangeEvent evt) {
163 textField.getDocument().addDocumentListener(tfDocumentListener);
164 }
165 });
166
167 // listen to our own events, and highlight null value
168 addDocumentListener(new DocumentListener() {
169 public void removeUpdate(DocumentEvent e) {
170 valueChanged();
171 }
172 public void insertUpdate(DocumentEvent e) {
173 valueChanged();
174 }
175
176 public void changedUpdate(DocumentEvent e) { }
177
178 private void valueChanged() {
179 if(getText() == null) {
180 textField.setBackground(nullBgColor);
181 } else {
182 textField.setBackground(normalBgColor);
183 }
184 }
185 });
186
187 }
188
189 /**
190 * Registers a new {@link DocumentListener} with this component. The provided
191 * listener will be forwarded all the events generated by the encapsulated
192 * {@link JTextField}. An event will also be generated when the user presses
193 * the delete button, causing the text value to be nullified.
194 * @param listener
195 */
196 public void addDocumentListener(DocumentListener listener) {
197 documentListeners.add(listener);
198 }
199
200 /**
201 * Removes a previously registered listener (see
202 * {@link #addDocumentListener(DocumentListener)}).
203 * @param listener
204 */
205 public void removeDocumentListener(DocumentListener listener) {
206 documentListeners.remove(listener);
207 }
208
209
210 protected void fireChangedUpdate(DocumentEvent e) {
211 for(DocumentListener aListener : documentListeners)
212 aListener.changedUpdate(e);
213 }
214
215 protected void fireInsertUpdate(DocumentEvent e) {
216 for(DocumentListener aListener : documentListeners)
217 aListener.insertUpdate(e);
218 }
219
220 protected void fireRemoveUpdate(DocumentEvent e) {
221 for(DocumentListener aListener : documentListeners)
222 aListener.removeUpdate(e);
223 }
224 }
|