Merge from mainline.
[official-gcc.git] / libjava / classpath / javax / swing / plaf / basic / BasicTextUI.java
blob3b620f049893c5ca2194804481bb13c427d8a0d7
1 /* BasicTextUI.java --
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)
9 any later version.
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
19 02110-1301 USA.
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
24 combination.
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;
90 /**
91 * The abstract base class from which the UI classes for Swings text
92 * components are derived. This provides most of the functionality for
93 * the UI classes.
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
106 public BasicCaret()
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. */
135 private View view;
138 * Creates a new RootView.
140 public RootView()
142 super(null);
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();
157 if (factory == null)
158 factory = BasicTextUI.this;
159 return factory;
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)
182 if (view != null)
183 view.setParent(null);
185 if (v != null)
186 v.setParent(this);
188 view = v;
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)
200 return view;
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()
212 int count = 0;
213 if (view != null)
214 count = 1;
215 return count;
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)
239 if (view != null)
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)
253 if (view != null)
255 Rectangle b = s.getBounds();
256 view.setSize(b.width, b.height);
257 view.paint(g, s);
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
265 * of zero.
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
281 * valid values
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
308 * real root view.
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
321 * real root view.
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
334 * real root view.
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
357 * on method exit
359 * @return the document position that is (visually) nearest to the given
360 * document position <code>pos</code> in the given direction
361 * <code>d</code>
363 * @throws BadLocationException if <code>pos</code> is not a valid offset in
364 * the document model
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
375 * of the document.
377 * @return the startOffset of this view
379 public int getStartOffset()
381 return 0;
385 * Returns the endOffset of this view, which is always the end
386 * of the document.
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"))
420 // Document changed.
421 modelChanged();
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();
486 * The root view.
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.
507 Color background;
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.
518 public BasicTextUI()
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
535 * component.
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)
561 super.installUI(c);
563 textComponent = (JTextComponent) c;
564 Document doc = textComponent.getDocument();
565 if (doc == null)
567 doc = getEditorKit(textComponent).createDefaultDocument();
568 textComponent.setDocument(doc);
570 installDefaults();
571 installListeners();
572 installKeyboardActions();
574 // We need to trigger this so that the view hierarchy gets initialized.
575 modelChanged();
580 * Installs UI defaults on the text components.
582 protected void installDefaults()
584 Caret caret = textComponent.getCaret();
585 if (caret == null)
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.
629 if (!e.isTemporary()
630 && textComponent.getSelectionStart()
631 != textComponent.getSelectionEnd())
633 SecurityManager sm = System.getSecurityManager();
636 if (sm != null)
637 sm.checkSystemClipboardAccess();
639 Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
640 if (cb != null)
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
649 // do not access it.
651 catch (HeadlessException he)
653 // There is no AWT: Ignore and do not access the
654 // clipboard.
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();
680 if (doc != null)
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);
698 return className;
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);
710 if (keymap == null)
712 Keymap parentMap =
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());
724 return keymap;
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);
763 if (name != null)
764 am.put(name, actions[i]);
766 return am;
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();
779 switch (condition)
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");
786 default:
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);
802 uninstallDefaults();
803 uninstallListeners();
804 uninstallKeyboardActions();
806 textComponent = null;
810 * Uninstalls all default properties that have previously been installed by
811 * this UI.
813 protected void uninstallDefaults()
815 // Do nothing here.
819 * Uninstalls all listeners that have previously been installed by
820 * this UI.
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
831 * this UI.
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
841 * are looked up.
843 * @return the property prefix by which the text component's UIDefaults
844 * are looked up
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
897 * painting.
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;
910 aDoc.readLock();
913 paintSafely(g);
915 finally
917 Document doc = textComponent.getDocument();
918 if (doc instanceof AbstractDocument)
920 AbstractDocument aDoc = (AbstractDocument) doc;
921 aDoc.readUnlock();
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:
931 * <ol>
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>
937 * </ol>
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())
947 paintBackground(g);
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())
966 caret.paint(g);
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());
979 g.setColor(old);
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)
992 paint(g, 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
1001 * is damaged
1002 * @param p1 the end location inside the document model of the range that
1003 * is damaged
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
1018 * is damaged
1019 * @param p1 the end location inside the document model of the range that
1020 * is damaged
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)
1029 return;
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
1037 // or backspace).
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);
1044 if (l1.y == l2.y)
1046 SwingUtilities.computeUnion(l2.x, l2.y, l2.width, l2.height, l1);
1047 t.repaint(l1);
1049 else
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
1057 // out the bounds.
1058 // 3. The final line is damaged from the left bound to the
1059 // position of p1.
1060 Insets insets = t.getInsets();
1062 // Damage first line until the end.
1063 l1.width = insets.right + t.getWidth() - l1.x;
1064 t.repaint(l1);
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);
1073 if (posBelow != -1
1074 && posBelow != p0
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
1079 // height.
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)
1094 break;
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.
1106 t.repaint(grow);
1109 // Damage last line from its beginning to the position of p1.
1110 l2.width += l2.x;
1111 l2.x = insets.left;
1112 t.repaint(l2);
1115 catch (BadLocationException ex)
1117 AssertionError err = new AssertionError("Unexpected bad location");
1118 err.initCause(ex);
1119 throw err;
1124 * Returns the {@link EditorKit} used for the text component that is managed
1125 * by this UI.
1127 * @param t the text component
1129 * @return the {@link EditorKit} used for the text component that is managed
1130 * by this UI
1132 public EditorKit getEditorKit(JTextComponent t)
1134 return kit;
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
1146 * value
1148 * @return the next position inside the document model that is visible on
1149 * screen
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
1158 // the root view.
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)
1171 return rootView;
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
1187 * valid values
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
1198 * of zero.
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
1212 * valid values
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()
1220 : null;
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
1231 * <code>pt</code>
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
1245 * return value
1247 * @return the position inside the document model that corresponds to
1248 * <code>pt</code>
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
1260 * @see ViewFactory
1262 public View create(Element elem)
1264 // Subclasses have to implement this to get this functionality.
1265 return null;
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
1275 * @see ViewFactory
1277 public View create(Element elem, int p0, int p1)
1279 // Subclasses have to implement this to get this functionality.
1280 return null;
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)
1299 return null;
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)
1326 return;
1327 ViewFactory factory = rootView.getViewFactory();
1328 if (factory == null)
1329 return;
1330 Document doc = textComponent.getDocument();
1331 if (doc == null)
1332 return;
1333 installDocumentListeners();
1334 Element elem = doc.getDefaultRootElement();
1335 if (elem == null)
1336 return;
1337 View view = factory.create(elem);
1338 setView(view);
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.