1 | /*
|
---|
2 | * 11/14/2003
|
---|
3 | *
|
---|
4 | * RTextArea.java - An extension of JTextArea that adds many features.
|
---|
5 | *
|
---|
6 | * This library is distributed under a modified BSD license. See the included
|
---|
7 | * RSyntaxTextArea.License.txt file for details.
|
---|
8 | */
|
---|
9 | package org.fife.ui.rtextarea;
|
---|
10 |
|
---|
11 | import java.awt.Color;
|
---|
12 | import java.awt.ComponentOrientation;
|
---|
13 | import java.awt.Graphics;
|
---|
14 | import java.awt.Toolkit;
|
---|
15 | import java.awt.event.ActionEvent;
|
---|
16 | import java.awt.event.FocusEvent;
|
---|
17 | import java.awt.event.KeyEvent;
|
---|
18 | import java.awt.event.MouseEvent;
|
---|
19 | import java.awt.print.PageFormat;
|
---|
20 | import java.awt.print.Printable;
|
---|
21 | import java.io.IOException;
|
---|
22 | import java.io.ObjectInputStream;
|
---|
23 | import java.io.ObjectOutputStream;
|
---|
24 | import java.io.Reader;
|
---|
25 | import java.io.Serializable;
|
---|
26 | import java.util.ArrayList;
|
---|
27 | import java.util.List;
|
---|
28 | import java.util.Locale;
|
---|
29 | import java.util.ResourceBundle;
|
---|
30 | import javax.swing.Action;
|
---|
31 | import javax.swing.Icon;
|
---|
32 | import javax.swing.InputMap;
|
---|
33 | import javax.swing.JMenuItem;
|
---|
34 | import javax.swing.JPopupMenu;
|
---|
35 | import javax.swing.KeyStroke;
|
---|
36 | import javax.swing.SwingUtilities;
|
---|
37 | import javax.swing.UIManager;
|
---|
38 | import javax.swing.event.CaretEvent;
|
---|
39 | import javax.swing.plaf.TextUI;
|
---|
40 | import javax.swing.text.AbstractDocument;
|
---|
41 | import javax.swing.text.BadLocationException;
|
---|
42 | import javax.swing.text.Caret;
|
---|
43 | import javax.swing.text.DefaultEditorKit;
|
---|
44 | import javax.swing.text.Document;
|
---|
45 | import javax.swing.text.Element;
|
---|
46 | import javax.swing.text.Highlighter;
|
---|
47 | import javax.swing.undo.CannotRedoException;
|
---|
48 | import javax.swing.undo.CannotUndoException;
|
---|
49 |
|
---|
50 | import org.fife.print.RPrintUtilities;
|
---|
51 | import org.fife.ui.rtextarea.Macro.MacroRecord;
|
---|
52 |
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * An extension of <code>JTextArea</code> that adds the following features:
|
---|
56 | * <ul>
|
---|
57 | * <li>Insert/Overwrite modes (can be toggled via the Insert key)
|
---|
58 | * <li>A right-click popup menu with standard editing options
|
---|
59 | * <li>Macro support
|
---|
60 | * <li>"Mark all" functionality.
|
---|
61 | * <li>A way to change the background to an image (gif/png/jpg)
|
---|
62 | * <li>Highlight the current line (can be toggled)
|
---|
63 | * <li>An easy way to print its text (implements Printable)
|
---|
64 | * <li>Hard/soft (emulated with spaces) tabs
|
---|
65 | * <li>Fixes a bug with setTabSize
|
---|
66 | * <li>Other handy new methods
|
---|
67 | * </ul>
|
---|
68 | * NOTE: If the background for an <code>RTextArea</code> is set to a color,
|
---|
69 | * its opaque property is set to <code>true</code> for performance reasons. If
|
---|
70 | * the background is set to an image, then the opaque property is set to
|
---|
71 | * <code>false</code>. This slows things down a little, but if it didn't happen
|
---|
72 | * then we would see garbage on-screen when the user scrolled through a document
|
---|
73 | * using the arrow keys (not the page-up/down keys though). You should never
|
---|
74 | * have to set the opaque property yourself; it is always done for you.
|
---|
75 | *
|
---|
76 | * @author Robert Futrell
|
---|
77 | * @version 1.0
|
---|
78 | */
|
---|
79 | public class RTextArea extends RTextAreaBase
|
---|
80 | implements Printable, Serializable {
|
---|
81 |
|
---|
82 | /**
|
---|
83 | * Constant representing insert mode.
|
---|
84 | *
|
---|
85 | * @see #setCaretStyle(int, int)
|
---|
86 | */
|
---|
87 | public static final int INSERT_MODE = 0;
|
---|
88 |
|
---|
89 | /**
|
---|
90 | * Constant representing overwrite mode.
|
---|
91 | *
|
---|
92 | * @see #setCaretStyle(int, int)
|
---|
93 | */
|
---|
94 | public static final int OVERWRITE_MODE = 1;
|
---|
95 |
|
---|
96 | /**
|
---|
97 | * The property fired when the "mark all" color changes.
|
---|
98 | */
|
---|
99 | public static final String MARK_ALL_COLOR_PROPERTY = "RTA.markAllColor";
|
---|
100 |
|
---|
101 | /*
|
---|
102 | * Constants for all actions.
|
---|
103 | */
|
---|
104 | private static final int MIN_ACTION_CONSTANT = 0;
|
---|
105 | public static final int COPY_ACTION = 0;
|
---|
106 | public static final int CUT_ACTION = 1;
|
---|
107 | public static final int DELETE_ACTION = 2;
|
---|
108 | public static final int PASTE_ACTION = 3;
|
---|
109 | public static final int REDO_ACTION = 4;
|
---|
110 | public static final int SELECT_ALL_ACTION = 5;
|
---|
111 | public static final int UNDO_ACTION = 6;
|
---|
112 | private static final int MAX_ACTION_CONSTANT = 6;
|
---|
113 |
|
---|
114 | private static final Color DEFAULT_MARK_ALL_COLOR = Color.ORANGE;
|
---|
115 |
|
---|
116 | /**
|
---|
117 | * The current text mode ({@link #INSERT_MODE} or {@link #OVERWRITE_MODE}).
|
---|
118 | */
|
---|
119 | private int textMode;
|
---|
120 |
|
---|
121 | // All macros are shared across all RTextAreas.
|
---|
122 | private static boolean recordingMacro; // Whether we're recording a macro.
|
---|
123 | private static Macro currentMacro;
|
---|
124 |
|
---|
125 | /**
|
---|
126 | * This text area's popup menu.
|
---|
127 | */
|
---|
128 | private JPopupMenu popupMenu;
|
---|
129 |
|
---|
130 | /**
|
---|
131 | * Whether the popup menu has been created.
|
---|
132 | */
|
---|
133 | private boolean popupMenuCreated;
|
---|
134 |
|
---|
135 | /**
|
---|
136 | * The text last searched for via Ctrl+K or Ctrl+Shift+K.
|
---|
137 | */
|
---|
138 | private static String selectedOccurrenceText;
|
---|
139 |
|
---|
140 | /**
|
---|
141 | * Can return tool tips for this text area. Subclasses can install a
|
---|
142 | * supplier as a means of adding custom tool tips without subclassing
|
---|
143 | * <tt>RTextArea</tt>. {@link #getToolTipText()} checks this supplier
|
---|
144 | * before calling the super class's version.
|
---|
145 | */
|
---|
146 | private ToolTipSupplier toolTipSupplier;
|
---|
147 |
|
---|
148 | private static RecordableTextAction cutAction;
|
---|
149 | private static RecordableTextAction copyAction;
|
---|
150 | private static RecordableTextAction pasteAction;
|
---|
151 | private static RecordableTextAction deleteAction;
|
---|
152 | private static RecordableTextAction undoAction;
|
---|
153 | private static RecordableTextAction redoAction;
|
---|
154 | private static RecordableTextAction selectAllAction;
|
---|
155 |
|
---|
156 | private static IconGroup iconGroup; // Info on icons for actions.
|
---|
157 |
|
---|
158 | private transient RUndoManager undoManager;
|
---|
159 |
|
---|
160 | private transient LineHighlightManager lineHighlightManager;
|
---|
161 |
|
---|
162 | private ArrayList markAllHighlights; // Highlights from "mark all".
|
---|
163 | private String markedWord; // Expression marked in "mark all."
|
---|
164 | private ChangeableHighlightPainter markAllHighlightPainter;
|
---|
165 |
|
---|
166 | private int[] carets; // Index 0=>insert caret, 1=>overwrite.
|
---|
167 |
|
---|
168 | private static final String MSG = "org.fife.ui.rtextarea.RTextArea";
|
---|
169 |
|
---|
170 |
|
---|
171 | /**
|
---|
172 | * Constructor.
|
---|
173 | */
|
---|
174 | public RTextArea() {
|
---|
175 | init(INSERT_MODE);
|
---|
176 | }
|
---|
177 |
|
---|
178 |
|
---|
179 | /**
|
---|
180 | * Constructor.
|
---|
181 | *
|
---|
182 | * @param doc The document for the editor.
|
---|
183 | */
|
---|
184 | public RTextArea(AbstractDocument doc) {
|
---|
185 | super(doc);
|
---|
186 | init(INSERT_MODE);
|
---|
187 | }
|
---|
188 |
|
---|
189 |
|
---|
190 | /**
|
---|
191 | * Constructor.
|
---|
192 | *
|
---|
193 | * @param text The initial text to display.
|
---|
194 | */
|
---|
195 | public RTextArea(String text) {
|
---|
196 | super(text);
|
---|
197 | init(INSERT_MODE);
|
---|
198 | }
|
---|
199 |
|
---|
200 |
|
---|
201 | /**
|
---|
202 | * Constructor.
|
---|
203 | *
|
---|
204 | * @param rows The number of rows to display.
|
---|
205 | * @param cols The number of columns to display.
|
---|
206 | * @throws IllegalArgumentException If either <code>rows</code> or
|
---|
207 | * <code>cols</code> is negative.
|
---|
208 | */
|
---|
209 | public RTextArea(int rows, int cols) {
|
---|
210 | super(rows, cols);
|
---|
211 | init(INSERT_MODE);
|
---|
212 | }
|
---|
213 |
|
---|
214 |
|
---|
215 | /**
|
---|
216 | * Constructor.
|
---|
217 | *
|
---|
218 | * @param text The initial text to display.
|
---|
219 | * @param rows The number of rows to display.
|
---|
220 | * @param cols The number of columns to display.
|
---|
221 | * @throws IllegalArgumentException If either <code>rows</code> or
|
---|
222 | * <code>cols</code> is negative.
|
---|
223 | */
|
---|
224 | public RTextArea(String text, int rows, int cols) {
|
---|
225 | super(text, rows, cols);
|
---|
226 | init(INSERT_MODE);
|
---|
227 | }
|
---|
228 |
|
---|
229 |
|
---|
230 | /**
|
---|
231 | * Constructor.
|
---|
232 | *
|
---|
233 | * @param doc The document for the editor.
|
---|
234 | * @param text The initial text to display.
|
---|
235 | * @param rows The number of rows to display.
|
---|
236 | * @param cols The number of columns to display.
|
---|
237 | * @throws IllegalArgumentException If either <code>rows</code> or
|
---|
238 | * <code>cols</code> is negative.
|
---|
239 | */
|
---|
240 | public RTextArea(AbstractDocument doc, String text, int rows, int cols) {
|
---|
241 | super(doc, text, rows, cols);
|
---|
242 | init(INSERT_MODE);
|
---|
243 | }
|
---|
244 |
|
---|
245 |
|
---|
246 | /**
|
---|
247 | * Creates a new <code>RTextArea</code>.
|
---|
248 | *
|
---|
249 | * @param textMode Either <code>INSERT_MODE</code> or
|
---|
250 | * <code>OVERWRITE_MODE</code>.
|
---|
251 | */
|
---|
252 | public RTextArea(int textMode) {
|
---|
253 | init(textMode);
|
---|
254 | }
|
---|
255 |
|
---|
256 |
|
---|
257 | /**
|
---|
258 | * Adds an action event to the current macro. This shouldn't be called
|
---|
259 | * directly, as it is called by the actions themselves.
|
---|
260 | *
|
---|
261 | * @param id The ID of the recordable text action.
|
---|
262 | * @param actionCommand The "command" of the action event passed to it.
|
---|
263 | */
|
---|
264 | static synchronized void addToCurrentMacro(String id,
|
---|
265 | String actionCommand) {
|
---|
266 | currentMacro.addMacroRecord(new Macro.MacroRecord(id, actionCommand));
|
---|
267 | }
|
---|
268 |
|
---|
269 |
|
---|
270 | /**
|
---|
271 | * Adds a line highlight.
|
---|
272 | *
|
---|
273 | * @param line The line to highlight. This is zero-based.
|
---|
274 | * @param color The color to highlight the line with.
|
---|
275 | * @throws BadLocationException If <code>line</code> is an invalid line
|
---|
276 | * number.
|
---|
277 | * @see #removeLineHighlight(Object)
|
---|
278 | * @see #removeAllLineHighlights()
|
---|
279 | */
|
---|
280 | public Object addLineHighlight(int line, Color color)
|
---|
281 | throws BadLocationException {
|
---|
282 | if (lineHighlightManager==null) {
|
---|
283 | lineHighlightManager = new LineHighlightManager(this);
|
---|
284 | }
|
---|
285 | return lineHighlightManager.addLineHighlight(line, color);
|
---|
286 | }
|
---|
287 |
|
---|
288 |
|
---|
289 | /**
|
---|
290 | * Begins an "atomic edit." All text editing operations between this call
|
---|
291 | * and the next call to <tt>endAtomicEdit()</tt> will be treated as a
|
---|
292 | * single operation by the undo manager.<p>
|
---|
293 | *
|
---|
294 | * Using this method should be done with great care. You should probably
|
---|
295 | * wrap the call to <tt>endAtomicEdit()</tt> in a <tt>finally</tt> block:
|
---|
296 | *
|
---|
297 | * <pre>
|
---|
298 | * textArea.beginAtomicEdit();
|
---|
299 | * try {
|
---|
300 | * // Do editing
|
---|
301 | * } finally {
|
---|
302 | * textArea.endAtomicEdit();
|
---|
303 | * }
|
---|
304 | * </pre>
|
---|
305 | *
|
---|
306 | * @see #endAtomicEdit()
|
---|
307 | */
|
---|
308 | public void beginAtomicEdit() {
|
---|
309 | undoManager.beginInternalAtomicEdit();
|
---|
310 | }
|
---|
311 |
|
---|
312 |
|
---|
313 | /**
|
---|
314 | * Begins recording a macro. After this method is called, all input/caret
|
---|
315 | * events, etc. are recorded until <code>endMacroRecording</code> is
|
---|
316 | * called. If this method is called but the text component is already
|
---|
317 | * recording a macro, nothing happens (but the macro keeps recording).
|
---|
318 | *
|
---|
319 | * @see #isRecordingMacro()
|
---|
320 | * @see #endRecordingMacro()
|
---|
321 | */
|
---|
322 | public static synchronized void beginRecordingMacro() {
|
---|
323 | if (isRecordingMacro()) {
|
---|
324 | //System.err.println("Macro already being recorded!");
|
---|
325 | return;
|
---|
326 | }
|
---|
327 | //JOptionPane.showMessageDialog(this, "Now recording a macro");
|
---|
328 | if (currentMacro!=null)
|
---|
329 | currentMacro = null; // May help gc?
|
---|
330 | currentMacro = new Macro();
|
---|
331 | recordingMacro = true;
|
---|
332 | }
|
---|
333 |
|
---|
334 |
|
---|
335 | /**
|
---|
336 | * Tells whether an undo is possible
|
---|
337 | *
|
---|
338 | * @see #canRedo()
|
---|
339 | * @see #undoLastAction()
|
---|
340 | */
|
---|
341 | public boolean canUndo() {
|
---|
342 | return undoManager.canUndo();
|
---|
343 | }
|
---|
344 |
|
---|
345 |
|
---|
346 | /**
|
---|
347 | * Tells whether a redo is possible
|
---|
348 | *
|
---|
349 | * @see #canUndo()
|
---|
350 | * @see #redoLastAction()
|
---|
351 | */
|
---|
352 | public boolean canRedo() {
|
---|
353 | return undoManager.canRedo();
|
---|
354 | }
|
---|
355 |
|
---|
356 |
|
---|
357 | /**
|
---|
358 | * Clears any "mark all" highlights, if any.
|
---|
359 | *
|
---|
360 | * @see #markAll
|
---|
361 | * @see #getMarkAllHighlightColor
|
---|
362 | * @see #setMarkAllHighlightColor
|
---|
363 | */
|
---|
364 | public void clearMarkAllHighlights() {
|
---|
365 | Highlighter h = getHighlighter();
|
---|
366 | if (h!=null && markAllHighlights!=null) {
|
---|
367 | int count = markAllHighlights.size();
|
---|
368 | for (int i=0; i<count; i++)
|
---|
369 | h.removeHighlight(markAllHighlights.get(i));
|
---|
370 | markAllHighlights.clear();
|
---|
371 | }
|
---|
372 | markedWord = null;
|
---|
373 | repaint();
|
---|
374 | }
|
---|
375 |
|
---|
376 |
|
---|
377 | /**
|
---|
378 | * Configures the popup menu for this text area. This method is called
|
---|
379 | * right before it is displayed, so a hosting application can do any
|
---|
380 | * custom configuration (configuring actions, adding/removing items, etc.).
|
---|
381 | * <p>
|
---|
382 | *
|
---|
383 | * The default implementation does nothing.<p>
|
---|
384 | *
|
---|
385 | * If you set the popup menu via {@link #setPopupMenu(JPopupMenu)}, you
|
---|
386 | * will want to override this method, especially if you removed any of the
|
---|
387 | * menu items in the default popup menu.
|
---|
388 | *
|
---|
389 | * @param popupMenu The popup menu. This will never be <code>null</code>.
|
---|
390 | * @see #createPopupMenu()
|
---|
391 | * @see #setPopupMenu(JPopupMenu)
|
---|
392 | */
|
---|
393 | protected void configurePopupMenu(JPopupMenu popupMenu) {
|
---|
394 | }
|
---|
395 |
|
---|
396 |
|
---|
397 | /**
|
---|
398 | * Returns the caret event/mouse listener for <code>RTextArea</code>s.
|
---|
399 | *
|
---|
400 | * @return The caret event/mouse listener.
|
---|
401 | */
|
---|
402 | protected RTAMouseListener createMouseListener() {
|
---|
403 | return new RTextAreaMutableCaretEvent(this);
|
---|
404 | }
|
---|
405 |
|
---|
406 |
|
---|
407 | /**
|
---|
408 | * Creates the right-click popup menu. Subclasses can override this method
|
---|
409 | * to replace or augment the popup menu returned.
|
---|
410 | *
|
---|
411 | * @return The popup menu.
|
---|
412 | * @see #setPopupMenu(JPopupMenu)
|
---|
413 | * @see #configurePopupMenu(JPopupMenu)
|
---|
414 | * @see #createPopupMenuItem(Action)
|
---|
415 | */
|
---|
416 | protected JPopupMenu createPopupMenu() {
|
---|
417 | JPopupMenu menu = new JPopupMenu();
|
---|
418 | menu.add(createPopupMenuItem(undoAction));
|
---|
419 | menu.add(createPopupMenuItem(redoAction));
|
---|
420 | menu.addSeparator();
|
---|
421 | menu.add(createPopupMenuItem(cutAction));
|
---|
422 | menu.add(createPopupMenuItem(copyAction));
|
---|
423 | menu.add(createPopupMenuItem(pasteAction));
|
---|
424 | menu.add(createPopupMenuItem(deleteAction));
|
---|
425 | menu.addSeparator();
|
---|
426 | menu.add(createPopupMenuItem(selectAllAction));
|
---|
427 | return menu;
|
---|
428 | }
|
---|
429 |
|
---|
430 |
|
---|
431 | /**
|
---|
432 | * Creates the actions used in the popup menu and retrievable by
|
---|
433 | * {@link #getAction(int)}.
|
---|
434 | * TODO: Remove these horrible hacks and move localizing of actions into
|
---|
435 | * the editor kits, where it should be! The context menu should contain
|
---|
436 | * actions from the editor kits.
|
---|
437 | */
|
---|
438 | private static void createPopupMenuActions() {
|
---|
439 |
|
---|
440 | // Create actions for right-click popup menu.
|
---|
441 | // 1.5.2004/pwy: Replaced the CTRL_MASK with the cross-platform version...
|
---|
442 | int mod = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
---|
443 | ResourceBundle msg = ResourceBundle.getBundle(MSG);
|
---|
444 |
|
---|
445 | cutAction = new RTextAreaEditorKit.CutAction();
|
---|
446 | cutAction.setProperties(msg, "Action.Cut");
|
---|
447 | cutAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, mod));
|
---|
448 | copyAction = new RTextAreaEditorKit.CopyAction();
|
---|
449 | copyAction.setProperties(msg, "Action.Copy");
|
---|
450 | copyAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, mod));
|
---|
451 | pasteAction = new RTextAreaEditorKit.PasteAction();
|
---|
452 | pasteAction.setProperties(msg, "Action.Paste");
|
---|
453 | pasteAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, mod));
|
---|
454 | deleteAction = new RTextAreaEditorKit.DeleteNextCharAction();
|
---|
455 | deleteAction.setProperties(msg, "Action.Delete");
|
---|
456 | deleteAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
|
---|
457 | undoAction = new RTextAreaEditorKit.UndoAction();
|
---|
458 | undoAction.setProperties(msg, "Action.Undo");
|
---|
459 | undoAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, mod));
|
---|
460 | redoAction = new RTextAreaEditorKit.RedoAction();
|
---|
461 | redoAction.setProperties(msg, "Action.Redo");
|
---|
462 | redoAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, mod));
|
---|
463 | selectAllAction = new RTextAreaEditorKit.SelectAllAction();
|
---|
464 | selectAllAction.setProperties(msg, "Action.SelectAll");
|
---|
465 | selectAllAction.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, mod));
|
---|
466 |
|
---|
467 | }
|
---|
468 |
|
---|
469 |
|
---|
470 | /**
|
---|
471 | * Creates and configures a menu item for used in the popup menu.
|
---|
472 | *
|
---|
473 | * @param a The action for the menu item.
|
---|
474 | * @return The menu item.
|
---|
475 | * @see #createPopupMenu()
|
---|
476 | */
|
---|
477 | protected JMenuItem createPopupMenuItem(Action a) {
|
---|
478 | JMenuItem item = new JMenuItem(a) {
|
---|
479 | public void setToolTipText(String text) {
|
---|
480 | // Ignore! Actions (e.g. undo/redo) set this when changing
|
---|
481 | // their text due to changing enabled state.
|
---|
482 | }
|
---|
483 | };
|
---|
484 | item.setAccelerator(null);
|
---|
485 | return item;
|
---|
486 | }
|
---|
487 |
|
---|
488 |
|
---|
489 | /**
|
---|
490 | * Returns the a real UI to install on this text area.
|
---|
491 | *
|
---|
492 | * @return The UI.
|
---|
493 | */
|
---|
494 | protected RTextAreaUI createRTextAreaUI() {
|
---|
495 | return new RTextAreaUI(this);
|
---|
496 | }
|
---|
497 |
|
---|
498 |
|
---|
499 | /**
|
---|
500 | * Creates an undo manager for use in this text area.
|
---|
501 | *
|
---|
502 | * @return The undo manager.
|
---|
503 | */
|
---|
504 | protected RUndoManager createUndoManager() {
|
---|
505 | return new RUndoManager(this);
|
---|
506 | }
|
---|
507 |
|
---|
508 |
|
---|
509 | /**
|
---|
510 | * Removes all undoable edits from this document's undo manager. This
|
---|
511 | * method also makes the undo/redo actions disabled.
|
---|
512 | */
|
---|
513 | /*
|
---|
514 | * NOTE: For some reason, it appears I have to create an entirely new
|
---|
515 | * <code>undoManager</code> for undo/redo to continue functioning
|
---|
516 | * properly; if I don't, it only ever lets you do one undo. Not
|
---|
517 | * too sure why this is...
|
---|
518 | */
|
---|
519 | public void discardAllEdits() {
|
---|
520 | undoManager.discardAllEdits();
|
---|
521 | getDocument().removeUndoableEditListener(undoManager);
|
---|
522 | undoManager = createUndoManager();
|
---|
523 | getDocument().addUndoableEditListener(undoManager);
|
---|
524 | undoManager.updateActions();
|
---|
525 | }
|
---|
526 |
|
---|
527 |
|
---|
528 | /**
|
---|
529 | * Completes an "atomic" edit.
|
---|
530 | *
|
---|
531 | * @see #beginAtomicEdit()
|
---|
532 | */
|
---|
533 | public void endAtomicEdit() {
|
---|
534 | undoManager.endInternalAtomicEdit();
|
---|
535 | }
|
---|
536 |
|
---|
537 |
|
---|
538 | /**
|
---|
539 | * Ends recording a macro. If this method is called but the text component
|
---|
540 | * is not recording a macro, nothing happens.
|
---|
541 | *
|
---|
542 | * @see #isRecordingMacro()
|
---|
543 | * @see #beginRecordingMacro()
|
---|
544 | */
|
---|
545 | /*
|
---|
546 | * FIXME: This should throw an exception if we're not recording a macro.
|
---|
547 | */
|
---|
548 | public static synchronized void endRecordingMacro() {
|
---|
549 | if (!isRecordingMacro()) {
|
---|
550 | //System.err.println("Not recording a macro!");
|
---|
551 | return;
|
---|
552 | }
|
---|
553 | recordingMacro = false;
|
---|
554 | }
|
---|
555 |
|
---|
556 |
|
---|
557 | /**
|
---|
558 | * Notifies all listeners that a caret change has occurred.
|
---|
559 | *
|
---|
560 | * @param e The caret event.
|
---|
561 | */
|
---|
562 | protected void fireCaretUpdate(CaretEvent e) {
|
---|
563 |
|
---|
564 | // Decide whether we need to repaint the current line background.
|
---|
565 | possiblyUpdateCurrentLineHighlightLocation();
|
---|
566 |
|
---|
567 | // Now, if there is a highlighted region of text, allow them to cut
|
---|
568 | // and copy.
|
---|
569 | if (e!=null && e.getDot()!=e.getMark()) {// && !cutAction.isEnabled()) {
|
---|
570 | cutAction.setEnabled(true);
|
---|
571 | copyAction.setEnabled(true);
|
---|
572 | }
|
---|
573 |
|
---|
574 | // Otherwise, if there is no highlighted region, don't let them cut
|
---|
575 | // or copy. The condition here should speed things up, because this
|
---|
576 | // way, we will only enable the actions the first time the selection
|
---|
577 | // becomes nothing.
|
---|
578 | else if (cutAction.isEnabled()) {
|
---|
579 | cutAction.setEnabled(false);
|
---|
580 | copyAction.setEnabled(false);
|
---|
581 | }
|
---|
582 |
|
---|
583 | super.fireCaretUpdate(e);
|
---|
584 |
|
---|
585 | }
|
---|
586 |
|
---|
587 |
|
---|
588 | /**
|
---|
589 | * Removes the "Ctrl+H <=> Backspace" behavior that Java shows, for some
|
---|
590 | * odd reason...
|
---|
591 | */
|
---|
592 | private void fixCtrlH() {
|
---|
593 | InputMap inputMap = getInputMap();
|
---|
594 | KeyStroke char010 = KeyStroke.getKeyStroke("typed \010");
|
---|
595 | InputMap parent = inputMap;
|
---|
596 | while (parent != null) {
|
---|
597 | parent.remove(char010);
|
---|
598 | parent = parent.getParent();
|
---|
599 | }
|
---|
600 | KeyStroke backspace = KeyStroke.getKeyStroke("BACK_SPACE");
|
---|
601 | inputMap.put(backspace, DefaultEditorKit.deletePrevCharAction);
|
---|
602 | }
|
---|
603 |
|
---|
604 |
|
---|
605 | /**
|
---|
606 | * Provides a way to gain access to the editor actions on the right-click
|
---|
607 | * popup menu. This way you can make toolbar/menu bar items use the actual
|
---|
608 | * actions used by all <code>RTextArea</code>s, so that icons stay
|
---|
609 | * synchronized and you don't have to worry about enabling/disabling them
|
---|
610 | * yourself.<p>
|
---|
611 | * Keep in mind that these actions are shared across all instances of
|
---|
612 | * <code>RTextArea</code>, so a change to any action returned by this
|
---|
613 | * method is global across all <code>RTextArea</code> editors in your
|
---|
614 | * application.
|
---|
615 | *
|
---|
616 | * @param action The action to retrieve, such as {@link #CUT_ACTION}.
|
---|
617 | * If the action name is invalid, <code>null</code> is returned.
|
---|
618 | * @return The action, or <code>null</code> if an invalid action is
|
---|
619 | * requested.
|
---|
620 | */
|
---|
621 | public static RecordableTextAction getAction(int action) {
|
---|
622 | if (action<MIN_ACTION_CONSTANT || action>MAX_ACTION_CONSTANT)
|
---|
623 | return null;
|
---|
624 | switch (action) {
|
---|
625 | case COPY_ACTION:
|
---|
626 | return copyAction;
|
---|
627 | case CUT_ACTION:
|
---|
628 | return cutAction;
|
---|
629 | case DELETE_ACTION:
|
---|
630 | return deleteAction;
|
---|
631 | case PASTE_ACTION:
|
---|
632 | return pasteAction;
|
---|
633 | case REDO_ACTION:
|
---|
634 | return redoAction;
|
---|
635 | case SELECT_ALL_ACTION:
|
---|
636 | return selectAllAction;
|
---|
637 | case UNDO_ACTION:
|
---|
638 | return undoAction;
|
---|
639 | }
|
---|
640 | return null;
|
---|
641 | }
|
---|
642 |
|
---|
643 |
|
---|
644 | /**
|
---|
645 | * Returns the macro currently stored in this <code>RTextArea</code>.
|
---|
646 | * Since macros are shared, all <code>RTextArea</code>s in the currently-
|
---|
647 | * running application are using this macro.
|
---|
648 | *
|
---|
649 | * @return The current macro, or <code>null</code> if no macro has been
|
---|
650 | * recorded/loaded.
|
---|
651 | * @see #loadMacro(Macro)
|
---|
652 | */
|
---|
653 | public static synchronized Macro getCurrentMacro() {
|
---|
654 | return currentMacro;
|
---|
655 | }
|
---|
656 |
|
---|
657 |
|
---|
658 | /**
|
---|
659 | * Returns the default color used for "mark all."
|
---|
660 | *
|
---|
661 | * @return The color.
|
---|
662 | * @see #getMarkAllHighlightColor()
|
---|
663 | * @see #setMarkAllHighlightColor(Color)
|
---|
664 | */
|
---|
665 | public static final Color getDefaultMarkAllHighlightColor() {
|
---|
666 | return DEFAULT_MARK_ALL_COLOR;
|
---|
667 | }
|
---|
668 |
|
---|
669 |
|
---|
670 | /**
|
---|
671 | * Returns the icon group being used for the actions of this text area.
|
---|
672 | *
|
---|
673 | * @return The icon group.
|
---|
674 | * @see #setIconGroup(IconGroup)
|
---|
675 | */
|
---|
676 | public static IconGroup getIconGroup() {
|
---|
677 | return iconGroup;
|
---|
678 | }
|
---|
679 |
|
---|
680 |
|
---|
681 | /**
|
---|
682 | * Returns the line highlight manager.
|
---|
683 | *
|
---|
684 | * @return The line highlight manager. This may be <code>null</code>.
|
---|
685 | */
|
---|
686 | LineHighlightManager getLineHighlightManager() {
|
---|
687 | return lineHighlightManager;
|
---|
688 | }
|
---|
689 |
|
---|
690 |
|
---|
691 | /**
|
---|
692 | * Returns the color used in "mark all."
|
---|
693 | *
|
---|
694 | * @return The color.
|
---|
695 | * @see #setMarkAllHighlightColor(Color)
|
---|
696 | */
|
---|
697 | public Color getMarkAllHighlightColor() {
|
---|
698 | return (Color)markAllHighlightPainter.getPaint();
|
---|
699 | }
|
---|
700 |
|
---|
701 |
|
---|
702 | /**
|
---|
703 | * Returns the maximum ascent of all fonts used in this text area. In
|
---|
704 | * the case of a standard <code>RTextArea</code>, this is simply the
|
---|
705 | * ascent of the current font.<p>
|
---|
706 | *
|
---|
707 | * This value could be useful, for example, to implement a line-numbering
|
---|
708 | * scheme.
|
---|
709 | *
|
---|
710 | * @return The ascent of the current font.
|
---|
711 | */
|
---|
712 | public int getMaxAscent() {
|
---|
713 | return getFontMetrics(getFont()).getAscent();
|
---|
714 | }
|
---|
715 |
|
---|
716 |
|
---|
717 | /**
|
---|
718 | * Returns the popup menu for this component, lazily creating it if
|
---|
719 | * necessary.
|
---|
720 | *
|
---|
721 | * @return The popup menu.
|
---|
722 | * @see #createPopupMenu()
|
---|
723 | * @see #setPopupMenu(JPopupMenu)
|
---|
724 | */
|
---|
725 | public JPopupMenu getPopupMenu() {
|
---|
726 | if (!popupMenuCreated) {
|
---|
727 | popupMenu = createPopupMenu();
|
---|
728 | if (popupMenu!=null) {
|
---|
729 | ComponentOrientation orientation = ComponentOrientation.
|
---|
730 | getOrientation(Locale.getDefault());
|
---|
731 | popupMenu.applyComponentOrientation(orientation);
|
---|
732 | }
|
---|
733 | popupMenuCreated = true;
|
---|
734 | }
|
---|
735 | return popupMenu;
|
---|
736 | }
|
---|
737 |
|
---|
738 |
|
---|
739 | /**
|
---|
740 | * Returns the text last selected and used in a Ctrl+K operation.
|
---|
741 | *
|
---|
742 | * @return The text, or <code>null</code> if none.
|
---|
743 | * @see #setSelectedOccurrenceText(String)
|
---|
744 | */
|
---|
745 | public static String getSelectedOccurrenceText() {
|
---|
746 | return selectedOccurrenceText;
|
---|
747 | }
|
---|
748 |
|
---|
749 |
|
---|
750 | /**
|
---|
751 | * Returns the text mode this editor pane is currently in.
|
---|
752 | *
|
---|
753 | * @return Either {@link #INSERT_MODE} or {@link #OVERWRITE_MODE}.
|
---|
754 | * @see #setTextMode(int)
|
---|
755 | */
|
---|
756 | public final int getTextMode() {
|
---|
757 | return textMode;
|
---|
758 | }
|
---|
759 |
|
---|
760 |
|
---|
761 | /**
|
---|
762 | * Returns the tool tip supplier.
|
---|
763 | *
|
---|
764 | * @return The tool tip supplier, or <code>null</code> if one isn't
|
---|
765 | * installed.
|
---|
766 | * @see #setToolTipSupplier(ToolTipSupplier)
|
---|
767 | */
|
---|
768 | public ToolTipSupplier getToolTipSupplier() {
|
---|
769 | return toolTipSupplier;
|
---|
770 | }
|
---|
771 |
|
---|
772 |
|
---|
773 | /**
|
---|
774 | * Returns the tooltip to display for a mouse event at the given
|
---|
775 | * location. This method is overridden to check for a
|
---|
776 | * {@link ToolTipSupplier}; if there is one installed, it is queried for
|
---|
777 | * tool tip text before using the super class's implementation of this
|
---|
778 | * method.
|
---|
779 | *
|
---|
780 | * @param e The mouse event.
|
---|
781 | * @return The tool tip text, or <code>null</code> if none.
|
---|
782 | * @see #getToolTipSupplier()
|
---|
783 | * @see #setToolTipSupplier(ToolTipSupplier)
|
---|
784 | */
|
---|
785 | public String getToolTipText(MouseEvent e) {
|
---|
786 | String tip = null;
|
---|
787 | if (getToolTipSupplier()!=null) {
|
---|
788 | tip = getToolTipSupplier().getToolTipText(this, e);
|
---|
789 | }
|
---|
790 | return tip!=null ? tip : super.getToolTipText();
|
---|
791 | }
|
---|
792 |
|
---|
793 |
|
---|
794 | /**
|
---|
795 | * Does the actual dirty-work of replacing the selected text in this
|
---|
796 | * text area (i.e., in its document). This method provides a hook for
|
---|
797 | * subclasses to handle this in a different way.
|
---|
798 | *
|
---|
799 | * @param content The content to add.
|
---|
800 | */
|
---|
801 | protected void handleReplaceSelection(String content) {
|
---|
802 | // Call into super to handle composed text (1.5+ only though).
|
---|
803 | super.replaceSelection(content);
|
---|
804 | }
|
---|
805 |
|
---|
806 |
|
---|
807 | /**
|
---|
808 | * Initializes this text area.
|
---|
809 | *
|
---|
810 | * @param textMode The text mode.
|
---|
811 | */
|
---|
812 | private void init(int textMode) {
|
---|
813 |
|
---|
814 | // NOTE: Our actions are created here instead of in a static block
|
---|
815 | // so they are only created when the first RTextArea is instantiated,
|
---|
816 | // not before. There have been reports of users calling static getters
|
---|
817 | // (e.g. RSyntaxTextArea.getDefaultBracketMatchBGColor()) which would
|
---|
818 | // cause these actions to be created and (possibly) incorrectly
|
---|
819 | // localized, if they were in a static block.
|
---|
820 | if (cutAction==null) {
|
---|
821 | createPopupMenuActions();
|
---|
822 | }
|
---|
823 |
|
---|
824 | // Install the undo manager.
|
---|
825 | undoManager = createUndoManager();
|
---|
826 | getDocument().addUndoableEditListener(undoManager);
|
---|
827 |
|
---|
828 | // Set the defaults for various stuff.
|
---|
829 | Color markAllHighlightColor = getDefaultMarkAllHighlightColor();
|
---|
830 | markAllHighlightPainter = new ChangeableHighlightPainter(
|
---|
831 | markAllHighlightColor);
|
---|
832 | setMarkAllHighlightColor(markAllHighlightColor);
|
---|
833 | carets = new int[2];
|
---|
834 | setCaretStyle(INSERT_MODE, ConfigurableCaret.THICK_VERTICAL_LINE_STYLE);
|
---|
835 | setCaretStyle(OVERWRITE_MODE, ConfigurableCaret.BLOCK_STYLE);
|
---|
836 | setDragEnabled(true); // Enable drag-and-drop.
|
---|
837 |
|
---|
838 | // Set values for stuff the user passed in.
|
---|
839 | setTextMode(textMode); // carets array must be initialized first!
|
---|
840 |
|
---|
841 | // Fix the odd "Ctrl+H <=> Backspace" Java behavior.
|
---|
842 | fixCtrlH();
|
---|
843 |
|
---|
844 | }
|
---|
845 |
|
---|
846 |
|
---|
847 | /**
|
---|
848 | * Returns whether or not a macro is being recorded.
|
---|
849 | *
|
---|
850 | * @return Whether or not a macro is being recorded.
|
---|
851 | * @see #beginRecordingMacro()
|
---|
852 | * @see #endRecordingMacro()
|
---|
853 | */
|
---|
854 | public static synchronized boolean isRecordingMacro() {
|
---|
855 | return recordingMacro;
|
---|
856 | }
|
---|
857 |
|
---|
858 |
|
---|
859 | /**
|
---|
860 | * Loads a macro to be used by all <code>RTextArea</code>s in the current
|
---|
861 | * application.
|
---|
862 | *
|
---|
863 | * @param macro The macro to load.
|
---|
864 | * @see #getCurrentMacro()
|
---|
865 | */
|
---|
866 | public static synchronized void loadMacro(Macro macro) {
|
---|
867 | currentMacro = macro;
|
---|
868 | }
|
---|
869 |
|
---|
870 |
|
---|
871 | /**
|
---|
872 | * Marks all instances of the specified text in this text area.
|
---|
873 | *
|
---|
874 | * @param toMark The text to mark.
|
---|
875 | * @param matchCase Whether the match should be case-sensitive.
|
---|
876 | * @param wholeWord Whether the matches should be surrounded by spaces
|
---|
877 | * or tabs.
|
---|
878 | * @param regex Whether <code>toMark</code> is a Java regular expression.
|
---|
879 | * @return The number of matches marked.
|
---|
880 | * @see #clearMarkAllHighlights
|
---|
881 | * @see #getMarkAllHighlightColor
|
---|
882 | * @see #setMarkAllHighlightColor
|
---|
883 | */
|
---|
884 | public int markAll(String toMark, boolean matchCase, boolean wholeWord,
|
---|
885 | boolean regex) {
|
---|
886 | Highlighter h = getHighlighter();
|
---|
887 | int numMarked = 0;
|
---|
888 | if (toMark!=null && !toMark.equals(markedWord) && h!=null) {
|
---|
889 | if (markAllHighlights!=null)
|
---|
890 | clearMarkAllHighlights();
|
---|
891 | else
|
---|
892 | markAllHighlights = new ArrayList(10);
|
---|
893 | int caretPos = getCaretPosition();
|
---|
894 | markedWord = toMark;
|
---|
895 | setCaretPosition(0);
|
---|
896 | SearchContext context = new SearchContext();
|
---|
897 | context.setSearchFor(toMark);
|
---|
898 | context.setMatchCase(matchCase);
|
---|
899 | context.setRegularExpression(regex);
|
---|
900 | context.setSearchForward(true);
|
---|
901 | context.setWholeWord(wholeWord);
|
---|
902 | boolean found = SearchEngine.find(this, context);
|
---|
903 | while (found) {
|
---|
904 | int start = getSelectionStart();
|
---|
905 | int end = getSelectionEnd();
|
---|
906 | try {
|
---|
907 | markAllHighlights.add(h.addHighlight(start, end,
|
---|
908 | markAllHighlightPainter));
|
---|
909 | } catch (BadLocationException ble) {
|
---|
910 | ble.printStackTrace();
|
---|
911 | }
|
---|
912 | numMarked++;
|
---|
913 | found = SearchEngine.find(this, context);
|
---|
914 | }
|
---|
915 | setCaretPosition(caretPos);
|
---|
916 | repaint();
|
---|
917 | }
|
---|
918 | return numMarked;
|
---|
919 | }
|
---|
920 |
|
---|
921 |
|
---|
922 | /**
|
---|
923 | * {@inheritDoc}
|
---|
924 | */
|
---|
925 | public void paste() {
|
---|
926 | // Treat paste operations as atomic, otherwise the removal and
|
---|
927 | // insertion are treated as two separate undo-able operations.
|
---|
928 | beginAtomicEdit();
|
---|
929 | try {
|
---|
930 | super.paste();
|
---|
931 | } finally {
|
---|
932 | endAtomicEdit();
|
---|
933 | }
|
---|
934 | }
|
---|
935 |
|
---|
936 |
|
---|
937 | /**
|
---|
938 | * "Plays back" the last recorded macro in this text area.
|
---|
939 | */
|
---|
940 | public synchronized void playbackLastMacro() {
|
---|
941 | if (currentMacro!=null) {
|
---|
942 | Action[] actions = getActions();
|
---|
943 | int numActions = actions.length;
|
---|
944 | List macroRecords = currentMacro.getMacroRecords();
|
---|
945 | int num = macroRecords.size();
|
---|
946 | if (num>0) {
|
---|
947 | undoManager.beginInternalAtomicEdit();
|
---|
948 | try {
|
---|
949 | for (int i=0; i<num; i++) {
|
---|
950 | MacroRecord record = (MacroRecord)macroRecords.get(i);
|
---|
951 | for (int j=0; j<numActions; j++) {
|
---|
952 | if ((actions[j] instanceof RecordableTextAction) &&
|
---|
953 | record.id.equals(
|
---|
954 | ((RecordableTextAction)actions[j]).getMacroID())) {
|
---|
955 | actions[j].actionPerformed(
|
---|
956 | new ActionEvent(this,
|
---|
957 | ActionEvent.ACTION_PERFORMED,
|
---|
958 | record.actionCommand));
|
---|
959 | break;
|
---|
960 | }
|
---|
961 | }
|
---|
962 | }
|
---|
963 | } finally {
|
---|
964 | undoManager.endInternalAtomicEdit();
|
---|
965 | }
|
---|
966 | }
|
---|
967 | }
|
---|
968 | }
|
---|
969 |
|
---|
970 |
|
---|
971 | /**
|
---|
972 | * Method called when it's time to print this badboy (the old-school,
|
---|
973 | * AWT way).
|
---|
974 | *
|
---|
975 | * @param g The context into which the page is drawn.
|
---|
976 | * @param pageFormat The size and orientation of the page being drawn.
|
---|
977 | * @param pageIndex The zero based index of the page to be drawn.
|
---|
978 | */
|
---|
979 | public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
|
---|
980 | return RPrintUtilities.printDocumentWordWrap(g, this, getFont(), pageIndex, pageFormat, getTabSize());
|
---|
981 | }
|
---|
982 |
|
---|
983 |
|
---|
984 | /**
|
---|
985 | * We override this method because the super version gives us an entirely
|
---|
986 | * new <code>Document</code>, thus requiring us to re-attach our Undo
|
---|
987 | * manager. With this version we just replace the text.
|
---|
988 | */
|
---|
989 | public void read(Reader in, Object desc) throws IOException {
|
---|
990 |
|
---|
991 | RTextAreaEditorKit kit = (RTextAreaEditorKit)getUI().getEditorKit(this);
|
---|
992 | setText(null);
|
---|
993 | Document doc = getDocument();
|
---|
994 | if (desc != null)
|
---|
995 | doc.putProperty(Document.StreamDescriptionProperty, desc);
|
---|
996 | try {
|
---|
997 | // NOTE: Resets the "line separator" property.
|
---|
998 | kit.read(in, doc, 0);
|
---|
999 | } catch (BadLocationException e) {
|
---|
1000 | throw new IOException(e.getMessage());
|
---|
1001 | }
|
---|
1002 |
|
---|
1003 | }
|
---|
1004 |
|
---|
1005 |
|
---|
1006 | /**
|
---|
1007 | * De-serializes a text area.
|
---|
1008 | *
|
---|
1009 | * @param s The stream to read from.
|
---|
1010 | * @throws ClassNotFoundException
|
---|
1011 | * @throws IOException
|
---|
1012 | */
|
---|
1013 | private void readObject(ObjectInputStream s)
|
---|
1014 | throws ClassNotFoundException, IOException {
|
---|
1015 |
|
---|
1016 | s.defaultReadObject();
|
---|
1017 |
|
---|
1018 | // UndoManagers cannot be serialized without Exceptions. See
|
---|
1019 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4275892
|
---|
1020 | undoManager = createUndoManager();
|
---|
1021 | getDocument().addUndoableEditListener(undoManager);
|
---|
1022 |
|
---|
1023 | lineHighlightManager = null; // Keep FindBugs happy.
|
---|
1024 |
|
---|
1025 | }
|
---|
1026 |
|
---|
1027 |
|
---|
1028 | /**
|
---|
1029 | * Attempt to redo the last action.
|
---|
1030 | *
|
---|
1031 | * @see #undoLastAction()
|
---|
1032 | */
|
---|
1033 | public void redoLastAction() {
|
---|
1034 | // NOTE: The try/catch block shouldn't be necessary...
|
---|
1035 | try {
|
---|
1036 | if (undoManager.canRedo())
|
---|
1037 | undoManager.redo();
|
---|
1038 | } catch (CannotRedoException cre) {
|
---|
1039 | cre.printStackTrace();
|
---|
1040 | }
|
---|
1041 | }
|
---|
1042 |
|
---|
1043 |
|
---|
1044 | /**
|
---|
1045 | * Removes all line highlights.
|
---|
1046 | *
|
---|
1047 | * @see #removeLineHighlight(Object)
|
---|
1048 | */
|
---|
1049 | public void removeAllLineHighlights() {
|
---|
1050 | if (lineHighlightManager!=null) {
|
---|
1051 | lineHighlightManager.removeAllLineHighlights();
|
---|
1052 | }
|
---|
1053 | }
|
---|
1054 |
|
---|
1055 |
|
---|
1056 | /**
|
---|
1057 | * Removes a line highlight.
|
---|
1058 | *
|
---|
1059 | * @param tag The tag of the line highlight to remove.
|
---|
1060 | * @see #removeAllLineHighlights()
|
---|
1061 | * @see #addLineHighlight(int, Color)
|
---|
1062 | */
|
---|
1063 | public void removeLineHighlight(Object tag) {
|
---|
1064 | if (lineHighlightManager!=null) {
|
---|
1065 | lineHighlightManager.removeLineHighlight(tag);
|
---|
1066 | }
|
---|
1067 | }
|
---|
1068 |
|
---|
1069 |
|
---|
1070 | /**
|
---|
1071 | * Replaces text from the indicated start to end position with the
|
---|
1072 | * new text specified. Does nothing if the model is null. Simply
|
---|
1073 | * does a delete if the new string is null or empty.
|
---|
1074 | * <p>
|
---|
1075 | * This method is thread safe, although most Swing methods
|
---|
1076 | * are not.<p>
|
---|
1077 | * This method is overridden so that our Undo manager remembers it as a
|
---|
1078 | * single operation (it has trouble with this, especially for
|
---|
1079 | * <code>RSyntaxTextArea</code> and the "auto-indent" feature).
|
---|
1080 | *
|
---|
1081 | * @param str the text to use as the replacement
|
---|
1082 | * @param start the start position >= 0
|
---|
1083 | * @param end the end position >= start
|
---|
1084 | * @exception IllegalArgumentException if part of the range is an
|
---|
1085 | * invalid position in the model
|
---|
1086 | * @see #insert(String, int)
|
---|
1087 | * @see #replaceRange(String, int, int)
|
---|
1088 | */
|
---|
1089 | public void replaceRange(String str, int start, int end) {
|
---|
1090 | if (end < start)
|
---|
1091 | throw new IllegalArgumentException("end before start");
|
---|
1092 | Document doc = getDocument();
|
---|
1093 | if (doc != null) {
|
---|
1094 | try {
|
---|
1095 | // Without this, in some cases we'll have to do two undos
|
---|
1096 | // for one logical operation (for example, try editing a
|
---|
1097 | // Java source file in an RSyntaxTextArea, and moving a line
|
---|
1098 | // with text already on it down via Enter. Without this
|
---|
1099 | // line, doing a single "undo" moves all later text up,
|
---|
1100 | // but the first line moved down isn't there! Doing a
|
---|
1101 | // second undo puts it back.
|
---|
1102 | undoManager.beginInternalAtomicEdit();
|
---|
1103 | ((AbstractDocument)doc).replace(start, end - start,
|
---|
1104 | str, null);
|
---|
1105 | } catch (BadLocationException e) {
|
---|
1106 | throw new IllegalArgumentException(e.getMessage());
|
---|
1107 | } finally {
|
---|
1108 | undoManager.endInternalAtomicEdit();
|
---|
1109 | }
|
---|
1110 | }
|
---|
1111 | }
|
---|
1112 |
|
---|
1113 |
|
---|
1114 | /**
|
---|
1115 | * This method overrides <code>JTextComponent</code>'s
|
---|
1116 | * <code>replaceSelection</code>, so that if <code>textMode</code> is
|
---|
1117 | * {@link #OVERWRITE_MODE}, it actually overwrites.
|
---|
1118 | *
|
---|
1119 | * @param text The content to replace the selection with.
|
---|
1120 | */
|
---|
1121 | public void replaceSelection(String text) {
|
---|
1122 |
|
---|
1123 | // It's legal for null to be used here...
|
---|
1124 | if (text==null) {
|
---|
1125 | handleReplaceSelection(text);
|
---|
1126 | return;
|
---|
1127 | }
|
---|
1128 |
|
---|
1129 | if (getTabsEmulated() && text.indexOf('\t')>-1) {
|
---|
1130 | text = replaceTabsWithSpaces(text);
|
---|
1131 | }
|
---|
1132 |
|
---|
1133 | // If the user wants to overwrite text...
|
---|
1134 | if (textMode==OVERWRITE_MODE && !"\n".equals(text)) {
|
---|
1135 |
|
---|
1136 | Caret caret = getCaret();
|
---|
1137 | int caretPos = caret.getDot();
|
---|
1138 | Document doc = getDocument();
|
---|
1139 | Element map = doc.getDefaultRootElement();
|
---|
1140 | int curLine = map.getElementIndex(caretPos);
|
---|
1141 | int lastLine = map.getElementCount() - 1;
|
---|
1142 |
|
---|
1143 | try {
|
---|
1144 |
|
---|
1145 | // If we're not at the end of a line, select the characters
|
---|
1146 | // that will be overwritten (otherwise JTextArea will simply
|
---|
1147 | // insert in front of them).
|
---|
1148 | int curLineEnd = getLineEndOffset(curLine);
|
---|
1149 | if (caretPos==caret.getMark() && caretPos!=curLineEnd) {
|
---|
1150 | if (curLine==lastLine)
|
---|
1151 | caretPos = Math.min(caretPos+text.length(), curLineEnd);
|
---|
1152 | else
|
---|
1153 | caretPos = Math.min(caretPos+text.length(), curLineEnd-1);
|
---|
1154 | caret.moveDot(caretPos);//moveCaretPosition(caretPos);
|
---|
1155 | }
|
---|
1156 |
|
---|
1157 | } catch (BadLocationException ble) { // Never happens
|
---|
1158 | UIManager.getLookAndFeel().provideErrorFeedback(this);
|
---|
1159 | ble.printStackTrace();
|
---|
1160 | }
|
---|
1161 |
|
---|
1162 | } // End of if (textMode==OVERWRITE_MODE).
|
---|
1163 |
|
---|
1164 | // Now, actually do the inserting/replacing. Our undoManager will
|
---|
1165 | // take care of remembering the remove/insert as atomic if we are in
|
---|
1166 | // overwrite mode.
|
---|
1167 | handleReplaceSelection(text);
|
---|
1168 |
|
---|
1169 | }
|
---|
1170 |
|
---|
1171 |
|
---|
1172 | private StringBuffer repTabsSB;
|
---|
1173 | /**
|
---|
1174 | * Replaces all instances of the tab character in <code>text</code> with
|
---|
1175 | * the number of spaces equivalent to a tab in this text area.<p>
|
---|
1176 | *
|
---|
1177 | * This method should only be called from thread-safe methods, such as
|
---|
1178 | * {@link #replaceSelection(String)}.
|
---|
1179 | *
|
---|
1180 | * @param text The <code>java.lang.String</code> in which to replace tabs
|
---|
1181 | * with spaces. This has already been verified to have at least
|
---|
1182 | * one tab character in it.
|
---|
1183 | * @return A <code>java.lang.String</code> just like <code>text</code>,
|
---|
1184 | * but with spaces instead of tabs.
|
---|
1185 | */
|
---|
1186 | private final String replaceTabsWithSpaces(String text) {
|
---|
1187 |
|
---|
1188 | String tabText = "";
|
---|
1189 | int temp = getTabSize();
|
---|
1190 | for (int i=0; i<temp; i++) {
|
---|
1191 | tabText += ' ';
|
---|
1192 | }
|
---|
1193 |
|
---|
1194 | // Common case: User's entering a single tab (pressed the tab key).
|
---|
1195 | if (text.length()==1) {
|
---|
1196 | return tabText;
|
---|
1197 | }
|
---|
1198 |
|
---|
1199 | // Otherwise, there may be more than one tab. Manually search for
|
---|
1200 | // tabs for performance, as opposed to using String#replaceAll().
|
---|
1201 | // This method is called for each character inserted when "replace
|
---|
1202 | // tabs with spaces" is enabled, so we need to be quick.
|
---|
1203 |
|
---|
1204 | //return text.replaceAll("\t", tabText);
|
---|
1205 | if (repTabsSB==null) {
|
---|
1206 | repTabsSB = new StringBuffer();
|
---|
1207 | }
|
---|
1208 | repTabsSB.setLength(0);
|
---|
1209 | char[] array = text.toCharArray(); // Wouldn't be needed in 1.5!
|
---|
1210 | int oldPos = 0;
|
---|
1211 | int pos = 0;
|
---|
1212 | while ((pos=text.indexOf('\t', oldPos))>-1) {
|
---|
1213 | //repTabsSB.append(text, oldPos, pos); // Added in Java 1.5
|
---|
1214 | if (pos>oldPos) {
|
---|
1215 | repTabsSB.append(array, oldPos, pos-oldPos);
|
---|
1216 | }
|
---|
1217 | repTabsSB.append(tabText);
|
---|
1218 | oldPos = pos + 1;
|
---|
1219 | }
|
---|
1220 | if (oldPos<array.length) {
|
---|
1221 | repTabsSB.append(array, oldPos, array.length-oldPos);
|
---|
1222 | }
|
---|
1223 |
|
---|
1224 | return repTabsSB.toString();
|
---|
1225 |
|
---|
1226 | }
|
---|
1227 |
|
---|
1228 |
|
---|
1229 |
|
---|
1230 | /**
|
---|
1231 | * Sets the properties of one of the actions this text area owns.
|
---|
1232 | *
|
---|
1233 | * @param action The action to modify; for example, {@link #CUT_ACTION}.
|
---|
1234 | * @param name The new name for the action.
|
---|
1235 | * @param mnemonic The new mnemonic for the action.
|
---|
1236 | * @param accelerator The new accelerator key for the action.
|
---|
1237 | */
|
---|
1238 | public static void setActionProperties(int action, String name,
|
---|
1239 | char mnemonic, KeyStroke accelerator) {
|
---|
1240 | setActionProperties(action, name, new Integer(mnemonic), accelerator);
|
---|
1241 | }
|
---|
1242 |
|
---|
1243 |
|
---|
1244 | /**
|
---|
1245 | * Sets the properties of one of the actions this text area owns.
|
---|
1246 | *
|
---|
1247 | * @param action The action to modify; for example, {@link #CUT_ACTION}.
|
---|
1248 | * @param name The new name for the action.
|
---|
1249 | * @param mnemonic The new mnemonic for the action.
|
---|
1250 | * @param accelerator The new accelerator key for the action.
|
---|
1251 | */
|
---|
1252 | public static void setActionProperties(int action, String name,
|
---|
1253 | Integer mnemonic, KeyStroke accelerator) {
|
---|
1254 |
|
---|
1255 | Action tempAction = null;
|
---|
1256 |
|
---|
1257 | switch (action) {
|
---|
1258 | case CUT_ACTION:
|
---|
1259 | tempAction = cutAction;
|
---|
1260 | break;
|
---|
1261 | case COPY_ACTION:
|
---|
1262 | tempAction = copyAction;
|
---|
1263 | break;
|
---|
1264 | case PASTE_ACTION:
|
---|
1265 | tempAction = pasteAction;
|
---|
1266 | break;
|
---|
1267 | case DELETE_ACTION:
|
---|
1268 | tempAction = deleteAction;
|
---|
1269 | break;
|
---|
1270 | case SELECT_ALL_ACTION:
|
---|
1271 | tempAction = selectAllAction;
|
---|
1272 | break;
|
---|
1273 | case UNDO_ACTION:
|
---|
1274 | case REDO_ACTION:
|
---|
1275 | default:
|
---|
1276 | return;
|
---|
1277 | }
|
---|
1278 |
|
---|
1279 | tempAction.putValue(Action.NAME, name);
|
---|
1280 | tempAction.putValue(Action.SHORT_DESCRIPTION, name);
|
---|
1281 | tempAction.putValue(Action.ACCELERATOR_KEY, accelerator);
|
---|
1282 | tempAction.putValue(Action.MNEMONIC_KEY, mnemonic);
|
---|
1283 |
|
---|
1284 | }
|
---|
1285 |
|
---|
1286 |
|
---|
1287 | /**
|
---|
1288 | * This method is overridden to make sure that instances of
|
---|
1289 | * <code>RTextArea</code> only use {@link ConfigurableCaret}s.
|
---|
1290 | * To set the style of caret (vertical line, block, etc.) used for
|
---|
1291 | * insert or overwrite mode, use {@link #setCaretStyle(int, int)}.
|
---|
1292 | *
|
---|
1293 | * @param caret The caret to use. If this is not an instance of
|
---|
1294 | * <code>ConfigurableCaret</code>, an exception is thrown.
|
---|
1295 | * @throws IllegalArgumentException If the specified caret is not an
|
---|
1296 | * <code>ConfigurableCaret</code>.
|
---|
1297 | * @see #setCaretStyle(int, int)
|
---|
1298 | */
|
---|
1299 | public void setCaret(Caret caret) {
|
---|
1300 | if (!(caret instanceof ConfigurableCaret)) {
|
---|
1301 | throw new IllegalArgumentException(
|
---|
1302 | "RTextArea needs ConfigurableCaret");
|
---|
1303 | }
|
---|
1304 | super.setCaret(caret);
|
---|
1305 | if (carets!=null) { // Called by setUI() before carets is initialized
|
---|
1306 | ((ConfigurableCaret)caret).setStyle(carets[getTextMode()]);
|
---|
1307 | }
|
---|
1308 | }
|
---|
1309 |
|
---|
1310 |
|
---|
1311 | /**
|
---|
1312 | * Sets the style of caret used when in insert or overwrite mode.
|
---|
1313 | *
|
---|
1314 | * @param mode Either {@link #INSERT_MODE} or {@link #OVERWRITE_MODE}.
|
---|
1315 | * @param style The style for the caret (such as
|
---|
1316 | * {@link ConfigurableCaret#VERTICAL_LINE_STYLE}).
|
---|
1317 | * @see org.fife.ui.rtextarea.ConfigurableCaret
|
---|
1318 | */
|
---|
1319 | public void setCaretStyle(int mode, int style) {
|
---|
1320 | style = (style>=ConfigurableCaret.MIN_STYLE &&
|
---|
1321 | style<=ConfigurableCaret.MAX_STYLE ?
|
---|
1322 | style : ConfigurableCaret.THICK_VERTICAL_LINE_STYLE);
|
---|
1323 | carets[mode] = style;
|
---|
1324 | if (mode==getTextMode()) {
|
---|
1325 | // Will repaint the caret if necessary.
|
---|
1326 | ((ConfigurableCaret)getCaret()).setStyle(style);
|
---|
1327 | }
|
---|
1328 | }
|
---|
1329 |
|
---|
1330 |
|
---|
1331 | /**
|
---|
1332 | * Sets the document used by this text area.
|
---|
1333 | *
|
---|
1334 | * @param document The new document to use.
|
---|
1335 | * @throws IllegalArgumentException If the document is not an instance of
|
---|
1336 | * {@link AbstractDocument}.
|
---|
1337 | */
|
---|
1338 | public void setDocument(Document document) {
|
---|
1339 | if (!(document instanceof AbstractDocument)) {
|
---|
1340 | throw new IllegalArgumentException("RTextArea requires " +
|
---|
1341 | "instances of AbstractDocument for its document");
|
---|
1342 | }
|
---|
1343 | if (undoManager!=null) { // First time through, undoManager==null
|
---|
1344 | Document old = getDocument();
|
---|
1345 | if (old!=null) {
|
---|
1346 | old.removeUndoableEditListener(undoManager);
|
---|
1347 | }
|
---|
1348 | }
|
---|
1349 | super.setDocument(document);
|
---|
1350 | if (undoManager!=null) {
|
---|
1351 | document.addUndoableEditListener(undoManager);
|
---|
1352 | discardAllEdits();
|
---|
1353 | }
|
---|
1354 | }
|
---|
1355 |
|
---|
1356 |
|
---|
1357 | /**
|
---|
1358 | * Sets the path in which to find images to associate with the editor's
|
---|
1359 | * actions. The path MUST contain the following images (with the
|
---|
1360 | * appropriate extension as defined by the icon group):<br>
|
---|
1361 | * <ul>
|
---|
1362 | * <li>cut</li>
|
---|
1363 | * <li>copy</li>
|
---|
1364 | * <li>paste</li>
|
---|
1365 | * <li>delete</li>
|
---|
1366 | * <li>undo</li>
|
---|
1367 | * <li>redo</li>
|
---|
1368 | * <li>selectall</li>
|
---|
1369 | * </ul>
|
---|
1370 | * If any of the above images don't exist, the corresponding action will
|
---|
1371 | * not have an icon.
|
---|
1372 | *
|
---|
1373 | * @param group The icon group to load.
|
---|
1374 | * @see #getIconGroup()
|
---|
1375 | */
|
---|
1376 | public static synchronized void setIconGroup(IconGroup group) {
|
---|
1377 | Icon icon = group.getIcon("cut");
|
---|
1378 | cutAction.putValue(Action.SMALL_ICON, icon);
|
---|
1379 | icon = group.getIcon("copy");
|
---|
1380 | copyAction.putValue(Action.SMALL_ICON, icon);
|
---|
1381 | icon = group.getIcon("paste");
|
---|
1382 | pasteAction.putValue(Action.SMALL_ICON, icon);
|
---|
1383 | icon = group.getIcon("delete");
|
---|
1384 | deleteAction.putValue(Action.SMALL_ICON, icon);
|
---|
1385 | icon = group.getIcon("undo");
|
---|
1386 | undoAction.putValue(Action.SMALL_ICON, icon);
|
---|
1387 | icon = group.getIcon("redo");
|
---|
1388 | redoAction.putValue(Action.SMALL_ICON, icon);
|
---|
1389 | icon = group.getIcon("selectall");
|
---|
1390 | selectAllAction.putValue(Action.SMALL_ICON, icon);
|
---|
1391 | iconGroup = group;
|
---|
1392 | }
|
---|
1393 |
|
---|
1394 |
|
---|
1395 | /**
|
---|
1396 | * Sets the color used for "mark all." This fires a property change of
|
---|
1397 | * type {@link #MARK_ALL_COLOR_PROPERTY}.
|
---|
1398 | *
|
---|
1399 | * @param color The color to use for "mark all."
|
---|
1400 | * @see #getMarkAllHighlightColor()
|
---|
1401 | */
|
---|
1402 | public void setMarkAllHighlightColor(Color color) {
|
---|
1403 | Color old = (Color)markAllHighlightPainter.getPaint();
|
---|
1404 | if (old!=null && !old.equals(color)) {
|
---|
1405 | markAllHighlightPainter.setPaint(color);
|
---|
1406 | if (markedWord!=null)
|
---|
1407 | repaint(); // Repaint if words are highlighted.
|
---|
1408 | firePropertyChange(MARK_ALL_COLOR_PROPERTY, old, color);
|
---|
1409 | }
|
---|
1410 | }
|
---|
1411 |
|
---|
1412 |
|
---|
1413 | /**
|
---|
1414 | * Sets the popup menu used by this text area.<p>
|
---|
1415 | *
|
---|
1416 | * If you set the popup menu with this method, you'll want to consider also
|
---|
1417 | * overriding {@link #configurePopupMenu(JPopupMenu)}, especially if you
|
---|
1418 | * removed any of the default menu items.
|
---|
1419 | *
|
---|
1420 | * @param popupMenu The popup menu. If this is <code>null</code>, no
|
---|
1421 | * popup menu will be displayed.
|
---|
1422 | * @see #getPopupMenu()
|
---|
1423 | * @see #configurePopupMenu(JPopupMenu)
|
---|
1424 | */
|
---|
1425 | public void setPopupMenu(JPopupMenu popupMenu) {
|
---|
1426 | this.popupMenu = popupMenu;
|
---|
1427 | popupMenuCreated = true;
|
---|
1428 | }
|
---|
1429 |
|
---|
1430 |
|
---|
1431 | /**
|
---|
1432 | * {@inheritDoc}
|
---|
1433 | */
|
---|
1434 | public void setRoundedSelectionEdges(boolean rounded) {
|
---|
1435 | if (getRoundedSelectionEdges()!=rounded) {
|
---|
1436 | markAllHighlightPainter.setRoundedEdges(rounded);
|
---|
1437 | super.setRoundedSelectionEdges(rounded); // Fires event.
|
---|
1438 | }
|
---|
1439 | }
|
---|
1440 |
|
---|
1441 |
|
---|
1442 | /**
|
---|
1443 | * Sets the text last selected/Ctrl+K'd in an <code>RTextArea</code>.
|
---|
1444 | * This text will be searched for in subsequent Ctrl+K/Ctrl+Shift+K
|
---|
1445 | * actions (Cmd+K on OS X).<p>
|
---|
1446 | *
|
---|
1447 | * Since the selected occurrence actions are built into RTextArea,
|
---|
1448 | * applications usually do not have to call this method directly, but can
|
---|
1449 | * choose to do so if they wish (for example, if they wish to set this
|
---|
1450 | * value when the user does a search via a Find dialog).
|
---|
1451 | *
|
---|
1452 | * @param text The selected text.
|
---|
1453 | * @see #getSelectedOccurrenceText()
|
---|
1454 | */
|
---|
1455 | public static void setSelectedOccurrenceText(String text) {
|
---|
1456 | selectedOccurrenceText = text;
|
---|
1457 | }
|
---|
1458 |
|
---|
1459 |
|
---|
1460 | /**
|
---|
1461 | * Sets the text mode for this editor pane.
|
---|
1462 | *
|
---|
1463 | * @param mode Either {@link #INSERT_MODE} or {@link #OVERWRITE_MODE}.
|
---|
1464 | */
|
---|
1465 | public void setTextMode(int mode) {
|
---|
1466 |
|
---|
1467 | if (mode!=INSERT_MODE && mode!=OVERWRITE_MODE)
|
---|
1468 | mode = INSERT_MODE;
|
---|
1469 |
|
---|
1470 | if (textMode != mode) {
|
---|
1471 | ConfigurableCaret cc = (ConfigurableCaret)getCaret();
|
---|
1472 | cc.setStyle(carets[mode]);
|
---|
1473 | textMode = mode;
|
---|
1474 | }
|
---|
1475 |
|
---|
1476 | }
|
---|
1477 |
|
---|
1478 |
|
---|
1479 | /**
|
---|
1480 | * Sets the tool tip supplier.
|
---|
1481 | *
|
---|
1482 | * @param supplier The new tool tip supplier, or <code>null</code> if
|
---|
1483 | * there is to be no supplier.
|
---|
1484 | * @see #getToolTipSupplier()
|
---|
1485 | */
|
---|
1486 | public void setToolTipSupplier(ToolTipSupplier supplier) {
|
---|
1487 | this.toolTipSupplier = supplier;
|
---|
1488 | }
|
---|
1489 |
|
---|
1490 |
|
---|
1491 | /**
|
---|
1492 | * Sets the UI used by this text area. This is overridden so only the
|
---|
1493 | * right-click popup menu's UI is updated. The look and feel of an
|
---|
1494 | * <code>RTextArea</code> is independent of the Java Look and Feel, and so
|
---|
1495 | * this method does not change the text area itself. Subclasses (such as
|
---|
1496 | * <code>RSyntaxTextArea</code> can call <code>setRTextAreaUI</code> if
|
---|
1497 | * they wish to install a new UI.
|
---|
1498 | *
|
---|
1499 | * @param ui This parameter is ignored.
|
---|
1500 | */
|
---|
1501 | public final void setUI(TextUI ui) {
|
---|
1502 |
|
---|
1503 | // Update the popup menu's ui.
|
---|
1504 | if (popupMenu!=null) {
|
---|
1505 | SwingUtilities.updateComponentTreeUI(popupMenu);
|
---|
1506 | }
|
---|
1507 |
|
---|
1508 | // Set things like selection color, selected text color, etc. to
|
---|
1509 | // laf defaults (if values are null or UIResource instances).
|
---|
1510 | RTextAreaUI rtaui = (RTextAreaUI)getUI();
|
---|
1511 | if (rtaui!=null) {
|
---|
1512 | rtaui.installDefaults();
|
---|
1513 | }
|
---|
1514 |
|
---|
1515 | }
|
---|
1516 |
|
---|
1517 |
|
---|
1518 | /**
|
---|
1519 | * Attempt to undo an "action" done in this text area.
|
---|
1520 | *
|
---|
1521 | * @see #redoLastAction()
|
---|
1522 | */
|
---|
1523 | public void undoLastAction() {
|
---|
1524 | // NOTE: that the try/catch block shouldn't be necessary...
|
---|
1525 | try {
|
---|
1526 | if (undoManager.canUndo())
|
---|
1527 | undoManager.undo();
|
---|
1528 | }
|
---|
1529 | catch (CannotUndoException cre) {
|
---|
1530 | cre.printStackTrace();
|
---|
1531 | }
|
---|
1532 | }
|
---|
1533 |
|
---|
1534 | /**
|
---|
1535 | * Serializes this text area.
|
---|
1536 | *
|
---|
1537 | * @param s The stream to write to.
|
---|
1538 | * @throws IOException If an IO error occurs.
|
---|
1539 | */
|
---|
1540 | private void writeObject(ObjectOutputStream s) throws IOException {
|
---|
1541 |
|
---|
1542 | // UndoManagers cannot be serialized without Exceptions. See
|
---|
1543 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4275892
|
---|
1544 | getDocument().removeUndoableEditListener(undoManager);
|
---|
1545 | s.defaultWriteObject();
|
---|
1546 | getDocument().addUndoableEditListener(undoManager);
|
---|
1547 |
|
---|
1548 | }
|
---|
1549 |
|
---|
1550 |
|
---|
1551 | /**
|
---|
1552 | * Modified from <code>MutableCaretEvent</code> in
|
---|
1553 | * <code>JTextComponent</code> so that mouse events get fired when the user
|
---|
1554 | * is selecting text with the mouse as well. This class also displays the
|
---|
1555 | * popup menu when the user right-clicks in the text area.
|
---|
1556 | */
|
---|
1557 | protected class RTextAreaMutableCaretEvent extends RTAMouseListener {
|
---|
1558 |
|
---|
1559 | protected RTextAreaMutableCaretEvent(RTextArea textArea) {
|
---|
1560 | super(textArea);
|
---|
1561 | }
|
---|
1562 |
|
---|
1563 | public void focusGained(FocusEvent e) {
|
---|
1564 | Caret c = getCaret();
|
---|
1565 | boolean enabled = c.getDot()!=c.getMark();
|
---|
1566 | cutAction.setEnabled(enabled);
|
---|
1567 | copyAction.setEnabled(enabled);
|
---|
1568 | undoManager.updateActions(); // To reflect this text area.
|
---|
1569 | }
|
---|
1570 |
|
---|
1571 | public void focusLost(FocusEvent e) {
|
---|
1572 | }
|
---|
1573 |
|
---|
1574 | public void mouseDragged(MouseEvent e) {
|
---|
1575 | if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
|
---|
1576 | Caret caret = getCaret();
|
---|
1577 | dot = caret.getDot();
|
---|
1578 | mark = caret.getMark();
|
---|
1579 | fireCaretUpdate(this);
|
---|
1580 | }
|
---|
1581 | }
|
---|
1582 |
|
---|
1583 | public void mousePressed(MouseEvent e) {
|
---|
1584 | // WORKAROUND: Since JTextComponent only updates the caret
|
---|
1585 | // location on mouse clicked and released, we'll do it on dragged
|
---|
1586 | // events when the left mouse button is clicked.
|
---|
1587 | if ((e.getModifiers() & MouseEvent.BUTTON1_MASK) != 0) {
|
---|
1588 | Caret caret = getCaret();
|
---|
1589 | dot = caret.getDot();
|
---|
1590 | mark = caret.getMark();
|
---|
1591 | fireCaretUpdate(this);
|
---|
1592 | }
|
---|
1593 | }
|
---|
1594 |
|
---|
1595 | public void mouseReleased(MouseEvent e) {
|
---|
1596 | if ((e.getModifiers()&MouseEvent.BUTTON3_MASK)!=0)
|
---|
1597 | showPopup(e);
|
---|
1598 | }
|
---|
1599 |
|
---|
1600 | /**
|
---|
1601 | * Shows a popup menu with cut, copy, paste, etc. options if the
|
---|
1602 | * user clicked the right button.
|
---|
1603 | *
|
---|
1604 | * @param e The mouse event that caused this method to be called.
|
---|
1605 | */
|
---|
1606 | private void showPopup(MouseEvent e) {
|
---|
1607 | JPopupMenu popupMenu = getPopupMenu();
|
---|
1608 | if (popupMenu!=null) {
|
---|
1609 | configurePopupMenu(popupMenu);
|
---|
1610 | popupMenu.show(e.getComponent(), e.getX(), e.getY());
|
---|
1611 | }
|
---|
1612 | }
|
---|
1613 |
|
---|
1614 | }
|
---|
1615 |
|
---|
1616 |
|
---|
1617 | } |
---|