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

    1   /*
    2    * Copyright (c) 1999, 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.util.Vector;
   29   import javax.swing.event;
   30   import javax.swing.SizeRequirements;
   31   
   32   /**
   33    * A View that tries to flow it's children into some
   34    * partially constrained space.  This can be used to
   35    * build things like paragraphs, pages, etc.  The
   36    * flow is made up of the following pieces of functionality.
   37    * <ul>
   38    * <li>A logical set of child views, which as used as a
   39    * layout pool from which a physical view is formed.
   40    * <li>A strategy for translating the logical view to
   41    * a physical (flowed) view.
   42    * <li>Constraints for the strategy to work against.
   43    * <li>A physical structure, that represents the flow.
   44    * The children of this view are where the pieces of
   45    * of the logical views are placed to create the flow.
   46    * </ul>
   47    *
   48    * @author  Timothy Prinzing
   49    * @see     View
   50    * @since 1.3
   51    */
   52   public abstract class FlowView extends BoxView {
   53   
   54       /**
   55        * Constructs a FlowView for the given element.
   56        *
   57        * @param elem the element that this view is responsible for
   58        * @param axis may be either View.X_AXIS or View.Y_AXIS
   59        */
   60       public FlowView(Element elem, int axis) {
   61           super(elem, axis);
   62           layoutSpan = Integer.MAX_VALUE;
   63           strategy = new FlowStrategy();
   64       }
   65   
   66       /**
   67        * Fetches the axis along which views should be
   68        * flowed.  By default, this will be the axis
   69        * orthogonal to the axis along which the flow
   70        * rows are tiled (the axis of the default flow
   71        * rows themselves).  This is typically used
   72        * by the <code>FlowStrategy</code>.
   73        */
   74       public int getFlowAxis() {
   75           if (getAxis() == Y_AXIS) {
   76               return X_AXIS;
   77           }
   78           return Y_AXIS;
   79       }
   80   
   81       /**
   82        * Fetch the constraining span to flow against for
   83        * the given child index.  This is called by the
   84        * FlowStrategy while it is updating the flow.
   85        * A flow can be shaped by providing different values
   86        * for the row constraints.  By default, the entire
   87        * span inside of the insets along the flow axis
   88        * is returned.
   89        *
   90        * @param index the index of the row being updated.
   91        *   This should be a value >= 0 and < getViewCount().
   92        * @see #getFlowStart
   93        */
   94       public int getFlowSpan(int index) {
   95           return layoutSpan;
   96       }
   97   
   98       /**
   99        * Fetch the location along the flow axis that the
  100        * flow span will start at.  This is called by the
  101        * FlowStrategy while it is updating the flow.
  102        * A flow can be shaped by providing different values
  103        * for the row constraints.
  104   
  105        * @param index the index of the row being updated.
  106        *   This should be a value >= 0 and < getViewCount().
  107        * @see #getFlowSpan
  108        */
  109       public int getFlowStart(int index) {
  110           return 0;
  111       }
  112   
  113       /**
  114        * Create a View that should be used to hold a
  115        * a rows worth of children in a flow.  This is
  116        * called by the FlowStrategy when new children
  117        * are added or removed (i.e. rows are added or
  118        * removed) in the process of updating the flow.
  119        */
  120       protected abstract View createRow();
  121   
  122       // ---- BoxView methods -------------------------------------
  123   
  124       /**
  125        * Loads all of the children to initialize the view.
  126        * This is called by the <code>setParent</code> method.
  127        * This is reimplemented to not load any children directly
  128        * (as they are created in the process of formatting).
  129        * If the layoutPool variable is null, an instance of
  130        * LogicalView is created to represent the logical view
  131        * that is used in the process of formatting.
  132        *
  133        * @param f the view factory
  134        */
  135       protected void loadChildren(ViewFactory f) {
  136           if (layoutPool == null) {
  137               layoutPool = new LogicalView(getElement());
  138           }
  139           layoutPool.setParent(this);
  140   
  141           // This synthetic insertUpdate call gives the strategy a chance
  142           // to initialize.
  143           strategy.insertUpdate(this, null, null);
  144       }
  145   
  146       /**
  147        * Fetches the child view index representing the given position in
  148        * the model.
  149        *
  150        * @param pos the position >= 0
  151        * @return  index of the view representing the given position, or
  152        *   -1 if no view represents that position
  153        */
  154       protected int getViewIndexAtPosition(int pos) {
  155           if (pos >= getStartOffset() && (pos < getEndOffset())) {
  156               for (int counter = 0; counter < getViewCount(); counter++) {
  157                   View v = getView(counter);
  158                   if(pos >= v.getStartOffset() &&
  159                      pos < v.getEndOffset()) {
  160                       return counter;
  161                   }
  162               }
  163           }
  164           return -1;
  165       }
  166   
  167       /**
  168        * Lays out the children.  If the span along the flow
  169        * axis has changed, layout is marked as invalid which
  170        * which will cause the superclass behavior to recalculate
  171        * the layout along the box axis.  The FlowStrategy.layout
  172        * method will be called to rebuild the flow rows as
  173        * appropriate.  If the height of this view changes
  174        * (determined by the perferred size along the box axis),
  175        * a preferenceChanged is called.  Following all of that,
  176        * the normal box layout of the superclass is performed.
  177        *
  178        * @param width  the width to lay out against >= 0.  This is
  179        *   the width inside of the inset area.
  180        * @param height the height to lay out against >= 0 This
  181        *   is the height inside of the inset area.
  182        */
  183       protected void layout(int width, int height) {
  184           final int faxis = getFlowAxis();
  185           int newSpan;
  186           if (faxis == X_AXIS) {
  187               newSpan = width;
  188           } else {
  189               newSpan = height;
  190           }
  191           if (layoutSpan != newSpan) {
  192               layoutChanged(faxis);
  193               layoutChanged(getAxis());
  194               layoutSpan = newSpan;
  195           }
  196   
  197           // repair the flow if necessary
  198           if (! isLayoutValid(faxis)) {
  199               final int heightAxis = getAxis();
  200               int oldFlowHeight = (heightAxis == X_AXIS)? getWidth() : getHeight();
  201               strategy.layout(this);
  202               int newFlowHeight = (int) getPreferredSpan(heightAxis);
  203               if (oldFlowHeight != newFlowHeight) {
  204                   View p = getParent();
  205                   if (p != null) {
  206                       p.preferenceChanged(this, (heightAxis == X_AXIS), (heightAxis == Y_AXIS));
  207                   }
  208   
  209                   // PENDING(shannonh)
  210                   // Temporary fix for 4250847
  211                   // Can be removed when TraversalContext is added
  212                   Component host = getContainer();
  213                   if (host != null) {
  214                       //nb idk 12/12/2001 host should not be equal to null. We need to add assertion here
  215                       host.repaint();
  216                   }
  217               }
  218           }
  219   
  220           super.layout(width, height);
  221       }
  222   
  223       /**
  224        * Calculate equirements along the minor axis.  This
  225        * is implemented to forward the request to the logical
  226        * view by calling getMinimumSpan, getPreferredSpan, and
  227        * getMaximumSpan on it.
  228        */
  229       protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
  230           if (r == null) {
  231               r = new SizeRequirements();
  232           }
  233           float pref = layoutPool.getPreferredSpan(axis);
  234           float min = layoutPool.getMinimumSpan(axis);
  235           // Don't include insets, Box.getXXXSpan will include them.
  236           r.minimum = (int)min;
  237           r.preferred = Math.max(r.minimum, (int) pref);
  238           r.maximum = Integer.MAX_VALUE;
  239           r.alignment = 0.5f;
  240           return r;
  241       }
  242   
  243       // ---- View methods ----------------------------------------------------
  244   
  245       /**
  246        * Gives notification that something was inserted into the document
  247        * in a location that this view is responsible for.
  248        *
  249        * @param changes the change information from the associated document
  250        * @param a the current allocation of the view
  251        * @param f the factory to use to rebuild if the view has children
  252        * @see View#insertUpdate
  253        */
  254       public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
  255           layoutPool.insertUpdate(changes, a, f);
  256           strategy.insertUpdate(this, changes, getInsideAllocation(a));
  257       }
  258   
  259       /**
  260        * Gives notification that something was removed from the document
  261        * in a location that this view is responsible for.
  262        *
  263        * @param changes the change information from the associated document
  264        * @param a the current allocation of the view
  265        * @param f the factory to use to rebuild if the view has children
  266        * @see View#removeUpdate
  267        */
  268       public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
  269           layoutPool.removeUpdate(changes, a, f);
  270           strategy.removeUpdate(this, changes, getInsideAllocation(a));
  271       }
  272   
  273       /**
  274        * Gives notification from the document that attributes were changed
  275        * in a location that this view is responsible for.
  276        *
  277        * @param changes the change information from the associated document
  278        * @param a the current allocation of the view
  279        * @param f the factory to use to rebuild if the view has children
  280        * @see View#changedUpdate
  281        */
  282       public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
  283           layoutPool.changedUpdate(changes, a, f);
  284           strategy.changedUpdate(this, changes, getInsideAllocation(a));
  285       }
  286   
  287       /** {@inheritDoc} */
  288       public void setParent(View parent) {
  289           super.setParent(parent);
  290           if (parent == null
  291                   && layoutPool != null ) {
  292               layoutPool.setParent(null);
  293           }
  294       }
  295   
  296       // --- variables -----------------------------------------------
  297   
  298       /**
  299        * Default constraint against which the flow is
  300        * created against.
  301        */
  302       protected int layoutSpan;
  303   
  304       /**
  305        * These are the views that represent the child elements
  306        * of the element this view represents (The logical view
  307        * to translate to a physical view).  These are not
  308        * directly children of this view.  These are either
  309        * placed into the rows directly or used for the purpose
  310        * of breaking into smaller chunks, to form the physical
  311        * view.
  312        */
  313       protected View layoutPool;
  314   
  315       /**
  316        * The behavior for keeping the flow updated.  By
  317        * default this is a singleton shared by all instances
  318        * of FlowView (FlowStrategy is stateless).  Subclasses
  319        * can create an alternative strategy, which might keep
  320        * state.
  321        */
  322       protected FlowStrategy strategy;
  323   
  324       /**
  325        * Strategy for maintaining the physical form
  326        * of the flow.  The default implementation is
  327        * completely stateless, and recalculates the
  328        * entire flow if the layout is invalid on the
  329        * given FlowView.  Alternative strategies can
  330        * be implemented by subclassing, and might
  331        * perform incrementatal repair to the layout
  332        * or alternative breaking behavior.
  333        * @since 1.3
  334        */
  335       public static class FlowStrategy {
  336           Position damageStart = null;
  337           Vector<View> viewBuffer;
  338   
  339           void addDamage(FlowView fv, int offset) {
  340               if (offset >= fv.getStartOffset() && offset < fv.getEndOffset()) {
  341                   if (damageStart == null || offset < damageStart.getOffset()) {
  342                       try {
  343                           damageStart = fv.getDocument().createPosition(offset);
  344                       } catch (BadLocationException e) {
  345                           // shouldn't happen since offset is inside view bounds
  346                           assert(false);
  347                       }
  348                   }
  349               }
  350           }
  351   
  352           void unsetDamage() {
  353               damageStart = null;
  354           }
  355   
  356           /**
  357            * Gives notification that something was inserted into the document
  358            * in a location that the given flow view is responsible for.  The
  359            * strategy should update the appropriate changed region (which
  360            * depends upon the strategy used for repair).
  361            *
  362            * @param e the change information from the associated document
  363            * @param alloc the current allocation of the view inside of the insets.
  364            *   This value will be null if the view has not yet been displayed.
  365            * @see View#insertUpdate
  366            */
  367           public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
  368               // FlowView.loadChildren() makes a synthetic call into this,
  369               // passing null as e
  370               if (e != null) {
  371                   addDamage(fv, e.getOffset());
  372               }
  373   
  374               if (alloc != null) {
  375                   Component host = fv.getContainer();
  376                   if (host != null) {
  377                       host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  378                   }
  379               } else {
  380                   fv.preferenceChanged(null, true, true);
  381               }
  382           }
  383   
  384           /**
  385            * Gives notification that something was removed from the document
  386            * in a location that the given flow view is responsible for.
  387            *
  388            * @param e the change information from the associated document
  389            * @param alloc the current allocation of the view inside of the insets.
  390            * @see View#removeUpdate
  391            */
  392           public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
  393               addDamage(fv, e.getOffset());
  394               if (alloc != null) {
  395                   Component host = fv.getContainer();
  396                   if (host != null) {
  397                       host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  398                   }
  399               } else {
  400                   fv.preferenceChanged(null, true, true);
  401               }
  402           }
  403   
  404           /**
  405            * Gives notification from the document that attributes were changed
  406            * in a location that this view is responsible for.
  407            *
  408            * @param fv     the <code>FlowView</code> containing the changes
  409            * @param e      the <code>DocumentEvent</code> describing the changes
  410            *               done to the Document
  411            * @param alloc  Bounds of the View
  412            * @see View#changedUpdate
  413            */
  414           public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) {
  415               addDamage(fv, e.getOffset());
  416               if (alloc != null) {
  417                   Component host = fv.getContainer();
  418                   if (host != null) {
  419                       host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
  420                   }
  421               } else {
  422                   fv.preferenceChanged(null, true, true);
  423               }
  424           }
  425   
  426           /**
  427            * This method gives flow strategies access to the logical
  428            * view of the FlowView.
  429            */
  430           protected View getLogicalView(FlowView fv) {
  431               return fv.layoutPool;
  432           }
  433   
  434           /**
  435            * Update the flow on the given FlowView.  By default, this causes
  436            * all of the rows (child views) to be rebuilt to match the given
  437            * constraints for each row.  This is called by a FlowView.layout
  438            * to update the child views in the flow.
  439            *
  440            * @param fv the view to reflow
  441            */
  442           public void layout(FlowView fv) {
  443               View pool = getLogicalView(fv);
  444               int rowIndex, p0;
  445               int p1 = fv.getEndOffset();
  446   
  447               if (fv.majorAllocValid) {
  448                   if (damageStart == null) {
  449                       return;
  450                   }
  451                   // In some cases there's no view at position damageStart, so
  452                   // step back and search again. See 6452106 for details.
  453                   int offset = damageStart.getOffset();
  454                   while ((rowIndex = fv.getViewIndexAtPosition(offset)) < 0) {
  455                       offset--;
  456                   }
  457                   if (rowIndex > 0) {
  458                       rowIndex--;
  459                   }
  460                   p0 = fv.getView(rowIndex).getStartOffset();
  461               } else {
  462                   rowIndex = 0;
  463                   p0 = fv.getStartOffset();
  464               }
  465               reparentViews(pool, p0);
  466   
  467               viewBuffer = new Vector<View>(10, 10);
  468               int rowCount = fv.getViewCount();
  469               while (p0 < p1) {
  470                   View row;
  471                   if (rowIndex >= rowCount) {
  472                       row = fv.createRow();
  473                       fv.append(row);
  474                   } else {
  475                       row = fv.getView(rowIndex);
  476                   }
  477                   p0 = layoutRow(fv, rowIndex, p0);
  478                   rowIndex++;
  479               }
  480               viewBuffer = null;
  481   
  482               if (rowIndex < rowCount) {
  483                   fv.replace(rowIndex, rowCount - rowIndex, null);
  484               }
  485               unsetDamage();
  486           }
  487   
  488           /**
  489            * Creates a row of views that will fit within the
  490            * layout span of the row.  This is called by the layout method.
  491            * This is implemented to fill the row by repeatedly calling
  492            * the createView method until the available span has been
  493            * exhausted, a forced break was encountered, or the createView
  494            * method returned null.  If the remaining span was exhaused,
  495            * the adjustRow method will be called to perform adjustments
  496            * to the row to try and make it fit into the given span.
  497            *
  498            * @param rowIndex the index of the row to fill in with views.  The
  499            *   row is assumed to be empty on entry.
  500            * @param pos  The current position in the children of
  501            *   this views element from which to start.
  502            * @return the position to start the next row
  503            */
  504           protected int layoutRow(FlowView fv, int rowIndex, int pos) {
  505               View row = fv.getView(rowIndex);
  506               float x = fv.getFlowStart(rowIndex);
  507               float spanLeft = fv.getFlowSpan(rowIndex);
  508               int end = fv.getEndOffset();
  509               TabExpander te = (fv instanceof TabExpander) ? (TabExpander)fv : null;
  510               final int flowAxis = fv.getFlowAxis();
  511   
  512               int breakWeight = BadBreakWeight;
  513               float breakX = 0f;
  514               float breakSpan = 0f;
  515               int breakIndex = -1;
  516               int n = 0;
  517   
  518               viewBuffer.clear();
  519               while (pos < end && spanLeft >= 0) {
  520                   View v = createView(fv, pos, (int)spanLeft, rowIndex);
  521                   if (v == null) {
  522                       break;
  523                   }
  524   
  525                   int bw = v.getBreakWeight(flowAxis, x, spanLeft);
  526                   if (bw >= ForcedBreakWeight) {
  527                       View w = v.breakView(flowAxis, pos, x, spanLeft);
  528                       if (w != null) {
  529                           viewBuffer.add(w);
  530                       } else if (n == 0) {
  531                           // if the view does not break, and it is the only view
  532                           // in a row, use the whole view
  533                           viewBuffer.add(v);
  534                       }
  535                       break;
  536                   } else if (bw >= breakWeight && bw > BadBreakWeight) {
  537                       breakWeight = bw;
  538                       breakX = x;
  539                       breakSpan = spanLeft;
  540                       breakIndex = n;
  541                   }
  542   
  543                   float chunkSpan;
  544                   if (flowAxis == X_AXIS && v instanceof TabableView) {
  545                       chunkSpan = ((TabableView)v).getTabbedSpan(x, te);
  546                   } else {
  547                       chunkSpan = v.getPreferredSpan(flowAxis);
  548                   }
  549   
  550                   if (chunkSpan > spanLeft && breakIndex >= 0) {
  551                       // row is too long, and we may break
  552                       if (breakIndex < n) {
  553                           v = viewBuffer.get(breakIndex);
  554                       }
  555                       for (int i = n - 1; i >= breakIndex; i--) {
  556                           viewBuffer.remove(i);
  557                       }
  558                       v = v.breakView(flowAxis, v.getStartOffset(), breakX, breakSpan);
  559                   }
  560   
  561                   spanLeft -= chunkSpan;
  562                   x += chunkSpan;
  563                   viewBuffer.add(v);
  564                   pos = v.getEndOffset();
  565                   n++;
  566               }
  567   
  568               View[] views = new View[viewBuffer.size()];
  569               viewBuffer.toArray(views);
  570               row.replace(0, row.getViewCount(), views);
  571               return (views.length > 0 ? row.getEndOffset() : pos);
  572           }
  573   
  574           /**
  575            * Adjusts the given row if possible to fit within the
  576            * layout span.  By default this will try to find the
  577            * highest break weight possible nearest the end of
  578            * the row.  If a forced break is encountered, the
  579            * break will be positioned there.
  580            *
  581            * @param rowIndex the row to adjust to the current layout
  582            *  span.
  583            * @param desiredSpan the current layout span >= 0
  584            * @param x the location r starts at.
  585            */
  586           protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
  587               final int flowAxis = fv.getFlowAxis();
  588               View r = fv.getView(rowIndex);
  589               int n = r.getViewCount();
  590               int span = 0;
  591               int bestWeight = BadBreakWeight;
  592               int bestSpan = 0;
  593               int bestIndex = -1;
  594               View v;
  595               for (int i = 0; i < n; i++) {
  596                   v = r.getView(i);
  597                   int spanLeft = desiredSpan - span;
  598   
  599                   int w = v.getBreakWeight(flowAxis, x + span, spanLeft);
  600                   if ((w >= bestWeight) && (w > BadBreakWeight)) {
  601                       bestWeight = w;
  602                       bestIndex = i;
  603                       bestSpan = span;
  604                       if (w >= ForcedBreakWeight) {
  605                           // it's a forced break, so there is
  606                           // no point in searching further.
  607                           break;
  608                       }
  609                   }
  610                   span += v.getPreferredSpan(flowAxis);
  611               }
  612               if (bestIndex < 0) {
  613                   // there is nothing that can be broken, leave
  614                   // it in it's current state.
  615                   return;
  616               }
  617   
  618               // Break the best candidate view, and patch up the row.
  619               int spanLeft = desiredSpan - bestSpan;
  620               v = r.getView(bestIndex);
  621               v = v.breakView(flowAxis, v.getStartOffset(), x + bestSpan, spanLeft);
  622               View[] va = new View[1];
  623               va[0] = v;
  624               View lv = getLogicalView(fv);
  625               int p0 = r.getView(bestIndex).getStartOffset();
  626               int p1 = r.getEndOffset();
  627               for (int i = 0; i < lv.getViewCount(); i++) {
  628                   View tmpView = lv.getView(i);
  629                   if (tmpView.getEndOffset() > p1) {
  630                       break;
  631                   }
  632                   if (tmpView.getStartOffset() >= p0) {
  633                       tmpView.setParent(lv);
  634                   }
  635               }
  636               r.replace(bestIndex, n - bestIndex, va);
  637           }
  638   
  639           void reparentViews(View pool, int startPos) {
  640               int n = pool.getViewIndex(startPos, Position.Bias.Forward);
  641               if (n >= 0) {
  642                   for (int i = n; i < pool.getViewCount(); i++) {
  643                       pool.getView(i).setParent(pool);
  644                   }
  645               }
  646           }
  647   
  648           /**
  649            * Creates a view that can be used to represent the current piece
  650            * of the flow.  This can be either an entire view from the
  651            * logical view, or a fragment of the logical view.
  652            *
  653            * @param fv the view holding the flow
  654            * @param startOffset the start location for the view being created
  655            * @param spanLeft the about of span left to fill in the row
  656            * @param rowIndex the row the view will be placed into
  657            */
  658           protected View createView(FlowView fv, int startOffset, int spanLeft, int rowIndex) {
  659               // Get the child view that contains the given starting position
  660               View lv = getLogicalView(fv);
  661               int childIndex = lv.getViewIndex(startOffset, Position.Bias.Forward);
  662               View v = lv.getView(childIndex);
  663               if (startOffset==v.getStartOffset()) {
  664                   // return the entire view
  665                   return v;
  666               }
  667   
  668               // return a fragment.
  669               v = v.createFragment(startOffset, v.getEndOffset());
  670               return v;
  671           }
  672       }
  673   
  674       /**
  675        * This class can be used to represent a logical view for
  676        * a flow.  It keeps the children updated to reflect the state
  677        * of the model, gives the logical child views access to the
  678        * view hierarchy, and calculates a preferred span.  It doesn't
  679        * do any rendering, layout, or model/view translation.
  680        */
  681       static class LogicalView extends CompositeView {
  682   
  683           LogicalView(Element elem) {
  684               super(elem);
  685           }
  686   
  687           protected int getViewIndexAtPosition(int pos) {
  688               Element elem = getElement();
  689               if (elem.isLeaf()) {
  690                   return 0;
  691               }
  692               return super.getViewIndexAtPosition(pos);
  693           }
  694   
  695           protected void loadChildren(ViewFactory f) {
  696               Element elem = getElement();
  697               if (elem.isLeaf()) {
  698                   View v = new LabelView(elem);
  699                   append(v);
  700               } else {
  701                   super.loadChildren(f);
  702               }
  703           }
  704   
  705           /**
  706            * Fetches the attributes to use when rendering.  This view
  707            * isn't directly responsible for an element so it returns
  708            * the outer classes attributes.
  709            */
  710           public AttributeSet getAttributes() {
  711               View p = getParent();
  712               return (p != null) ? p.getAttributes() : null;
  713           }
  714   
  715           /**
  716            * Determines the preferred span for this view along an
  717            * axis.
  718            *
  719            * @param axis may be either View.X_AXIS or View.Y_AXIS
  720            * @return   the span the view would like to be rendered into.
  721            *           Typically the view is told to render into the span
  722            *           that is returned, although there is no guarantee.
  723            *           The parent may choose to resize or break the view.
  724            * @see View#getPreferredSpan
  725            */
  726           public float getPreferredSpan(int axis) {
  727               float maxpref = 0;
  728               float pref = 0;
  729               int n = getViewCount();
  730               for (int i = 0; i < n; i++) {
  731                   View v = getView(i);
  732                   pref += v.getPreferredSpan(axis);
  733                   if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) >= ForcedBreakWeight) {
  734                       maxpref = Math.max(maxpref, pref);
  735                       pref = 0;
  736                   }
  737               }
  738               maxpref = Math.max(maxpref, pref);
  739               return maxpref;
  740           }
  741   
  742           /**
  743            * Determines the minimum span for this view along an
  744            * axis.  The is implemented to find the minimum unbreakable
  745            * span.
  746            *
  747            * @param axis may be either View.X_AXIS or View.Y_AXIS
  748            * @return  the span the view would like to be rendered into.
  749            *           Typically the view is told to render into the span
  750            *           that is returned, although there is no guarantee.
  751            *           The parent may choose to resize or break the view.
  752            * @see View#getPreferredSpan
  753            */
  754           public float getMinimumSpan(int axis) {
  755               float maxmin = 0;
  756               float min = 0;
  757               boolean nowrap = false;
  758               int n = getViewCount();
  759               for (int i = 0; i < n; i++) {
  760                   View v = getView(i);
  761                   if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) == BadBreakWeight) {
  762                       min += v.getPreferredSpan(axis);
  763                       nowrap = true;
  764                   } else if (nowrap) {
  765                       maxmin = Math.max(min, maxmin);
  766                       nowrap = false;
  767                       min = 0;
  768                   }
  769                   if (v instanceof ComponentView) {
  770                       maxmin = Math.max(maxmin, v.getMinimumSpan(axis));
  771                   }
  772               }
  773               maxmin = Math.max(maxmin, min);
  774               return maxmin;
  775           }
  776   
  777           /**
  778            * Forward the DocumentEvent to the given child view.  This
  779            * is implemented to reparent the child to the logical view
  780            * (the children may have been parented by a row in the flow
  781            * if they fit without breaking) and then execute the superclass
  782            * behavior.
  783            *
  784            * @param v the child view to forward the event to.
  785            * @param e the change information from the associated document
  786            * @param a the current allocation of the view
  787            * @param f the factory to use to rebuild if the view has children
  788            * @see #forwardUpdate
  789            * @since 1.3
  790            */
  791           protected void forwardUpdateToView(View v, DocumentEvent e,
  792                                              Shape a, ViewFactory f) {
  793               View parent = v.getParent();
  794               v.setParent(this);
  795               super.forwardUpdateToView(v, e, a, f);
  796               v.setParent(parent);
  797           }
  798   
  799           // The following methods don't do anything useful, they
  800           // simply keep the class from being abstract.
  801   
  802           /**
  803            * Renders using the given rendering surface and area on that
  804            * surface.  This is implemented to do nothing, the logical
  805            * view is never visible.
  806            *
  807            * @param g the rendering surface to use
  808            * @param allocation the allocated region to render into
  809            * @see View#paint
  810            */
  811           public void paint(Graphics g, Shape allocation) {
  812           }
  813   
  814           /**
  815            * Tests whether a point lies before the rectangle range.
  816            * Implemented to return false, as hit detection is not
  817            * performed on the logical view.
  818            *
  819            * @param x the X coordinate >= 0
  820            * @param y the Y coordinate >= 0
  821            * @param alloc the rectangle
  822            * @return true if the point is before the specified range
  823            */
  824           protected boolean isBefore(int x, int y, Rectangle alloc) {
  825               return false;
  826           }
  827   
  828           /**
  829            * Tests whether a point lies after the rectangle range.
  830            * Implemented to return false, as hit detection is not
  831            * performed on the logical view.
  832            *
  833            * @param x the X coordinate >= 0
  834            * @param y the Y coordinate >= 0
  835            * @param alloc the rectangle
  836            * @return true if the point is after the specified range
  837            */
  838           protected boolean isAfter(int x, int y, Rectangle alloc) {
  839               return false;
  840           }
  841   
  842           /**
  843            * Fetches the child view at the given point.
  844            * Implemented to return null, as hit detection is not
  845            * performed on the logical view.
  846            *
  847            * @param x the X coordinate >= 0
  848            * @param y the Y coordinate >= 0
  849            * @param alloc the parent's allocation on entry, which should
  850            *   be changed to the child's allocation on exit
  851            * @return the child view
  852            */
  853           protected View getViewAtPoint(int x, int y, Rectangle alloc) {
  854               return null;
  855           }
  856   
  857           /**
  858            * Returns the allocation for a given child.
  859            * Implemented to do nothing, as the logical view doesn't
  860            * perform layout on the children.
  861            *
  862            * @param index the index of the child, >= 0 && < getViewCount()
  863            * @param a  the allocation to the interior of the box on entry,
  864            *   and the allocation of the child view at the index on exit.
  865            */
  866           protected void childAllocation(int index, Rectangle a) {
  867           }
  868       }
  869   
  870   
  871   }

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