Save This Page
Home » openjdk-7 » javax » swing » text » [javadoc | source]
    1   /*
    2    * Copyright 1997-2007 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   package javax.swing.text;
   26   
   27   import java.awt;
   28   import java.beans.PropertyChangeEvent;
   29   import java.beans.PropertyChangeListener;
   30   import javax.swing.SwingUtilities;
   31   import javax.swing.event;
   32   
   33   /**
   34    * Component decorator that implements the view interface.  The
   35    * entire element is used to represent the component.  This acts
   36    * as a gateway from the display-only View implementations to
   37    * interactive lightweight components (ie it allows components
   38    * to be embedded into the View hierarchy).
   39    * <p>
   40    * The component is placed relative to the text baseline
   41    * according to the value returned by
   42    * <code>Component.getAlignmentY</code>.  For Swing components
   43    * this value can be conveniently set using the method
   44    * <code>JComponent.setAlignmentY</code>.  For example, setting
   45    * a value of <code>0.75</code> will cause 75 percent of the
   46    * component to be above the baseline, and 25 percent of the
   47    * component to be below the baseline.
   48    * <p>
   49    * This class is implemented to do the extra work necessary to
   50    * work properly in the presence of multiple threads (i.e. from
   51    * asynchronous notification of model changes for example) by
   52    * ensuring that all component access is done on the event thread.
   53    * <p>
   54    * The component used is determined by the return value of the
   55    * createComponent method.  The default implementation of this
   56    * method is to return the component held as an attribute of
   57    * the element (by calling StyleConstants.getComponent).  A
   58    * limitation of this behavior is that the component cannot
   59    * be used by more than one text component (i.e. with a shared
   60    * model).  Subclasses can remove this constraint by implementing
   61    * the createComponent to actually create a component based upon
   62    * some kind of specification contained in the attributes.  The
   63    * ObjectView class in the html package is an example of a
   64    * ComponentView implementation that supports multiple component
   65    * views of a shared model.
   66    *
   67    * @author Timothy Prinzing
   68    */
   69   public class ComponentView extends View  {
   70   
   71       /**
   72        * Creates a new ComponentView object.
   73        *
   74        * @param elem the element to decorate
   75        */
   76       public ComponentView(Element elem) {
   77           super(elem);
   78       }
   79   
   80       /**
   81        * Create the component that is associated with
   82        * this view.  This will be called when it has
   83        * been determined that a new component is needed.
   84        * This would result from a call to setParent or
   85        * as a result of being notified that attributes
   86        * have changed.
   87        */
   88       protected Component createComponent() {
   89           AttributeSet attr = getElement().getAttributes();
   90           Component comp = StyleConstants.getComponent(attr);
   91           return comp;
   92       }
   93   
   94       /**
   95        * Fetch the component associated with the view.
   96        */
   97       public final Component getComponent() {
   98           return createdC;
   99       }
  100   
  101       // --- View methods ---------------------------------------------
  102   
  103       /**
  104        * The real paint behavior occurs naturally from the association
  105        * that the component has with its parent container (the same
  106        * container hosting this view).  This is implemented to do nothing.
  107        *
  108        * @param g the graphics context
  109        * @param a the shape
  110        * @see View#paint
  111        */
  112       public void paint(Graphics g, Shape a) {
  113           if (c != null) {
  114               Rectangle alloc = (a instanceof Rectangle) ?
  115                   (Rectangle) a : a.getBounds();
  116               c.setBounds(alloc.x, alloc.y, alloc.width, alloc.height);
  117           }
  118       }
  119   
  120       /**
  121        * Determines the preferred span for this view along an
  122        * axis.  This is implemented to return the value
  123        * returned by Component.getPreferredSize along the
  124        * axis of interest.
  125        *
  126        * @param axis may be either View.X_AXIS or View.Y_AXIS
  127        * @return   the span the view would like to be rendered into >= 0.
  128        *           Typically the view is told to render into the span
  129        *           that is returned, although there is no guarantee.
  130        *           The parent may choose to resize or break the view.
  131        * @exception IllegalArgumentException for an invalid axis
  132        */
  133       public float getPreferredSpan(int axis) {
  134           if ((axis != X_AXIS) && (axis != Y_AXIS)) {
  135               throw new IllegalArgumentException("Invalid axis: " + axis);
  136           }
  137           if (c != null) {
  138               Dimension size = c.getPreferredSize();
  139               if (axis == View.X_AXIS) {
  140                   return size.width;
  141               } else {
  142                   return size.height;
  143               }
  144           }
  145           return 0;
  146       }
  147   
  148       /**
  149        * Determines the minimum span for this view along an
  150        * axis.  This is implemented to return the value
  151        * returned by Component.getMinimumSize along the
  152        * axis of interest.
  153        *
  154        * @param axis may be either View.X_AXIS or View.Y_AXIS
  155        * @return   the span the view would like to be rendered into >= 0.
  156        *           Typically the view is told to render into the span
  157        *           that is returned, although there is no guarantee.
  158        *           The parent may choose to resize or break the view.
  159        * @exception IllegalArgumentException for an invalid axis
  160        */
  161       public float getMinimumSpan(int axis) {
  162           if ((axis != X_AXIS) && (axis != Y_AXIS)) {
  163               throw new IllegalArgumentException("Invalid axis: " + axis);
  164           }
  165           if (c != null) {
  166               Dimension size = c.getMinimumSize();
  167               if (axis == View.X_AXIS) {
  168                   return size.width;
  169               } else {
  170                   return size.height;
  171               }
  172           }
  173           return 0;
  174       }
  175   
  176       /**
  177        * Determines the maximum span for this view along an
  178        * axis.  This is implemented to return the value
  179        * returned by Component.getMaximumSize along the
  180        * axis of interest.
  181        *
  182        * @param axis may be either View.X_AXIS or View.Y_AXIS
  183        * @return   the span the view would like to be rendered into >= 0.
  184        *           Typically the view is told to render into the span
  185        *           that is returned, although there is no guarantee.
  186        *           The parent may choose to resize or break the view.
  187        * @exception IllegalArgumentException for an invalid axis
  188        */
  189       public float getMaximumSpan(int axis) {
  190           if ((axis != X_AXIS) && (axis != Y_AXIS)) {
  191               throw new IllegalArgumentException("Invalid axis: " + axis);
  192           }
  193           if (c != null) {
  194               Dimension size = c.getMaximumSize();
  195               if (axis == View.X_AXIS) {
  196                   return size.width;
  197               } else {
  198                   return size.height;
  199               }
  200           }
  201           return 0;
  202       }
  203   
  204       /**
  205        * Determines the desired alignment for this view along an
  206        * axis.  This is implemented to give the alignment of the
  207        * embedded component.
  208        *
  209        * @param axis may be either View.X_AXIS or View.Y_AXIS
  210        * @return the desired alignment.  This should be a value
  211        *   between 0.0 and 1.0 where 0 indicates alignment at the
  212        *   origin and 1.0 indicates alignment to the full span
  213        *   away from the origin.  An alignment of 0.5 would be the
  214        *   center of the view.
  215        */
  216       public float getAlignment(int axis) {
  217           if (c != null) {
  218               switch (axis) {
  219               case View.X_AXIS:
  220                   return c.getAlignmentX();
  221               case View.Y_AXIS:
  222                   return c.getAlignmentY();
  223               }
  224           }
  225           return super.getAlignment(axis);
  226       }
  227   
  228       /**
  229        * Sets the parent for a child view.
  230        * The parent calls this on the child to tell it who its
  231        * parent is, giving the view access to things like
  232        * the hosting Container.  The superclass behavior is
  233        * executed, followed by a call to createComponent if
  234        * the parent view parameter is non-null and a component
  235        * has not yet been created. The embedded components parent
  236        * is then set to the value returned by <code>getContainer</code>.
  237        * If the parent view parameter is null, this view is being
  238        * cleaned up, thus the component is removed from its parent.
  239        * <p>
  240        * The changing of the component hierarchy will
  241        * touch the component lock, which is the one thing
  242        * that is not safe from the View hierarchy.  Therefore,
  243        * this functionality is executed immediately if on the
  244        * event thread, or is queued on the event queue if
  245        * called from another thread (notification of change
  246        * from an asynchronous update).
  247        *
  248        * @param p the parent
  249        */
  250       public void setParent(View p) {
  251           super.setParent(p);
  252           if (SwingUtilities.isEventDispatchThread()) {
  253               setComponentParent();
  254           } else {
  255               Runnable callSetComponentParent = new Runnable() {
  256                   public void run() {
  257                       Document doc = getDocument();
  258                       try {
  259                           if (doc instanceof AbstractDocument) {
  260                               ((AbstractDocument)doc).readLock();
  261                           }
  262                           setComponentParent();
  263                           Container host = getContainer();
  264                           if (host != null) {
  265                               preferenceChanged(null, true, true);
  266                               host.repaint();
  267                           }
  268                       } finally {
  269                           if (doc instanceof AbstractDocument) {
  270                               ((AbstractDocument)doc).readUnlock();
  271                           }
  272                       }
  273                   }
  274               };
  275               SwingUtilities.invokeLater(callSetComponentParent);
  276           }
  277       }
  278   
  279       /**
  280        * Set the parent of the embedded component
  281        * with assurance that it is thread-safe.
  282        */
  283       void setComponentParent() {
  284           View p = getParent();
  285           if (p != null) {
  286               Container parent = getContainer();
  287               if (parent != null) {
  288                   if (c == null) {
  289                       // try to build a component
  290                       Component comp = createComponent();
  291                       if (comp != null) {
  292                           createdC = comp;
  293                           c = new Invalidator(comp);
  294                       }
  295                   }
  296                   if (c != null) {
  297                       if (c.getParent() == null) {
  298                           // components associated with the View tree are added
  299                           // to the hosting container with the View as a constraint.
  300                           parent.add(c, this);
  301                           parent.addPropertyChangeListener("enabled", c);
  302                       }
  303                   }
  304               }
  305           } else {
  306               if (c != null) {
  307                   Container parent = c.getParent();
  308                   if (parent != null) {
  309                       // remove the component from its hosting container
  310                       parent.remove(c);
  311                       parent.removePropertyChangeListener("enabled", c);
  312                   }
  313               }
  314           }
  315       }
  316   
  317       /**
  318        * Provides a mapping from the coordinate space of the model to
  319        * that of the view.
  320        *
  321        * @param pos the position to convert >= 0
  322        * @param a the allocated region to render into
  323        * @return the bounding box of the given position is returned
  324        * @exception BadLocationException  if the given position does not
  325        *   represent a valid location in the associated document
  326        * @see View#modelToView
  327        */
  328       public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  329           int p0 = getStartOffset();
  330           int p1 = getEndOffset();
  331           if ((pos >= p0) && (pos <= p1)) {
  332               Rectangle r = a.getBounds();
  333               if (pos == p1) {
  334                   r.x += r.width;
  335               }
  336               r.width = 0;
  337               return r;
  338           }
  339           throw new BadLocationException(pos + " not in range " + p0 + "," + p1, pos);
  340       }
  341   
  342       /**
  343        * Provides a mapping from the view coordinate space to the logical
  344        * coordinate space of the model.
  345        *
  346        * @param x the X coordinate >= 0
  347        * @param y the Y coordinate >= 0
  348        * @param a the allocated region to render into
  349        * @return the location within the model that best represents
  350        *    the given point in the view
  351        * @see View#viewToModel
  352        */
  353       public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
  354           Rectangle alloc = (Rectangle) a;
  355           if (x < alloc.x + (alloc.width / 2)) {
  356               bias[0] = Position.Bias.Forward;
  357               return getStartOffset();
  358           }
  359           bias[0] = Position.Bias.Backward;
  360           return getEndOffset();
  361       }
  362   
  363       // --- member variables ------------------------------------------------
  364   
  365       private Component createdC;
  366       private Invalidator c;
  367   
  368       /**
  369        * This class feeds the invalidate back to the
  370        * hosting View.  This is needed to get the View
  371        * hierarchy to consider giving the component
  372        * a different size (i.e. layout may have been
  373        * cached between the associated view and the
  374        * container hosting this component).
  375        */
  376       class Invalidator extends Container implements PropertyChangeListener {
  377   
  378           // NOTE: When we remove this class we are going to have to some
  379           // how enforce setting of the focus traversal keys on the children
  380           // so that they don't inherit them from the JEditorPane. We need
  381           // to do this as JEditorPane has abnormal bindings (it is a focus cycle
  382           // root) and the children typically don't want these bindings as well.
  383   
  384           Invalidator(Component child) {
  385               setLayout(null);
  386               add(child);
  387               cacheChildSizes();
  388           }
  389   
  390           /**
  391            * The components invalid layout needs
  392            * to be propagated through the view hierarchy
  393            * so the views (which position the component)
  394            * can have their layout recomputed.
  395            */
  396           public void invalidate() {
  397               super.invalidate();
  398               if (getParent() != null) {
  399                   preferenceChanged(null, true, true);
  400               }
  401           }
  402   
  403           public void doLayout() {
  404               cacheChildSizes();
  405           }
  406   
  407           public void setBounds(int x, int y, int w, int h) {
  408               super.setBounds(x, y, w, h);
  409               if (getComponentCount() > 0) {
  410                   getComponent(0).setSize(w, h);
  411               }
  412               cacheChildSizes();
  413           }
  414   
  415           public void validateIfNecessary() {
  416               if (!isValid()) {
  417                   validate();
  418                }
  419           }
  420   
  421           private void cacheChildSizes() {
  422               if (getComponentCount() > 0) {
  423                   Component child = getComponent(0);
  424                   min = child.getMinimumSize();
  425                   pref = child.getPreferredSize();
  426                   max = child.getMaximumSize();
  427                   yalign = child.getAlignmentY();
  428                   xalign = child.getAlignmentX();
  429               } else {
  430                   min = pref = max = new Dimension(0, 0);
  431               }
  432           }
  433   
  434           /**
  435            * Shows or hides this component depending on the value of parameter
  436            * <code>b</code>.
  437            * @param <code>b</code>  If <code>true</code>, shows this component;
  438            * otherwise, hides this component.
  439            * @see #isVisible
  440            * @since JDK1.1
  441            */
  442           public void setVisible(boolean b) {
  443               super.setVisible(b);
  444               if (getComponentCount() > 0) {
  445                   getComponent(0).setVisible(b);
  446               }
  447           }
  448   
  449           /**
  450            * Overridden to fix 4759054. Must return true so that content
  451            * is painted when inside a CellRendererPane which is normally
  452            * invisible.
  453            */
  454           public boolean isShowing() {
  455               return true;
  456           }
  457   
  458           public Dimension getMinimumSize() {
  459               validateIfNecessary();
  460               return min;
  461           }
  462   
  463           public Dimension getPreferredSize() {
  464               validateIfNecessary();
  465               return pref;
  466           }
  467   
  468           public Dimension getMaximumSize() {
  469               validateIfNecessary();
  470               return max;
  471           }
  472   
  473           public float getAlignmentX() {
  474               validateIfNecessary();
  475               return xalign;
  476           }
  477   
  478           public float getAlignmentY() {
  479               validateIfNecessary();
  480               return yalign;
  481           }
  482   
  483           public java.util.Set getFocusTraversalKeys(int id) {
  484               return KeyboardFocusManager.getCurrentKeyboardFocusManager().
  485                       getDefaultFocusTraversalKeys(id);
  486           }
  487   
  488           public void propertyChange(PropertyChangeEvent ev) {
  489               Boolean enable = (Boolean) ev.getNewValue();
  490               if (getComponentCount() > 0) {
  491                   getComponent(0).setEnabled(enable);
  492               }
  493           }
  494   
  495           Dimension min;
  496           Dimension pref;
  497           Dimension max;
  498           float yalign;
  499           float xalign;
  500   
  501       }
  502   
  503   }

Save This Page
Home » openjdk-7 » javax » swing » text » [javadoc | source]