1 /*
2 * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25 package javax.swing.text;
26
27 import java.lang.reflect.Method;
28
29 import java.security.AccessController;
30 import java.security.PrivilegedAction;
31
32 import java.beans.Transient;
33 import java.util.Collections;
34 import java.util.HashMap;
35 import java.util.Hashtable;
36 import java.util.Enumeration;
37 import java.util.Vector;
38 import java.util.Iterator;
39 import java.util.Map;
40 import java.util.Map.Entry;
41 import java.util.Set;
42
43 import java.util.concurrent;
44
45 import java.io;
46
47 import java.awt;
48 import java.awt.event;
49 import java.awt.print;
50 import java.awt.datatransfer;
51 import java.awt.im.InputContext;
52 import java.awt.im.InputMethodRequests;
53 import java.awt.font.TextHitInfo;
54 import java.awt.font.TextAttribute;
55
56 import java.awt.print.Printable;
57 import java.awt.print.PrinterException;
58
59 import javax.print.PrintService;
60 import javax.print.attribute.PrintRequestAttributeSet;
61
62 import java.text;
63 import java.text.AttributedCharacterIterator.Attribute;
64
65 import javax.swing;
66 import javax.swing.event;
67 import javax.swing.plaf;
68
69 import javax.accessibility;
70
71 import javax.print.attribute;
72
73 import sun.awt.AppContext;
74
75
76 import sun.swing.PrintingStatus;
77 import sun.swing.SwingUtilities2;
78 import sun.swing.text.TextComponentPrintable;
79
80 /**
81 * <code>JTextComponent</code> is the base class for swing text
82 * components. It tries to be compatible with the
83 * <code>java.awt.TextComponent</code> class
84 * where it can reasonably do so. Also provided are other services
85 * for additional flexibility (beyond the pluggable UI and bean
86 * support).
87 * You can find information on how to use the functionality
88 * this class provides in
89 * <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/generaltext.html">General Rules for Using Text Components</a>,
90 * a section in <em>The Java Tutorial.</em>
91 *
92 * <p>
93 * <dl>
94 * <dt><b><font size=+1>Caret Changes</font></b>
95 * <dd>
96 * The caret is a pluggable object in swing text components.
97 * Notification of changes to the caret position and the selection
98 * are sent to implementations of the <code>CaretListener</code>
99 * interface that have been registered with the text component.
100 * The UI will install a default caret unless a customized caret
101 * has been set. <br>
102 * By default the caret tracks all the document changes
103 * performed on the Event Dispatching Thread and updates it's position
104 * accordingly if an insertion occurs before or at the caret position
105 * or a removal occurs before the caret position. <code>DefaultCaret</code>
106 * tries to make itself visible which may lead to scrolling
107 * of a text component within <code>JScrollPane</code>. The default caret
108 * behavior can be changed by the {@link DefaultCaret#setUpdatePolicy} method.
109 * <br>
110 * <b>Note</b>: Non-editable text components also have a caret though
111 * it may not be painted.
112 *
113 * <p>
114 * <dt><b><font size=+1>Commands</font></b>
115 * <dd>
116 * Text components provide a number of commands that can be used
117 * to manipulate the component. This is essentially the way that
118 * the component expresses its capabilities. These are expressed
119 * in terms of the swing <code>Action</code> interface,
120 * using the <code>TextAction</code> implementation.
121 * The set of commands supported by the text component can be
122 * found with the {@link #getActions} method. These actions
123 * can be bound to key events, fired from buttons, etc.
124 *
125 * <p>
126 * <dt><b><font size=+1>Text Input</font></b>
127 * <dd>
128 * The text components support flexible and internationalized text input, using
129 * keymaps and the input method framework, while maintaining compatibility with
130 * the AWT listener model.
131 * <p>
132 * A {@link javax.swing.text.Keymap} lets an application bind key
133 * strokes to actions.
134 * In order to allow keymaps to be shared across multiple text components, they
135 * can use actions that extend <code>TextAction</code>.
136 * <code>TextAction</code> can determine which <code>JTextComponent</code>
137 * most recently has or had focus and therefore is the subject of
138 * the action (In the case that the <code>ActionEvent</code>
139 * sent to the action doesn't contain the target text component as its source).
140 * <p>
141 * The <a href="../../../../technotes/guides/imf/spec.html">input method framework</a>
142 * lets text components interact with input methods, separate software
143 * components that preprocess events to let users enter thousands of
144 * different characters using keyboards with far fewer keys.
145 * <code>JTextComponent</code> is an <em>active client</em> of
146 * the framework, so it implements the preferred user interface for interacting
147 * with input methods. As a consequence, some key events do not reach the text
148 * component because they are handled by an input method, and some text input
149 * reaches the text component as committed text within an {@link
150 * java.awt.event.InputMethodEvent} instead of as a key event.
151 * The complete text input is the combination of the characters in
152 * <code>keyTyped</code> key events and committed text in input method events.
153 * <p>
154 * The AWT listener model lets applications attach event listeners to
155 * components in order to bind events to actions. Swing encourages the
156 * use of keymaps instead of listeners, but maintains compatibility
157 * with listeners by giving the listeners a chance to steal an event
158 * by consuming it.
159 * <p>
160 * Keyboard event and input method events are handled in the following stages,
161 * with each stage capable of consuming the event:
162 *
163 * <table border=1 summary="Stages of keyboard and input method event handling">
164 * <tr>
165 * <th id="stage"><p align="left">Stage</p></th>
166 * <th id="ke"><p align="left">KeyEvent</p></th>
167 * <th id="ime"><p align="left">InputMethodEvent</p></th></tr>
168 * <tr><td headers="stage">1. </td>
169 * <td headers="ke">input methods </td>
170 * <td headers="ime">(generated here)</td></tr>
171 * <tr><td headers="stage">2. </td>
172 * <td headers="ke">focus manager </td>
173 * <td headers="ime"></td>
174 * </tr>
175 * <tr>
176 * <td headers="stage">3. </td>
177 * <td headers="ke">registered key listeners</td>
178 * <td headers="ime">registered input method listeners</tr>
179 * <tr>
180 * <td headers="stage">4. </td>
181 * <td headers="ke"></td>
182 * <td headers="ime">input method handling in JTextComponent</tr>
183 * <tr>
184 * <td headers="stage">5. </td><td headers="ke ime" colspan=2>keymap handling using the current keymap</td></tr>
185 * <tr><td headers="stage">6. </td><td headers="ke">keyboard handling in JComponent (e.g. accelerators, component navigation, etc.)</td>
186 * <td headers="ime"></td></tr>
187 * </table>
188 *
189 * <p>
190 * To maintain compatibility with applications that listen to key
191 * events but are not aware of input method events, the input
192 * method handling in stage 4 provides a compatibility mode for
193 * components that do not process input method events. For these
194 * components, the committed text is converted to keyTyped key events
195 * and processed in the key event pipeline starting at stage 3
196 * instead of in the input method event pipeline.
197 * <p>
198 * By default the component will create a keymap (named <b>DEFAULT_KEYMAP</b>)
199 * that is shared by all JTextComponent instances as the default keymap.
200 * Typically a look-and-feel implementation will install a different keymap
201 * that resolves to the default keymap for those bindings not found in the
202 * different keymap. The minimal bindings include:
203 * <ul>
204 * <li>inserting content into the editor for the
205 * printable keys.
206 * <li>removing content with the backspace and del
207 * keys.
208 * <li>caret movement forward and backward
209 * </ul>
210 *
211 * <p>
212 * <dt><b><font size=+1>Model/View Split</font></b>
213 * <dd>
214 * The text components have a model-view split. A text component pulls
215 * together the objects used to represent the model, view, and controller.
216 * The text document model may be shared by other views which act as observers
217 * of the model (e.g. a document may be shared by multiple components).
218 *
219 * <p align=center><img src="doc-files/editor.gif" alt="Diagram showing interaction between Controller, Document, events, and ViewFactory"
220 * HEIGHT=358 WIDTH=587></p>
221 *
222 * <p>
223 * The model is defined by the {@link Document} interface.
224 * This is intended to provide a flexible text storage mechanism
225 * that tracks change during edits and can be extended to more sophisticated
226 * models. The model interfaces are meant to capture the capabilities of
227 * expression given by SGML, a system used to express a wide variety of
228 * content.
229 * Each modification to the document causes notification of the
230 * details of the change to be sent to all observers in the form of a
231 * {@link DocumentEvent} which allows the views to stay up to date with the model.
232 * This event is sent to observers that have implemented the
233 * {@link DocumentListener}
234 * interface and registered interest with the model being observed.
235 *
236 * <p>
237 * <dt><b><font size=+1>Location Information</font></b>
238 * <dd>
239 * The capability of determining the location of text in
240 * the view is provided. There are two methods, {@link #modelToView}
241 * and {@link #viewToModel} for determining this information.
242 *
243 * <p>
244 * <dt><b><font size=+1>Undo/Redo support</font></b>
245 * <dd>
246 * Support for an edit history mechanism is provided to allow
247 * undo/redo operations. The text component does not itself
248 * provide the history buffer by default, but does provide
249 * the <code>UndoableEdit</code> records that can be used in conjunction
250 * with a history buffer to provide the undo/redo support.
251 * The support is provided by the Document model, which allows
252 * one to attach UndoableEditListener implementations.
253 *
254 * <p>
255 * <dt><b><font size=+1>Thread Safety</font></b>
256 * <dd>
257 * The swing text components provide some support of thread
258 * safe operations. Because of the high level of configurability
259 * of the text components, it is possible to circumvent the
260 * protection provided. The protection primarily comes from
261 * the model, so the documentation of <code>AbstractDocument</code>
262 * describes the assumptions of the protection provided.
263 * The methods that are safe to call asynchronously are marked
264 * with comments.
265 *
266 * <p>
267 * <dt><b><font size=+1>Newlines</font></b>
268 * <dd>
269 * For a discussion on how newlines are handled, see
270 * <a href="DefaultEditorKit.html">DefaultEditorKit</a>.
271 *
272 * <p>
273 * <dt><b><font size=+1>Printing support</font></b>
274 * <dd>
275 * Several {@link #print print} methods are provided for basic
276 * document printing. If more advanced printing is needed, use the
277 * {@link #getPrintable} method.
278 * </dl>
279 *
280 * <p>
281 * <strong>Warning:</strong>
282 * Serialized objects of this class will not be compatible with
283 * future Swing releases. The current serialization support is
284 * appropriate for short term storage or RMI between applications running
285 * the same version of Swing. As of 1.4, support for long term storage
286 * of all JavaBeans<sup><font size="-2">TM</font></sup>
287 * has been added to the <code>java.beans</code> package.
288 * Please see {@link java.beans.XMLEncoder}.
289 *
290 * @beaninfo
291 * attribute: isContainer false
292 *
293 * @author Timothy Prinzing
294 * @author Igor Kushnirskiy (printing support)
295 * @see Document
296 * @see DocumentEvent
297 * @see DocumentListener
298 * @see Caret
299 * @see CaretEvent
300 * @see CaretListener
301 * @see TextUI
302 * @see View
303 * @see ViewFactory
304 */
305 public abstract class JTextComponent extends JComponent implements Scrollable, Accessible
306 {
307 /**
308 * Creates a new <code>JTextComponent</code>.
309 * Listeners for caret events are established, and the pluggable
310 * UI installed. The component is marked as editable. No layout manager
311 * is used, because layout is managed by the view subsystem of text.
312 * The document model is set to <code>null</code>.
313 */
314 public JTextComponent() {
315 super();
316 // enable InputMethodEvent for on-the-spot pre-editing
317 enableEvents(AWTEvent.KEY_EVENT_MASK | AWTEvent.INPUT_METHOD_EVENT_MASK);
318 caretEvent = new MutableCaretEvent(this);
319 addMouseListener(caretEvent);
320 addFocusListener(caretEvent);
321 setEditable(true);
322 setDragEnabled(false);
323 setLayout(null); // layout is managed by View hierarchy
324 updateUI();
325 }
326
327 /**
328 * Fetches the user-interface factory for this text-oriented editor.
329 *
330 * @return the factory
331 */
332 public TextUI getUI() { return (TextUI)ui; }
333
334 /**
335 * Sets the user-interface factory for this text-oriented editor.
336 *
337 * @param ui the factory
338 */
339 public void setUI(TextUI ui) {
340 super.setUI(ui);
341 }
342
343 /**
344 * Reloads the pluggable UI. The key used to fetch the
345 * new interface is <code>getUIClassID()</code>. The type of
346 * the UI is <code>TextUI</code>. <code>invalidate</code>
347 * is called after setting the UI.
348 */
349 public void updateUI() {
350 setUI((TextUI)UIManager.getUI(this));
351 invalidate();
352 }
353
354 /**
355 * Adds a caret listener for notification of any changes
356 * to the caret.
357 *
358 * @param listener the listener to be added
359 * @see javax.swing.event.CaretEvent
360 */
361 public void addCaretListener(CaretListener listener) {
362 listenerList.add(CaretListener.class, listener);
363 }
364
365 /**
366 * Removes a caret listener.
367 *
368 * @param listener the listener to be removed
369 * @see javax.swing.event.CaretEvent
370 */
371 public void removeCaretListener(CaretListener listener) {
372 listenerList.remove(CaretListener.class, listener);
373 }
374
375 /**
376 * Returns an array of all the caret listeners
377 * registered on this text component.
378 *
379 * @return all of this component's <code>CaretListener</code>s
380 * or an empty
381 * array if no caret listeners are currently registered
382 *
383 * @see #addCaretListener
384 * @see #removeCaretListener
385 *
386 * @since 1.4
387 */
388 public CaretListener[] getCaretListeners() {
389 return (CaretListener[])listenerList.getListeners(CaretListener.class);
390 }
391
392 /**
393 * Notifies all listeners that have registered interest for
394 * notification on this event type. The event instance
395 * is lazily created using the parameters passed into
396 * the fire method. The listener list is processed in a
397 * last-to-first manner.
398 *
399 * @param e the event
400 * @see EventListenerList
401 */
402 protected void fireCaretUpdate(CaretEvent e) {
403 // Guaranteed to return a non-null array
404 Object[] listeners = listenerList.getListenerList();
405 // Process the listeners last to first, notifying
406 // those that are interested in this event
407 for (int i = listeners.length-2; i>=0; i-=2) {
408 if (listeners[i]==CaretListener.class) {
409 ((CaretListener)listeners[i+1]).caretUpdate(e);
410 }
411 }
412 }
413
414 /**
415 * Associates the editor with a text document.
416 * The currently registered factory is used to build a view for
417 * the document, which gets displayed by the editor after revalidation.
418 * A PropertyChange event ("document") is propagated to each listener.
419 *
420 * @param doc the document to display/edit
421 * @see #getDocument
422 * @beaninfo
423 * description: the text document model
424 * bound: true
425 * expert: true
426 */
427 public void setDocument(Document doc) {
428 Document old = model;
429
430 /*
431 * aquire a read lock on the old model to prevent notification of
432 * mutations while we disconnecting the old model.
433 */
434 try {
435 if (old instanceof AbstractDocument) {
436 ((AbstractDocument)old).readLock();
437 }
438 if (accessibleContext != null) {
439 model.removeDocumentListener(
440 ((AccessibleJTextComponent)accessibleContext));
441 }
442 if (inputMethodRequestsHandler != null) {
443 model.removeDocumentListener((DocumentListener)inputMethodRequestsHandler);
444 }
445 model = doc;
446
447 // Set the document's run direction property to match the
448 // component's ComponentOrientation property.
449 Boolean runDir = getComponentOrientation().isLeftToRight()
450 ? TextAttribute.RUN_DIRECTION_LTR
451 : TextAttribute.RUN_DIRECTION_RTL;
452 if (runDir != doc.getProperty(TextAttribute.RUN_DIRECTION)) {
453 doc.putProperty(TextAttribute.RUN_DIRECTION, runDir );
454 }
455 firePropertyChange("document", old, doc);
456 } finally {
457 if (old instanceof AbstractDocument) {
458 ((AbstractDocument)old).readUnlock();
459 }
460 }
461
462 revalidate();
463 repaint();
464 if (accessibleContext != null) {
465 model.addDocumentListener(
466 ((AccessibleJTextComponent)accessibleContext));
467 }
468 if (inputMethodRequestsHandler != null) {
469 model.addDocumentListener((DocumentListener)inputMethodRequestsHandler);
470 }
471 }
472
473 /**
474 * Fetches the model associated with the editor. This is
475 * primarily for the UI to get at the minimal amount of
476 * state required to be a text editor. Subclasses will
477 * return the actual type of the model which will typically
478 * be something that extends Document.
479 *
480 * @return the model
481 */
482 public Document getDocument() {
483 return model;
484 }
485
486 // Override of Component.setComponentOrientation
487 public void setComponentOrientation( ComponentOrientation o ) {
488 // Set the document's run direction property to match the
489 // ComponentOrientation property.
490 Document doc = getDocument();
491 if( doc != null ) {
492 Boolean runDir = o.isLeftToRight()
493 ? TextAttribute.RUN_DIRECTION_LTR
494 : TextAttribute.RUN_DIRECTION_RTL;
495 doc.putProperty( TextAttribute.RUN_DIRECTION, runDir );
496 }
497 super.setComponentOrientation( o );
498 }
499
500 /**
501 * Fetches the command list for the editor. This is
502 * the list of commands supported by the plugged-in UI
503 * augmented by the collection of commands that the
504 * editor itself supports. These are useful for binding
505 * to events, such as in a keymap.
506 *
507 * @return the command list
508 */
509 public Action[] getActions() {
510 return getUI().getEditorKit(this).getActions();
511 }
512
513 /**
514 * Sets margin space between the text component's border
515 * and its text. The text component's default <code>Border</code>
516 * object will use this value to create the proper margin.
517 * However, if a non-default border is set on the text component,
518 * it is that <code>Border</code> object's responsibility to create the
519 * appropriate margin space (else this property will effectively
520 * be ignored). This causes a redraw of the component.
521 * A PropertyChange event ("margin") is sent to all listeners.
522 *
523 * @param m the space between the border and the text
524 * @beaninfo
525 * description: desired space between the border and text area
526 * bound: true
527 */
528 public void setMargin(Insets m) {
529 Insets old = margin;
530 margin = m;
531 firePropertyChange("margin", old, m);
532 invalidate();
533 }
534
535 /**
536 * Returns the margin between the text component's border and
537 * its text.
538 *
539 * @return the margin
540 */
541 public Insets getMargin() {
542 return margin;
543 }
544
545 /**
546 * Sets the <code>NavigationFilter</code>. <code>NavigationFilter</code>
547 * is used by <code>DefaultCaret</code> and the default cursor movement
548 * actions as a way to restrict the cursor movement.
549 *
550 * @since 1.4
551 */
552 public void setNavigationFilter(NavigationFilter filter) {
553 navigationFilter = filter;
554 }
555
556 /**
557 * Returns the <code>NavigationFilter</code>. <code>NavigationFilter</code>
558 * is used by <code>DefaultCaret</code> and the default cursor movement
559 * actions as a way to restrict the cursor movement. A null return value
560 * implies the cursor movement and selection should not be restricted.
561 *
562 * @since 1.4
563 * @return the NavigationFilter
564 */
565 public NavigationFilter getNavigationFilter() {
566 return navigationFilter;
567 }
568
569 /**
570 * Fetches the caret that allows text-oriented navigation over
571 * the view.
572 *
573 * @return the caret
574 */
575 @Transient
576 public Caret getCaret() {
577 return caret;
578 }
579
580 /**
581 * Sets the caret to be used. By default this will be set
582 * by the UI that gets installed. This can be changed to
583 * a custom caret if desired. Setting the caret results in a
584 * PropertyChange event ("caret") being fired.
585 *
586 * @param c the caret
587 * @see #getCaret
588 * @beaninfo
589 * description: the caret used to select/navigate
590 * bound: true
591 * expert: true
592 */
593 public void setCaret(Caret c) {
594 if (caret != null) {
595 caret.removeChangeListener(caretEvent);
596 caret.deinstall(this);
597 }
598 Caret old = caret;
599 caret = c;
600 if (caret != null) {
601 caret.install(this);
602 caret.addChangeListener(caretEvent);
603 }
604 firePropertyChange("caret", old, caret);
605 }
606
607 /**
608 * Fetches the object responsible for making highlights.
609 *
610 * @return the highlighter
611 */
612 public Highlighter getHighlighter() {
613 return highlighter;
614 }
615
616 /**
617 * Sets the highlighter to be used. By default this will be set
618 * by the UI that gets installed. This can be changed to
619 * a custom highlighter if desired. The highlighter can be set to
620 * <code>null</code> to disable it.
621 * A PropertyChange event ("highlighter") is fired
622 * when a new highlighter is installed.
623 *
624 * @param h the highlighter
625 * @see #getHighlighter
626 * @beaninfo
627 * description: object responsible for background highlights
628 * bound: true
629 * expert: true
630 */
631 public void setHighlighter(Highlighter h) {
632 if (highlighter != null) {
633 highlighter.deinstall(this);
634 }
635 Highlighter old = highlighter;
636 highlighter = h;
637 if (highlighter != null) {
638 highlighter.install(this);
639 }
640 firePropertyChange("highlighter", old, h);
641 }
642
643 /**
644 * Sets the keymap to use for binding events to
645 * actions. Setting to <code>null</code> effectively disables
646 * keyboard input.
647 * A PropertyChange event ("keymap") is fired when a new keymap
648 * is installed.
649 *
650 * @param map the keymap
651 * @see #getKeymap
652 * @beaninfo
653 * description: set of key event to action bindings to use
654 * bound: true
655 */
656 public void setKeymap(Keymap map) {
657 Keymap old = keymap;
658 keymap = map;
659 firePropertyChange("keymap", old, keymap);
660 updateInputMap(old, map);
661 }
662
663 /**
664 * Turns on or off automatic drag handling. In order to enable automatic
665 * drag handling, this property should be set to {@code true}, and the
666 * component's {@code TransferHandler} needs to be {@code non-null}.
667 * The default value of the {@code dragEnabled} property is {@code false}.
668 * <p>
669 * The job of honoring this property, and recognizing a user drag gesture,
670 * lies with the look and feel implementation, and in particular, the component's
671 * {@code TextUI}. When automatic drag handling is enabled, most look and
672 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
673 * drag and drop operation whenever the user presses the mouse button over
674 * a selection and then moves the mouse a few pixels. Setting this property to
675 * {@code true} can therefore have a subtle effect on how selections behave.
676 * <p>
677 * If a look and feel is used that ignores this property, you can still
678 * begin a drag and drop operation by calling {@code exportAsDrag} on the
679 * component's {@code TransferHandler}.
680 *
681 * @param b whether or not to enable automatic drag handling
682 * @exception HeadlessException if
683 * <code>b</code> is <code>true</code> and
684 * <code>GraphicsEnvironment.isHeadless()</code>
685 * returns <code>true</code>
686 * @see java.awt.GraphicsEnvironment#isHeadless
687 * @see #getDragEnabled
688 * @see #setTransferHandler
689 * @see TransferHandler
690 * @since 1.4
691 *
692 * @beaninfo
693 * description: determines whether automatic drag handling is enabled
694 * bound: false
695 */
696 public void setDragEnabled(boolean b) {
697 if (b && GraphicsEnvironment.isHeadless()) {
698 throw new HeadlessException();
699 }
700 dragEnabled = b;
701 }
702
703 /**
704 * Returns whether or not automatic drag handling is enabled.
705 *
706 * @return the value of the {@code dragEnabled} property
707 * @see #setDragEnabled
708 * @since 1.4
709 */
710 public boolean getDragEnabled() {
711 return dragEnabled;
712 }
713
714 /**
715 * Sets the drop mode for this component. For backward compatibility,
716 * the default for this property is <code>DropMode.USE_SELECTION</code>.
717 * Usage of <code>DropMode.INSERT</code> is recommended, however,
718 * for an improved user experience. It offers similar behavior of dropping
719 * between text locations, but does so without affecting the actual text
720 * selection and caret location.
721 * <p>
722 * <code>JTextComponents</code> support the following drop modes:
723 * <ul>
724 * <li><code>DropMode.USE_SELECTION</code></li>
725 * <li><code>DropMode.INSERT</code></li>
726 * </ul>
727 * <p>
728 * The drop mode is only meaningful if this component has a
729 * <code>TransferHandler</code> that accepts drops.
730 *
731 * @param dropMode the drop mode to use
732 * @throws IllegalArgumentException if the drop mode is unsupported
733 * or <code>null</code>
734 * @see #getDropMode
735 * @see #getDropLocation
736 * @see #setTransferHandler
737 * @see javax.swing.TransferHandler
738 * @since 1.6
739 */
740 public final void setDropMode(DropMode dropMode) {
741 if (dropMode != null) {
742 switch (dropMode) {
743 case USE_SELECTION:
744 case INSERT:
745 this.dropMode = dropMode;
746 return;
747 }
748 }
749
750 throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for text");
751 }
752
753 /**
754 * Returns the drop mode for this component.
755 *
756 * @return the drop mode for this component
757 * @see #setDropMode
758 * @since 1.6
759 */
760 public final DropMode getDropMode() {
761 return dropMode;
762 }
763
764
765 /**
766 * Calculates a drop location in this component, representing where a
767 * drop at the given point should insert data.
768 * <p>
769 * Note: This method is meant to override
770 * <code>JComponent.dropLocationForPoint()</code>, which is package-private
771 * in javax.swing. <code>TransferHandler</code> will detect text components
772 * and call this method instead via reflection. It's name should therefore
773 * not be changed.
774 *
775 * @param p the point to calculate a drop location for
776 * @return the drop location, or <code>null</code>
777 */
778 DropLocation dropLocationForPoint(Point p) {
779 Position.Bias[] bias = new Position.Bias[1];
780 int index = getUI().viewToModel(this, p, bias);
781
782 // viewToModel currently returns null for some HTML content
783 // when the point is within the component's top inset
784 if (bias[0] == null) {
785 bias[0] = Position.Bias.Forward;
786 }
787
788 return new DropLocation(p, index, bias[0]);
789 }
790
791 /**
792 * Called to set or clear the drop location during a DnD operation.
793 * In some cases, the component may need to use it's internal selection
794 * temporarily to indicate the drop location. To help facilitate this,
795 * this method returns and accepts as a parameter a state object.
796 * This state object can be used to store, and later restore, the selection
797 * state. Whatever this method returns will be passed back to it in
798 * future calls, as the state parameter. If it wants the DnD system to
799 * continue storing the same state, it must pass it back every time.
800 * Here's how this is used:
801 * <p>
802 * Let's say that on the first call to this method the component decides
803 * to save some state (because it is about to use the selection to show
804 * a drop index). It can return a state object to the caller encapsulating
805 * any saved selection state. On a second call, let's say the drop location
806 * is being changed to something else. The component doesn't need to
807 * restore anything yet, so it simply passes back the same state object
808 * to have the DnD system continue storing it. Finally, let's say this
809 * method is messaged with <code>null</code>. This means DnD
810 * is finished with this component for now, meaning it should restore
811 * state. At this point, it can use the state parameter to restore
812 * said state, and of course return <code>null</code> since there's
813 * no longer anything to store.
814 * <p>
815 * Note: This method is meant to override
816 * <code>JComponent.setDropLocation()</code>, which is package-private
817 * in javax.swing. <code>TransferHandler</code> will detect text components
818 * and call this method instead via reflection. It's name should therefore
819 * not be changed.
820 *
821 * @param location the drop location (as calculated by
822 * <code>dropLocationForPoint</code>) or <code>null</code>
823 * if there's no longer a valid drop location
824 * @param state the state object saved earlier for this component,
825 * or <code>null</code>
826 * @param forDrop whether or not the method is being called because an
827 * actual drop occurred
828 * @return any saved state for this component, or <code>null</code> if none
829 */
830 Object setDropLocation(TransferHandler.DropLocation location,
831 Object state,
832 boolean forDrop) {
833
834 Object retVal = null;
835 DropLocation textLocation = (DropLocation)location;
836
837 if (dropMode == DropMode.USE_SELECTION) {
838 if (textLocation == null) {
839 if (state != null) {
840 /*
841 * This object represents the state saved earlier.
842 * If the caret is a DefaultCaret it will be
843 * an Object array containing, in order:
844 * - the saved caret mark (Integer)
845 * - the saved caret dot (Integer)
846 * - the saved caret visibility (Boolean)
847 * - the saved mark bias (Position.Bias)
848 * - the saved dot bias (Position.Bias)
849 * If the caret is not a DefaultCaret it will
850 * be similar, but will not contain the dot
851 * or mark bias.
852 */
853 Object[] vals = (Object[])state;
854
855 if (!forDrop) {
856 if (caret instanceof DefaultCaret) {
857 ((DefaultCaret)caret).setDot(((Integer)vals[0]).intValue(),
858 (Position.Bias)vals[3]);
859 ((DefaultCaret)caret).moveDot(((Integer)vals[1]).intValue(),
860 (Position.Bias)vals[4]);
861 } else {
862 caret.setDot(((Integer)vals[0]).intValue());
863 caret.moveDot(((Integer)vals[1]).intValue());
864 }
865 }
866
867 caret.setVisible(((Boolean)vals[2]).booleanValue());
868 }
869 } else {
870 if (dropLocation == null) {
871 boolean visible;
872
873 if (caret instanceof DefaultCaret) {
874 DefaultCaret dc = (DefaultCaret)caret;
875 visible = dc.isActive();
876 retVal = new Object[] {Integer.valueOf(dc.getMark()),
877 Integer.valueOf(dc.getDot()),
878 Boolean.valueOf(visible),
879 dc.getMarkBias(),
880 dc.getDotBias()};
881 } else {
882 visible = caret.isVisible();
883 retVal = new Object[] {Integer.valueOf(caret.getMark()),
884 Integer.valueOf(caret.getDot()),
885 Boolean.valueOf(visible)};
886 }
887
888 caret.setVisible(true);
889 } else {
890 retVal = state;
891 }
892
893 if (caret instanceof DefaultCaret) {
894 ((DefaultCaret)caret).setDot(textLocation.getIndex(), textLocation.getBias());
895 } else {
896 caret.setDot(textLocation.getIndex());
897 }
898 }
899 } else {
900 if (textLocation == null) {
901 if (state != null) {
902 caret.setVisible(((Boolean)state).booleanValue());
903 }
904 } else {
905 if (dropLocation == null) {
906 boolean visible = caret instanceof DefaultCaret
907 ? ((DefaultCaret)caret).isActive()
908 : caret.isVisible();
909 retVal = Boolean.valueOf(visible);
910 caret.setVisible(false);
911 } else {
912 retVal = state;
913 }
914 }
915 }
916
917 DropLocation old = dropLocation;
918 dropLocation = textLocation;
919 firePropertyChange("dropLocation", old, dropLocation);
920
921 return retVal;
922 }
923
924 /**
925 * Returns the location that this component should visually indicate
926 * as the drop location during a DnD operation over the component,
927 * or {@code null} if no location is to currently be shown.
928 * <p>
929 * This method is not meant for querying the drop location
930 * from a {@code TransferHandler}, as the drop location is only
931 * set after the {@code TransferHandler}'s <code>canImport</code>
932 * has returned and has allowed for the location to be shown.
933 * <p>
934 * When this property changes, a property change event with
935 * name "dropLocation" is fired by the component.
936 *
937 * @return the drop location
938 * @see #setDropMode
939 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
940 * @since 1.6
941 */
942 public final DropLocation getDropLocation() {
943 return dropLocation;
944 }
945
946
947 /**
948 * Updates the <code>InputMap</code>s in response to a
949 * <code>Keymap</code> change.
950 * @param oldKm the old <code>Keymap</code>
951 * @param newKm the new <code>Keymap</code>
952 */
953 void updateInputMap(Keymap oldKm, Keymap newKm) {
954 // Locate the current KeymapWrapper.
955 InputMap km = getInputMap(JComponent.WHEN_FOCUSED);
956 InputMap last = km;
957 while (km != null && !(km instanceof KeymapWrapper)) {
958 last = km;
959 km = km.getParent();
960 }
961 if (km != null) {
962 // Found it, tweak the InputMap that points to it, as well
963 // as anything it points to.
964 if (newKm == null) {
965 if (last != km) {
966 last.setParent(km.getParent());
967 }
968 else {
969 last.setParent(null);
970 }
971 }
972 else {
973 InputMap newKM = new KeymapWrapper(newKm);
974 last.setParent(newKM);
975 if (last != km) {
976 newKM.setParent(km.getParent());
977 }
978 }
979 }
980 else if (newKm != null) {
981 km = getInputMap(JComponent.WHEN_FOCUSED);
982 if (km != null) {
983 // Couldn't find it.
984 // Set the parent of WHEN_FOCUSED InputMap to be the new one.
985 InputMap newKM = new KeymapWrapper(newKm);
986 newKM.setParent(km.getParent());
987 km.setParent(newKM);
988 }
989 }
990
991 // Do the same thing with the ActionMap
992 ActionMap am = getActionMap();
993 ActionMap lastAM = am;
994 while (am != null && !(am instanceof KeymapActionMap)) {
995 lastAM = am;
996 am = am.getParent();
997 }
998 if (am != null) {
999 // Found it, tweak the Actionap that points to it, as well
1000 // as anything it points to.
1001 if (newKm == null) {
1002 if (lastAM != am) {
1003 lastAM.setParent(am.getParent());
1004 }
1005 else {
1006 lastAM.setParent(null);
1007 }
1008 }
1009 else {
1010 ActionMap newAM = new KeymapActionMap(newKm);
1011 lastAM.setParent(newAM);
1012 if (lastAM != am) {
1013 newAM.setParent(am.getParent());
1014 }
1015 }
1016 }
1017 else if (newKm != null) {
1018 am = getActionMap();
1019 if (am != null) {
1020 // Couldn't find it.
1021 // Set the parent of ActionMap to be the new one.
1022 ActionMap newAM = new KeymapActionMap(newKm);
1023 newAM.setParent(am.getParent());
1024 am.setParent(newAM);
1025 }
1026 }
1027 }
1028
1029 /**
1030 * Fetches the keymap currently active in this text
1031 * component.
1032 *
1033 * @return the keymap
1034 */
1035 public Keymap getKeymap() {
1036 return keymap;
1037 }
1038
1039 /**
1040 * Adds a new keymap into the keymap hierarchy. Keymap bindings
1041 * resolve from bottom up so an attribute specified in a child
1042 * will override an attribute specified in the parent.
1043 *
1044 * @param nm the name of the keymap (must be unique within the
1045 * collection of named keymaps in the document); the name may
1046 * be <code>null</code> if the keymap is unnamed,
1047 * but the caller is responsible for managing the reference
1048 * returned as an unnamed keymap can't
1049 * be fetched by name
1050 * @param parent the parent keymap; this may be <code>null</code> if
1051 * unspecified bindings need not be resolved in some other keymap
1052 * @return the keymap
1053 */
1054 public static Keymap addKeymap(String nm, Keymap parent) {
1055 Keymap map = new DefaultKeymap(nm, parent);
1056 if (nm != null) {
1057 // add a named keymap, a class of bindings
1058 getKeymapTable().put(nm, map);
1059 }
1060 return map;
1061 }
1062
1063 /**
1064 * Removes a named keymap previously added to the document. Keymaps
1065 * with <code>null</code> names may not be removed in this way.
1066 *
1067 * @param nm the name of the keymap to remove
1068 * @return the keymap that was removed
1069 */
1070 public static Keymap removeKeymap(String nm) {
1071 return getKeymapTable().remove(nm);
1072 }
1073
1074 /**
1075 * Fetches a named keymap previously added to the document.
1076 * This does not work with <code>null</code>-named keymaps.
1077 *
1078 * @param nm the name of the keymap
1079 * @return the keymap
1080 */
1081 public static Keymap getKeymap(String nm) {
1082 return getKeymapTable().get(nm);
1083 }
1084
1085 private static HashMap<String,Keymap> getKeymapTable() {
1086 synchronized (KEYMAP_TABLE) {
1087 AppContext appContext = AppContext.getAppContext();
1088 HashMap<String,Keymap> keymapTable =
1089 (HashMap<String,Keymap>)appContext.get(KEYMAP_TABLE);
1090 if (keymapTable == null) {
1091 keymapTable = new HashMap<String,Keymap>(17);
1092 appContext.put(KEYMAP_TABLE, keymapTable);
1093 //initialize default keymap
1094 Keymap binding = addKeymap(DEFAULT_KEYMAP, null);
1095 binding.setDefaultAction(new
1096 DefaultEditorKit.DefaultKeyTypedAction());
1097 }
1098 return keymapTable;
1099 }
1100 }
1101
1102 /**
1103 * Binding record for creating key bindings.
1104 * <p>
1105 * <strong>Warning:</strong>
1106 * Serialized objects of this class will not be compatible with
1107 * future Swing releases. The current serialization support is
1108 * appropriate for short term storage or RMI between applications running
1109 * the same version of Swing. As of 1.4, support for long term storage
1110 * of all JavaBeans<sup><font size="-2">TM</font></sup>
1111 * has been added to the <code>java.beans</code> package.
1112 * Please see {@link java.beans.XMLEncoder}.
1113 */
1114 public static class KeyBinding {
1115
1116 /**
1117 * The key.
1118 */
1119 public KeyStroke key;
1120
1121 /**
1122 * The name of the action for the key.
1123 */
1124 public String actionName;
1125
1126 /**
1127 * Creates a new key binding.
1128 *
1129 * @param key the key
1130 * @param actionName the name of the action for the key
1131 */
1132 public KeyBinding(KeyStroke key, String actionName) {
1133 this.key = key;
1134 this.actionName = actionName;
1135 }
1136 }
1137
1138 /**
1139 * <p>
1140 * Loads a keymap with a bunch of
1141 * bindings. This can be used to take a static table of
1142 * definitions and load them into some keymap. The following
1143 * example illustrates an example of binding some keys to
1144 * the cut, copy, and paste actions associated with a
1145 * JTextComponent. A code fragment to accomplish
1146 * this might look as follows:
1147 * <pre><code>
1148 *
1149 * static final JTextComponent.KeyBinding[] defaultBindings = {
1150 * new JTextComponent.KeyBinding(
1151 * KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
1152 * DefaultEditorKit.copyAction),
1153 * new JTextComponent.KeyBinding(
1154 * KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
1155 * DefaultEditorKit.pasteAction),
1156 * new JTextComponent.KeyBinding(
1157 * KeyStroke.getKeyStroke(KeyEvent.VK_X, InputEvent.CTRL_MASK),
1158 * DefaultEditorKit.cutAction),
1159 * };
1160 *
1161 * JTextComponent c = new JTextPane();
1162 * Keymap k = c.getKeymap();
1163 * JTextComponent.loadKeymap(k, defaultBindings, c.getActions());
1164 *
1165 * </code></pre>
1166 * The sets of bindings and actions may be empty but must be
1167 * non-<code>null</code>.
1168 *
1169 * @param map the keymap
1170 * @param bindings the bindings
1171 * @param actions the set of actions
1172 */
1173 public static void loadKeymap(Keymap map, KeyBinding[] bindings, Action[] actions) {
1174 Hashtable h = new Hashtable();
1175 for (int i = 0; i < actions.length; i++) {
1176 Action a = actions[i];
1177 String value = (String)a.getValue(Action.NAME);
1178 h.put((value!=null ? value:""), a);
1179 }
1180 for (int i = 0; i < bindings.length; i++) {
1181 Action a = (Action) h.get(bindings[i].actionName);
1182 if (a != null) {
1183 map.addActionForKeyStroke(bindings[i].key, a);
1184 }
1185 }
1186 }
1187
1188 /**
1189 * Returns true if <code>klass</code> is NOT a JTextComponent and it or
1190 * one of its superclasses (stoping at JTextComponent) overrides
1191 * <code>processInputMethodEvent</code>. It is assumed this will be
1192 * invoked from within a <code>doPrivileged</code>, and it is also
1193 * assumed <code>klass</code> extends <code>JTextComponent</code>.
1194 */
1195 private static Boolean isProcessInputMethodEventOverridden(Class klass) {
1196 if (klass == JTextComponent.class) {
1197 return Boolean.FALSE;
1198 }
1199 Boolean retValue = (Boolean)overrideMap.get(klass.getName());
1200
1201 if (retValue != null) {
1202 return retValue;
1203 }
1204 Boolean sOverriden = isProcessInputMethodEventOverridden(
1205 klass.getSuperclass());
1206
1207 if (sOverriden.booleanValue()) {
1208 // If our superclass has overriden it, then by definition klass
1209 // overrides it.
1210 overrideMap.put(klass.getName(), sOverriden);
1211 return sOverriden;
1212 }
1213 // klass's superclass didn't override it, check for an override in
1214 // klass.
1215 try {
1216 Class[] classes = new Class[1];
1217 classes[0] = InputMethodEvent.class;
1218
1219 Method m = klass.getDeclaredMethod("processInputMethodEvent",
1220 classes);
1221 retValue = Boolean.TRUE;
1222 } catch (NoSuchMethodException nsme) {
1223 retValue = Boolean.FALSE;
1224 }
1225 overrideMap.put(klass.getName(), retValue);
1226 return retValue;
1227 }
1228
1229 /**
1230 * Fetches the current color used to render the
1231 * caret.
1232 *
1233 * @return the color
1234 */
1235 public Color getCaretColor() {
1236 return caretColor;
1237 }
1238
1239 /**
1240 * Sets the current color used to render the caret.
1241 * Setting to <code>null</code> effectively restores the default color.
1242 * Setting the color results in a PropertyChange event ("caretColor")
1243 * being fired.
1244 *
1245 * @param c the color
1246 * @see #getCaretColor
1247 * @beaninfo
1248 * description: the color used to render the caret
1249 * bound: true
1250 * preferred: true
1251 */
1252 public void setCaretColor(Color c) {
1253 Color old = caretColor;
1254 caretColor = c;
1255 firePropertyChange("caretColor", old, caretColor);
1256 }
1257
1258 /**
1259 * Fetches the current color used to render the
1260 * selection.
1261 *
1262 * @return the color
1263 */
1264 public Color getSelectionColor() {
1265 return selectionColor;
1266 }
1267
1268 /**
1269 * Sets the current color used to render the selection.
1270 * Setting the color to <code>null</code> is the same as setting
1271 * <code>Color.white</code>. Setting the color results in a
1272 * PropertyChange event ("selectionColor").
1273 *
1274 * @param c the color
1275 * @see #getSelectionColor
1276 * @beaninfo
1277 * description: color used to render selection background
1278 * bound: true
1279 * preferred: true
1280 */
1281 public void setSelectionColor(Color c) {
1282 Color old = selectionColor;
1283 selectionColor = c;
1284 firePropertyChange("selectionColor", old, selectionColor);
1285 }
1286
1287 /**
1288 * Fetches the current color used to render the
1289 * selected text.
1290 *
1291 * @return the color
1292 */
1293 public Color getSelectedTextColor() {
1294 return selectedTextColor;
1295 }
1296
1297 /**
1298 * Sets the current color used to render the selected text.
1299 * Setting the color to <code>null</code> is the same as
1300 * <code>Color.black</code>. Setting the color results in a
1301 * PropertyChange event ("selectedTextColor") being fired.
1302 *
1303 * @param c the color
1304 * @see #getSelectedTextColor
1305 * @beaninfo
1306 * description: color used to render selected text
1307 * bound: true
1308 * preferred: true
1309 */
1310 public void setSelectedTextColor(Color c) {
1311 Color old = selectedTextColor;
1312 selectedTextColor = c;
1313 firePropertyChange("selectedTextColor", old, selectedTextColor);
1314 }
1315
1316 /**
1317 * Fetches the current color used to render the
1318 * disabled text.
1319 *
1320 * @return the color
1321 */
1322 public Color getDisabledTextColor() {
1323 return disabledTextColor;
1324 }
1325
1326 /**
1327 * Sets the current color used to render the
1328 * disabled text. Setting the color fires off a
1329 * PropertyChange event ("disabledTextColor").
1330 *
1331 * @param c the color
1332 * @see #getDisabledTextColor
1333 * @beaninfo
1334 * description: color used to render disabled text
1335 * bound: true
1336 * preferred: true
1337 */
1338 public void setDisabledTextColor(Color c) {
1339 Color old = disabledTextColor;
1340 disabledTextColor = c;
1341 firePropertyChange("disabledTextColor", old, disabledTextColor);
1342 }
1343
1344 /**
1345 * Replaces the currently selected content with new content
1346 * represented by the given string. If there is no selection
1347 * this amounts to an insert of the given text. If there
1348 * is no replacement text this amounts to a removal of the
1349 * current selection.
1350 * <p>
1351 * This is the method that is used by the default implementation
1352 * of the action for inserting content that gets bound to the
1353 * keymap actions.
1354 *
1355 * @param content the content to replace the selection with
1356 */
1357 public void replaceSelection(String content) {
1358 Document doc = getDocument();
1359 if (doc != null) {
1360 try {
1361 boolean composedTextSaved = saveComposedText(caret.getDot());
1362 int p0 = Math.min(caret.getDot(), caret.getMark());
1363 int p1 = Math.max(caret.getDot(), caret.getMark());
1364 if (doc instanceof AbstractDocument) {
1365 ((AbstractDocument)doc).replace(p0, p1 - p0, content,null);
1366 }
1367 else {
1368 if (p0 != p1) {
1369 doc.remove(p0, p1 - p0);
1370 }
1371 if (content != null && content.length() > 0) {
1372 doc.insertString(p0, content, null);
1373 }
1374 }
1375 if (composedTextSaved) {
1376 restoreComposedText();
1377 }
1378 } catch (BadLocationException e) {
1379 UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
1380 }
1381 }
1382 }
1383
1384 /**
1385 * Fetches a portion of the text represented by the
1386 * component. Returns an empty string if length is 0.
1387 *
1388 * @param offs the offset >= 0
1389 * @param len the length >= 0
1390 * @return the text
1391 * @exception BadLocationException if the offset or length are invalid
1392 */
1393 public String getText(int offs, int len) throws BadLocationException {
1394 return getDocument().getText(offs, len);
1395 }
1396
1397 /**
1398 * Converts the given location in the model to a place in
1399 * the view coordinate system.
1400 * The component must have a positive size for
1401 * this translation to be computed (i.e. layout cannot
1402 * be computed until the component has been sized). The
1403 * component does not have to be visible or painted.
1404 *
1405 * @param pos the position >= 0
1406 * @return the coordinates as a rectangle, with (r.x, r.y) as the location
1407 * in the coordinate system, or null if the component does
1408 * not yet have a positive size.
1409 * @exception BadLocationException if the given position does not
1410 * represent a valid location in the associated document
1411 * @see TextUI#modelToView
1412 */
1413 public Rectangle modelToView(int pos) throws BadLocationException {
1414 return getUI().modelToView(this, pos);
1415 }
1416
1417 /**
1418 * Converts the given place in the view coordinate system
1419 * to the nearest representative location in the model.
1420 * The component must have a positive size for
1421 * this translation to be computed (i.e. layout cannot
1422 * be computed until the component has been sized). The
1423 * component does not have to be visible or painted.
1424 *
1425 * @param pt the location in the view to translate
1426 * @return the offset >= 0 from the start of the document,
1427 * or -1 if the component does not yet have a positive
1428 * size.
1429 * @see TextUI#viewToModel
1430 */
1431 public int viewToModel(Point pt) {
1432 return getUI().viewToModel(this, pt);
1433 }
1434
1435 /**
1436 * Transfers the currently selected range in the associated
1437 * text model to the system clipboard, removing the contents
1438 * from the model. The current selection is reset. Does nothing
1439 * for <code>null</code> selections.
1440 *
1441 * @see java.awt.Toolkit#getSystemClipboard
1442 * @see java.awt.datatransfer.Clipboard
1443 */
1444 public void cut() {
1445 if (isEditable() && isEnabled()) {
1446 invokeAction("cut", TransferHandler.getCutAction());
1447 }
1448 }
1449
1450 /**
1451 * Transfers the currently selected range in the associated
1452 * text model to the system clipboard, leaving the contents
1453 * in the text model. The current selection remains intact.
1454 * Does nothing for <code>null</code> selections.
1455 *
1456 * @see java.awt.Toolkit#getSystemClipboard
1457 * @see java.awt.datatransfer.Clipboard
1458 */
1459 public void copy() {
1460 invokeAction("copy", TransferHandler.getCopyAction());
1461 }
1462
1463 /**
1464 * Transfers the contents of the system clipboard into the
1465 * associated text model. If there is a selection in the
1466 * associated view, it is replaced with the contents of the
1467 * clipboard. If there is no selection, the clipboard contents
1468 * are inserted in front of the current insert position in
1469 * the associated view. If the clipboard is empty, does nothing.
1470 *
1471 * @see #replaceSelection
1472 * @see java.awt.Toolkit#getSystemClipboard
1473 * @see java.awt.datatransfer.Clipboard
1474 */
1475 public void paste() {
1476 if (isEditable() && isEnabled()) {
1477 invokeAction("paste", TransferHandler.getPasteAction());
1478 }
1479 }
1480
1481 /**
1482 * This is a conveniance method that is only useful for
1483 * <code>cut</code>, <code>copy</code> and <code>paste</code>. If
1484 * an <code>Action</code> with the name <code>name</code> does not
1485 * exist in the <code>ActionMap</code>, this will attemp to install a
1486 * <code>TransferHandler</code> and then use <code>altAction</code>.
1487 */
1488 private void invokeAction(String name, Action altAction) {
1489 ActionMap map = getActionMap();
1490 Action action = null;
1491
1492 if (map != null) {
1493 action = map.get(name);
1494 }
1495 if (action == null) {
1496 installDefaultTransferHandlerIfNecessary();
1497 action = altAction;
1498 }
1499 action.actionPerformed(new ActionEvent(this,
1500 ActionEvent.ACTION_PERFORMED, (String)action.
1501 getValue(Action.NAME),
1502 EventQueue.getMostRecentEventTime(),
1503 getCurrentEventModifiers()));
1504 }
1505
1506 /**
1507 * If the current <code>TransferHandler</code> is null, this will
1508 * install a new one.
1509 */
1510 private void installDefaultTransferHandlerIfNecessary() {
1511 if (getTransferHandler() == null) {
1512 if (defaultTransferHandler == null) {
1513 defaultTransferHandler = new DefaultTransferHandler();
1514 }
1515 setTransferHandler(defaultTransferHandler);
1516 }
1517 }
1518
1519 /**
1520 * Moves the caret to a new position, leaving behind a mark
1521 * defined by the last time <code>setCaretPosition</code> was
1522 * called. This forms a selection.
1523 * If the document is <code>null</code>, does nothing. The position
1524 * must be between 0 and the length of the component's text or else
1525 * an exception is thrown.
1526 *
1527 * @param pos the position
1528 * @exception IllegalArgumentException if the value supplied
1529 * for <code>position</code> is less than zero or greater
1530 * than the component's text length
1531 * @see #setCaretPosition
1532 */
1533 public void moveCaretPosition(int pos) {
1534 Document doc = getDocument();
1535 if (doc != null) {
1536 if (pos > doc.getLength() || pos < 0) {
1537 throw new IllegalArgumentException("bad position: " + pos);
1538 }
1539 caret.moveDot(pos);
1540 }
1541 }
1542
1543 /**
1544 * The bound property name for the focus accelerator.
1545 */
1546 public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey";
1547
1548 /**
1549 * Sets the key accelerator that will cause the receiving text
1550 * component to get the focus. The accelerator will be the
1551 * key combination of the <em>alt</em> key and the character
1552 * given (converted to upper case). By default, there is no focus
1553 * accelerator key. Any previous key accelerator setting will be
1554 * superseded. A '\0' key setting will be registered, and has the
1555 * effect of turning off the focus accelerator. When the new key
1556 * is set, a PropertyChange event (FOCUS_ACCELERATOR_KEY) will be fired.
1557 *
1558 * @param aKey the key
1559 * @see #getFocusAccelerator
1560 * @beaninfo
1561 * description: accelerator character used to grab focus
1562 * bound: true
1563 */
1564 public void setFocusAccelerator(char aKey) {
1565 aKey = Character.toUpperCase(aKey);
1566 char old = focusAccelerator;
1567 focusAccelerator = aKey;
1568 // Fix for 4341002: value of FOCUS_ACCELERATOR_KEY is wrong.
1569 // So we fire both FOCUS_ACCELERATOR_KEY, for compatibility,
1570 // and the correct event here.
1571 firePropertyChange(FOCUS_ACCELERATOR_KEY, old, focusAccelerator);
1572 firePropertyChange("focusAccelerator", old, focusAccelerator);
1573 }
1574
1575 /**
1576 * Returns the key accelerator that will cause the receiving
1577 * text component to get the focus. Return '\0' if no focus
1578 * accelerator has been set.
1579 *
1580 * @return the key
1581 */
1582 public char getFocusAccelerator() {
1583 return focusAccelerator;
1584 }
1585
1586 /**
1587 * Initializes from a stream. This creates a
1588 * model of the type appropriate for the component
1589 * and initializes the model from the stream.
1590 * By default this will load the model as plain
1591 * text. Previous contents of the model are discarded.
1592 *
1593 * @param in the stream to read from
1594 * @param desc an object describing the stream; this
1595 * might be a string, a File, a URL, etc. Some kinds
1596 * of documents (such as html for example) might be
1597 * able to make use of this information; if non-<code>null</code>,
1598 * it is added as a property of the document
1599 * @exception IOException as thrown by the stream being
1600 * used to initialize
1601 * @see EditorKit#createDefaultDocument
1602 * @see #setDocument
1603 * @see PlainDocument
1604 */
1605 public void read(Reader in, Object desc) throws IOException {
1606 EditorKit kit = getUI().getEditorKit(this);
1607 Document doc = kit.createDefaultDocument();
1608 if (desc != null) {
1609 doc.putProperty(Document.StreamDescriptionProperty, desc);
1610 }
1611 try {
1612 kit.read(in, doc, 0);
1613 setDocument(doc);
1614 } catch (BadLocationException e) {
1615 throw new IOException(e.getMessage());
1616 }
1617 }
1618
1619 /**
1620 * Stores the contents of the model into the given
1621 * stream. By default this will store the model as plain
1622 * text.
1623 *
1624 * @param out the output stream
1625 * @exception IOException on any I/O error
1626 */
1627 public void write(Writer out) throws IOException {
1628 Document doc = getDocument();
1629 try {
1630 getUI().getEditorKit(this).write(out, doc, 0, doc.getLength());
1631 } catch (BadLocationException e) {
1632 throw new IOException(e.getMessage());
1633 }
1634 }
1635
1636 public void removeNotify() {
1637 super.removeNotify();
1638 if (getFocusedComponent() == this) {
1639 AppContext.getAppContext().remove(FOCUSED_COMPONENT);
1640 }
1641 }
1642
1643 // --- java.awt.TextComponent methods ------------------------
1644
1645 /**
1646 * Sets the position of the text insertion caret for the
1647 * <code>TextComponent</code>. Note that the caret tracks change,
1648 * so this may move if the underlying text of the component is changed.
1649 * If the document is <code>null</code>, does nothing. The position
1650 * must be between 0 and the length of the component's text or else
1651 * an exception is thrown.
1652 *
1653 * @param position the position
1654 * @exception IllegalArgumentException if the value supplied
1655 * for <code>position</code> is less than zero or greater
1656 * than the component's text length
1657 * @beaninfo
1658 * description: the caret position
1659 */
1660 public void setCaretPosition(int position) {
1661 Document doc = getDocument();
1662 if (doc != null) {
1663 if (position > doc.getLength() || position < 0) {
1664 throw new IllegalArgumentException("bad position: " + position);
1665 }
1666 caret.setDot(position);
1667 }
1668 }
1669
1670 /**
1671 * Returns the position of the text insertion caret for the
1672 * text component.
1673 *
1674 * @return the position of the text insertion caret for the
1675 * text component >= 0
1676 */
1677 @Transient
1678 public int getCaretPosition() {
1679 return caret.getDot();
1680 }
1681
1682 /**
1683 * Sets the text of this <code>TextComponent</code>
1684 * to the specified text. If the text is <code>null</code>
1685 * or empty, has the effect of simply deleting the old text.
1686 * When text has been inserted, the resulting caret location
1687 * is determined by the implementation of the caret class.
1688 *
1689 * <p>
1690 * Note that text is not a bound property, so no <code>PropertyChangeEvent
1691 * </code> is fired when it changes. To listen for changes to the text,
1692 * use <code>DocumentListener</code>.
1693 *
1694 * @param t the new text to be set
1695 * @see #getText
1696 * @see DefaultCaret
1697 * @beaninfo
1698 * description: the text of this component
1699 */
1700 public void setText(String t) {
1701 try {
1702 Document doc = getDocument();
1703 if (doc instanceof AbstractDocument) {
1704 ((AbstractDocument)doc).replace(0, doc.getLength(), t,null);
1705 }
1706 else {
1707 doc.remove(0, doc.getLength());
1708 doc.insertString(0, t, null);
1709 }
1710 } catch (BadLocationException e) {
1711 UIManager.getLookAndFeel().provideErrorFeedback(JTextComponent.this);
1712 }
1713 }
1714
1715 /**
1716 * Returns the text contained in this <code>TextComponent</code>.
1717 * If the underlying document is <code>null</code>,
1718 * will give a <code>NullPointerException</code>.
1719 *
1720 * Note that text is not a bound property, so no <code>PropertyChangeEvent
1721 * </code> is fired when it changes. To listen for changes to the text,
1722 * use <code>DocumentListener</code>.
1723 *
1724 * @return the text
1725 * @exception NullPointerException if the document is <code>null</code>
1726 * @see #setText
1727 */
1728 public String getText() {
1729 Document doc = getDocument();
1730 String txt;
1731 try {
1732 txt = doc.getText(0, doc.getLength());
1733 } catch (BadLocationException e) {
1734 txt = null;
1735 }
1736 return txt;
1737 }
1738
1739 /**
1740 * Returns the selected text contained in this
1741 * <code>TextComponent</code>. If the selection is
1742 * <code>null</code> or the document empty, returns <code>null</code>.
1743 *
1744 * @return the text
1745 * @exception IllegalArgumentException if the selection doesn't
1746 * have a valid mapping into the document for some reason
1747 * @see #setText
1748 */
1749 public String getSelectedText() {
1750 String txt = null;
1751 int p0 = Math.min(caret.getDot(), caret.getMark());
1752 int p1 = Math.max(caret.getDot(), caret.getMark());
1753 if (p0 != p1) {
1754 try {
1755 Document doc = getDocument();
1756 txt = doc.getText(p0, p1 - p0);
1757 } catch (BadLocationException e) {
1758 throw new IllegalArgumentException(e.getMessage());
1759 }
1760 }
1761 return txt;
1762 }
1763
1764 /**
1765 * Returns the boolean indicating whether this
1766 * <code>TextComponent</code> is editable or not.
1767 *
1768 * @return the boolean value
1769 * @see #setEditable
1770 */
1771 public boolean isEditable() {
1772 return editable;
1773 }
1774
1775 /**
1776 * Sets the specified boolean to indicate whether or not this
1777 * <code>TextComponent</code> should be editable.
1778 * A PropertyChange event ("editable") is fired when the
1779 * state is changed.
1780 *
1781 * @param b the boolean to be set
1782 * @see #isEditable
1783 * @beaninfo
1784 * description: specifies if the text can be edited
1785 * bound: true
1786 */
1787 public void setEditable(boolean b) {
1788 if (b != editable) {
1789 boolean oldVal = editable;
1790 editable = b;
1791 enableInputMethods(editable);
1792 firePropertyChange("editable", Boolean.valueOf(oldVal), Boolean.valueOf(editable));
1793 repaint();
1794 }
1795 }
1796
1797 /**
1798 * Returns the selected text's start position. Return 0 for an
1799 * empty document, or the value of dot if no selection.
1800 *
1801 * @return the start position >= 0
1802 */
1803 @Transient
1804 public int getSelectionStart() {
1805 int start = Math.min(caret.getDot(), caret.getMark());
1806 return start;
1807 }
1808
1809 /**
1810 * Sets the selection start to the specified position. The new
1811 * starting point is constrained to be before or at the current
1812 * selection end.
1813 * <p>
1814 * This is available for backward compatibility to code
1815 * that called this method on <code>java.awt.TextComponent</code>.
1816 * This is implemented to forward to the <code>Caret</code>
1817 * implementation which is where the actual selection is maintained.
1818 *
1819 * @param selectionStart the start position of the text >= 0
1820 * @beaninfo
1821 * description: starting location of the selection.
1822 */
1823 public void setSelectionStart(int selectionStart) {
1824 /* Route through select method to enforce consistent policy
1825 * between selectionStart and selectionEnd.
1826 */
1827 select(selectionStart, getSelectionEnd());
1828 }
1829
1830 /**
1831 * Returns the selected text's end position. Return 0 if the document
1832 * is empty, or the value of dot if there is no selection.
1833 *
1834 * @return the end position >= 0
1835 */
1836 @Transient
1837 public int getSelectionEnd() {
1838 int end = Math.max(caret.getDot(), caret.getMark());
1839 return end;
1840 }
1841
1842 /**
1843 * Sets the selection end to the specified position. The new
1844 * end point is constrained to be at or after the current
1845 * selection start.
1846 * <p>
1847 * This is available for backward compatibility to code
1848 * that called this method on <code>java.awt.TextComponent</code>.
1849 * This is implemented to forward to the <code>Caret</code>
1850 * implementation which is where the actual selection is maintained.
1851 *
1852 * @param selectionEnd the end position of the text >= 0
1853 * @beaninfo
1854 * description: ending location of the selection.
1855 */
1856 public void setSelectionEnd(int selectionEnd) {
1857 /* Route through select method to enforce consistent policy
1858 * between selectionStart and selectionEnd.
1859 */
1860 select(getSelectionStart(), selectionEnd);
1861 }
1862
1863 /**
1864 * Selects the text between the specified start and end positions.
1865 * <p>
1866 * This method sets the start and end positions of the
1867 * selected text, enforcing the restriction that the start position
1868 * must be greater than or equal to zero. The end position must be
1869 * greater than or equal to the start position, and less than or
1870 * equal to the length of the text component's text.
1871 * <p>
1872 * If the caller supplies values that are inconsistent or out of
1873 * bounds, the method enforces these constraints silently, and
1874 * without failure. Specifically, if the start position or end
1875 * position is greater than the length of the text, it is reset to
1876 * equal the text length. If the start position is less than zero,
1877 * it is reset to zero, and if the end position is less than the
1878 * start position, it is reset to the start position.
1879 * <p>
1880 * This call is provided for backward compatibility.
1881 * It is routed to a call to <code>setCaretPosition</code>
1882 * followed by a call to <code>moveCaretPosition</code>.
1883 * The preferred way to manage selection is by calling
1884 * those methods directly.
1885 *
1886 * @param selectionStart the start position of the text
1887 * @param selectionEnd the end position of the text
1888 * @see #setCaretPosition
1889 * @see #moveCaretPosition
1890 */
1891 public void select(int selectionStart, int selectionEnd) {
1892 // argument adjustment done by java.awt.TextComponent
1893 int docLength = getDocument().getLength();
1894
1895 if (selectionStart < 0) {
1896 selectionStart = 0;
1897 }
1898 if (selectionStart > docLength) {
1899 selectionStart = docLength;
1900 }
1901 if (selectionEnd > docLength) {
1902 selectionEnd = docLength;
1903 }
1904 if (selectionEnd < selectionStart) {
1905 selectionEnd = selectionStart;
1906 }
1907
1908 setCaretPosition(selectionStart);
1909 moveCaretPosition(selectionEnd);
1910 }
1911
1912 /**
1913 * Selects all the text in the <code>TextComponent</code>.
1914 * Does nothing on a <code>null</code> or empty document.
1915 */
1916 public void selectAll() {
1917 Document doc = getDocument();
1918 if (doc != null) {
1919 setCaretPosition(0);
1920 moveCaretPosition(doc.getLength());
1921 }
1922 }
1923
1924 // --- Tooltip Methods ---------------------------------------------
1925
1926 /**
1927 * Returns the string to be used as the tooltip for <code>event</code>.
1928 * This will return one of:
1929 * <ol>
1930 * <li>If <code>setToolTipText</code> has been invoked with a
1931 * non-<code>null</code>
1932 * value, it will be returned, otherwise
1933 * <li>The value from invoking <code>getToolTipText</code> on
1934 * the UI will be returned.
1935 * </ol>
1936 * By default <code>JTextComponent</code> does not register
1937 * itself with the <code>ToolTipManager</code>.
1938 * This means that tooltips will NOT be shown from the
1939 * <code>TextUI</code> unless <code>registerComponent</code> has
1940 * been invoked on the <code>ToolTipManager</code>.
1941 *
1942 * @param event the event in question
1943 * @return the string to be used as the tooltip for <code>event</code>
1944 * @see javax.swing.JComponent#setToolTipText
1945 * @see javax.swing.plaf.TextUI#getToolTipText
1946 * @see javax.swing.ToolTipManager#registerComponent
1947 */
1948 public String getToolTipText(MouseEvent event) {
1949 String retValue = super.getToolTipText(event);
1950
1951 if (retValue == null) {
1952 TextUI ui = getUI();
1953 if (ui != null) {
1954 retValue = ui.getToolTipText(this, new Point(event.getX(),
1955 event.getY()));
1956 }
1957 }
1958 return retValue;
1959 }
1960
1961 // --- Scrollable methods ---------------------------------------------
1962
1963 /**
1964 * Returns the preferred size of the viewport for a view component.
1965 * This is implemented to do the default behavior of returning
1966 * the preferred size of the component.
1967 *
1968 * @return the <code>preferredSize</code> of a <code>JViewport</code>
1969 * whose view is this <code>Scrollable</code>
1970 */
1971 public Dimension getPreferredScrollableViewportSize() {
1972 return getPreferredSize();
1973 }
1974
1975
1976 /**
1977 * Components that display logical rows or columns should compute
1978 * the scroll increment that will completely expose one new row
1979 * or column, depending on the value of orientation. Ideally,
1980 * components should handle a partially exposed row or column by
1981 * returning the distance required to completely expose the item.
1982 * <p>
1983 * The default implementation of this is to simply return 10% of
1984 * the visible area. Subclasses are likely to be able to provide
1985 * a much more reasonable value.
1986 *
1987 * @param visibleRect the view area visible within the viewport
1988 * @param orientation either <code>SwingConstants.VERTICAL</code> or
1989 * <code>SwingConstants.HORIZONTAL</code>
1990 * @param direction less than zero to scroll up/left, greater than
1991 * zero for down/right
1992 * @return the "unit" increment for scrolling in the specified direction
1993 * @exception IllegalArgumentException for an invalid orientation
1994 * @see JScrollBar#setUnitIncrement
1995 */
1996 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
1997 switch(orientation) {
1998 case SwingConstants.VERTICAL:
1999 return visibleRect.height / 10;
2000 case SwingConstants.HORIZONTAL:
2001 return visibleRect.width / 10;
2002 default:
2003 throw new IllegalArgumentException("Invalid orientation: " + orientation);
2004 }
2005 }
2006
2007
2008 /**
2009 * Components that display logical rows or columns should compute
2010 * the scroll increment that will completely expose one block
2011 * of rows or columns, depending on the value of orientation.
2012 * <p>
2013 * The default implementation of this is to simply return the visible
2014 * area. Subclasses will likely be able to provide a much more
2015 * reasonable value.
2016 *
2017 * @param visibleRect the view area visible within the viewport
2018 * @param orientation either <code>SwingConstants.VERTICAL</code> or
2019 * <code>SwingConstants.HORIZONTAL</code>
2020 * @param direction less than zero to scroll up/left, greater than zero
2021 * for down/right
2022 * @return the "block" increment for scrolling in the specified direction
2023 * @exception IllegalArgumentException for an invalid orientation
2024 * @see JScrollBar#setBlockIncrement
2025 */
2026 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
2027 switch(orientation) {
2028 case SwingConstants.VERTICAL:
2029 return visibleRect.height;
2030 case SwingConstants.HORIZONTAL:
2031 return visibleRect.width;
2032 default:
2033 throw new IllegalArgumentException("Invalid orientation: " + orientation);
2034 }
2035 }
2036
2037
2038 /**
2039 * Returns true if a viewport should always force the width of this
2040 * <code>Scrollable</code> to match the width of the viewport.
2041 * For example a normal text view that supported line wrapping
2042 * would return true here, since it would be undesirable for
2043 * wrapped lines to disappear beyond the right
2044 * edge of the viewport. Note that returning true for a
2045 * <code>Scrollable</code> whose ancestor is a <code>JScrollPane</code>
2046 * effectively disables horizontal scrolling.
2047 * <p>
2048 * Scrolling containers, like <code>JViewport</code>,
2049 * will use this method each time they are validated.
2050 *
2051 * @return true if a viewport should force the <code>Scrollable</code>s
2052 * width to match its own
2053 */
2054 public boolean getScrollableTracksViewportWidth() {
2055 if (getParent() instanceof JViewport) {
2056 return (((JViewport)getParent()).getWidth() > getPreferredSize().width);
2057 }
2058 return false;
2059 }
2060
2061 /**
2062 * Returns true if a viewport should always force the height of this
2063 * <code>Scrollable</code> to match the height of the viewport.
2064 * For example a columnar text view that flowed text in left to
2065 * right columns could effectively disable vertical scrolling by
2066 * returning true here.
2067 * <p>
2068 * Scrolling containers, like <code>JViewport</code>,
2069 * will use this method each time they are validated.
2070 *
2071 * @return true if a viewport should force the Scrollables height
2072 * to match its own
2073 */
2074 public boolean getScrollableTracksViewportHeight() {
2075 if (getParent() instanceof JViewport) {
2076 return (((JViewport)getParent()).getHeight() > getPreferredSize().height);
2077 }
2078 return false;
2079 }
2080
2081
2082 //////////////////
2083 // Printing Support
2084 //////////////////
2085
2086 /**
2087 * A convenience print method that displays a print dialog, and then
2088 * prints this {@code JTextComponent} in <i>interactive</i> mode with no
2089 * header or footer text. Note: this method
2090 * blocks until printing is done.
2091 * <p>
2092 * Note: In <i>headless</i> mode, no dialogs will be shown.
2093 *
2094 * <p> This method calls the full featured
2095 * {@link #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2096 * print} method to perform printing.
2097 * @return {@code true}, unless printing is canceled by the user
2098 * @throws PrinterException if an error in the print system causes the job
2099 * to be aborted
2100 * @throws SecurityException if this thread is not allowed to
2101 * initiate a print job request
2102 *
2103 * @see #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2104 *
2105 * @since 1.6
2106 */
2107
2108 public boolean print() throws PrinterException {
2109 return print(null, null, true, null, null, true);
2110 }
2111
2112 /**
2113 * A convenience print method that displays a print dialog, and then
2114 * prints this {@code JTextComponent} in <i>interactive</i> mode with
2115 * the specified header and footer text. Note: this method
2116 * blocks until printing is done.
2117 * <p>
2118 * Note: In <i>headless</i> mode, no dialogs will be shown.
2119 *
2120 * <p> This method calls the full featured
2121 * {@link #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2122 * print} method to perform printing.
2123 * @param headerFormat the text, in {@code MessageFormat}, to be
2124 * used as the header, or {@code null} for no header
2125 * @param footerFormat the text, in {@code MessageFormat}, to be
2126 * used as the footer, or {@code null} for no footer
2127 * @return {@code true}, unless printing is canceled by the user
2128 * @throws PrinterException if an error in the print system causes the job
2129 * to be aborted
2130 * @throws SecurityException if this thread is not allowed to
2131 * initiate a print job request
2132 *
2133 * @see #print(MessageFormat, MessageFormat, boolean, PrintService, PrintRequestAttributeSet, boolean)
2134 * @see java.text.MessageFormat
2135 * @since 1.6
2136 */
2137 public boolean print(final MessageFormat headerFormat,
2138 final MessageFormat footerFormat) throws PrinterException {
2139 return print(headerFormat, footerFormat, true, null, null, true);
2140 }
2141
2142 /**
2143 * Prints the content of this {@code JTextComponent}. Note: this method
2144 * blocks until printing is done.
2145 *
2146 * <p>
2147 * Page header and footer text can be added to the output by providing
2148 * {@code MessageFormat} arguments. The printing code requests
2149 * {@code Strings} from the formats, providing a single item which may be
2150 * included in the formatted string: an {@code Integer} representing the
2151 * current page number.
2152 *
2153 * <p>
2154 * {@code showPrintDialog boolean} parameter allows you to specify whether
2155 * a print dialog is displayed to the user. When it is, the user
2156 * may use the dialog to change printing attributes or even cancel the
2157 * print.
2158 *
2159 * <p>
2160 * {@code service} allows you to provide the initial
2161 * {@code PrintService} for the print dialog, or to specify
2162 * {@code PrintService} to print to when the dialog is not shown.
2163 *
2164 * <p>
2165 * {@code attributes} can be used to provide the
2166 * initial values for the print dialog, or to supply any needed
2167 * attributes when the dialog is not shown. {@code attributes} can
2168 * be used to control how the job will print, for example
2169 * <i>duplex</i> or <i>single-sided</i>.
2170 *
2171 * <p>
2172 * {@code interactive boolean} parameter allows you to specify
2173 * whether to perform printing in <i>interactive</i>
2174 * mode. If {@code true}, a progress dialog, with an abort option,
2175 * is displayed for the duration of printing. This dialog is
2176 * <i>modal</i> when {@code print} is invoked on the <i>Event Dispatch
2177 * Thread</i> and <i>non-modal</i> otherwise. <b>Warning</b>:
2178 * calling this method on the <i>Event Dispatch Thread</i> with {@code
2179 * interactive false} blocks <i>all</i> events, including repaints, from
2180 * being processed until printing is complete. It is only
2181 * recommended when printing from an application with no
2182 * visible GUI.
2183 *
2184 * <p>
2185 * Note: In <i>headless</i> mode, {@code showPrintDialog} and
2186 * {@code interactive} parameters are ignored and no dialogs are
2187 * shown.
2188 *
2189 * <p>
2190 * This method ensures the {@code document} is not mutated during printing.
2191 * To indicate it visually, {@code setEnabled(false)} is set for the
2192 * duration of printing.
2193 *
2194 * <p>
2195 * This method uses {@link #getPrintable} to render document content.
2196 *
2197 * <p>
2198 * This method is thread-safe, although most Swing methods are not. Please
2199 * see <A
2200 * HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">
2201 * How to Use Threads</A> for more information.
2202 *
2203 * <p>
2204 * <b>Sample Usage</b>. This code snippet shows a cross-platform print
2205 * dialog and then prints the {@code JTextComponent} in <i>interactive</i> mode
2206 * unless the user cancels the dialog:
2207 *
2208 * <pre>
2209 * textComponent.print(new MessageFormat("My text component header"),
2210 * new MessageFormat("Footer. Page - {0}"), true, null, null, true);
2211 * </pre>
2212 * <p>
2213 * Executing this code off the <i>Event Dispatch Thread</i>
2214 * performs printing on the <i>background</i>.
2215 * The following pattern might be used for <i>background</i>
2216 * printing:
2217 * <pre>
2218 * FutureTask<Boolean> future =
2219 * new FutureTask<Boolean>(
2220 * new Callable<Boolean>() {
2221 * public Boolean call() {
2222 * return textComponent.print(.....);
2223 * }
2224 * });
2225 * executor.execute(future);
2226 * </pre>
2227 *
2228 * @param headerFormat the text, in {@code MessageFormat}, to be
2229 * used as the header, or {@code null} for no header
2230 * @param footerFormat the text, in {@code MessageFormat}, to be
2231 * used as the footer, or {@code null} for no footer
2232 * @param showPrintDialog {@code true} to display a print dialog,
2233 * {@code false} otherwise
2234 * @param service initial {@code PrintService}, or {@code null} for the
2235 * default
2236 * @param attributes the job attributes to be applied to the print job, or
2237 * {@code null} for none
2238 * @param interactive whether to print in an interactive mode
2239 * @return {@code true}, unless printing is canceled by the user
2240 * @throws PrinterException if an error in the print system causes the job
2241 * to be aborted
2242 * @throws SecurityException if this thread is not allowed to
2243 * initiate a print job request
2244 *
2245 * @see #getPrintable
2246 * @see java.text.MessageFormat
2247 * @see java.awt.GraphicsEnvironment#isHeadless
2248 * @see java.util.concurrent.FutureTask
2249 *
2250 * @since 1.6
2251 */
2252 public boolean print(final MessageFormat headerFormat,
2253 final MessageFormat footerFormat,
2254 final boolean showPrintDialog,
2255 final PrintService service,
2256 final PrintRequestAttributeSet attributes,
2257 final boolean interactive)
2258 throws PrinterException {
2259
2260 final PrinterJob job = PrinterJob.getPrinterJob();
2261 final Printable printable;
2262 final PrintingStatus printingStatus;
2263 final boolean isHeadless = GraphicsEnvironment.isHeadless();
2264 final boolean isEventDispatchThread =
2265 SwingUtilities.isEventDispatchThread();
2266 final Printable textPrintable = getPrintable(headerFormat, footerFormat);
2267 if (interactive && ! isHeadless) {
2268 printingStatus =
2269 PrintingStatus.createPrintingStatus(this, job);
2270 printable =
2271 printingStatus.createNotificationPrintable(textPrintable);
2272 } else {
2273 printingStatus = null;
2274 printable = textPrintable;
2275 }
2276
2277 if (service != null) {
2278 job.setPrintService(service);
2279 }
2280
2281 job.setPrintable(printable);
2282
2283 final PrintRequestAttributeSet attr = (attributes == null)
2284 ? new HashPrintRequestAttributeSet()
2285 : attributes;
2286
2287 if (showPrintDialog && ! isHeadless && ! job.printDialog(attr)) {
2288 return false;
2289 }
2290
2291 /*
2292 * there are three cases for printing:
2293 * 1. print non interactively (! interactive || isHeadless)
2294 * 2. print interactively off EDT
2295 * 3. print interactively on EDT
2296 *
2297 * 1 and 2 prints on the current thread (3 prints on another thread)
2298 * 2 and 3 deal with PrintingStatusDialog
2299 */
2300 final Callable<Object> doPrint =
2301 new Callable<Object>() {
2302 public Object call() throws Exception {
2303 try {
2304 job.print(attr);
2305 } finally {
2306 if (printingStatus != null) {
2307 printingStatus.dispose();
2308 }
2309 }
2310 return null;
2311 }
2312 };
2313
2314 final FutureTask<Object> futurePrinting =
2315 new FutureTask<Object>(doPrint);
2316
2317 &nb