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

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

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