2 Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package javax
.swing
.plaf
.basic
;
41 import gnu
.classpath
.NotImplementedException
;
43 import java
.awt
.Color
;
44 import java
.awt
.Container
;
45 import java
.awt
.Dimension
;
46 import java
.awt
.Graphics
;
47 import java
.awt
.HeadlessException
;
48 import java
.awt
.Insets
;
49 import java
.awt
.Point
;
50 import java
.awt
.Rectangle
;
51 import java
.awt
.Shape
;
52 import java
.awt
.Toolkit
;
53 import java
.awt
.datatransfer
.Clipboard
;
54 import java
.awt
.datatransfer
.StringSelection
;
55 import java
.awt
.event
.FocusEvent
;
56 import java
.awt
.event
.FocusListener
;
57 import java
.beans
.PropertyChangeEvent
;
58 import java
.beans
.PropertyChangeListener
;
60 import javax
.swing
.Action
;
61 import javax
.swing
.ActionMap
;
62 import javax
.swing
.InputMap
;
63 import javax
.swing
.JComponent
;
64 import javax
.swing
.LookAndFeel
;
65 import javax
.swing
.SwingConstants
;
66 import javax
.swing
.SwingUtilities
;
67 import javax
.swing
.UIManager
;
68 import javax
.swing
.event
.DocumentEvent
;
69 import javax
.swing
.event
.DocumentListener
;
70 import javax
.swing
.plaf
.ActionMapUIResource
;
71 import javax
.swing
.plaf
.TextUI
;
72 import javax
.swing
.plaf
.UIResource
;
73 import javax
.swing
.text
.AbstractDocument
;
74 import javax
.swing
.text
.BadLocationException
;
75 import javax
.swing
.text
.Caret
;
76 import javax
.swing
.text
.DefaultCaret
;
77 import javax
.swing
.text
.DefaultEditorKit
;
78 import javax
.swing
.text
.DefaultHighlighter
;
79 import javax
.swing
.text
.Document
;
80 import javax
.swing
.text
.EditorKit
;
81 import javax
.swing
.text
.Element
;
82 import javax
.swing
.text
.Highlighter
;
83 import javax
.swing
.text
.JTextComponent
;
84 import javax
.swing
.text
.Keymap
;
85 import javax
.swing
.text
.Position
;
86 import javax
.swing
.text
.Utilities
;
87 import javax
.swing
.text
.View
;
88 import javax
.swing
.text
.ViewFactory
;
91 * The abstract base class from which the UI classes for Swings text
92 * components are derived. This provides most of the functionality for
95 * @author original author unknown
96 * @author Roman Kennke (roman@kennke.org)
98 public abstract class BasicTextUI
extends TextUI
99 implements ViewFactory
102 * A {@link DefaultCaret} that implements {@link UIResource}.
104 public static class BasicCaret
extends DefaultCaret
implements UIResource
108 // Nothing to do here.
113 * A {@link DefaultHighlighter} that implements {@link UIResource}.
115 public static class BasicHighlighter
extends DefaultHighlighter
116 implements UIResource
118 public BasicHighlighter()
120 // Nothing to do here.
125 * This view forms the root of the View hierarchy. However, it delegates
126 * most calls to another View which is the real root of the hierarchy.
127 * The purpose is to make sure that all Views in the hierarchy, including
128 * the (real) root have a well-defined parent to which they can delegate
129 * calls like {@link #preferenceChanged}, {@link #getViewFactory} and
130 * {@link #getContainer}.
132 private class RootView
extends View
134 /** The real root view. */
138 * Creates a new RootView.
146 * Returns the ViewFactory for this RootView. If the current EditorKit
147 * provides a ViewFactory, this is used. Otherwise the TextUI itself
148 * is returned as a ViewFactory.
150 * @return the ViewFactory for this RootView
152 public ViewFactory
getViewFactory()
154 ViewFactory factory
= null;
155 EditorKit editorKit
= BasicTextUI
.this.getEditorKit(getComponent());
156 factory
= editorKit
.getViewFactory();
158 factory
= BasicTextUI
.this;
163 * Indicates that the preferences of one of the child view has changed.
164 * This calls revalidate on the text component.
166 * @param v the child view which's preference has changed
167 * @param width <code>true</code> if the width preference has changed
168 * @param height <code>true</code> if the height preference has changed
170 public void preferenceChanged(View v
, boolean width
, boolean height
)
172 textComponent
.revalidate();
176 * Sets the real root view.
178 * @param v the root view to set
180 public void setView(View v
)
183 view
.setParent(null);
192 * Returns the real root view, regardless of the index.
194 * @param index not used here
196 * @return the real root view, regardless of the index.
198 public View
getView(int index
)
204 * Returns <code>1</code> since the RootView always contains one
205 * child, that is the real root of the View hierarchy.
207 * @return <code>1</code> since the RootView always contains one
208 * child, that is the real root of the View hierarchy
210 public int getViewCount()
219 * Returns the <code>Container</code> that contains this view. This
220 * normally will be the text component that is managed by this TextUI.
222 * @return the <code>Container</code> that contains this view
224 public Container
getContainer()
226 return textComponent
;
230 * Returns the preferred span along the specified <code>axis</code>.
231 * This is delegated to the real root view.
233 * @param axis the axis for which the preferred span is queried
235 * @return the preferred span along the axis
237 public float getPreferredSpan(int axis
)
240 return view
.getPreferredSpan(axis
);
242 return Integer
.MAX_VALUE
;
246 * Paints the view. This is delegated to the real root view.
248 * @param g the <code>Graphics</code> context to paint to
249 * @param s the allocation for the View
251 public void paint(Graphics g
, Shape s
)
255 Rectangle b
= s
.getBounds();
256 view
.setSize(b
.width
, b
.height
);
263 * Maps a position in the document into the coordinate space of the View.
264 * The output rectangle usually reflects the font height but has a width
267 * This is delegated to the real root view.
269 * @param position the position of the character in the model
270 * @param a the area that is occupied by the view
271 * @param bias either {@link Position.Bias#Forward} or
272 * {@link Position.Bias#Backward} depending on the preferred
273 * direction bias. If <code>null</code> this defaults to
274 * <code>Position.Bias.Forward</code>
276 * @return a rectangle that gives the location of the document position
277 * inside the view coordinate space
279 * @throws BadLocationException if <code>pos</code> is invalid
280 * @throws IllegalArgumentException if b is not one of the above listed
283 public Shape
modelToView(int position
, Shape a
, Position
.Bias bias
)
284 throws BadLocationException
286 return view
.modelToView(position
, a
, bias
);
290 * Maps coordinates from the <code>View</code>'s space into a position
291 * in the document model.
293 * @param x the x coordinate in the view space
294 * @param y the y coordinate in the view space
295 * @param a the allocation of this <code>View</code>
296 * @param b the bias to use
298 * @return the position in the document that corresponds to the screen
299 * coordinates <code>x, y</code>
301 public int viewToModel(float x
, float y
, Shape a
, Position
.Bias
[] b
)
303 return view
.viewToModel(x
, y
, a
, b
);
307 * Notification about text insertions. These are forwarded to the
310 * @param ev the DocumentEvent describing the change
311 * @param shape the current allocation of the view's display
312 * @param vf the ViewFactory to use for creating new Views
314 public void insertUpdate(DocumentEvent ev
, Shape shape
, ViewFactory vf
)
316 view
.insertUpdate(ev
, shape
, vf
);
320 * Notification about text removals. These are forwarded to the
323 * @param ev the DocumentEvent describing the change
324 * @param shape the current allocation of the view's display
325 * @param vf the ViewFactory to use for creating new Views
327 public void removeUpdate(DocumentEvent ev
, Shape shape
, ViewFactory vf
)
329 view
.removeUpdate(ev
, shape
, vf
);
333 * Notification about text changes. These are forwarded to the
336 * @param ev the DocumentEvent describing the change
337 * @param shape the current allocation of the view's display
338 * @param vf the ViewFactory to use for creating new Views
340 public void changedUpdate(DocumentEvent ev
, Shape shape
, ViewFactory vf
)
342 view
.changedUpdate(ev
, shape
, vf
);
346 * Returns the document position that is (visually) nearest to the given
347 * document position <code>pos</code> in the given direction <code>d</code>.
349 * @param pos the document position
350 * @param b the bias for <code>pos</code>
351 * @param a the allocation for the view
352 * @param d the direction, must be either {@link SwingConstants#NORTH},
353 * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
354 * {@link SwingConstants#EAST}
355 * @param biasRet an array of {@link Position.Bias} that can hold at least
356 * one element, which is filled with the bias of the return position
359 * @return the document position that is (visually) nearest to the given
360 * document position <code>pos</code> in the given direction
363 * @throws BadLocationException if <code>pos</code> is not a valid offset in
366 public int getNextVisualPositionFrom(int pos
, Position
.Bias b
, Shape a
,
367 int d
, Position
.Bias
[] biasRet
)
368 throws BadLocationException
370 return view
.getNextVisualPositionFrom(pos
, b
, a
, d
, biasRet
);
374 * Returns the startOffset of this view, which is always the beginning
377 * @return the startOffset of this view
379 public int getStartOffset()
385 * Returns the endOffset of this view, which is always the end
388 * @return the endOffset of this view
390 public int getEndOffset()
392 return getDocument().getLength();
396 * Returns the document associated with this view.
398 * @return the document associated with this view
400 public Document
getDocument()
402 return textComponent
.getDocument();
407 * Receives notifications when properties of the text component change.
409 private class PropertyChangeHandler
implements PropertyChangeListener
412 * Notifies when a property of the text component changes.
414 * @param event the PropertyChangeEvent describing the change
416 public void propertyChange(PropertyChangeEvent event
)
418 if (event
.getPropertyName().equals("document"))
424 BasicTextUI
.this.propertyChange(event
);
429 * Listens for changes on the underlying model and forwards notifications
430 * to the View. This also updates the caret position of the text component.
432 * TODO: Maybe this should somehow be handled through EditorKits
434 class DocumentHandler
implements DocumentListener
437 * Notification about a document change event.
439 * @param ev the DocumentEvent describing the change
441 public void changedUpdate(DocumentEvent ev
)
443 // Updates are forwarded to the View even if 'getVisibleEditorRect'
444 // method returns null. This means the View classes have to be
445 // aware of that possibility.
446 rootView
.changedUpdate(ev
, getVisibleEditorRect(),
447 rootView
.getViewFactory());
451 * Notification about a document insert event.
453 * @param ev the DocumentEvent describing the insertion
455 public void insertUpdate(DocumentEvent ev
)
457 // Updates are forwarded to the View even if 'getVisibleEditorRect'
458 // method returns null. This means the View classes have to be
459 // aware of that possibility.
460 rootView
.insertUpdate(ev
, getVisibleEditorRect(),
461 rootView
.getViewFactory());
465 * Notification about a document removal event.
467 * @param ev the DocumentEvent describing the removal
469 public void removeUpdate(DocumentEvent ev
)
471 // Updates are forwarded to the View even if 'getVisibleEditorRect'
472 // method returns null. This means the View classes have to be
473 // aware of that possibility.
474 rootView
.removeUpdate(ev
, getVisibleEditorRect(),
475 rootView
.getViewFactory());
480 * The EditorKit used by this TextUI.
482 // FIXME: should probably be non-static.
483 static EditorKit kit
= new DefaultEditorKit();
488 RootView rootView
= new RootView();
491 * The text component that we handle.
493 JTextComponent textComponent
;
496 * Receives notification when the model changes.
498 private PropertyChangeHandler updateHandler
= new PropertyChangeHandler();
500 /** The DocumentEvent handler. */
501 DocumentHandler documentHandler
= new DocumentHandler();
504 * The standard background color. This is the color which is used to paint
505 * text in enabled text components.
510 * The inactive background color. This is the color which is used to paint
511 * text in disabled text components.
513 Color inactiveBackground
;
516 * Creates a new <code>BasicTextUI</code> instance.
520 // Nothing to do here.
524 * Creates a {@link Caret} that should be installed into the text component.
526 * @return a caret that should be installed into the text component
528 protected Caret
createCaret()
530 return new BasicCaret();
534 * Creates a {@link Highlighter} that should be installed into the text
537 * @return a <code>Highlighter</code> for the text component
539 protected Highlighter
createHighlighter()
541 return new BasicHighlighter();
545 * The text component that is managed by this UI.
547 * @return the text component that is managed by this UI
549 protected final JTextComponent
getComponent()
551 return textComponent
;
555 * Installs this UI on the text component.
557 * @param c the text component on which to install the UI
559 public void installUI(final JComponent c
)
563 textComponent
= (JTextComponent
) c
;
564 Document doc
= textComponent
.getDocument();
567 doc
= getEditorKit(textComponent
).createDefaultDocument();
568 textComponent
.setDocument(doc
);
572 installKeyboardActions();
574 // We need to trigger this so that the view hierarchy gets initialized.
580 * Installs UI defaults on the text components.
582 protected void installDefaults()
584 Caret caret
= textComponent
.getCaret();
587 caret
= createCaret();
588 textComponent
.setCaret(caret
);
591 Highlighter highlighter
= textComponent
.getHighlighter();
592 if (highlighter
== null)
593 textComponent
.setHighlighter(createHighlighter());
595 String prefix
= getPropertyPrefix();
596 LookAndFeel
.installColorsAndFont(textComponent
, prefix
+ ".background",
597 prefix
+ ".foreground", prefix
+ ".font");
598 LookAndFeel
.installBorder(textComponent
, prefix
+ ".border");
599 textComponent
.setMargin(UIManager
.getInsets(prefix
+ ".margin"));
601 caret
.setBlinkRate(UIManager
.getInt(prefix
+ ".caretBlinkRate"));
603 // Fetch the colors for enabled/disabled text components.
604 background
= UIManager
.getColor(prefix
+ ".background");
605 inactiveBackground
= UIManager
.getColor(prefix
+ ".inactiveBackground");
606 textComponent
.setDisabledTextColor
607 (UIManager
.getColor(prefix
+ ".inactiveForeground"));
608 textComponent
.setSelectedTextColor(UIManager
.getColor(prefix
+ ".selectionForeground"));
609 textComponent
.setSelectionColor(UIManager
.getColor(prefix
+ ".selectionBackground"));
613 * This FocusListener triggers repaints on focus shift.
615 private FocusListener focuslistener
= new FocusListener() {
616 public void focusGained(FocusEvent e
)
618 textComponent
.repaint();
620 public void focusLost(FocusEvent e
)
622 textComponent
.repaint();
624 // Integrates Swing text components with the system clipboard:
625 // The idea is that if one wants to copy text around X11-style
626 // (select text and middle-click in the target component) the focus
627 // will move to the new component which gives the old focus owner the
628 // possibility to paste its selection into the clipboard.
630 && textComponent
.getSelectionStart()
631 != textComponent
.getSelectionEnd())
633 SecurityManager sm
= System
.getSecurityManager();
637 sm
.checkSystemClipboardAccess();
639 Clipboard cb
= Toolkit
.getDefaultToolkit().getSystemSelection();
642 StringSelection selection
= new StringSelection(textComponent
.getSelectedText());
643 cb
.setContents(selection
, selection
);
646 catch (SecurityException se
)
648 // Not allowed to access the clipboard: Ignore and
651 catch (HeadlessException he
)
653 // There is no AWT: Ignore and do not access the
656 catch (IllegalStateException ise
)
658 // Clipboard is currently unavaible.
665 * Install all listeners on the text component.
667 protected void installListeners()
669 textComponent
.addFocusListener(focuslistener
);
670 textComponent
.addPropertyChangeListener(updateHandler
);
671 installDocumentListeners();
675 * Installs the document listeners on the textComponent's model.
677 private void installDocumentListeners()
679 Document doc
= textComponent
.getDocument();
681 doc
.addDocumentListener(documentHandler
);
685 * Returns the name of the keymap for this type of TextUI.
687 * This is implemented so that the classname of this TextUI
688 * without the package prefix is returned. This way subclasses
689 * don't have to override this method.
691 * @return the name of the keymap for this TextUI
693 protected String
getKeymapName()
695 String fullClassName
= getClass().getName();
696 int index
= fullClassName
.lastIndexOf('.');
697 String className
= fullClassName
.substring(index
+ 1);
702 * Creates the {@link Keymap} that is installed on the text component.
704 * @return the {@link Keymap} that is installed on the text component
706 protected Keymap
createKeymap()
708 String keymapName
= getKeymapName();
709 Keymap keymap
= JTextComponent
.getKeymap(keymapName
);
713 JTextComponent
.getKeymap(JTextComponent
.DEFAULT_KEYMAP
);
714 keymap
= JTextComponent
.addKeymap(keymapName
, parentMap
);
715 Object val
= UIManager
.get(getPropertyPrefix() + ".keyBindings");
716 if (val
!= null && val
instanceof JTextComponent
.KeyBinding
[])
718 JTextComponent
.KeyBinding
[] bindings
=
719 (JTextComponent
.KeyBinding
[]) val
;
720 JTextComponent
.loadKeymap(keymap
, bindings
,
721 getComponent().getActions());
728 * Installs the keyboard actions on the text components.
730 protected void installKeyboardActions()
732 // This is only there for backwards compatibility.
733 textComponent
.setKeymap(createKeymap());
735 // load any bindings for the newer InputMap / ActionMap interface
736 SwingUtilities
.replaceUIInputMap(textComponent
, JComponent
.WHEN_FOCUSED
,
737 getInputMap(JComponent
.WHEN_FOCUSED
));
738 SwingUtilities
.replaceUIActionMap(textComponent
, createActionMap());
740 ActionMap parentActionMap
= new ActionMapUIResource();
741 Action
[] actions
= textComponent
.getActions();
742 for (int j
= 0; j
< actions
.length
; j
++)
744 Action currAction
= actions
[j
];
745 parentActionMap
.put(currAction
.getValue(Action
.NAME
), currAction
);
748 SwingUtilities
.replaceUIActionMap(textComponent
, parentActionMap
);
752 * Creates an ActionMap to be installed on the text component.
754 * @return an ActionMap to be installed on the text component
756 ActionMap
createActionMap()
758 Action
[] actions
= textComponent
.getActions();
759 ActionMap am
= new ActionMapUIResource();
760 for (int i
= 0; i
< actions
.length
; ++i
)
762 String name
= (String
) actions
[i
].getValue(Action
.NAME
);
764 am
.put(name
, actions
[i
]);
770 * Gets the input map for the specified <code>condition</code>.
772 * @param condition the condition for the InputMap
774 * @return the InputMap for the specified condition
776 InputMap
getInputMap(int condition
)
778 String prefix
= getPropertyPrefix();
781 case JComponent
.WHEN_IN_FOCUSED_WINDOW
:
782 // FIXME: is this the right string? nobody seems to use it.
783 return (InputMap
) UIManager
.get(prefix
+ ".windowInputMap");
784 case JComponent
.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
:
785 return (InputMap
) UIManager
.get(prefix
+ ".ancestorInputMap");
787 case JComponent
.WHEN_FOCUSED
:
788 return (InputMap
) UIManager
.get(prefix
+ ".focusInputMap");
793 * Uninstalls this TextUI from the text component.
795 * @param component the text component to uninstall the UI from
797 public void uninstallUI(final JComponent component
)
799 super.uninstallUI(component
);
800 rootView
.setView(null);
803 uninstallListeners();
804 uninstallKeyboardActions();
806 textComponent
= null;
810 * Uninstalls all default properties that have previously been installed by
813 protected void uninstallDefaults()
819 * Uninstalls all listeners that have previously been installed by
822 protected void uninstallListeners()
824 textComponent
.removePropertyChangeListener(updateHandler
);
825 textComponent
.removeFocusListener(focuslistener
);
826 textComponent
.getDocument().removeDocumentListener(documentHandler
);
830 * Uninstalls all keyboard actions that have previously been installed by
833 protected void uninstallKeyboardActions()
834 throws NotImplementedException
836 // FIXME: Uninstall keyboard actions here.
840 * Returns the property prefix by which the text component's UIDefaults
843 * @return the property prefix by which the text component's UIDefaults
846 protected abstract String
getPropertyPrefix();
849 * Returns the preferred size of the text component.
851 * @param c not used here
853 * @return the preferred size of the text component
855 public Dimension
getPreferredSize(JComponent c
)
857 View v
= getRootView(textComponent
);
859 float w
= v
.getPreferredSpan(View
.X_AXIS
);
860 float h
= v
.getPreferredSpan(View
.Y_AXIS
);
862 Insets i
= c
.getInsets();
863 return new Dimension((int) w
+ i
.left
+ i
.right
,
864 (int) h
+ i
.top
+ i
.bottom
);
868 * Returns the maximum size for text components that use this UI.
870 * This returns (Integer.MAX_VALUE, Integer.MAX_VALUE).
872 * @param c not used here
874 * @return the maximum size for text components that use this UI
876 public Dimension
getMaximumSize(JComponent c
)
878 // Sun's implementation returns Integer.MAX_VALUE here, so do we.
879 return new Dimension(Integer
.MAX_VALUE
, Integer
.MAX_VALUE
);
883 * Returns the minimum size for text components. This returns the size
884 * of the component's insets.
886 * @return the minimum size for text components
888 public Dimension
getMinimumSize(JComponent c
)
890 Insets i
= c
.getInsets();
891 return new Dimension(i
.left
+ i
.right
, i
.top
+ i
.bottom
);
895 * Paints the text component. This acquires a read lock on the model and then
896 * calls {@link #paintSafely(Graphics)} in order to actually perform the
899 * @param g the <code>Graphics</code> context to paint to
900 * @param c not used here
902 public final void paint(Graphics g
, JComponent c
)
906 Document doc
= textComponent
.getDocument();
907 if (doc
instanceof AbstractDocument
)
909 AbstractDocument aDoc
= (AbstractDocument
) doc
;
917 Document doc
= textComponent
.getDocument();
918 if (doc
instanceof AbstractDocument
)
920 AbstractDocument aDoc
= (AbstractDocument
) doc
;
927 * This paints the text component while beeing sure that the model is not
928 * modified while painting.
930 * The following is performed in this order:
932 * <li>If the text component is opaque, the background is painted by
933 * calling {@link #paintBackground(Graphics)}.</li>
934 * <li>If there is a highlighter, the highlighter is painted.</li>
935 * <li>The view hierarchy is painted.</li>
936 * <li>The Caret is painter.</li>
939 * @param g the <code>Graphics</code> context to paint to
941 protected void paintSafely(Graphics g
)
943 Caret caret
= textComponent
.getCaret();
944 Highlighter highlighter
= textComponent
.getHighlighter();
946 if (textComponent
.isOpaque())
949 // Try painting with the highlighter without checking whether there
950 // is a selection because a highlighter can be used to do more than
951 // marking selected text.
952 if (highlighter
!= null)
954 // Handle restoring of the color here to prevent
955 // drawing problems when the Highlighter implementor
956 // forgets to restore it.
957 Color oldColor
= g
.getColor();
958 highlighter
.paint(g
);
959 g
.setColor(oldColor
);
963 rootView
.paint(g
, getVisibleEditorRect());
965 if (caret
!= null && textComponent
.hasFocus())
970 * Paints the background of the text component.
972 * @param g the <code>Graphics</code> context to paint to
974 protected void paintBackground(Graphics g
)
976 Color old
= g
.getColor();
977 g
.setColor(textComponent
.getBackground());
978 g
.fillRect(0, 0, textComponent
.getWidth(), textComponent
.getHeight());
983 * Overridden for better control over background painting. This now simply
984 * calls {@link #paint} and this delegates the background painting to
985 * {@link #paintBackground}.
987 * @param g the graphics to use
988 * @param c the component to be painted
990 public void update(Graphics g
, JComponent c
)
996 * Marks the specified range inside the text component's model as
997 * damaged and queues a repaint request.
999 * @param t the text component
1000 * @param p0 the start location inside the document model of the range that
1002 * @param p1 the end location inside the document model of the range that
1005 public void damageRange(JTextComponent t
, int p0
, int p1
)
1007 damageRange(t
, p0
, p1
, null, null);
1011 * Marks the specified range inside the text component's model as
1012 * damaged and queues a repaint request. This variant of this method
1013 * allows a {@link Position.Bias} object to be specified for the start
1014 * and end location of the range.
1016 * @param t the text component
1017 * @param p0 the start location inside the document model of the range that
1019 * @param p1 the end location inside the document model of the range that
1021 * @param firstBias the bias for the start location
1022 * @param secondBias the bias for the end location
1024 public void damageRange(JTextComponent t
, int p0
, int p1
,
1025 Position
.Bias firstBias
, Position
.Bias secondBias
)
1027 // Do nothing if the component cannot be properly displayed.
1028 if (t
.getWidth() == 0 || t
.getHeight() == 0)
1033 // Limit p0 and p1 to sane values to prevent unfriendly
1034 // BadLocationExceptions. This makes it possible for the highlighter
1035 // to send us illegal values which can happen when a large number
1036 // of selected characters are removed (eg. by pressing delete
1038 // The reference implementation does not throw an exception, too.
1039 p0
= Math
.min(p0
, t
.getDocument().getLength());
1040 p1
= Math
.min(p1
, t
.getDocument().getLength());
1042 Rectangle l1
= modelToView(t
, p0
, firstBias
);
1043 Rectangle l2
= modelToView(t
, p1
, secondBias
);
1046 SwingUtilities
.computeUnion(l2
.x
, l2
.y
, l2
.width
, l2
.height
, l1
);
1051 // The two rectangles lie on different lines and we need a
1052 // different algorithm to calculate the damaged area:
1053 // 1. The line of p0 is damaged from the position of p0
1054 // to the right border.
1055 // 2. All lines between the ones where p0 and p1 lie on
1056 // are completely damaged. Use the allocation area to find
1058 // 3. The final line is damaged from the left bound to the
1060 Insets insets
= t
.getInsets();
1062 // Damage first line until the end.
1063 l1
.width
= insets
.right
+ t
.getWidth() - l1
.x
;
1066 // Note: Utilities.getPositionBelow() may return the offset
1067 // that was put in. In that case there is no next line and
1068 // we should stop searching for one.
1070 int posBelow
= Utilities
.getPositionBelow(t
, p0
, l1
.x
);
1071 int p1RowStart
= Utilities
.getRowStart(t
, p1
);
1075 && Utilities
.getRowStart(t
, posBelow
) != p1RowStart
)
1077 // Take the rectangle of the offset we just found and grow it
1078 // to the maximum width. Retain y because this is our start
1080 Rectangle grow
= modelToView(t
, posBelow
);
1081 grow
.x
= insets
.left
;
1082 grow
.width
= t
.getWidth() + insets
.right
;
1084 // Find further lines which have to be damaged completely.
1085 int nextPosBelow
= posBelow
;
1086 while (nextPosBelow
!= -1
1087 && posBelow
!= nextPosBelow
1088 && Utilities
.getRowStart(t
, nextPosBelow
) != p1RowStart
)
1090 posBelow
= nextPosBelow
;
1091 nextPosBelow
= Utilities
.getPositionBelow(t
, posBelow
, l1
.x
);
1093 if (posBelow
== nextPosBelow
)
1096 // Now posBelow is an offset on the last line which has to be damaged
1097 // completely. (newPosBelow is on the same line as p1)
1099 // Retrieve the rectangle of posBelow and use its y and height
1100 // value to calculate the final height of the multiple line
1101 // spanning rectangle.
1102 Rectangle end
= modelToView(t
, posBelow
);
1103 grow
.height
= end
.y
+ end
.height
- grow
.y
;
1105 // Mark that area as damage.
1109 // Damage last line from its beginning to the position of p1.
1115 catch (BadLocationException ex
)
1117 AssertionError err
= new AssertionError("Unexpected bad location");
1124 * Returns the {@link EditorKit} used for the text component that is managed
1127 * @param t the text component
1129 * @return the {@link EditorKit} used for the text component that is managed
1132 public EditorKit
getEditorKit(JTextComponent t
)
1138 * Gets the next position inside the document model that is visible on
1139 * screen, starting from <code>pos</code>.
1141 * @param t the text component
1142 * @param pos the start positionn
1143 * @param b the bias for pos
1144 * @param direction the search direction
1145 * @param biasRet filled by the method to indicate the bias of the return
1148 * @return the next position inside the document model that is visible on
1151 public int getNextVisualPositionFrom(JTextComponent t
, int pos
,
1152 Position
.Bias b
, int direction
,
1153 Position
.Bias
[] biasRet
)
1154 throws BadLocationException
1156 // A comment in the spec of NavigationFilter.getNextVisualPositionFrom()
1157 // suggests that this method should be implemented by forwarding the call
1159 return rootView
.getNextVisualPositionFrom(pos
, b
,
1160 getVisibleEditorRect(),
1161 direction
, biasRet
);
1165 * Returns the root {@link View} of a text component.
1167 * @return the root {@link View} of a text component
1169 public View
getRootView(JTextComponent t
)
1175 * Maps a position in the document into the coordinate space of the View.
1176 * The output rectangle usually reflects the font height but has a width
1177 * of zero. A bias of {@link Position.Bias#Forward} is used in this method.
1179 * @param t the text component
1180 * @param pos the position of the character in the model
1182 * @return a rectangle that gives the location of the document position
1183 * inside the view coordinate space
1185 * @throws BadLocationException if <code>pos</code> is invalid
1186 * @throws IllegalArgumentException if b is not one of the above listed
1189 public Rectangle
modelToView(JTextComponent t
, int pos
)
1190 throws BadLocationException
1192 return modelToView(t
, pos
, Position
.Bias
.Forward
);
1196 * Maps a position in the document into the coordinate space of the View.
1197 * The output rectangle usually reflects the font height but has a width
1200 * @param t the text component
1201 * @param pos the position of the character in the model
1202 * @param bias either {@link Position.Bias#Forward} or
1203 * {@link Position.Bias#Backward} depending on the preferred
1204 * direction bias. If <code>null</code> this defaults to
1205 * <code>Position.Bias.Forward</code>
1207 * @return a rectangle that gives the location of the document position
1208 * inside the view coordinate space
1210 * @throws BadLocationException if <code>pos</code> is invalid
1211 * @throws IllegalArgumentException if b is not one of the above listed
1214 public Rectangle
modelToView(JTextComponent t
, int pos
, Position
.Bias bias
)
1215 throws BadLocationException
1217 Rectangle r
= getVisibleEditorRect();
1219 return (r
!= null) ? rootView
.modelToView(pos
, r
, bias
).getBounds()
1224 * Maps a point in the <code>View</code> coordinate space to a position
1225 * inside a document model.
1227 * @param t the text component
1228 * @param pt the point to be mapped
1230 * @return the position inside the document model that corresponds to
1233 public int viewToModel(JTextComponent t
, Point pt
)
1235 return viewToModel(t
, pt
, null);
1239 * Maps a point in the <code>View</code> coordinate space to a position
1240 * inside a document model.
1242 * @param t the text component
1243 * @param pt the point to be mapped
1244 * @param biasReturn filled in by the method to indicate the bias of the
1247 * @return the position inside the document model that corresponds to
1250 public int viewToModel(JTextComponent t
, Point pt
, Position
.Bias
[] biasReturn
)
1252 return rootView
.viewToModel(pt
.x
, pt
.y
, getVisibleEditorRect(), biasReturn
);
1256 * Creates a {@link View} for the specified {@link Element}.
1258 * @param elem the <code>Element</code> to create a <code>View</code> for
1262 public View
create(Element elem
)
1264 // Subclasses have to implement this to get this functionality.
1269 * Creates a {@link View} for the specified {@link Element}.
1271 * @param elem the <code>Element</code> to create a <code>View</code> for
1272 * @param p0 the start offset
1273 * @param p1 the end offset
1277 public View
create(Element elem
, int p0
, int p1
)
1279 // Subclasses have to implement this to get this functionality.
1284 * Returns the allocation to give the root view.
1286 * @return the allocation to give the root view
1288 * @specnote The allocation has nothing to do with visibility. According
1289 * to the specs the naming of this method is unfortunate and
1290 * has historical reasons
1292 protected Rectangle
getVisibleEditorRect()
1294 int width
= textComponent
.getWidth();
1295 int height
= textComponent
.getHeight();
1297 // Return null if the component has no valid size.
1298 if (width
<= 0 || height
<= 0)
1301 Insets insets
= textComponent
.getInsets();
1302 return new Rectangle(insets
.left
, insets
.top
,
1303 width
- insets
.left
- insets
.right
,
1304 height
- insets
.top
- insets
.bottom
);
1308 * Sets the root view for the text component.
1310 * @param view the <code>View</code> to be set as root view
1312 protected final void setView(View view
)
1314 rootView
.setView(view
);
1315 textComponent
.revalidate();
1316 textComponent
.repaint();
1320 * Indicates that the model of a text component has changed. This
1321 * triggers a rebuild of the view hierarchy.
1323 protected void modelChanged()
1325 if (textComponent
== null || rootView
== null)
1327 ViewFactory factory
= rootView
.getViewFactory();
1328 if (factory
== null)
1330 Document doc
= textComponent
.getDocument();
1333 installDocumentListeners();
1334 Element elem
= doc
.getDefaultRootElement();
1337 View view
= factory
.create(elem
);
1342 * Receives notification whenever one of the text component's bound
1343 * properties changes. This default implementation does nothing.
1344 * It is a hook that enables subclasses to react to property changes
1345 * on the text component.
1347 * @param ev the property change event
1349 protected void propertyChange(PropertyChangeEvent ev
)
1351 // The default implementation does nothing.