home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / DefaultTextUI.java < prev    next >
Text File  |  1998-02-26  |  37KB  |  1,086 lines

  1. /*
  2.  * @(#)DefaultTextUI.java    1.79 98/02/02
  3.  *
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  *
  19.  */
  20. package com.sun.java.swing.text;
  21.  
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import java.beans.*;
  25. import java.io.*;
  26. import com.sun.java.swing.*;
  27. import com.sun.java.swing.plaf.*;
  28. import com.sun.java.swing.text.*;
  29. import com.sun.java.swing.event.*;
  30. import com.sun.java.swing.border.Border;
  31.  
  32. /**
  33.  * <p>
  34.  * Basis of a text components look-and-feel.  This provides the
  35.  * basic editor view and controller services that may be useful
  36.  * when creating a look-and-feel for an extension of
  37.  * JTextComponent.
  38.  * <p>
  39.  * To minimize the effort required to build a text UI, this class
  40.  * does a number of things on the programmers behalf.  To build a
  41.  * view of the model, one of the strategies can be employed.
  42.  * <ol>
  43.  * <li>
  44.  * One strategy is to simply redefine the 
  45.  * ViewFactory interface in the UI.  By default, this UI itself acts
  46.  * as the factory for View implementations.  This is useful
  47.  * for simple factories.  To do this reimplement the 
  48.  * <a href="#create">create</a> method.
  49.  * <li>
  50.  * A common strategy for creating more complex types of documents
  51.  * is to have the EditorKit implementation return a factory.  Since
  52.  * the EditorKit ties all of the pieces necessary to maintain a type
  53.  * of document, the factory is typically an important part of that
  54.  * and should be produced by the EditorKit implementation.
  55.  * <li>
  56.  * A less common way to create more complex types is to have
  57.  * the UI implementation create a.
  58.  * seperate object for the factory.  To do this, the 
  59.  * <a href="#createViewFactory">createViewFactory</a>
  60.  * method should be reimplemented to return some factory.
  61.  * </ol>
  62.  * <p>
  63.  * Most state is held in the associated JTextComponent as bound
  64.  * properties, and the UI installs default values for the 
  65.  * various properties.  This default will install something for
  66.  * all of the properties.  Typically, a LAF implementation will
  67.  * do more however.  At a minimum, a LAF would generally install
  68.  * key bindings.
  69.  * <p>
  70.  * Warning: serialized objects of this class will not be compatible with
  71.  * future swing releases.  The current serialization support is appropriate 
  72.  * for short term storage or RMI between Swing1.0 applications.  It will
  73.  * not be possible to load serialized Swing1.0 objects with future releases
  74.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  75.  * baseline for the serialized form of Swing objects.
  76.  *
  77.  * @author  Timothy Prinzing
  78.  * @version 1.79 02/02/98
  79.  */
  80. public abstract class DefaultTextUI extends TextUI implements ViewFactory, Serializable {
  81.  
  82.     /**
  83.      * Creates a new UI.
  84.      */
  85.     public DefaultTextUI() {
  86.         painted = false;
  87.     }
  88.  
  89.     /**
  90.      * Creates the object to use for a caret.  By default an
  91.      * instance of JCaret is created.  This method
  92.      * can be redefined to provide something else that implements
  93.      * the InputPosition interface or a subclass of JCaret.
  94.      *
  95.      * @return the caret object
  96.      */
  97.     protected Caret createCaret() {
  98.         return new DefaultCaret();
  99.     }
  100.  
  101.     /**
  102.      * Creates the object to use for adding highlights.  By default
  103.      * an instance of JHighlighter is created.  This method
  104.      * can be redefined to provide something else that implements
  105.      * the Highlighter interface or a subclass of JHighlighter.
  106.      *
  107.      * @return the highlighter
  108.      */
  109.     protected Highlighter createHighlighter() {
  110.         return new DefaultHighlighter();
  111.     }
  112.  
  113.     /**
  114.      * Creates the keymap to use for the text component, and installs
  115.      * any necessary bindings into it.
  116.      *
  117.      * @return the keymap
  118.      */
  119.     protected abstract Keymap createKeymap();
  120.  
  121.     /**
  122.      * This method gets called when a bound property is changed
  123.      * on the associated JTextComponent.  This is a hook
  124.      * which UI implementations may change to reflect how the
  125.      * UI displays bound properties of JTextComponent subclasses.
  126.      * This is implemented to do nothing (ie. the response to
  127.      * properties in JTextComponent itself are handled prior
  128.      * to calling this method).
  129.      */
  130.     protected void propertyChange(PropertyChangeEvent evt) {
  131.     }
  132.  
  133.     /**
  134.      * Gets the name used as a key to lookup properties through the
  135.      * UIManager.  This is used as a prefix to all the standard
  136.      * text properties.
  137.      *
  138.      * @return the name
  139.      */
  140.     protected abstract String getPropertyPrefix();
  141.  
  142.     /**
  143.      * Initializes component properties, e.g. font, foreground, and 
  144.      * background.  The font, foreground, and background
  145.      * properties are only set if their current value is either null
  146.      * or a UIResource, other properties are set if the current
  147.      * value is null.
  148.      * 
  149.      * @see #uninstallDefaults
  150.      * @see #installUI
  151.      */
  152.     protected void installDefaults(JComponent c) 
  153.     {
  154.         String prefix = getPropertyPrefix();
  155.         Font f = editor.getFont();
  156.         if ((f == null) || (f instanceof UIResource)) {
  157.             editor.setFont(UIManager.getFont(prefix + ".font"));
  158.         }
  159.  
  160.         Color bg = editor.getBackground();
  161.         if ((bg == null) || (bg instanceof UIResource)) {
  162.             editor.setBackground(UIManager.getColor(prefix + ".background"));
  163.         }
  164.         
  165.         Color fg = editor.getForeground();
  166.         if ((fg == null) || (fg instanceof UIResource)) {
  167.             editor.setForeground(UIManager.getColor(prefix + ".foreground"));
  168.         }
  169.  
  170.         Color color = editor.getCaretColor();
  171.         if ((color == null) || (color instanceof UIResource)) {
  172.             editor.setCaretColor(UIManager.getColor(prefix + ".caretForeground"));
  173.         }
  174.  
  175.         Color s = editor.getSelectionColor();
  176.         if ((s == null) || (s instanceof UIResource)) {
  177.             editor.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground"));
  178.         }
  179.  
  180.         Color sfg = editor.getSelectedTextColor();
  181.         if ((sfg == null) || (sfg instanceof UIResource)) {
  182.             editor.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground"));
  183.         }
  184.  
  185.         Color dfg = editor.getDisabledTextColor();
  186.         if ((dfg == null) || (dfg instanceof UIResource)) {
  187.             editor.setDisabledTextColor(UIManager.getColor(prefix + ".inactiveForeground"));
  188.         }
  189.  
  190.         Border b = editor.getBorder();
  191.         if ((b == null) || (b instanceof UIResource)) {
  192.             editor.setBorder(UIManager.getBorder(prefix + ".border"));
  193.         }
  194.  
  195.         // PENDING(prinz) these should be marked with UIResource if 
  196.         // created by the LAF.
  197.         Caret caret = createCaret();
  198.         editor.setCaret(caret);
  199.         Object o = UIManager.get(prefix + ".caretBlinkRate");
  200.         if ((o != null) && (o instanceof Integer)) {
  201.             Integer rate = (Integer) o;
  202.             caret.setBlinkRate(rate.intValue());
  203.         }
  204.     }
  205.  
  206.     /**
  207.      * Sets the component properties that haven't been explicitly overriden to 
  208.      * null.  A property is considered overridden if its current value
  209.      * is not a UIResource.
  210.      * 
  211.      * @see #installDefaults
  212.      * @see #uninstallUI
  213.      */
  214.     protected void uninstallDefaults(JComponent c) 
  215.     {
  216.         if (editor.getCaretColor() instanceof UIResource) {
  217.             editor.setCaretColor(null);
  218.         }
  219.                                                                                          
  220.         if (editor.getSelectionColor() instanceof UIResource) {
  221.             editor.setSelectionColor(null);
  222.         }
  223.  
  224.         if (editor.getDisabledTextColor() instanceof UIResource) {
  225.             editor.setDisabledTextColor(null);
  226.         }
  227.  
  228.         if (editor.getSelectedTextColor() instanceof UIResource) {
  229.             editor.setSelectedTextColor(null);
  230.         }
  231.  
  232.         if (editor.getBorder() instanceof UIResource) {
  233.             editor.setBorder(null);
  234.         }
  235.         // PENDING(prinz) these should be marked as a UIResource
  236.         // if installed by the UI.
  237.         editor.setCaret(null);
  238.     }
  239.  
  240.     protected void installListeners(JComponent c) {
  241.     }
  242.  
  243.     protected void uninstallListeners(JComponent c) {
  244.     }
  245.  
  246.     /**
  247.      * Paints a background for the view.  This will only be
  248.      * called if isOpaque() on the associated component is
  249.      * true.  The default is to paint the background color 
  250.      * of the component.
  251.      *
  252.      * @param g the graphics context
  253.      */
  254.     protected void paintBackground(Graphics g) {
  255.         g.setColor(editor.getBackground());
  256.         Dimension d = editor.getSize();
  257.         g.fillRect(0, 0, d.width, d.height);
  258.     }
  259.  
  260.     /**
  261.      * Fetches the text component associated with this
  262.      * UI implementation.  This will be null until
  263.      * the ui has been installed.
  264.      *
  265.      * @return the component
  266.      */
  267.     protected final JTextComponent getComponent() {
  268.         return editor;
  269.     }
  270.  
  271.     /**
  272.      * Flags model changes.
  273.      * This is called whenever the model has changed.
  274.      * It is implemented to rebuild the view hierarchy
  275.      * to represent the default root element of the
  276.      * associated model.
  277.      */
  278.     protected void modelChanged() {
  279.         // create a view hierarchy
  280.         ViewFactory f = rootView.getViewFactory();
  281.         Document doc = editor.getDocument();
  282.         Element elem = doc.getDefaultRootElement();
  283.         setView(f.create(elem));
  284.     }
  285.  
  286.     /**
  287.      * Sets the current root of the view hierarchy.
  288.      *
  289.      * @param v the root view
  290.      */
  291.     protected final void setView(View v) {
  292.         rootView.setView(v);
  293.         painted = false;
  294.         editor.invalidate();
  295.     }
  296.  
  297.     /**
  298.      * Paints the interface safely with a guarantee that
  299.      * the model won't change from the view of this thread.  
  300.      * This does the following things, rendering from 
  301.      * back to front.
  302.      * <ol>
  303.      * <li>
  304.      * If the component is marked as opaque, the background
  305.      * is painted in the current background color of the
  306.      * component.
  307.      * <li>
  308.      * The highlights (if any) are painted.
  309.      * <li>
  310.      * The view hierarchy is painted.
  311.      * <li>
  312.      * The caret is painted.
  313.      * </ol>
  314.      *
  315.      * @param g the graphics context
  316.      * @param c the editor component
  317.      */
  318.     protected void paintSafely(Graphics g) {
  319.     painted = true;
  320.     Highlighter highlighter = editor.getHighlighter();
  321.     Caret caret = editor.getCaret();
  322.     
  323.     // paint the background
  324.     if (editor.isOpaque()) {
  325.         paintBackground(g);
  326.     }
  327.     
  328.     // paint the highlights
  329.     if (highlighter != null) {
  330.         highlighter.paint(g);
  331.     }
  332.  
  333.     // paint the view hierarchy
  334.     Rectangle alloc = getVisibleEditorRect();
  335.     rootView.paint(g, alloc);
  336.         
  337.     // paint the caret
  338.     if (caret != null) {
  339.         caret.paint(g);
  340.     }
  341.     }
  342.  
  343.     // --- ComponentUI methods --------------------------------------------
  344.  
  345.     /**
  346.      * Installs the UI for a component.  This does the following
  347.      * things.
  348.      * <ol>
  349.      * <li>
  350.      * Set the associated component to opaque (can be changed
  351.      * easily by a subclass or on JTextComponent directly),
  352.      * which is the most common case.  This will cause the
  353.      * component's background color to be painted.
  354.      * <li>
  355.      * Install the default caret and highlighter into the 
  356.      * associated component.
  357.      * <li>
  358.      * Attach to the editor and model.  If there is no 
  359.      * model, a default one is created.
  360.      * <li>
  361.      * create the view factory and the view hierarchy used
  362.      * to represent the model.
  363.      * </ol>
  364.      *
  365.      * @param c the component
  366.      * @see ComponentUI#installUI
  367.      */
  368.     public void installUI(JComponent c) {
  369.         if (c instanceof JTextComponent) {
  370.             editor = (JTextComponent) c;
  371.  
  372.             // install defaults
  373.             installDefaults(c);
  374.  
  375.             // common case is background painted... this can
  376.             // easily be changed by subclasses or from outside
  377.             // of the component.
  378.             editor.setOpaque(true);
  379.             editor.setAutoscrolls(true);
  380.  
  381.             editor.setHighlighter(createHighlighter());
  382.  
  383.             // attach to the model and editor
  384.             editor.addPropertyChangeListener(updateHandler);
  385.             Document doc = editor.getDocument();
  386.             if (doc == null) {
  387.                 // no model, create a default one.  This will
  388.                 // fire a notification to the updateHandler 
  389.                 // which takes care of the rest. 
  390.                 editor.setDocument(getEditorKit().createDefaultDocument());
  391.             } else {
  392.                 doc.addDocumentListener(updateHandler);
  393.                 modelChanged();
  394.             }
  395.  
  396.             // install keymap
  397.             installListeners(c);
  398.             editor.setKeymap(createKeymap());       
  399.  
  400.         } else {
  401.             throw new Error("TextUI needs JTextComponent");
  402.         }
  403.     }
  404.  
  405.     /**
  406.      * Deinstalls the UI for a component.
  407.      *
  408.      * @param c the component
  409.      * @see ComponentUI#uninstallUI
  410.      */
  411.     public void uninstallUI(JComponent c) {
  412.         // detach from the model
  413.         editor.removePropertyChangeListener(updateHandler);
  414.         editor.getDocument().removeDocumentListener(updateHandler);
  415.  
  416.         // view part
  417.         painted = false;
  418.         uninstallDefaults(c);
  419.         editor.setHighlighter(null);
  420.         rootView.setView(null);
  421.         c.removeAll();
  422.  
  423.         // controller part
  424.         editor.setKeymap(null);
  425.         uninstallListeners(c);
  426.     }
  427.  
  428.     /**
  429.      * Paints the interface.  This is routed to
  430.      * <code>paintSafely</code> under the guarantee that
  431.      * the model won't change from the view of this thread
  432.      * while it's rendering.  This enables the model to
  433.      * potentially be updated asynchronously.
  434.      *
  435.      * @param g the graphics context
  436.      * @param c the editor component
  437.      */
  438.     public final void paint(Graphics g, JComponent c) {
  439.     if ((rootView.getViewCount() > 0) && (rootView.getView(0) != null)) {
  440.         Runnable painter = new SafePainter(g);
  441.         Document doc = editor.getDocument();
  442.         doc.render(painter);
  443.     }
  444.     }
  445.  
  446.     /**
  447.      * Gets the preferred size for the editor component.
  448.      *
  449.      * @param c the editor component
  450.      * @return the size
  451.      */
  452.     public Dimension getPreferredSize(JComponent c) {
  453.         Insets i = c.getInsets();
  454.         return new Dimension(
  455.             (int) Math.min((long) rootView.getPreferredSpan(View.X_AXIS) +
  456.                            (long) i.left + (long) i.right, Integer.MAX_VALUE),
  457.             (int) Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) +
  458.                            (long) i.top + (long) i.bottom, Integer.MAX_VALUE)
  459.         );
  460.     }
  461.  
  462.     /**
  463.      * Gets the minimum size for the editor component.
  464.      *
  465.      * @param c the editor component
  466.      * @return the size
  467.      */
  468.     public Dimension getMinimumSize(JComponent c) {
  469.         Insets i = c.getInsets();
  470.         long width = (rootView.getResizeWeight(View.X_AXIS) > 0) ? 1 : 
  471.             Math.min((long) rootView.getPreferredSpan(View.X_AXIS) + (long) i.left + (long) i.right, Integer.MAX_VALUE);
  472.         long height = (rootView.getResizeWeight(View.Y_AXIS) > 0) ? 1 : 
  473.             Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) + (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  474.         return new Dimension((int) width, (int) height);
  475.     }
  476.  
  477.     /**
  478.      * Gets the maximum size for the editor component.
  479.      *
  480.      * @param c the editor component
  481.      * @return the size
  482.      */
  483.     public Dimension getMaximumSize(JComponent c) {
  484.         Insets i = c.getInsets();
  485.         long width = (rootView.getResizeWeight(View.X_AXIS) > 0) ? Integer.MAX_VALUE : 
  486.             Math.min((long) rootView.getPreferredSpan(View.X_AXIS) + (long) i.left + (long) i.right, Integer.MAX_VALUE);
  487.         long height = (rootView.getResizeWeight(View.Y_AXIS) > 0) ? Integer.MAX_VALUE : 
  488.             Math.min((long) rootView.getPreferredSpan(View.Y_AXIS) + (long) i.top + (long) i.bottom, Integer.MAX_VALUE);
  489.         return new Dimension((int) width, (int) height);
  490.     }
  491.  
  492.     // ---- TextUI methods -------------------------------------------
  493.  
  494.  
  495.     /**
  496.      * Gets the portion of the editor visibile on the screen.
  497.      *
  498.      * @return the bounding box for the visible portion
  499.      */
  500.     protected Rectangle getVisibleEditorRect() {
  501.         Rectangle alloc = new Rectangle(editor.getSize());
  502.         Insets insets = editor.getInsets();
  503.         alloc.x += insets.left;
  504.         alloc.y += insets.top;
  505.         alloc.width -= insets.left + insets.right;
  506.         alloc.height -= insets.top + insets.bottom;
  507.         return alloc;
  508.     }
  509.  
  510.     /**
  511.      * Converts the given location in the model to a place in
  512.      * the view coordinate system.
  513.      *
  514.      * @param pos the local location in the model to translate
  515.      * @return the coordinates as a rectangle
  516.      * @exception BadLocationException  if the given position does not
  517.      * represent a valid location in the associated document
  518.      * @see TextUI#modelToView
  519.      */
  520.     public Rectangle modelToView(int pos) throws BadLocationException {
  521.         if (painted) {
  522.             Rectangle alloc = getVisibleEditorRect();
  523.             Shape s = rootView.modelToView(pos, alloc);
  524.             return s.getBounds();
  525.         }
  526.         return null;
  527.     }
  528.  
  529.     /**
  530.      * Converts the given place in the view coordinate system
  531.      * to the nearest representative location in the model.
  532.      *
  533.      * @param pt the location in the view to translate.  This
  534.      *  should be in the same coordinate system as the mouse events.
  535.      * @return the offset from the start of the document
  536.      * @see TextUI#viewToModel
  537.      */
  538.     public int viewToModel(Point pt) {
  539.         if (painted) {
  540.             Rectangle alloc = getVisibleEditorRect();
  541.             return rootView.viewToModel(pt.x, pt.y, alloc);
  542.         }
  543.         return -1;
  544.     }
  545.  
  546.     /**
  547.      * Causes the portion of the view responsible for the
  548.      * given part of the model to be repainted.
  549.      *
  550.      * @param p0 the beginning of the range
  551.      * @param p1 the end of the range
  552.      * @see TextUI#damageRange
  553.      */
  554.     public void damageRange(int p0, int p1) {
  555.         if (painted) {
  556.             Rectangle alloc = getVisibleEditorRect();
  557.             try {
  558.                 Shape s0 = rootView.modelToView(p0,alloc);
  559.                 Shape s1 = rootView.modelToView(p1,alloc);
  560.                 if (s0 != null && s1 != null) {
  561.                     Rectangle r0 = s0.getBounds();
  562.                     Rectangle r1 = s1.getBounds();
  563.                     if (r0.y == r1.y) {
  564.                         editor.repaint(r0.x, r0.y, r1.x - r0.x + 1, r0.height);
  565.                     } else {
  566.                         editor.repaint(alloc.x, r0.y, alloc.width, r1.y - r0.y + r1.height);
  567.                     }
  568.                 }
  569.             } catch (BadLocationException e) {}
  570.         }
  571.     }
  572.  
  573.     /**
  574.      * Fetches the EditorKit for the UI.
  575.      *
  576.      * @return the editor capabilities
  577.      * @see TextUI#getEditorKit
  578.      */
  579.     public EditorKit getEditorKit() {
  580.         return defaultKit;
  581.     }
  582.  
  583.     /**
  584.      * Fetches a View with the allocation of the associated 
  585.      * text component (ie the root of the hierarchy) that 
  586.      * can be traversed to determine how the model is being
  587.      * represented spatially.
  588.      *
  589.      * @return the view
  590.      * @see TextUI#getRootView
  591.      */
  592.     public View getRootView() {
  593.         return rootView;
  594.     }
  595.  
  596.  
  597.     /**
  598.      * Fetches the default margin space for the text ui.
  599.      *
  600.      * @return the margins
  601.      */
  602.     public Insets getDefaultMargin() {
  603.         return new Insets(0,0,0,0);
  604.     }
  605.  
  606.  
  607.     // --- ViewFactory methods ------------------------------
  608.  
  609.     /**
  610.      * Creates a view for an element.
  611.      * If a subclass wishes to directly implement the factory
  612.      * producing the view(s), it should reimplement this 
  613.      * method.  By default it simply returns null indicating
  614.      * it is unable to represent the element.
  615.      *
  616.      * @param elem the element
  617.      * @return the view
  618.      */
  619.     public View create(Element elem) {
  620.         return null;
  621.     }
  622.  
  623.     /**
  624.      * Creates a view for an element.
  625.      * If a subclass wishes to directly implement the factory
  626.      * producing the view(s), it should reimplement this 
  627.      * method.  By default it simply returns null indicating
  628.      * it is unable to represent the part of the element.
  629.      *
  630.      * @param elem the element
  631.      * @param p0 the starting offset
  632.      * @param p1 the ending offset
  633.      * @return the view
  634.      */
  635.     public View create(Element elem, int p0, int p1) {
  636.         return null;
  637.     }
  638.  
  639.     // --- serialization ---------------------------------------------
  640.  
  641.     private void readObject(ObjectInputStream s)
  642.       throws ClassNotFoundException, IOException 
  643.     {
  644.         s.defaultReadObject();
  645.         rootView = new RootView();
  646.         updateHandler = new UpdateHandler();
  647.     }
  648.  
  649.     // ----- member variables ---------------------------------------
  650.  
  651.     private static final EditorKit defaultKit = new DefaultEditorKit();
  652.     transient JTextComponent editor;
  653.     transient boolean painted;
  654.     transient RootView rootView = new RootView();
  655.     transient UpdateHandler updateHandler = new UpdateHandler();
  656.  
  657.     /**
  658.      * Root view that acts as a gateway between the component
  659.      * and the View hierarchy.
  660.      */
  661.     class RootView extends View {
  662.  
  663.         RootView() {
  664.             super(null);
  665.         }
  666.  
  667.         void setView(View v) {
  668.             if (view != null) {
  669.                 // get rid of back reference so that the old
  670.                 // hierarchy can be garbage collected.
  671.                 view.setParent(null);
  672.             }
  673.             view = v;
  674.             if (view != null) {
  675.                 view.setParent(this);
  676.             }
  677.         }
  678.  
  679.         /**
  680.          * Determines the preferred span for this view along an axis.
  681.          *
  682.          * @param axis may be either X_AXIS or Y_AXIS
  683.          * @return the span the view would like to be rendered into.
  684.          *         Typically the view is told to render into the span
  685.          *         that is returned, although there is no guarantee.
  686.          *         The parent may choose to resize or break the view.
  687.          */
  688.         public float getPreferredSpan(int axis) {
  689.             if (view != null) {
  690.                 return view.getPreferredSpan(axis);
  691.             }
  692.             return 10;
  693.         }
  694.  
  695.         /**
  696.          * Specifies that a preference has changed.
  697.          * Child views can call this on the parent to indicate that
  698.          * the preference has changed.  The root view routes this to
  699.          * invalidate on the hosting component.
  700.          * <p>
  701.          * This can be called on a different thread from the
  702.          * event dispatching thread and is basically unsafe to
  703.          * propagate into the component.  To make this safe,
  704.          * the operation is transferred over to the event dispatching 
  705.          * thread for completion.  It is a design goal that all view
  706.          * methods be safe to call without concern for concurrency,
  707.          * and this behavior helps make that true.
  708.          *
  709.          * @param child the child view
  710.          * @param width true if the width preference has changed
  711.          * @param height true if the height preference has changed
  712.          */ 
  713.         public void preferenceChanged(View child, boolean width, boolean height) {
  714.             editor.revalidate();
  715.         }
  716.  
  717.         /**
  718.          * Determines the desired alignment for this view along an axis.
  719.          *
  720.          * @param axis may be either X_AXIS or Y_AXIS
  721.          * @return the desired alignment, where 0.0 indicates the origin
  722.          *     and 1.0 the full span away from the origin
  723.          */
  724.         public float getAlignment(int axis) {
  725.             if (view != null) {
  726.                 return view.getAlignment(axis);
  727.             }
  728.             return 0;
  729.         }
  730.  
  731.         /**
  732.          * Renders the view.
  733.          *
  734.          * @param g the graphics context
  735.          * @param allocation the region to render into
  736.          */
  737.         public void paint(Graphics g, Shape allocation) {
  738.             if (view != null) {
  739.                 Rectangle alloc = allocation.getBounds();
  740.                 view.setSize(alloc.width, alloc.height);
  741.                 view.paint(g, allocation);
  742.             }
  743.         }
  744.         
  745.         /**
  746.          * Sets the view parent.
  747.          *
  748.          * @param parent the parent view
  749.          */
  750.         public void setParent(View parent) {
  751.             throw new StateInvariantError("Can't set parent on root view");
  752.         }
  753.  
  754.         /** 
  755.          * Returns the number of views in this view.  Since
  756.          * this view simply wraps the root of the view hierarchy
  757.          * it has exactly one child.
  758.          *
  759.          * @return the number of views
  760.          * @see #getView
  761.          */
  762.         public int getViewCount() {
  763.             return 1;
  764.         }
  765.  
  766.         /** 
  767.          * Gets the n-th view in this container.
  768.          *
  769.          * @param n the number of the view to get
  770.          * @return the view
  771.          */
  772.         public View getView(int n) {
  773.             return view;
  774.         }
  775.  
  776.         /**
  777.          * Fetches the allocation for the given child view. 
  778.          * This enables finding out where various views
  779.          * are located, without assuming the views store
  780.          * their location.  This returns the given allocation
  781.          * since this view simply acts as a gateway between
  782.          * the view hierarchy and the associated component.
  783.          *
  784.          * @param index the index of the child
  785.          * @param a  the allocation to this view.
  786.          * @return the allocation to the child
  787.          */
  788.         public Shape getChildAllocation(int index, Shape a) {
  789.             return a;
  790.         }
  791.  
  792.         /**
  793.          * Provides a mapping from the document model coordinate space
  794.          * to the coordinate space of the view mapped to it.
  795.          *
  796.          * @param pos the position to convert
  797.          * @param a the allocated region to render into
  798.          * @return the bounding box of the given position
  799.          */
  800.         public Shape modelToView(int pos, Shape a) throws BadLocationException {
  801.             if (view != null) {
  802.                 return view.modelToView(pos, a);
  803.             }
  804.             return null;
  805.         }
  806.  
  807.         /**
  808.          * Provides a mapping from the view coordinate space to the logical
  809.          * coordinate space of the model.
  810.          *
  811.          * @param x x coordinate of the view location to convert
  812.          * @param y y coordinate of the view location to convert
  813.          * @param a the allocated region to render into
  814.          * @return the location within the model that best represents the
  815.          *    given point in the view
  816.          */
  817.         public int viewToModel(float x, float y, Shape a) {
  818.             if (view != null) {
  819.                 return view.viewToModel(x, y, a);
  820.             }
  821.             return -1;
  822.         }
  823.  
  824.         /**
  825.          * Gives notification that something was inserted into the document
  826.          * in a location that this view is responsible for.
  827.          *
  828.          * @param e the change information from the associated document
  829.          * @param a the current allocation of the view
  830.          * @param f the factory to use to rebuild if the view has children
  831.          */
  832.         public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  833.             if (view != null) {
  834.                 view.insertUpdate(e, a, f);
  835.             }
  836.         }
  837.         
  838.         /**
  839.          * Gives notification that something was removed from the document
  840.          * in a location that this view is responsible for.
  841.          *
  842.          * @param e the change information from the associated document
  843.          * @param a the current allocation of the view
  844.          * @param f the factory to use to rebuild if the view has children
  845.          */
  846.         public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  847.             if (view != null) {
  848.                 view.removeUpdate(e, a, f);
  849.             }
  850.         }
  851.  
  852.         /**
  853.          * Gives notification from the document that attributes were changed
  854.          * in a location that this view is responsible for.
  855.          *
  856.          * @param e the change information from the associated document
  857.          * @param a the current allocation of the view
  858.          * @param f the factory to use to rebuild if the view has children
  859.          */
  860.         public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
  861.             if (view != null) {
  862.                 view.changedUpdate(e, a, f);
  863.             }
  864.         }
  865.  
  866.         /**
  867.          * Returns the document model underlying the view.
  868.          *
  869.          * @return the model
  870.          */
  871.         public Document getDocument() {
  872.             return editor.getDocument();
  873.         }
  874.         
  875.         /**
  876.          * Returns the starting offset into the model for this view.
  877.          *
  878.          * @return the starting offset
  879.          */
  880.         public int getStartOffset() {
  881.             if (view != null) {
  882.                 return view.getStartOffset();
  883.             }
  884.             return getElement().getStartOffset();
  885.         }
  886.  
  887.         /**
  888.          * Returns the ending offset into the model for this view.
  889.          *
  890.          * @return the ending offset
  891.          */
  892.         public int getEndOffset() {
  893.             if (view != null) {
  894.                 return view.getEndOffset();
  895.             }
  896.             return getElement().getEndOffset();
  897.         }
  898.  
  899.         /**
  900.          * Gets the element that this view is mapped to.
  901.          *
  902.          * @return the view
  903.          */
  904.         public Element getElement() {
  905.             if (view != null) {
  906.                 return view.getElement();
  907.             }
  908.             return editor.getDocument().getDefaultRootElement();
  909.         }
  910.  
  911.         /**
  912.          * Breaks this view on the given axis at the given length.
  913.          *
  914.          * @param axis may be either X_AXIS or Y_AXIS
  915.          * @param len specifies where a break is desired in the span
  916.          * @param the current allocation of the view
  917.          * @return the fragment of the view that represents the given span
  918.          *   if the view can be broken, otherwise null
  919.          */
  920.         public View breakView(int axis, float len, Shape a) {
  921.             throw new StateInvariantError("Can't break root view");
  922.         }
  923.  
  924.         /**
  925.          * Determines the resizability of the view along the
  926.          * given axis.  A value of 0 or less is not resizable.
  927.          *
  928.          * @param axis may be either X_AXIS or Y_AXIS
  929.          * @return the weight
  930.          */
  931.         public int getResizeWeight(int axis) {
  932.             if (view != null) {
  933.                 return view.getResizeWeight(axis);
  934.             }
  935.             return 0;
  936.         }
  937.  
  938.         /**
  939.          * Sets the view size.
  940.          *
  941.          * @param width the width
  942.          * @param height the height
  943.          */
  944.         public void setSize(float width, float height) {
  945.             if (view != null) {
  946.                 view.setSize(width, height);
  947.             }
  948.         }
  949.  
  950.         /**
  951.          * Fetches the container hosting the view.  This is useful for
  952.          * things like scheduling a repaint, finding out the host 
  953.          * components font, etc.  The default implementation
  954.          * of this is to forward the query to the parent view.
  955.          *
  956.          * @return the container
  957.          */
  958.         public Container getContainer() {
  959.             return editor;
  960.         }
  961.         
  962.         /**
  963.          * Fetches the factory to be used for building the
  964.          * various view fragments that make up the view that
  965.          * represents the model.  This is what determines
  966.          * how the model will be represented.  This is implemented
  967.          * to fetch the factory provided by the associated
  968.          * EditorKit unless that is null, in which case this
  969.          * simply returns the DefaultTextUI itself which allows
  970.          * subclasses to implement a simple factory directly without
  971.          * creating extra objects.  
  972.          *
  973.          * @return the factory
  974.          */
  975.         public ViewFactory getViewFactory() {
  976.             EditorKit kit = getEditorKit();
  977.             ViewFactory f = kit.getViewFactory();
  978.             if (f != null) {
  979.                 return f;
  980.             }
  981.             return DefaultTextUI.this;
  982.         }
  983.  
  984.         private View view;
  985.  
  986.     }
  987.  
  988.     class SafePainter implements Runnable {
  989.         
  990.         SafePainter(Graphics g) {
  991.             this.g = g;
  992.         }
  993.  
  994.         /**
  995.          * Render the UI.  This will be called by the 
  996.          * associated model.
  997.          */
  998.         public void run() {
  999.             paintSafely(g);
  1000.         }
  1001.  
  1002.         Graphics g;
  1003.     }
  1004.  
  1005.     /**
  1006.      * Handles updates from various places.  If the model is changed,
  1007.      * this class unregisters as a listener to the old model and 
  1008.      * registers with the new model.  If the document model changes,
  1009.      * the change is forwarded to the root view.  If the focus
  1010.      * accelerator changes, a new keystroke is registered to request
  1011.      * focus.
  1012.      */
  1013.     class UpdateHandler implements PropertyChangeListener, DocumentListener {
  1014.  
  1015.         // --- PropertyChangeListener methods -----------------------
  1016.  
  1017.         /**
  1018.          * This method gets called when a bound property is changed.
  1019.          * We are looking for document changes on the editor.
  1020.          */
  1021.         public final void propertyChange(PropertyChangeEvent evt) {
  1022.             Object oldValue = evt.getOldValue();
  1023.             Object newValue = evt.getNewValue();
  1024.             if ((oldValue instanceof Document) || (newValue instanceof Document)) {
  1025.                 if (oldValue != null) {
  1026.                     ((Document)oldValue).removeDocumentListener(this);
  1027.                 }
  1028.                 if (newValue != null) {
  1029.                     ((Document)newValue).addDocumentListener(this);
  1030.                 }
  1031.                 modelChanged();
  1032.             }
  1033.             DefaultTextUI.this.propertyChange(evt);
  1034.         }
  1035.  
  1036.         // --- DocumentListener methods -----------------------
  1037.  
  1038.         /**
  1039.          * The insert notification.  Gets sent to the root of the view structure
  1040.          * that represents the portion of the model being represented by the
  1041.          * editor.  The factory is added as an argument to the update so that
  1042.          * the views can update themselves in a dynamic (not hardcoded) way.
  1043.          *
  1044.          * @param e  The change notification from the currently associated
  1045.          *  document.
  1046.          * @see DocumentListener#insertUpdate
  1047.          */
  1048.         public final void insertUpdate(DocumentEvent e) {
  1049.             Rectangle alloc = (painted) ? new Rectangle(editor.getSize()) : null;
  1050.             rootView.insertUpdate(e, alloc, rootView.getViewFactory());
  1051.         }
  1052.  
  1053.         /**
  1054.          * The remove notification.  Gets sent to the root of the view structure
  1055.          * that represents the portion of the model being represented by the
  1056.          * editor.  The factory is added as an argument to the update so that
  1057.          * the views can update themselves in a dynamic (not hardcoded) way.
  1058.          *
  1059.          * @param e  The change notification from the currently associated
  1060.          *  document.
  1061.          * @see DocumentListener#removeUpdate
  1062.          */
  1063.         public final void removeUpdate(DocumentEvent e) {
  1064.             Rectangle alloc = (painted) ? new Rectangle(editor.getSize()) : null;
  1065.             rootView.removeUpdate(e, alloc, rootView.getViewFactory());
  1066.         }
  1067.  
  1068.         /**
  1069.          * The change notification.  Gets sent to the root of the view structure
  1070.          * that represents the portion of the model being represented by the
  1071.          * editor.  The factory is added as an argument to the update so that
  1072.          * the views can update themselves in a dynamic (not hardcoded) way.
  1073.          *
  1074.          * @param e  The change notification from the currently associated
  1075.          *  document.
  1076.          * @see DocumentListener#changeUpdate
  1077.          */
  1078.         public final void changedUpdate(DocumentEvent e) {
  1079.             Rectangle alloc = (painted) ? new Rectangle(editor.getSize()) : null;
  1080.             rootView.changedUpdate(e, alloc, rootView.getViewFactory());
  1081.         }
  1082.     }
  1083.  
  1084. }
  1085.  
  1086.