mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			842 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			842 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			Java
		
	
	
	
/* FlowView.java -- A composite View
 | 
						|
   Copyright (C) 2005  Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package javax.swing.text;
 | 
						|
 | 
						|
import java.awt.Component;
 | 
						|
import java.awt.Graphics;
 | 
						|
import java.awt.Rectangle;
 | 
						|
import java.awt.Shape;
 | 
						|
 | 
						|
import javax.swing.SizeRequirements;
 | 
						|
import javax.swing.event.DocumentEvent;
 | 
						|
 | 
						|
/**
 | 
						|
 * A <code>View</code> that can flows it's children into it's layout space.
 | 
						|
 *
 | 
						|
 * The <code>FlowView</code> manages a set of logical views (that are
 | 
						|
 * the children of the {@link #layoutPool} field). These are translated
 | 
						|
 * at layout time into a set of physical views. These are the views that
 | 
						|
 * are managed as the real child views. Each of these child views represents
 | 
						|
 * a row and are laid out within a box using the superclasses behaviour.
 | 
						|
 * The concrete implementation of the rows must be provided by subclasses.
 | 
						|
 *
 | 
						|
 * @author Roman Kennke (roman@kennke.org)
 | 
						|
 */
 | 
						|
public abstract class FlowView extends BoxView
 | 
						|
{
 | 
						|
  /**
 | 
						|
   * A strategy for translating the logical views of a <code>FlowView</code>
 | 
						|
   * into the real views.
 | 
						|
   */
 | 
						|
  public static class FlowStrategy
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Creates a new instance of <code>FlowStragegy</code>.
 | 
						|
     */
 | 
						|
    public FlowStrategy()
 | 
						|
    {
 | 
						|
      // Nothing to do here.
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives notification from a <code>FlowView</code> that some content
 | 
						|
     * has been inserted into the document at a location that the
 | 
						|
     * <code>FlowView</code> is responsible for.
 | 
						|
     *
 | 
						|
     * The default implementation simply calls {@link #layout}.
 | 
						|
     *
 | 
						|
     * @param fv the flow view that sends the notification
 | 
						|
     * @param e the document event describing the change
 | 
						|
     * @param alloc the current allocation of the flow view
 | 
						|
     */
 | 
						|
    public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
 | 
						|
    {
 | 
						|
      if (alloc == null)
 | 
						|
        {
 | 
						|
          fv.layoutChanged(X_AXIS);
 | 
						|
          fv.layoutChanged(Y_AXIS);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          Component host = fv.getContainer();
 | 
						|
          if (host != null)
 | 
						|
            host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives notification from a <code>FlowView</code> that some content
 | 
						|
     * has been removed from the document at a location that the
 | 
						|
     * <code>FlowView</code> is responsible for.
 | 
						|
     *
 | 
						|
     * The default implementation simply calls {@link #layout}.
 | 
						|
     *
 | 
						|
     * @param fv the flow view that sends the notification
 | 
						|
     * @param e the document event describing the change
 | 
						|
     * @param alloc the current allocation of the flow view
 | 
						|
     */
 | 
						|
    public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
 | 
						|
    {
 | 
						|
      if (alloc == null)
 | 
						|
        {
 | 
						|
          fv.layoutChanged(X_AXIS);
 | 
						|
          fv.layoutChanged(Y_AXIS);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          Component host = fv.getContainer();
 | 
						|
          if (host != null)
 | 
						|
            host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives notification from a <code>FlowView</code> that some attributes
 | 
						|
     * have changed in the document at a location that the
 | 
						|
     * <code>FlowView</code> is responsible for.
 | 
						|
     *
 | 
						|
     * The default implementation simply calls {@link #layout}.
 | 
						|
     *
 | 
						|
     * @param fv the flow view that sends the notification
 | 
						|
     * @param e the document event describing the change
 | 
						|
     * @param alloc the current allocation of the flow view
 | 
						|
     */
 | 
						|
    public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc)
 | 
						|
    {
 | 
						|
      if (alloc == null)
 | 
						|
        {
 | 
						|
          fv.layoutChanged(X_AXIS);
 | 
						|
          fv.layoutChanged(Y_AXIS);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          Component host = fv.getContainer();
 | 
						|
          if (host != null)
 | 
						|
            host.repaint(alloc.x, alloc.y, alloc.width, alloc.height);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the logical view of the managed <code>FlowView</code>.
 | 
						|
     *
 | 
						|
     * @param fv the flow view for which to return the logical view
 | 
						|
     *
 | 
						|
     * @return the logical view of the managed <code>FlowView</code>
 | 
						|
     */
 | 
						|
    protected View getLogicalView(FlowView fv)
 | 
						|
    {
 | 
						|
      return fv.layoutPool;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Performs the layout for the whole view. By default this rebuilds
 | 
						|
     * all the physical views from the logical views of the managed FlowView.
 | 
						|
     *
 | 
						|
     * This is called by {@link FlowView#layout} to update the layout of
 | 
						|
     * the view.
 | 
						|
     *
 | 
						|
     * @param fv the flow view for which we perform the layout
 | 
						|
     */
 | 
						|
    public void layout(FlowView fv)
 | 
						|
    {
 | 
						|
      int start = fv.getStartOffset();
 | 
						|
      int end = fv.getEndOffset();
 | 
						|
 | 
						|
      // Preserve the views from the logical view from beeing removed.
 | 
						|
      View lv = getLogicalView(fv);
 | 
						|
      int viewCount = lv.getViewCount();
 | 
						|
      for (int i = 0; i < viewCount; i++)
 | 
						|
        {
 | 
						|
          View v = lv.getView(i);
 | 
						|
          v.setParent(lv);
 | 
						|
        }
 | 
						|
 | 
						|
      // Then remove all views from the flow view.
 | 
						|
      fv.removeAll();
 | 
						|
 | 
						|
      for (int rowIndex = 0; start < end; rowIndex++)
 | 
						|
        {
 | 
						|
          View row = fv.createRow();
 | 
						|
          fv.append(row);
 | 
						|
          int next = layoutRow(fv, rowIndex, start);
 | 
						|
          if (row.getViewCount() == 0)
 | 
						|
            {
 | 
						|
              row.append(createView(fv, start, Integer.MAX_VALUE, rowIndex));
 | 
						|
              next = row.getEndOffset();
 | 
						|
            }
 | 
						|
          if (start < next)
 | 
						|
            start = next;
 | 
						|
          else
 | 
						|
            assert false: "May not happen";
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Lays out one row of the flow view. This is called by {@link #layout} to
 | 
						|
     * fill one row with child views until the available span is exhausted. The
 | 
						|
     * default implementation fills the row by calling
 | 
						|
     * {@link #createView(FlowView, int, int, int)} until the available space is
 | 
						|
     * exhausted, a forced break is encountered or there are no more views in
 | 
						|
     * the logical view. If the available space is exhausted,
 | 
						|
     * {@link #adjustRow(FlowView, int, int, int)} is called to fit the row into
 | 
						|
     * the available span.
 | 
						|
     *
 | 
						|
     * @param fv the flow view for which we perform the layout
 | 
						|
     * @param rowIndex the index of the row
 | 
						|
     * @param pos the model position for the beginning of the row
 | 
						|
     * @return the start position of the next row
 | 
						|
     */
 | 
						|
    protected int layoutRow(FlowView fv, int rowIndex, int pos)
 | 
						|
    {
 | 
						|
      View row = fv.getView(rowIndex);
 | 
						|
      int axis = fv.getFlowAxis();
 | 
						|
      int span = fv.getFlowSpan(rowIndex);
 | 
						|
      int x = fv.getFlowStart(rowIndex);
 | 
						|
      int end = fv.getEndOffset();
 | 
						|
 | 
						|
      // Needed for adjusting indentation in adjustRow().
 | 
						|
      int preX = x;
 | 
						|
      int availableSpan = span;
 | 
						|
 | 
						|
      TabExpander tabExp = fv instanceof TabExpander ? (TabExpander) fv : null;
 | 
						|
 | 
						|
      boolean forcedBreak = false;
 | 
						|
      while (pos < end && span >= 0)
 | 
						|
        {
 | 
						|
          View view = createView(fv, pos, span, rowIndex);
 | 
						|
          if (view == null
 | 
						|
              || (span == 0 && view.getPreferredSpan(axis) > 0))
 | 
						|
            break;
 | 
						|
 | 
						|
          int viewSpan;
 | 
						|
          if (axis == X_AXIS && view instanceof TabableView)
 | 
						|
            viewSpan = (int) ((TabableView) view).getTabbedSpan(x, tabExp);
 | 
						|
          else
 | 
						|
            viewSpan = (int) view.getPreferredSpan(axis);
 | 
						|
 | 
						|
          // Break if the line if the view does not fit in this row or the
 | 
						|
          // line just must be broken.
 | 
						|
          int breakWeight = view.getBreakWeight(axis, pos, span);
 | 
						|
          if (breakWeight >= ForcedBreakWeight)
 | 
						|
            {
 | 
						|
              int rowViewCount = row.getViewCount();
 | 
						|
              if (rowViewCount > 0)
 | 
						|
                {
 | 
						|
                  view = view.breakView(axis, pos, x, span);
 | 
						|
                  if (view != null)
 | 
						|
                    {
 | 
						|
                      if (axis == X_AXIS && view instanceof TabableView)
 | 
						|
                        viewSpan =
 | 
						|
                          (int) ((TabableView) view).getTabbedSpan(x, tabExp);
 | 
						|
                      else
 | 
						|
                        viewSpan = (int) view.getPreferredSpan(axis);
 | 
						|
                    }
 | 
						|
                  else
 | 
						|
                    viewSpan = 0;
 | 
						|
                }
 | 
						|
              forcedBreak = true;
 | 
						|
            }
 | 
						|
          span -= viewSpan;
 | 
						|
          x += viewSpan;
 | 
						|
          if (view != null)
 | 
						|
            {
 | 
						|
              row.append(view);
 | 
						|
              pos = view.getEndOffset();
 | 
						|
            }
 | 
						|
          if (forcedBreak)
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
      if (span < 0)
 | 
						|
        adjustRow(fv, rowIndex, availableSpan, preX);
 | 
						|
      else if (row.getViewCount() == 0)
 | 
						|
        {
 | 
						|
          View view = createView(fv, pos, Integer.MAX_VALUE, rowIndex);
 | 
						|
          row.append(view);
 | 
						|
        }
 | 
						|
      return row.getEndOffset();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates physical views that form the rows of the flow view. This
 | 
						|
     * can be an entire view from the logical view (if it fits within the
 | 
						|
     * available span), a fragment of such a view (if it doesn't fit in the
 | 
						|
     * available span and can be broken down) or <code>null</code> (if it does
 | 
						|
     * not fit in the available span and also cannot be broken down).
 | 
						|
     *
 | 
						|
     * The default implementation fetches the logical view at the specified
 | 
						|
     * <code>startOffset</code>. If that view has a different startOffset than
 | 
						|
     * specified in the argument, a fragment is created using
 | 
						|
     * {@link View#createFragment(int, int)} that has the correct startOffset
 | 
						|
     * and the logical view's endOffset.
 | 
						|
     *
 | 
						|
     * @param fv the flow view
 | 
						|
     * @param startOffset the start offset for the view to be created
 | 
						|
     * @param spanLeft the available span
 | 
						|
     * @param rowIndex the index of the row
 | 
						|
     *
 | 
						|
     * @return a view to fill the row with, or <code>null</code> if there
 | 
						|
     *         is no view or view fragment that fits in the available span
 | 
						|
     */
 | 
						|
    protected View createView(FlowView fv, int startOffset, int spanLeft,
 | 
						|
                              int rowIndex)
 | 
						|
    {
 | 
						|
       View logicalView = getLogicalView(fv);
 | 
						|
       int index = logicalView.getViewIndex(startOffset,
 | 
						|
                                            Position.Bias.Forward);
 | 
						|
       View retVal = logicalView.getView(index);
 | 
						|
       if (retVal.getStartOffset() != startOffset)
 | 
						|
         retVal = retVal.createFragment(startOffset, retVal.getEndOffset());
 | 
						|
       return retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Tries to adjust the specified row to fit within the desired span. The
 | 
						|
     * default implementation iterates through the children of the specified
 | 
						|
     * row to find the view that has the highest break weight and - if there
 | 
						|
     * is more than one view with such a break weight - which is nearest to
 | 
						|
     * the end of the row. If there is such a view that has a break weight >
 | 
						|
     * {@link View#BadBreakWeight}, this view is broken using the
 | 
						|
     * {@link View#breakView(int, int, float, float)} method and this view and
 | 
						|
     * all views after the now broken view are replaced by the broken view.
 | 
						|
     *
 | 
						|
     * @param fv the flow view
 | 
						|
     * @param rowIndex the index of the row to be adjusted
 | 
						|
     * @param desiredSpan the layout span
 | 
						|
     * @param x the X location at which the row starts
 | 
						|
     */
 | 
						|
    protected void adjustRow(FlowView fv, int rowIndex, int desiredSpan, int x) {
 | 
						|
      // Determine the last view that has the highest break weight.
 | 
						|
      int axis = fv.getFlowAxis();
 | 
						|
      View row = fv.getView(rowIndex);
 | 
						|
      int count = row.getViewCount();
 | 
						|
      int breakIndex = -1;
 | 
						|
      int breakWeight = BadBreakWeight;
 | 
						|
      int breakSpan = 0;
 | 
						|
      int currentSpan = 0;
 | 
						|
      for (int i = 0; i < count; ++i)
 | 
						|
        {
 | 
						|
          View view = row.getView(i);
 | 
						|
          int spanLeft = desiredSpan - currentSpan;
 | 
						|
          int weight = view.getBreakWeight(axis, x + currentSpan, spanLeft);
 | 
						|
          if (weight >= breakWeight && weight > BadBreakWeight)
 | 
						|
            {
 | 
						|
              breakIndex = i;
 | 
						|
              breakSpan = currentSpan;
 | 
						|
              breakWeight = weight;
 | 
						|
              if (weight >= ForcedBreakWeight)
 | 
						|
                // Don't search further.
 | 
						|
                break;
 | 
						|
            }
 | 
						|
          currentSpan += view.getPreferredSpan(axis);
 | 
						|
        }
 | 
						|
 | 
						|
      // If there is a potential break location found, break the row at
 | 
						|
      // this location.
 | 
						|
      if (breakIndex >= 0)
 | 
						|
        {
 | 
						|
          int spanLeft = desiredSpan - breakSpan;
 | 
						|
          View toBeBroken = row.getView(breakIndex);
 | 
						|
          View brokenView = toBeBroken.breakView(axis,
 | 
						|
                                                 toBeBroken.getStartOffset(),
 | 
						|
                                                 x + breakSpan, spanLeft);
 | 
						|
          View lv = getLogicalView(fv);
 | 
						|
          for (int i = breakIndex; i < count; i++)
 | 
						|
            {
 | 
						|
              View tmp = row.getView(i);
 | 
						|
              if (contains(lv, tmp))
 | 
						|
                tmp.setParent(lv);
 | 
						|
              else if (tmp.getViewCount() > 0)
 | 
						|
                reparent(tmp, lv);
 | 
						|
            }
 | 
						|
          row.replace(breakIndex, count - breakIndex,
 | 
						|
                      new View[]{ brokenView });
 | 
						|
        }
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Helper method to determine if one view contains another as child.
 | 
						|
     */
 | 
						|
    private boolean contains(View view, View child)
 | 
						|
    {
 | 
						|
      boolean ret = false;
 | 
						|
      int n  = view.getViewCount();
 | 
						|
      for (int i = 0; i < n && ret == false; i++)
 | 
						|
        {
 | 
						|
          if (view.getView(i) == child)
 | 
						|
            ret = true;
 | 
						|
        }
 | 
						|
      return ret;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Helper method that reparents the <code>view</code> and all of its
 | 
						|
     * decendents to the <code>parent</code> (the logical view).
 | 
						|
     *
 | 
						|
     * @param view the view to reparent
 | 
						|
     * @param parent the new parent
 | 
						|
     */
 | 
						|
    private void reparent(View view, View parent)
 | 
						|
    {
 | 
						|
      int n = view.getViewCount();
 | 
						|
      for (int i = 0; i < n; i++)
 | 
						|
        {
 | 
						|
          View tmp = view.getView(i);
 | 
						|
          if (contains(parent, tmp))
 | 
						|
            tmp.setParent(parent);
 | 
						|
          else
 | 
						|
            reparent(tmp, parent);
 | 
						|
        }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This special subclass of <code>View</code> is used to represent
 | 
						|
   * the logical representation of this view. It does not support any
 | 
						|
   * visual representation, this is handled by the physical view implemented
 | 
						|
   * in the <code>FlowView</code>.
 | 
						|
   */
 | 
						|
  class LogicalView extends CompositeView
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Creates a new LogicalView instance.
 | 
						|
     */
 | 
						|
    LogicalView(Element el)
 | 
						|
    {
 | 
						|
      super(el);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to return the attributes of the parent
 | 
						|
     * (== the FlowView instance).
 | 
						|
     */
 | 
						|
    public AttributeSet getAttributes()
 | 
						|
    {
 | 
						|
      View p = getParent();
 | 
						|
      return p != null ? p.getAttributes() : null;
 | 
						|
    }
 | 
						|
 | 
						|
    protected void childAllocation(int index, Rectangle a)
 | 
						|
    {
 | 
						|
      // Nothing to do here (not visual).
 | 
						|
    }
 | 
						|
 | 
						|
    protected View getViewAtPoint(int x, int y, Rectangle r)
 | 
						|
    {
 | 
						|
      // Nothing to do here (not visual).
 | 
						|
      return null;
 | 
						|
    }
 | 
						|
 | 
						|
    protected boolean isAfter(int x, int y, Rectangle r)
 | 
						|
    {
 | 
						|
      // Nothing to do here (not visual).
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    protected boolean isBefore(int x, int y, Rectangle r)
 | 
						|
    {
 | 
						|
      // Nothing to do here (not visual).
 | 
						|
      return false;
 | 
						|
    }
 | 
						|
 | 
						|
    public float getPreferredSpan(int axis)
 | 
						|
    {
 | 
						|
      float max = 0;
 | 
						|
      float pref = 0;
 | 
						|
      int n = getViewCount();
 | 
						|
      for (int i = 0; i < n; i++)
 | 
						|
        {
 | 
						|
          View v = getView(i);
 | 
						|
          pref += v.getPreferredSpan(axis);
 | 
						|
          if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE)
 | 
						|
              >= ForcedBreakWeight)
 | 
						|
            {
 | 
						|
              max = Math.max(max, pref);
 | 
						|
              pref = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
      max = Math.max(max, pref);
 | 
						|
      return max;
 | 
						|
    }
 | 
						|
 | 
						|
    public float getMinimumSpan(int axis)
 | 
						|
    {
 | 
						|
      float max = 0;
 | 
						|
      float min = 0;
 | 
						|
      boolean wrap = true;
 | 
						|
      int n = getViewCount();
 | 
						|
      for (int i = 0; i < n; i++)
 | 
						|
        {
 | 
						|
          View v = getView(i);
 | 
						|
          if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE)
 | 
						|
              == BadBreakWeight)
 | 
						|
            {
 | 
						|
              min += v.getPreferredSpan(axis);
 | 
						|
              wrap = false;
 | 
						|
            }
 | 
						|
          else if (! wrap)
 | 
						|
            {
 | 
						|
              max = Math.max(min, max);
 | 
						|
              wrap = true;
 | 
						|
              min = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
      max = Math.max(max, min);
 | 
						|
      return max;
 | 
						|
    }
 | 
						|
 | 
						|
    public void paint(Graphics g, Shape s)
 | 
						|
    {
 | 
						|
      // Nothing to do here (not visual).
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to handle possible leaf elements.
 | 
						|
     */
 | 
						|
    protected void loadChildren(ViewFactory f)
 | 
						|
    {
 | 
						|
      Element el = getElement();
 | 
						|
      if (el.isLeaf())
 | 
						|
        {
 | 
						|
          View v = new LabelView(el);
 | 
						|
          append(v);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        super.loadChildren(f);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to reparent the children to this logical view, in case
 | 
						|
     * they have been parented by a row.
 | 
						|
     */
 | 
						|
    protected void forwardUpdateToView(View v, DocumentEvent e, Shape a,
 | 
						|
                                       ViewFactory f)
 | 
						|
    {
 | 
						|
      v.setParent(this);
 | 
						|
      super.forwardUpdateToView(v, e, a, f);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to handle possible leaf element.
 | 
						|
     */
 | 
						|
    protected int getViewIndexAtPosition(int pos)
 | 
						|
    {
 | 
						|
      int index = 0;
 | 
						|
      if (! getElement().isLeaf())
 | 
						|
        index = super.getViewIndexAtPosition(pos);
 | 
						|
      return index;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * The shared instance of FlowStrategy.
 | 
						|
   */
 | 
						|
  static final FlowStrategy sharedStrategy = new FlowStrategy();
 | 
						|
 | 
						|
  /**
 | 
						|
   * The span of the <code>FlowView</code> that should be flowed.
 | 
						|
   */
 | 
						|
  protected int layoutSpan;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Represents the logical child elements of this view, encapsulated within
 | 
						|
   * one parent view (an instance of a package private <code>LogicalView</code>
 | 
						|
   * class). These will be translated to a set of real views that are then
 | 
						|
   * displayed on screen. This translation is performed by the inner class
 | 
						|
   * {@link FlowStrategy}.
 | 
						|
   */
 | 
						|
  protected View layoutPool;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The <code>FlowStrategy</code> to use for translating between the
 | 
						|
   * logical and physical view.
 | 
						|
   */
 | 
						|
  protected FlowStrategy strategy;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new <code>FlowView</code> for the given
 | 
						|
   * <code>Element</code> and <code>axis</code>.
 | 
						|
   *
 | 
						|
   * @param element the element that is rendered by this FlowView
 | 
						|
   * @param axis the axis along which the view is tiled, either
 | 
						|
   *        <code>View.X_AXIS</code> or <code>View.Y_AXIS</code>, the flow
 | 
						|
   *        axis is orthogonal to this one
 | 
						|
   */
 | 
						|
  public FlowView(Element element, int axis)
 | 
						|
  {
 | 
						|
    super(element, axis);
 | 
						|
    strategy = sharedStrategy;
 | 
						|
    layoutSpan = Short.MAX_VALUE;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the axis along which the view should be flowed. This is
 | 
						|
   * orthogonal to the axis along which the boxes are tiled.
 | 
						|
   *
 | 
						|
   * @return the axis along which the view should be flowed
 | 
						|
   */
 | 
						|
  public int getFlowAxis()
 | 
						|
  {
 | 
						|
    int axis = getAxis();
 | 
						|
    int flowAxis;
 | 
						|
 | 
						|
    if (axis == X_AXIS)
 | 
						|
      flowAxis = Y_AXIS;
 | 
						|
    else
 | 
						|
      flowAxis = X_AXIS;
 | 
						|
 | 
						|
    return flowAxis;
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the span of the flow for the specified child view. A flow
 | 
						|
   * layout can be shaped by providing different span values for different
 | 
						|
   * child indices. The default implementation returns the entire available
 | 
						|
   * span inside the view.
 | 
						|
   *
 | 
						|
   * @param index the index of the child for which to return the span
 | 
						|
   *
 | 
						|
   * @return the span of the flow for the specified child view
 | 
						|
   */
 | 
						|
  public int getFlowSpan(int index)
 | 
						|
  {
 | 
						|
    return layoutSpan;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the location along the flow axis where the flow span starts
 | 
						|
   * given a child view index. The flow can be shaped by providing
 | 
						|
   * different values here.
 | 
						|
   *
 | 
						|
   * @param index the index of the child for which to return the flow location
 | 
						|
   *
 | 
						|
   * @return the location along the flow axis where the flow span starts
 | 
						|
   */
 | 
						|
  public int getFlowStart(int index)
 | 
						|
  {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new view that represents a row within a flow.
 | 
						|
   *
 | 
						|
   * @return a view for a new row
 | 
						|
   */
 | 
						|
  protected abstract View createRow();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Loads the children of this view. The <code>FlowView</code> does not
 | 
						|
   * directly load its children. Instead it creates a logical view
 | 
						|
   * ({@link #layoutPool}) which is filled by the logical child views.
 | 
						|
   * The real children are created at layout time and each represent one
 | 
						|
   * row.
 | 
						|
   *
 | 
						|
   * This method is called by {@link View#setParent} in order to initialize
 | 
						|
   * the view.
 | 
						|
   *
 | 
						|
   * @param vf the view factory to use for creating the child views
 | 
						|
   */
 | 
						|
  protected void loadChildren(ViewFactory vf)
 | 
						|
  {
 | 
						|
    if (layoutPool == null)
 | 
						|
      {
 | 
						|
        layoutPool = new LogicalView(getElement());
 | 
						|
      }
 | 
						|
    layoutPool.setParent(this);
 | 
						|
    // Initialize the flow strategy.
 | 
						|
    strategy.insertUpdate(this, null, null);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Performs the layout of this view. If the span along the flow axis changed,
 | 
						|
   * this first calls {@link FlowStrategy#layout} in order to rebuild the
 | 
						|
   * rows of this view. Then the superclass's behaviour is called to arrange
 | 
						|
   * the rows within the box.
 | 
						|
   *
 | 
						|
   * @param width the width of the view
 | 
						|
   * @param height the height of the view
 | 
						|
   */
 | 
						|
  protected void layout(int width, int height)
 | 
						|
  {
 | 
						|
    int flowAxis = getFlowAxis();
 | 
						|
    int span;
 | 
						|
    if (flowAxis == X_AXIS)
 | 
						|
      span = (int) width;
 | 
						|
    else
 | 
						|
      span = (int) height;
 | 
						|
 | 
						|
    if (layoutSpan != span)
 | 
						|
      {
 | 
						|
        layoutChanged(flowAxis);
 | 
						|
        layoutChanged(getAxis());
 | 
						|
        layoutSpan = span;
 | 
						|
      }
 | 
						|
 | 
						|
    if (! isLayoutValid(flowAxis))
 | 
						|
      {
 | 
						|
        int axis = getAxis();
 | 
						|
        int oldSpan = axis == X_AXIS ? getWidth() : getHeight();
 | 
						|
        strategy.layout(this);
 | 
						|
        int newSpan = (int) getPreferredSpan(axis);
 | 
						|
        if (oldSpan != newSpan)
 | 
						|
          {
 | 
						|
            View parent = getParent();
 | 
						|
            if (parent != null)
 | 
						|
              parent.preferenceChanged(this, axis == X_AXIS, axis == Y_AXIS);
 | 
						|
          }
 | 
						|
      }
 | 
						|
 | 
						|
    super.layout(width, height);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Receice notification that some content has been inserted in the region
 | 
						|
   * that this view is responsible for. This calls
 | 
						|
   * {@link FlowStrategy#insertUpdate}.
 | 
						|
   *
 | 
						|
   * @param changes the document event describing the changes
 | 
						|
   * @param a the current allocation of the view
 | 
						|
   * @param vf the view factory that is used for creating new child views
 | 
						|
   */
 | 
						|
  public void insertUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
 | 
						|
  {
 | 
						|
    // First we must send the insertUpdate to the logical view so it can
 | 
						|
    // be updated accordingly.
 | 
						|
    layoutPool.insertUpdate(changes, a, vf);
 | 
						|
    strategy.insertUpdate(this, changes, getInsideAllocation(a));
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Receice notification that some content has been removed from the region
 | 
						|
   * that this view is responsible for. This calls
 | 
						|
   * {@link FlowStrategy#removeUpdate}.
 | 
						|
   *
 | 
						|
   * @param changes the document event describing the changes
 | 
						|
   * @param a the current allocation of the view
 | 
						|
   * @param vf the view factory that is used for creating new child views
 | 
						|
   */
 | 
						|
  public void removeUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
 | 
						|
  {
 | 
						|
    layoutPool.removeUpdate(changes, a, vf);
 | 
						|
    strategy.removeUpdate(this, changes, getInsideAllocation(a));
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Receice notification that some attributes changed in the region
 | 
						|
   * that this view is responsible for. This calls
 | 
						|
   * {@link FlowStrategy#changedUpdate}.
 | 
						|
   *
 | 
						|
   * @param changes the document event describing the changes
 | 
						|
   * @param a the current allocation of the view
 | 
						|
   * @param vf the view factory that is used for creating new child views
 | 
						|
   */
 | 
						|
  public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory vf)
 | 
						|
  {
 | 
						|
    layoutPool.changedUpdate(changes, a, vf);
 | 
						|
    strategy.changedUpdate(this, changes, getInsideAllocation(a));
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the index of the child <code>View</code> for the given model
 | 
						|
   * position.
 | 
						|
   *
 | 
						|
   * This is implemented to iterate over the children of this
 | 
						|
   * view (the rows) and return the index of the first view that contains
 | 
						|
   * the given position.
 | 
						|
   *
 | 
						|
   * @param pos the model position for whicht the child <code>View</code> is
 | 
						|
   *        queried
 | 
						|
   *
 | 
						|
   * @return the index of the child <code>View</code> for the given model
 | 
						|
   *         position
 | 
						|
   */
 | 
						|
  protected int getViewIndexAtPosition(int pos)
 | 
						|
  {
 | 
						|
    // First make sure we have a valid layout.
 | 
						|
    if (!isAllocationValid())
 | 
						|
      layout(getWidth(), getHeight());
 | 
						|
 | 
						|
    int count = getViewCount();
 | 
						|
    int result = -1;
 | 
						|
 | 
						|
    for (int i = 0; i < count; ++i)
 | 
						|
      {
 | 
						|
        View child = getView(i);
 | 
						|
        int start = child.getStartOffset();
 | 
						|
        int end = child.getEndOffset();
 | 
						|
        if (start <= pos && end > pos)
 | 
						|
          {
 | 
						|
            result = i;
 | 
						|
            break;
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Calculates the size requirements of this <code>BoxView</code> along
 | 
						|
   * its minor axis, that is the axis opposite to the axis specified in the
 | 
						|
   * constructor.
 | 
						|
   *
 | 
						|
   * This is overridden and forwards the request to the logical view.
 | 
						|
   *
 | 
						|
   * @param axis the axis that is examined
 | 
						|
   * @param r the <code>SizeRequirements</code> object to hold the result,
 | 
						|
   *        if <code>null</code>, a new one is created
 | 
						|
   *
 | 
						|
   * @return the size requirements for this <code>BoxView</code> along
 | 
						|
   *         the specified axis
 | 
						|
   */
 | 
						|
  protected SizeRequirements calculateMinorAxisRequirements(int axis,
 | 
						|
                                                            SizeRequirements r)
 | 
						|
  {
 | 
						|
    SizeRequirements res = r;
 | 
						|
    if (res == null)
 | 
						|
      res = new SizeRequirements();
 | 
						|
    res.minimum = (int) layoutPool.getMinimumSpan(axis);
 | 
						|
    res.preferred = Math.max(res.minimum,
 | 
						|
                             (int) layoutPool.getPreferredSpan(axis));
 | 
						|
    res.maximum = Integer.MAX_VALUE;
 | 
						|
    res.alignment = 0.5F;
 | 
						|
    return res;
 | 
						|
  }
 | 
						|
}
 |