45 |
import java.awt.event.MouseEvent; |
import java.awt.event.MouseEvent; |
46 |
import java.awt.event.MouseListener; |
import java.awt.event.MouseListener; |
47 |
import java.awt.event.MouseMotionListener; |
import java.awt.event.MouseMotionListener; |
48 |
|
import java.beans.PropertyChangeEvent; |
49 |
|
import java.beans.PropertyChangeListener; |
50 |
import java.util.EventListener; |
import java.util.EventListener; |
51 |
|
|
52 |
|
import javax.swing.SwingUtilities; |
53 |
import javax.swing.event.ChangeEvent; |
import javax.swing.event.ChangeEvent; |
54 |
import javax.swing.event.ChangeListener; |
import javax.swing.event.ChangeListener; |
55 |
|
import javax.swing.event.DocumentEvent; |
56 |
|
import javax.swing.event.DocumentListener; |
57 |
import javax.swing.event.EventListenerList; |
import javax.swing.event.EventListenerList; |
58 |
|
|
59 |
/** |
/** |
65 |
public class DefaultCaret extends Rectangle |
public class DefaultCaret extends Rectangle |
66 |
implements Caret, FocusListener, MouseListener, MouseMotionListener |
implements Caret, FocusListener, MouseListener, MouseMotionListener |
67 |
{ |
{ |
68 |
|
/** |
69 |
|
* Listens for changes in the text component's document and updates the |
70 |
|
* caret accordingly. |
71 |
|
* |
72 |
|
* @author Roman Kennke (kennke@aicas.com) |
73 |
|
*/ |
74 |
|
private class DocumentHandler implements DocumentListener |
75 |
|
{ |
76 |
|
/** |
77 |
|
* Receives notification that some text attributes have changed. No action |
78 |
|
* is taken here. |
79 |
|
* |
80 |
|
* @param event the document event |
81 |
|
*/ |
82 |
|
public void changedUpdate(DocumentEvent event) |
83 |
|
{ |
84 |
|
// Nothing to do here. |
85 |
|
} |
86 |
|
|
87 |
|
/** |
88 |
|
* Receives notification that some text has been inserted from the text |
89 |
|
* component. The caret is moved forward accordingly. |
90 |
|
* |
91 |
|
* @param event the document event |
92 |
|
*/ |
93 |
|
public void insertUpdate(DocumentEvent event) |
94 |
|
{ |
95 |
|
if (policy == ALWAYS_UPDATE || |
96 |
|
(SwingUtilities.isEventDispatchThread() && |
97 |
|
policy == UPDATE_WHEN_ON_EDT)) |
98 |
|
{ |
99 |
|
int dot = getDot(); |
100 |
|
setDot(dot + event.getLength()); |
101 |
|
} |
102 |
|
} |
103 |
|
|
104 |
|
/** |
105 |
|
* Receives notification that some text has been removed into the text |
106 |
|
* component. The caret is moved backwards accordingly. |
107 |
|
* |
108 |
|
* @param event the document event |
109 |
|
*/ |
110 |
|
public void removeUpdate(DocumentEvent event) |
111 |
|
{ |
112 |
|
if (policy == ALWAYS_UPDATE || |
113 |
|
(SwingUtilities.isEventDispatchThread() && |
114 |
|
policy == UPDATE_WHEN_ON_EDT)) |
115 |
|
{ |
116 |
|
int dot = getDot(); |
117 |
|
setDot(dot - event.getLength()); |
118 |
|
} |
119 |
|
else if (policy == NEVER_UPDATE) |
120 |
|
{ |
121 |
|
int docLength = event.getDocument().getLength(); |
122 |
|
if (getDot() > docLength) |
123 |
|
setDot(docLength); |
124 |
|
} |
125 |
|
} |
126 |
|
} |
127 |
|
|
128 |
|
/** |
129 |
|
* Listens for property changes on the text document. This is used to add and |
130 |
|
* remove our document listener, if the document of the text component has |
131 |
|
* changed. |
132 |
|
* |
133 |
|
* @author Roman Kennke (kennke@aicas.com) |
134 |
|
*/ |
135 |
|
private class PropertyChangeHandler implements PropertyChangeListener |
136 |
|
{ |
137 |
|
|
138 |
|
/** |
139 |
|
* Receives notification when a property has changed on the text component. |
140 |
|
* This adds/removes our document listener from the text component's |
141 |
|
* document when the document changes. |
142 |
|
* |
143 |
|
* @param e the property change event |
144 |
|
*/ |
145 |
|
public void propertyChange(PropertyChangeEvent e) |
146 |
|
{ |
147 |
|
if (e.getPropertyName().equals("document")) |
148 |
|
{ |
149 |
|
Document oldDoc = (Document) e.getOldValue(); |
150 |
|
oldDoc.removeDocumentListener(documentListener); |
151 |
|
Document newDoc = (Document) e.getNewValue(); |
152 |
|
newDoc.addDocumentListener(documentListener); |
153 |
|
} |
154 |
|
} |
155 |
|
|
156 |
|
} |
157 |
|
|
158 |
/** The serialization UID (compatible with JDK1.5). */ |
/** The serialization UID (compatible with JDK1.5). */ |
159 |
private static final long serialVersionUID = 4325555698756477346L; |
private static final long serialVersionUID = 4325555698756477346L; |
160 |
|
|
161 |
|
/** |
162 |
|
* Indicates the Caret position should always be updated after Document |
163 |
|
* changes even if the updates are not performed on the Event Dispatching |
164 |
|
* thread. |
165 |
|
* |
166 |
|
* @since 1.5 |
167 |
|
*/ |
168 |
|
public static final int ALWAYS_UPDATE = 2; |
169 |
|
|
170 |
|
/** |
171 |
|
* Indicates the Caret position should not be changed unless the Document |
172 |
|
* length becomes less than the Caret position, in which case the Caret |
173 |
|
* is moved to the end of the Document. |
174 |
|
* |
175 |
|
* @since 1.5 |
176 |
|
*/ |
177 |
|
public static final int NEVER_UPDATE = 1; |
178 |
|
|
179 |
|
/** |
180 |
|
* Indicates the Caret position should be updated only if Document changes |
181 |
|
* are made on the Event Dispatcher thread. |
182 |
|
* |
183 |
|
* @since 1.5 |
184 |
|
*/ |
185 |
|
public static final int UPDATE_WHEN_ON_EDT = 0; |
186 |
|
|
187 |
|
/** Keeps track of the current update policy **/ |
188 |
|
int policy = UPDATE_WHEN_ON_EDT; |
189 |
|
|
190 |
/** |
/** |
191 |
* The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}. |
* The <code>ChangeEvent</code> that is fired by {@link #fireStateChanged()}. |
192 |
*/ |
*/ |
198 |
protected EventListenerList listenerList = new EventListenerList(); |
protected EventListenerList listenerList = new EventListenerList(); |
199 |
|
|
200 |
/** |
/** |
201 |
|
* Our document listener. |
202 |
|
*/ |
203 |
|
DocumentListener documentListener; |
204 |
|
|
205 |
|
/** |
206 |
|
* Our property listener. |
207 |
|
*/ |
208 |
|
PropertyChangeListener propertyChangeListener; |
209 |
|
|
210 |
|
/** |
211 |
* The text component in which this caret is installed. |
* The text component in which this caret is installed. |
212 |
*/ |
*/ |
213 |
private JTextComponent textComponent; |
private JTextComponent textComponent; |
248 |
private Object highlightEntry; |
private Object highlightEntry; |
249 |
|
|
250 |
/** |
/** |
251 |
|
* Sets the Caret update policy. |
252 |
|
* |
253 |
|
* @param policy the new policy. Valid values are: |
254 |
|
* ALWAYS_UPDATE: always update the Caret position, even when Document |
255 |
|
* updates don't occur on the Event Dispatcher thread. |
256 |
|
* NEVER_UPDATE: don't update the Caret position unless the Document |
257 |
|
* length becomes less than the Caret position (then update the |
258 |
|
* Caret to the end of the Document). |
259 |
|
* UPDATE_WHEN_ON_EDT: update the Caret position when the |
260 |
|
* Document updates occur on the Event Dispatcher thread. This is the |
261 |
|
* default. |
262 |
|
* |
263 |
|
* @since 1.5 |
264 |
|
* @throws IllegalArgumentException if policy is not one of the above. |
265 |
|
*/ |
266 |
|
public void setUpdatePolicy (int policy) |
267 |
|
{ |
268 |
|
if (policy != ALWAYS_UPDATE && policy != NEVER_UPDATE |
269 |
|
&& policy != UPDATE_WHEN_ON_EDT) |
270 |
|
throw new |
271 |
|
IllegalArgumentException |
272 |
|
("policy must be ALWAYS_UPDATE, NEVER__UPDATE, or UPDATE_WHEN_ON_EDT"); |
273 |
|
this.policy = policy; |
274 |
|
} |
275 |
|
|
276 |
|
/** |
277 |
|
* Gets the caret update policy. |
278 |
|
* |
279 |
|
* @return the caret update policy. |
280 |
|
* @since 1.5 |
281 |
|
*/ |
282 |
|
public int getUpdatePolicy () |
283 |
|
{ |
284 |
|
return policy; |
285 |
|
} |
286 |
|
|
287 |
|
/** |
288 |
* Moves the caret position when the mouse is dragged over the text |
* Moves the caret position when the mouse is dragged over the text |
289 |
* component, modifying the selection accordingly. |
* component, modifying the selection accordingly. |
290 |
* |
* |
344 |
*/ |
*/ |
345 |
public void mouseExited(MouseEvent event) |
public void mouseExited(MouseEvent event) |
346 |
{ |
{ |
347 |
|
// TODO: What to do here, if anything? |
348 |
} |
} |
349 |
|
|
350 |
/** |
/** |
358 |
public void mousePressed(MouseEvent event) |
public void mousePressed(MouseEvent event) |
359 |
{ |
{ |
360 |
// FIXME: Implement this properly. |
// FIXME: Implement this properly. |
361 |
|
if (!(event.getButton() == MouseEvent.BUTTON1)) |
362 |
|
return; |
363 |
|
setDot(textComponent.viewToModel(event.getPoint())); |
364 |
} |
} |
365 |
|
|
366 |
/** |
/** |
381 |
*/ |
*/ |
382 |
public void focusGained(FocusEvent event) |
public void focusGained(FocusEvent event) |
383 |
{ |
{ |
384 |
|
// TODO: Implement this properly. |
385 |
} |
} |
386 |
|
|
387 |
/** |
/** |
391 |
*/ |
*/ |
392 |
public void focusLost(FocusEvent event) |
public void focusLost(FocusEvent event) |
393 |
{ |
{ |
394 |
|
// TODO: Implement this properly. |
395 |
} |
} |
396 |
|
|
397 |
/** |
/** |
428 |
textComponent.removeFocusListener(this); |
textComponent.removeFocusListener(this); |
429 |
textComponent.removeMouseListener(this); |
textComponent.removeMouseListener(this); |
430 |
textComponent.removeMouseMotionListener(this); |
textComponent.removeMouseMotionListener(this); |
431 |
|
textComponent.getDocument().removeDocumentListener(documentListener); |
432 |
|
documentListener = null; |
433 |
|
textComponent.removePropertyChangeListener(propertyChangeListener); |
434 |
|
propertyChangeListener = null; |
435 |
textComponent = null; |
textComponent = null; |
436 |
} |
} |
437 |
|
|
448 |
textComponent.addFocusListener(this); |
textComponent.addFocusListener(this); |
449 |
textComponent.addMouseListener(this); |
textComponent.addMouseListener(this); |
450 |
textComponent.addMouseMotionListener(this); |
textComponent.addMouseMotionListener(this); |
451 |
|
propertyChangeListener = new PropertyChangeHandler(); |
452 |
|
textComponent.addPropertyChangeListener(propertyChangeListener); |
453 |
|
documentListener = new DocumentHandler(); |
454 |
|
textComponent.getDocument().addDocumentListener(documentListener); |
455 |
repaint(); |
repaint(); |
456 |
} |
} |
457 |
|
|
559 |
*/ |
*/ |
560 |
protected final void repaint() |
protected final void repaint() |
561 |
{ |
{ |
562 |
// FIXME: Is this good? This possibly causes alot of the component |
textComponent.repaint(this); |
|
// hierarchy to be repainted on every caret blink. |
|
|
if (textComponent != null) |
|
|
textComponent.repaint(); |
|
563 |
} |
} |
564 |
|
|
565 |
/** |
/** |
754 |
*/ |
*/ |
755 |
public void setVisible(boolean v) |
public void setVisible(boolean v) |
756 |
{ |
{ |
757 |
visible = v; |
if (v != visible) |
758 |
repaint(); |
{ |
759 |
|
visible = v; |
760 |
|
repaint(); |
761 |
|
} |
762 |
} |
} |
763 |
|
|
764 |
/** |
/** |