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

    1   /*
    2    * Copyright (c) 1997, 2006, 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.io.PrintStream;
   28   import java.util.Vector;
   29   import java.awt;
   30   import javax.swing.event.DocumentEvent;
   31   import javax.swing.SizeRequirements;
   32   
   33   /**
   34    * A view that arranges its children into a box shape by tiling
   35    * its children along an axis.  The box is somewhat like that
   36    * found in TeX where there is alignment of the
   37    * children, flexibility of the children is considered, etc.
   38    * This is a building block that might be useful to represent
   39    * things like a collection of lines, paragraphs,
   40    * lists, columns, pages, etc.  The axis along which the children are tiled is
   41    * considered the major axis.  The orthoginal axis is the minor axis.
   42    * <p>
   43    * Layout for each axis is handled separately by the methods
   44    * <code>layoutMajorAxis</code> and <code>layoutMinorAxis</code>.
   45    * Subclasses can change the layout algorithm by
   46    * reimplementing these methods.    These methods will be called
   47    * as necessary depending upon whether or not there is cached
   48    * layout information and the cache is considered
   49    * valid.  These methods are typically called if the given size
   50    * along the axis changes, or if <code>layoutChanged</code> is
   51    * called to force an updated layout.  The <code>layoutChanged</code>
   52    * method invalidates cached layout information, if there is any.
   53    * The requirements published to the parent view are calculated by
   54    * the methods <code>calculateMajorAxisRequirements</code>
   55    * and  <code>calculateMinorAxisRequirements</code>.
   56    * If the layout algorithm is changed, these methods will
   57    * likely need to be reimplemented.
   58    *
   59    * @author  Timothy Prinzing
   60    */
   61   public class BoxView extends CompositeView {
   62   
   63       /**
   64        * Constructs a <code>BoxView</code>.
   65        *
   66        * @param elem the element this view is responsible for
   67        * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   68        */
   69       public BoxView(Element elem, int axis) {
   70           super(elem);
   71           tempRect = new Rectangle();
   72           this.majorAxis = axis;
   73   
   74           majorOffsets = new int[0];
   75           majorSpans = new int[0];
   76           majorReqValid = false;
   77           majorAllocValid = false;
   78           minorOffsets = new int[0];
   79           minorSpans = new int[0];
   80           minorReqValid = false;
   81           minorAllocValid = false;
   82       }
   83   
   84       /**
   85        * Fetches the tile axis property.  This is the axis along which
   86        * the child views are tiled.
   87        *
   88        * @return the major axis of the box, either
   89        *  <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
   90        *
   91        * @since 1.3
   92        */
   93       public int getAxis() {
   94           return majorAxis;
   95       }
   96   
   97       /**
   98        * Sets the tile axis property.  This is the axis along which
   99        * the child views are tiled.
  100        *
  101        * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
  102        *
  103        * @since 1.3
  104        */
  105       public void setAxis(int axis) {
  106           boolean axisChanged = (axis != majorAxis);
  107           majorAxis = axis;
  108           if (axisChanged) {
  109               preferenceChanged(null, true, true);
  110           }
  111       }
  112   
  113       /**
  114        * Invalidates the layout along an axis.  This happens
  115        * automatically if the preferences have changed for
  116        * any of the child views.  In some cases the layout
  117        * may need to be recalculated when the preferences
  118        * have not changed.  The layout can be marked as
  119        * invalid by calling this method.  The layout will
  120        * be updated the next time the <code>setSize</code> method
  121        * is called on this view (typically in paint).
  122        *
  123        * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
  124        *
  125        * @since 1.3
  126        */
  127       public void layoutChanged(int axis) {
  128           if (axis == majorAxis) {
  129               majorAllocValid = false;
  130           } else {
  131               minorAllocValid = false;
  132           }
  133       }
  134   
  135       /**
  136        * Determines if the layout is valid along the given axis.
  137        *
  138        * @param axis either <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
  139        *
  140        * @since 1.4
  141        */
  142       protected boolean isLayoutValid(int axis) {
  143           if (axis == majorAxis) {
  144               return majorAllocValid;
  145           } else {
  146               return minorAllocValid;
  147           }
  148       }
  149   
  150       /**
  151        * Paints a child.  By default
  152        * that is all it does, but a subclass can use this to paint
  153        * things relative to the child.
  154        *
  155        * @param g the graphics context
  156        * @param alloc the allocated region to paint into
  157        * @param index the child index, >= 0 && < getViewCount()
  158        */
  159       protected void paintChild(Graphics g, Rectangle alloc, int index) {
  160           View child = getView(index);
  161           child.paint(g, alloc);
  162       }
  163   
  164       // --- View methods ---------------------------------------------
  165   
  166       /**
  167        * Invalidates the layout and resizes the cache of
  168        * requests/allocations.  The child allocations can still
  169        * be accessed for the old layout, but the new children
  170        * will have an offset and span of 0.
  171        *
  172        * @param index the starting index into the child views to insert
  173        *   the new views; this should be a value >= 0 and <= getViewCount
  174        * @param length the number of existing child views to remove;
  175        *   This should be a value >= 0 and <= (getViewCount() - offset)
  176        * @param elems the child views to add; this value can be
  177        *   <code>null</code>to indicate no children are being added
  178        *   (useful to remove)
  179        */
  180       public void replace(int index, int length, View[] elems) {
  181           super.replace(index, length, elems);
  182   
  183           // invalidate cache
  184           int nInserted = (elems != null) ? elems.length : 0;
  185           majorOffsets = updateLayoutArray(majorOffsets, index, nInserted);
  186           majorSpans = updateLayoutArray(majorSpans, index, nInserted);
  187           majorReqValid = false;
  188           majorAllocValid = false;
  189           minorOffsets = updateLayoutArray(minorOffsets, index, nInserted);
  190           minorSpans = updateLayoutArray(minorSpans, index, nInserted);
  191           minorReqValid = false;
  192           minorAllocValid = false;
  193       }
  194   
  195       /**
  196        * Resizes the given layout array to match the new number of
  197        * child views.  The current number of child views are used to
  198        * produce the new array.  The contents of the old array are
  199        * inserted into the new array at the appropriate places so that
  200        * the old layout information is transferred to the new array.
  201        *
  202        * @param oldArray the original layout array
  203        * @param offset location where new views will be inserted
  204        * @param nInserted the number of child views being inserted;
  205        *          therefore the number of blank spaces to leave in the
  206        *          new array at location <code>offset</code>
  207        * @return the new layout array
  208        */
  209       int[] updateLayoutArray(int[] oldArray, int offset, int nInserted) {
  210           int n = getViewCount();
  211           int[] newArray = new int[n];
  212   
  213           System.arraycopy(oldArray, 0, newArray, 0, offset);
  214           System.arraycopy(oldArray, offset,
  215                            newArray, offset + nInserted, n - nInserted - offset);
  216           return newArray;
  217       }
  218   
  219       /**
  220        * Forwards the given <code>DocumentEvent</code> to the child views
  221        * that need to be notified of the change to the model.
  222        * If a child changed its requirements and the allocation
  223        * was valid prior to forwarding the portion of the box
  224        * from the starting child to the end of the box will
  225        * be repainted.
  226        *
  227        * @param ec changes to the element this view is responsible
  228        *  for (may be <code>null</code> if there were no changes)
  229        * @param e the change information from the associated document
  230        * @param a the current allocation of the view
  231        * @param f the factory to use to rebuild if the view has children
  232        * @see #insertUpdate
  233        * @see #removeUpdate
  234        * @see #changedUpdate
  235        * @since 1.3
  236        */
  237       protected void forwardUpdate(DocumentEvent.ElementChange ec,
  238                                    DocumentEvent e, Shape a, ViewFactory f) {
  239           boolean wasValid = isLayoutValid(majorAxis);
  240           super.forwardUpdate(ec, e, a, f);
  241   
  242           // determine if a repaint is needed
  243           if (wasValid && (! isLayoutValid(majorAxis))) {
  244               // Repaint is needed because one of the tiled children
  245               // have changed their span along the major axis.  If there
  246               // is a hosting component and an allocated shape we repaint.
  247               Component c = getContainer();
  248               if ((a != null) && (c != null)) {
  249                   int pos = e.getOffset();
  250                   int index = getViewIndexAtPosition(pos);
  251                   Rectangle alloc = getInsideAllocation(a);
  252                   if (majorAxis == X_AXIS) {
  253                       alloc.x += majorOffsets[index];
  254                       alloc.width -= majorOffsets[index];
  255                   } else {
  256                       alloc.y += minorOffsets[index];
  257                       alloc.height -= minorOffsets[index];
  258                   }
  259                   c.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  260               }
  261           }
  262       }
  263   
  264       /**
  265        * This is called by a child to indicate its
  266        * preferred span has changed.  This is implemented to
  267        * throw away cached layout information so that new
  268        * calculations will be done the next time the children
  269        * need an allocation.
  270        *
  271        * @param child the child view
  272        * @param width true if the width preference should change
  273        * @param height true if the height preference should change
  274        */
  275       public void preferenceChanged(View child, boolean width, boolean height) {
  276           boolean majorChanged = (majorAxis == X_AXIS) ? width : height;
  277           boolean minorChanged = (majorAxis == X_AXIS) ? height : width;
  278           if (majorChanged) {
  279               majorReqValid = false;
  280               majorAllocValid = false;
  281           }
  282           if (minorChanged) {
  283               minorReqValid = false;
  284               minorAllocValid = false;
  285           }
  286           super.preferenceChanged(child, width, height);
  287       }
  288   
  289       /**
  290        * Gets the resize weight.  A value of 0 or less is not resizable.
  291        *
  292        * @param axis may be either <code>View.X_AXIS</code> or
  293        *          <code>View.Y_AXIS</code>
  294        * @return the weight
  295        * @exception IllegalArgumentException for an invalid axis
  296        */
  297       public int getResizeWeight(int axis) {
  298           checkRequests(axis);
  299           if (axis == majorAxis) {
  300               if ((majorRequest.preferred != majorRequest.minimum) ||
  301                   (majorRequest.preferred != majorRequest.maximum)) {
  302                   return 1;
  303               }
  304           } else {
  305               if ((minorRequest.preferred != minorRequest.minimum) ||
  306                   (minorRequest.preferred != minorRequest.maximum)) {
  307                   return 1;
  308               }
  309           }
  310           return 0;
  311       }
  312   
  313       /**
  314        * Sets the size of the view along an axis.  This should cause
  315        * layout of the view along the given axis.
  316        *
  317        * @param axis may be either <code>View.X_AXIS</code> or
  318        *          <code>View.Y_AXIS</code>
  319        * @param span the span to layout to >= 0
  320        */
  321       void setSpanOnAxis(int axis, float span) {
  322           if (axis == majorAxis) {
  323               if (majorSpan != (int) span) {
  324                   majorAllocValid = false;
  325               }
  326               if (! majorAllocValid) {
  327                   // layout the major axis
  328                   majorSpan = (int) span;
  329                   checkRequests(majorAxis);
  330                   layoutMajorAxis(majorSpan, axis, majorOffsets, majorSpans);
  331                   majorAllocValid = true;
  332   
  333                   // flush changes to the children
  334                   updateChildSizes();
  335               }
  336           } else {
  337               if (((int) span) != minorSpan) {
  338                   minorAllocValid = false;
  339               }
  340               if (! minorAllocValid) {
  341                   // layout the minor axis
  342                   minorSpan = (int) span;
  343                   checkRequests(axis);
  344                   layoutMinorAxis(minorSpan, axis, minorOffsets, minorSpans);
  345                   minorAllocValid = true;
  346   
  347                   // flush changes to the children
  348                   updateChildSizes();
  349               }
  350           }
  351       }
  352   
  353       /**
  354        * Propagates the current allocations to the child views.
  355        */
  356       void updateChildSizes() {
  357           int n = getViewCount();
  358           if (majorAxis == X_AXIS) {
  359               for (int i = 0; i < n; i++) {
  360                   View v = getView(i);
  361                   v.setSize((float) majorSpans[i], (float) minorSpans[i]);
  362               }
  363           } else {
  364               for (int i = 0; i < n; i++) {
  365                   View v = getView(i);
  366                   v.setSize((float) minorSpans[i], (float) majorSpans[i]);
  367               }
  368           }
  369       }
  370   
  371       /**
  372        * Returns the size of the view along an axis.  This is implemented
  373        * to return zero.
  374        *
  375        * @param axis may be either <code>View.X_AXIS</code> or
  376        *          <code>View.Y_AXIS</code>
  377        * @return the current span of the view along the given axis, >= 0
  378        */
  379       float getSpanOnAxis(int axis) {
  380           if (axis == majorAxis) {
  381               return majorSpan;
  382           } else {
  383               return minorSpan;
  384           }
  385       }
  386   
  387       /**
  388        * Sets the size of the view.  This should cause
  389        * layout of the view if the view caches any layout
  390        * information.  This is implemented to call the
  391        * layout method with the sizes inside of the insets.
  392        *
  393        * @param width the width >= 0
  394        * @param height the height >= 0
  395        */
  396       public void setSize(float width, float height) {
  397           layout(Math.max(0, (int)(width - getLeftInset() - getRightInset())),
  398                  Math.max(0, (int)(height - getTopInset() - getBottomInset())));
  399       }
  400   
  401       /**
  402        * Renders the <code>BoxView</code> using the given
  403        * rendering surface and area
  404        * on that surface.  Only the children that intersect
  405        * the clip bounds of the given <code>Graphics</code>
  406        * will be rendered.
  407        *
  408        * @param g the rendering surface to use
  409        * @param allocation the allocated region to render into
  410        * @see View#paint
  411        */
  412       public void paint(Graphics g, Shape allocation) {
  413           Rectangle alloc = (allocation instanceof Rectangle) ?
  414                              (Rectangle)allocation : allocation.getBounds();
  415           int n = getViewCount();
  416           int x = alloc.x + getLeftInset();
  417           int y = alloc.y + getTopInset();
  418           Rectangle clip = g.getClipBounds();
  419           for (int i = 0; i < n; i++) {
  420               tempRect.x = x + getOffset(X_AXIS, i);
  421               tempRect.y = y + getOffset(Y_AXIS, i);
  422               tempRect.width = getSpan(X_AXIS, i);
  423               tempRect.height = getSpan(Y_AXIS, i);
  424               int trx0 = tempRect.x, trx1 = trx0 + tempRect.width;
  425               int try0 = tempRect.y, try1 = try0 + tempRect.height;
  426               int crx0 = clip.x, crx1 = crx0 + clip.width;
  427               int cry0 = clip.y, cry1 = cry0 + clip.height;
  428               // We should paint views that intersect with clipping region
  429               // even if the intersection has no inside points (is a line).
  430               // This is needed for supporting views that have zero width, like
  431               // views that contain only combining marks.
  432               if ((trx1 >= crx0) && (try1 >= cry0) && (crx1 >= trx0) && (cry1 >= try0)) {
  433                   paintChild(g, tempRect, i);
  434               }
  435           }
  436       }
  437   
  438       /**
  439        * Fetches the allocation for the given child view.
  440        * This enables finding out where various views
  441        * are located.  This is implemented to return
  442        * <code>null</code> if the layout is invalid,
  443        * otherwise the superclass behavior is executed.
  444        *
  445        * @param index the index of the child, >= 0 && < getViewCount()
  446        * @param a  the allocation to this view
  447        * @return the allocation to the child; or <code>null</code>
  448        *          if <code>a</code> is <code>null</code>;
  449        *          or <code>null</code> if the layout is invalid
  450        */
  451       public Shape getChildAllocation(int index, Shape a) {
  452           if (a != null) {
  453               Shape ca = super.getChildAllocation(index, a);
  454               if ((ca != null) && (! isAllocationValid())) {
  455                   // The child allocation may not have been set yet.
  456                   Rectangle r = (ca instanceof Rectangle) ?
  457                       (Rectangle) ca : ca.getBounds();
  458                   if ((r.width == 0) && (r.height == 0)) {
  459                       return null;
  460                   }
  461               }
  462               return ca;
  463           }
  464           return null;
  465       }
  466   
  467       /**
  468        * Provides a mapping from the document model coordinate space
  469        * to the coordinate space of the view mapped to it.  This makes
  470        * sure the allocation is valid before calling the superclass.
  471        *
  472        * @param pos the position to convert >= 0
  473        * @param a the allocated region to render into
  474        * @return the bounding box of the given position
  475        * @exception BadLocationException  if the given position does
  476        *  not represent a valid location in the associated document
  477        * @see View#modelToView
  478        */
  479       public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
  480           if (! isAllocationValid()) {
  481               Rectangle alloc = a.getBounds();
  482               setSize(alloc.width, alloc.height);
  483           }
  484           return super.modelToView(pos, a, b);
  485       }
  486   
  487       /**
  488        * Provides a mapping from the view coordinate space to the logical
  489        * coordinate space of the model.
  490        *
  491        * @param x   x coordinate of the view location to convert >= 0
  492        * @param y   y coordinate of the view location to convert >= 0
  493        * @param a the allocated region to render into
  494        * @return the location within the model that best represents the
  495        *  given point in the view >= 0
  496        * @see View#viewToModel
  497        */
  498       public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
  499           if (! isAllocationValid()) {
  500               Rectangle alloc = a.getBounds();
  501               setSize(alloc.width, alloc.height);
  502           }
  503           return super.viewToModel(x, y, a, bias);
  504       }
  505   
  506       /**
  507        * Determines the desired alignment for this view along an
  508        * axis.  This is implemented to give the total alignment
  509        * needed to position the children with the alignment points
  510        * lined up along the axis orthoginal to the axis that is
  511        * being tiled.  The axis being tiled will request to be
  512        * centered (i.e. 0.5f).
  513        *
  514        * @param axis may be either <code>View.X_AXIS</code>
  515        *   or <code>View.Y_AXIS</code>
  516        * @return the desired alignment >= 0.0f && <= 1.0f; this should
  517        *   be a value between 0.0 and 1.0 where 0 indicates alignment at the
  518        *   origin and 1.0 indicates alignment to the full span
  519        *   away from the origin; an alignment of 0.5 would be the
  520        *   center of the view
  521        * @exception IllegalArgumentException for an invalid axis
  522        */
  523       public float getAlignment(int axis) {
  524           checkRequests(axis);
  525           if (axis == majorAxis) {
  526               return majorRequest.alignment;
  527           } else {
  528               return minorRequest.alignment;
  529           }
  530       }
  531   
  532       /**
  533        * Determines the preferred span for this view along an
  534        * axis.
  535        *
  536        * @param axis may be either <code>View.X_AXIS</code>
  537        *           or <code>View.Y_AXIS</code>
  538        * @return   the span the view would like to be rendered into >= 0;
  539        *           typically the view is told to render into the span
  540        *           that is returned, although there is no guarantee;
  541        *           the parent may choose to resize or break the view
  542        * @exception IllegalArgumentException for an invalid axis type
  543        */
  544       public float getPreferredSpan(int axis) {
  545           checkRequests(axis);
  546           float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
  547               getTopInset() + getBottomInset();
  548           if (axis == majorAxis) {
  549               return ((float)majorRequest.preferred) + marginSpan;
  550           } else {
  551               return ((float)minorRequest.preferred) + marginSpan;
  552           }
  553       }
  554   
  555       /**
  556        * Determines the minimum span for this view along an
  557        * axis.
  558        *
  559        * @param axis may be either <code>View.X_AXIS</code>
  560        *           or <code>View.Y_AXIS</code>
  561        * @return  the span the view would like to be rendered into >= 0;
  562        *           typically the view is told to render into the span
  563        *           that is returned, although there is no guarantee;
  564        *           the parent may choose to resize or break the view
  565        * @exception IllegalArgumentException for an invalid axis type
  566        */
  567       public float getMinimumSpan(int axis) {
  568           checkRequests(axis);
  569           float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
  570               getTopInset() + getBottomInset();
  571           if (axis == majorAxis) {
  572               return ((float)majorRequest.minimum) + marginSpan;
  573           } else {
  574               return ((float)minorRequest.minimum) + marginSpan;
  575           }
  576       }
  577   
  578       /**
  579        * Determines the maximum span for this view along an
  580        * axis.
  581        *
  582        * @param axis may be either <code>View.X_AXIS</code>
  583        *           or <code>View.Y_AXIS</code>
  584        * @return   the span the view would like to be rendered into >= 0;
  585        *           typically the view is told to render into the span
  586        *           that is returned, although there is no guarantee;
  587        *           the parent may choose to resize or break the view
  588        * @exception IllegalArgumentException for an invalid axis type
  589        */
  590       public float getMaximumSpan(int axis) {
  591           checkRequests(axis);
  592           float marginSpan = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
  593               getTopInset() + getBottomInset();
  594           if (axis == majorAxis) {
  595               return ((float)majorRequest.maximum) + marginSpan;
  596           } else {
  597               return ((float)minorRequest.maximum) + marginSpan;
  598           }
  599       }
  600   
  601       // --- local methods ----------------------------------------------------
  602   
  603       /**
  604        * Are the allocations for the children still
  605        * valid?
  606        *
  607        * @return true if allocations still valid
  608        */
  609       protected boolean isAllocationValid() {
  610           return (majorAllocValid && minorAllocValid);
  611       }
  612   
  613       /**
  614        * Determines if a point falls before an allocated region.
  615        *
  616        * @param x the X coordinate >= 0
  617        * @param y the Y coordinate >= 0
  618        * @param innerAlloc the allocated region; this is the area
  619        *   inside of the insets
  620        * @return true if the point lies before the region else false
  621        */
  622       protected boolean isBefore(int x, int y, Rectangle innerAlloc) {
  623           if (majorAxis == View.X_AXIS) {
  624               return (x < innerAlloc.x);
  625           } else {
  626               return (y < innerAlloc.y);
  627           }
  628       }
  629   
  630       /**
  631        * Determines if a point falls after an allocated region.
  632        *
  633        * @param x the X coordinate >= 0
  634        * @param y the Y coordinate >= 0
  635        * @param innerAlloc the allocated region; this is the area
  636        *   inside of the insets
  637        * @return true if the point lies after the region else false
  638        */
  639       protected boolean isAfter(int x, int y, Rectangle innerAlloc) {
  640           if (majorAxis == View.X_AXIS) {
  641               return (x > (innerAlloc.width + innerAlloc.x));
  642           } else {
  643               return (y > (innerAlloc.height + innerAlloc.y));
  644           }
  645       }
  646   
  647       /**
  648        * Fetches the child view at the given coordinates.
  649        *
  650        * @param x the X coordinate >= 0
  651        * @param y the Y coordinate >= 0
  652        * @param alloc the parents inner allocation on entry, which should
  653        *   be changed to the childs allocation on exit
  654        * @return the view
  655        */
  656       protected View getViewAtPoint(int x, int y, Rectangle alloc) {
  657           int n = getViewCount();
  658           if (majorAxis == View.X_AXIS) {
  659               if (x < (alloc.x + majorOffsets[0])) {
  660                   childAllocation(0, alloc);
  661                   return getView(0);
  662               }
  663               for (int i = 0; i < n; i++) {
  664                   if (x < (alloc.x + majorOffsets[i])) {
  665                       childAllocation(i - 1, alloc);
  666                       return getView(i - 1);
  667                   }
  668               }
  669               childAllocation(n - 1, alloc);
  670               return getView(n - 1);
  671           } else {
  672               if (y < (alloc.y + majorOffsets[0])) {
  673                   childAllocation(0, alloc);
  674                   return getView(0);
  675               }
  676               for (int i = 0; i < n; i++) {
  677                   if (y < (alloc.y + majorOffsets[i])) {
  678                       childAllocation(i - 1, alloc);
  679                       return getView(i - 1);
  680                   }
  681               }
  682               childAllocation(n - 1, alloc);
  683               return getView(n - 1);
  684           }
  685       }
  686   
  687       /**
  688        * Allocates a region for a child view.
  689        *
  690        * @param index the index of the child view to
  691        *   allocate, >= 0 && < getViewCount()
  692        * @param alloc the allocated region
  693        */
  694       protected void childAllocation(int index, Rectangle alloc) {
  695           alloc.x += getOffset(X_AXIS, index);
  696           alloc.y += getOffset(Y_AXIS, index);
  697           alloc.width = getSpan(X_AXIS, index);
  698           alloc.height = getSpan(Y_AXIS, index);
  699       }
  700   
  701       /**
  702        * Perform layout on the box
  703        *
  704        * @param width the width (inside of the insets) >= 0
  705        * @param height the height (inside of the insets) >= 0
  706        */
  707       protected void layout(int width, int height) {
  708           setSpanOnAxis(X_AXIS, width);
  709           setSpanOnAxis(Y_AXIS, height);
  710       }
  711   
  712       /**
  713        * Returns the current width of the box.  This is the width that
  714        * it was last allocated.
  715        * @return the current width of the box
  716        */
  717       public int getWidth() {
  718           int span;
  719           if (majorAxis == X_AXIS) {
  720               span = majorSpan;
  721           } else {
  722               span = minorSpan;
  723           }
  724           span += getLeftInset() - getRightInset();
  725           return span;
  726       }
  727   
  728       /**
  729        * Returns the current height of the box.  This is the height that
  730        * it was last allocated.
  731        * @return the current height of the box
  732        */
  733       public int getHeight() {
  734           int span;
  735           if (majorAxis == Y_AXIS) {
  736               span = majorSpan;
  737           } else {
  738               span = minorSpan;
  739           }
  740           span += getTopInset() - getBottomInset();
  741           return span;
  742       }
  743   
  744       /**
  745        * Performs layout for the major axis of the box (i.e. the
  746        * axis that it represents). The results of the layout (the
  747        * offset and span for each children) are placed in the given
  748        * arrays which represent the allocations to the children
  749        * along the major axis.
  750        *
  751        * @param targetSpan the total span given to the view, which
  752        *  would be used to layout the children
  753        * @param axis the axis being layed out
  754        * @param offsets the offsets from the origin of the view for
  755        *  each of the child views; this is a return value and is
  756        *  filled in by the implementation of this method
  757        * @param spans the span of each child view; this is a return
  758        *  value and is filled in by the implementation of this method
  759        */
  760       protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  761           /*
  762            * first pass, calculate the preferred sizes
  763            * and the flexibility to adjust the sizes.
  764            */
  765           long preferred = 0;
  766           int n = getViewCount();
  767           for (int i = 0; i < n; i++) {
  768               View v = getView(i);
  769               spans[i] = (int) v.getPreferredSpan(axis);
  770               preferred += spans[i];
  771           }
  772   
  773           /*
  774            * Second pass, expand or contract by as much as possible to reach
  775            * the target span.
  776            */
  777   
  778           // determine the adjustment to be made
  779           long desiredAdjustment = targetSpan - preferred;
  780           float adjustmentFactor = 0.0f;
  781           int[] diffs = null;
  782   
  783           if (desiredAdjustment != 0) {
  784               long totalSpan = 0;
  785               diffs = new int[n];
  786               for (int i = 0; i < n; i++) {
  787                   View v = getView(i);
  788                   int tmp;
  789                   if (desiredAdjustment < 0) {
  790                       tmp = (int)v.getMinimumSpan(axis);
  791                       diffs[i] = spans[i] - tmp;
  792                   } else {
  793                       tmp = (int)v.getMaximumSpan(axis);
  794                       diffs[i] = tmp - spans[i];
  795                   }
  796                   totalSpan += tmp;
  797               }
  798   
  799               float maximumAdjustment = Math.abs(totalSpan - preferred);
  800                   adjustmentFactor = desiredAdjustment / maximumAdjustment;
  801                   adjustmentFactor = Math.min(adjustmentFactor, 1.0f);
  802                   adjustmentFactor = Math.max(adjustmentFactor, -1.0f);
  803               }
  804   
  805           // make the adjustments
  806           int totalOffset = 0;
  807           for (int i = 0; i < n; i++) {
  808               offsets[i] = totalOffset;
  809               if (desiredAdjustment != 0) {
  810                   float adjF = adjustmentFactor * diffs[i];
  811                   spans[i] += Math.round(adjF);
  812               }
  813               totalOffset = (int) Math.min((long) totalOffset + (long) spans[i], Integer.MAX_VALUE);
  814           }
  815       }
  816   
  817       /**
  818        * Performs layout for the minor axis of the box (i.e. the
  819        * axis orthoginal to the axis that it represents). The results
  820        * of the layout (the offset and span for each children) are
  821        * placed in the given arrays which represent the allocations to
  822        * the children along the minor axis.
  823        *
  824        * @param targetSpan the total span given to the view, which
  825        *  would be used to layout the children
  826        * @param axis the axis being layed out
  827        * @param offsets the offsets from the origin of the view for
  828        *  each of the child views; this is a return value and is
  829        *  filled in by the implementation of this method
  830        * @param spans the span of each child view; this is a return
  831        *  value and is filled in by the implementation of this method
  832        */
  833       protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
  834           int n = getViewCount();
  835           for (int i = 0; i < n; i++) {
  836               View v = getView(i);
  837               int max = (int) v.getMaximumSpan(axis);
  838               if (max < targetSpan) {
  839                   // can't make the child this wide, align it
  840                   float align = v.getAlignment(axis);
  841                   offsets[i] = (int) ((targetSpan - max) * align);
  842                   spans[i] = max;
  843               } else {
  844                   // make it the target width, or as small as it can get.
  845                   int min = (int)v.getMinimumSpan(axis);
  846                   offsets[i] = 0;
  847                   spans[i] = Math.max(min, targetSpan);
  848               }
  849           }
  850       }
  851   
  852       /**
  853        * Calculates the size requirements for the major axis
  854        * <code>axis</code>.
  855        *
  856        * @param axis the axis being studied
  857        * @param r the <code>SizeRequirements</code> object;
  858        *          if <code>null</code> one will be created
  859        * @return the newly initialized <code>SizeRequirements</code> object
  860        * @see javax.swing.SizeRequirements
  861        */
  862       protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
  863           // calculate tiled request
  864           float min = 0;
  865           float pref = 0;
  866           float max = 0;
  867   
  868           int n = getViewCount();
  869           for (int i = 0; i < n; i++) {
  870               View v = getView(i);
  871               min += v.getMinimumSpan(axis);
  872               pref += v.getPreferredSpan(axis);
  873               max += v.getMaximumSpan(axis);
  874           }
  875   
  876           if (r == null) {
  877               r = new SizeRequirements();
  878           }
  879           r.alignment = 0.5f;
  880           r.minimum = (int) min;
  881           r.preferred = (int) pref;
  882           r.maximum = (int) max;
  883           return r;
  884       }
  885   
  886       /**
  887        * Calculates the size requirements for the minor axis
  888        * <code>axis</code>.
  889        *
  890        * @param axis the axis being studied
  891        * @param r the <code>SizeRequirements</code> object;
  892        *          if <code>null</code> one will be created
  893        * @return the newly initialized <code>SizeRequirements</code> object
  894        * @see javax.swing.SizeRequirements
  895        */
  896       protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
  897           int min = 0;
  898           long pref = 0;
  899           int max = Integer.MAX_VALUE;
  900           int n = getViewCount();
  901           for (int i = 0; i < n; i++) {
  902               View v = getView(i);
  903               min = Math.max((int) v.getMinimumSpan(axis), min);
  904               pref = Math.max((int) v.getPreferredSpan(axis), pref);
  905               max = Math.max((int) v.getMaximumSpan(axis), max);
  906           }
  907   
  908           if (r == null) {
  909               r = new SizeRequirements();
  910               r.alignment = 0.5f;
  911           }
  912           r.preferred = (int) pref;
  913           r.minimum = min;
  914           r.maximum = max;
  915           return r;
  916       }
  917   
  918       /**
  919        * Checks the request cache and update if needed.
  920        * @param axis the axis being studied
  921        * @exception IllegalArgumentException if <code>axis</code> is
  922        *  neither <code>View.X_AXIS</code> nor <code>View.Y_AXIS</code>
  923        */
  924       void checkRequests(int axis) {
  925           if ((axis != X_AXIS) && (axis != Y_AXIS)) {
  926               throw new IllegalArgumentException("Invalid axis: " + axis);
  927           }
  928           if (axis == majorAxis) {
  929               if (!majorReqValid) {
  930                   majorRequest = calculateMajorAxisRequirements(axis,
  931                                                                 majorRequest);
  932                   majorReqValid = true;
  933               }
  934           } else if (! minorReqValid) {
  935               minorRequest = calculateMinorAxisRequirements(axis, minorRequest);
  936               minorReqValid = true;
  937           }
  938       }
  939   
  940       /**
  941        * Computes the location and extent of each child view
  942        * in this <code>BoxView</code> given the <code>targetSpan</code>,
  943        * which is the width (or height) of the region we have to
  944        * work with.
  945        *
  946        * @param targetSpan the total span given to the view, which
  947        *  would be used to layout the children
  948        * @param axis the axis being studied, either
  949        *          <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>
  950        * @param offsets an empty array filled by this method with
  951        *          values specifying the location  of each child view
  952        * @param spans  an empty array filled by this method with
  953        *          values specifying the extent of each child view
  954        */
  955       protected void baselineLayout(int targetSpan, int axis, int[] offsets, int[] spans) {
  956           int totalAscent = (int)(targetSpan * getAlignment(axis));
  957           int totalDescent = targetSpan - totalAscent;
  958   
  959           int n = getViewCount();
  960   
  961           for (int i = 0; i < n; i++) {
  962               View v = getView(i);
  963               float align = v.getAlignment(axis);
  964               float viewSpan;
  965   
  966               if (v.getResizeWeight(axis) > 0) {
  967                   // if resizable then resize to the best fit
  968   
  969                   // the smallest span possible
  970                   float minSpan = v.getMinimumSpan(axis);
  971                   // the largest span possible
  972                   float maxSpan = v.getMaximumSpan(axis);
  973   
  974                   if (align == 0.0f) {
  975                       // if the alignment is 0 then we need to fit into the descent
  976                       viewSpan = Math.max(Math.min(maxSpan, totalDescent), minSpan);
  977                   } else if (align == 1.0f) {
  978                       // if the alignment is 1 then we need to fit into the ascent
  979                       viewSpan = Math.max(Math.min(maxSpan, totalAscent), minSpan);
  980                   } else {
  981                       // figure out the span that we must fit into
  982                       float fitSpan = Math.min(totalAscent / align,
  983                                                totalDescent / (1.0f - align));
  984                       // fit into the calculated span
  985                       viewSpan = Math.max(Math.min(maxSpan, fitSpan), minSpan);
  986                   }
  987               } else {
  988                   // otherwise use the preferred spans
  989                   viewSpan = v.getPreferredSpan(axis);
  990               }
  991   
  992               offsets[i] = totalAscent - (int)(viewSpan * align);
  993               spans[i] = (int)viewSpan;
  994           }
  995       }
  996   
  997       /**
  998        * Calculates the size requirements for this <code>BoxView</code>
  999        * by examining the size of each child view.
 1000        *
 1001        * @param axis the axis being studied
 1002        * @param r the <code>SizeRequirements</code> object;
 1003        *          if <code>null</code> one will be created
 1004        * @return the newly initialized <code>SizeRequirements</code> object
 1005        */
 1006       protected SizeRequirements baselineRequirements(int axis, SizeRequirements r) {
 1007           SizeRequirements totalAscent = new SizeRequirements();
 1008           SizeRequirements totalDescent = new SizeRequirements();
 1009   
 1010           if (r == null) {
 1011               r = new SizeRequirements();
 1012           }
 1013   
 1014           r.alignment = 0.5f;
 1015   
 1016           int n = getViewCount();
 1017   
 1018           // loop through all children calculating the max of all their ascents and
 1019           // descents at minimum, preferred, and maximum sizes
 1020           for (int i = 0; i < n; i++) {
 1021               View v = getView(i);
 1022               float align = v.getAlignment(axis);
 1023               float span;
 1024               int ascent;
 1025               int descent;
 1026   
 1027               // find the maximum of the preferred ascents and descents
 1028               span = v.getPreferredSpan(axis);
 1029               ascent = (int)(align * span);
 1030               descent = (int)(span - ascent);
 1031               totalAscent.preferred = Math.max(ascent, totalAscent.preferred);
 1032               totalDescent.preferred = Math.max(descent, totalDescent.preferred);
 1033   
 1034               if (v.getResizeWeight(axis) > 0) {
 1035                   // if the view is resizable then do the same for the minimum and
 1036                   // maximum ascents and descents
 1037                   span = v.getMinimumSpan(axis);
 1038                   ascent = (int)(align * span);
 1039                   descent = (int)(span - ascent);
 1040                   totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
 1041                   totalDescent.minimum = Math.max(descent, totalDescent.minimum);
 1042   
 1043                   span = v.getMaximumSpan(axis);
 1044                   ascent = (int)(align * span);
 1045                   descent = (int)(span - ascent);
 1046                   totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
 1047                   totalDescent.maximum = Math.max(descent, totalDescent.maximum);
 1048               } else {
 1049                   // otherwise use the preferred
 1050                   totalAscent.minimum = Math.max(ascent, totalAscent.minimum);
 1051                   totalDescent.minimum = Math.max(descent, totalDescent.minimum);
 1052                   totalAscent.maximum = Math.max(ascent, totalAscent.maximum);
 1053                   totalDescent.maximum = Math.max(descent, totalDescent.maximum);
 1054               }
 1055           }
 1056   
 1057           // we now have an overall preferred, minimum, and maximum ascent and descent
 1058   
 1059           // calculate the preferred span as the sum of the preferred ascent and preferred descent
 1060           r.preferred = (int)Math.min((long)totalAscent.preferred + (long)totalDescent.preferred,
 1061                                       Integer.MAX_VALUE);
 1062   
 1063           // calculate the preferred alignment as the preferred ascent divided by the preferred span
 1064           if (r.preferred > 0) {
 1065               r.alignment = (float)totalAscent.preferred / r.preferred;
 1066           }
 1067   
 1068   
 1069           if (r.alignment == 0.0f) {
 1070               // if the preferred alignment is 0 then the minimum and maximum spans are simply
 1071               // the minimum and maximum descents since there's nothing above the baseline
 1072               r.minimum = totalDescent.minimum;
 1073               r.maximum = totalDescent.maximum;
 1074           } else if (r.alignment == 1.0f) {
 1075               // if the preferred alignment is 1 then the minimum and maximum spans are simply
 1076               // the minimum and maximum ascents since there's nothing below the baseline
 1077               r.minimum = totalAscent.minimum;
 1078               r.maximum = totalAscent.maximum;
 1079           } else {
 1080               // we want to honor the preferred alignment so we calculate two possible minimum
 1081               // span values using 1) the minimum ascent and the alignment, and 2) the minimum
 1082               // descent and the alignment. We'll choose the larger of these two numbers.
 1083               r.minimum = Math.round(Math.max(totalAscent.minimum / r.alignment,
 1084                                             totalDescent.minimum / (1.0f - r.alignment)));
 1085               // a similar calculation is made for the maximum but we choose the smaller number.
 1086               r.maximum = Math.round(Math.min(totalAscent.maximum / r.alignment,
 1087                                             totalDescent.maximum / (1.0f - r.alignment)));
 1088           }
 1089   
 1090           return r;
 1091       }
 1092   
 1093       /**
 1094        * Fetches the offset of a particular child's current layout.
 1095        * @param axis the axis being studied
 1096        * @param childIndex the index of the requested child
 1097        * @return the offset (location) for the specified child
 1098        */
 1099       protected int getOffset(int axis, int childIndex) {
 1100           int[] offsets = (axis == majorAxis) ? majorOffsets : minorOffsets;
 1101           return offsets[childIndex];
 1102       }
 1103   
 1104       /**
 1105        * Fetches the span of a particular childs current layout.
 1106        * @param axis the axis being studied
 1107        * @param childIndex the index of the requested child
 1108        * @return the span (width or height) of the specified child
 1109        */
 1110       protected int getSpan(int axis, int childIndex) {
 1111           int[] spans = (axis == majorAxis) ? majorSpans : minorSpans;
 1112           return spans[childIndex];
 1113       }
 1114   
 1115       /**
 1116        * Determines in which direction the next view lays.
 1117        * Consider the View at index n. Typically the <code>View</code>s
 1118        * are layed out from left to right, so that the <code>View</code>
 1119        * to the EAST will be at index n + 1, and the <code>View</code>
 1120        * to the WEST will be at index n - 1. In certain situations,
 1121        * such as with bidirectional text, it is possible
 1122        * that the <code>View</code> to EAST is not at index n + 1,
 1123        * but rather at index n - 1, or that the <code>View</code>
 1124        * to the WEST is not at index n - 1, but index n + 1.
 1125        * In this case this method would return true,
 1126        * indicating the <code>View</code>s are layed out in
 1127        * descending order. Otherwise the method would return false
 1128        * indicating the <code>View</code>s are layed out in ascending order.
 1129        * <p>
 1130        * If the receiver is laying its <code>View</code>s along the
 1131        * <code>Y_AXIS</code>, this will will return the value from
 1132        * invoking the same method on the <code>View</code>
 1133        * responsible for rendering <code>position</code> and
 1134        * <code>bias</code>. Otherwise this will return false.
 1135        *
 1136        * @param position position into the model
 1137        * @param bias either <code>Position.Bias.Forward</code> or
 1138        *          <code>Position.Bias.Backward</code>
 1139        * @return true if the <code>View</code>s surrounding the
 1140        *          <code>View</code> responding for rendering
 1141        *          <code>position</code> and <code>bias</code>
 1142        *          are layed out in descending order; otherwise false
 1143        */
 1144       protected boolean flipEastAndWestAtEnds(int position,
 1145                                               Position.Bias bias) {
 1146           if(majorAxis == Y_AXIS) {
 1147               int testPos = (bias == Position.Bias.Backward) ?
 1148                             Math.max(0, position - 1) : position;
 1149               int index = getViewIndexAtPosition(testPos);
 1150               if(index != -1) {
 1151                   View v = getView(index);
 1152                   if(v != null && v instanceof CompositeView) {
 1153                       return ((CompositeView)v).flipEastAndWestAtEnds(position,
 1154                                                                       bias);
 1155                   }
 1156               }
 1157           }
 1158           return false;
 1159       }
 1160   
 1161       // --- variables ------------------------------------------------
 1162   
 1163       int majorAxis;
 1164   
 1165       int majorSpan;
 1166       int minorSpan;
 1167   
 1168       /*
 1169        * Request cache
 1170        */
 1171       boolean majorReqValid;
 1172       boolean minorReqValid;
 1173       SizeRequirements majorRequest;
 1174       SizeRequirements minorRequest;
 1175   
 1176       /*
 1177        * Allocation cache
 1178        */
 1179       boolean majorAllocValid;
 1180       int[] majorOffsets;
 1181       int[] majorSpans;
 1182       boolean minorAllocValid;
 1183       int[] minorOffsets;
 1184       int[] minorSpans;
 1185   
 1186       /** used in paint. */
 1187       Rectangle tempRect;
 1188   }

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