mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			1481 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			1481 lines
		
	
	
		
			39 KiB
		
	
	
	
		
			Java
		
	
	
	
/* AsyncBoxView.java -- A box view that performs layout asynchronously
 | 
						|
   Copyright (C) 2006 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 java.util.ArrayList;
 | 
						|
 | 
						|
import javax.swing.event.DocumentEvent;
 | 
						|
import javax.swing.text.Position.Bias;
 | 
						|
 | 
						|
/**
 | 
						|
 * A {@link View} implementation that lays out its child views in a box, either
 | 
						|
 * vertically or horizontally. The difference to {@link BoxView} is that the
 | 
						|
 * layout is performed in an asynchronous manner. This helps to keep the
 | 
						|
 * eventqueue free from non-GUI related tasks.
 | 
						|
 *
 | 
						|
 * This view is currently not used in standard text components. In order to
 | 
						|
 * use it you would have to implement a special {@link EditorKit} with a
 | 
						|
 * {@link ViewFactory} that returns this view. For example:
 | 
						|
 *
 | 
						|
 * <pre>
 | 
						|
 * static class AsyncEditorKit extends StyledEditorKit implements ViewFactory
 | 
						|
 * {
 | 
						|
 *   public View create(Element el)
 | 
						|
 *   {
 | 
						|
 *     if (el.getName().equals(AbstractDocument.SectionElementName))
 | 
						|
 *       return new AsyncBoxView(el, View.Y_AXIS);
 | 
						|
 *     return super.getViewFactory().create(el);
 | 
						|
 *   }
 | 
						|
 *   public ViewFactory getViewFactory() {
 | 
						|
 *     return this;
 | 
						|
 *   }
 | 
						|
 * }
 | 
						|
 * </pre>
 | 
						|
 *
 | 
						|
 * @author Roman Kennke (kennke@aicas.com)
 | 
						|
 *
 | 
						|
 * @since 1.3
 | 
						|
 */
 | 
						|
public class AsyncBoxView
 | 
						|
  extends View
 | 
						|
{
 | 
						|
 | 
						|
  /**
 | 
						|
   * Manages the effective position of child views. That keeps the visible
 | 
						|
   * layout stable while the AsyncBoxView might be changing until the layout
 | 
						|
   * thread decides to publish the new layout.
 | 
						|
   */
 | 
						|
  public class ChildLocator
 | 
						|
  {
 | 
						|
 | 
						|
    /**
 | 
						|
     * The last valid location.
 | 
						|
     */
 | 
						|
    protected ChildState lastValidOffset;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The last allocation.
 | 
						|
     */
 | 
						|
    protected Rectangle lastAlloc;
 | 
						|
 | 
						|
    /**
 | 
						|
     * A Rectangle used for child allocation calculation to avoid creation
 | 
						|
     * of lots of garbage Rectangle objects.
 | 
						|
     */
 | 
						|
    protected Rectangle childAlloc;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates a new ChildLocator.
 | 
						|
     */
 | 
						|
    public ChildLocator()
 | 
						|
    {
 | 
						|
      lastAlloc = new Rectangle();
 | 
						|
      childAlloc = new Rectangle();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Receives notification that a child has changed. This is called by
 | 
						|
     * child state objects that have changed it's major span.
 | 
						|
     *
 | 
						|
     * This sets the {@link #lastValidOffset} field to <code>cs</code> if
 | 
						|
     * the new child state's view start offset is smaller than the start offset
 | 
						|
     * of the current child state's view or when <code>lastValidOffset</code>
 | 
						|
     * is <code>null</code>.
 | 
						|
     *
 | 
						|
     * @param cs the child state object that has changed
 | 
						|
     */
 | 
						|
    public synchronized void childChanged(ChildState cs)
 | 
						|
    {
 | 
						|
      if (lastValidOffset == null
 | 
						|
          || cs.getChildView().getStartOffset()
 | 
						|
             < lastValidOffset.getChildView().getStartOffset())
 | 
						|
        {
 | 
						|
          lastValidOffset = cs;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the view index of the view that occupies the specified area, or
 | 
						|
     * <code>-1</code> if there is no such child view.
 | 
						|
     *
 | 
						|
     * @param x the x coordinate (relative to <code>a</code>)
 | 
						|
     * @param y the y coordinate (relative to <code>a</code>)
 | 
						|
     * @param a the current allocation of this view
 | 
						|
     *
 | 
						|
     * @return the view index of the view that occupies the specified area, or
 | 
						|
     *         <code>-1</code> if there is no such child view
 | 
						|
     */
 | 
						|
    public int getViewIndexAtPoint(float x, float y, Shape a)
 | 
						|
    {
 | 
						|
      setAllocation(a);
 | 
						|
      float targetOffset = (getMajorAxis() == X_AXIS) ? x - lastAlloc.x
 | 
						|
                                                      : y - lastAlloc.y;
 | 
						|
      int index = getViewIndexAtVisualOffset(targetOffset);
 | 
						|
      return index;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the current allocation for a child view. This updates the
 | 
						|
     * offsets for all children <em>before</em> the requested child view.
 | 
						|
     *
 | 
						|
     * @param index the index of the child view
 | 
						|
     * @param a the current allocation of this view
 | 
						|
     *
 | 
						|
     * @return the current allocation for a child view
 | 
						|
     */
 | 
						|
    public synchronized Shape getChildAllocation(int index, Shape a)
 | 
						|
    {
 | 
						|
      if (a == null)
 | 
						|
        return null;
 | 
						|
      setAllocation(a);
 | 
						|
      ChildState cs = getChildState(index);
 | 
						|
      if (cs.getChildView().getStartOffset()
 | 
						|
          > lastValidOffset.getChildView().getStartOffset())
 | 
						|
        {
 | 
						|
          updateChildOffsetsToIndex(index);
 | 
						|
        }
 | 
						|
      Shape ca = getChildAllocation(index);
 | 
						|
      return ca;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Paints all child views.
 | 
						|
     *
 | 
						|
     * @param g the graphics context to use
 | 
						|
     */
 | 
						|
    public synchronized void paintChildren(Graphics g)
 | 
						|
    {
 | 
						|
      Rectangle clip = g.getClipBounds();
 | 
						|
      float targetOffset = (getMajorAxis() == X_AXIS) ? clip.x - lastAlloc.x
 | 
						|
                                                      : clip.y - lastAlloc.y;
 | 
						|
      int index = getViewIndexAtVisualOffset(targetOffset);
 | 
						|
      int n = getViewCount();
 | 
						|
      float offs = getChildState(index).getMajorOffset();
 | 
						|
      for (int i = index; i < n; i++)
 | 
						|
        {
 | 
						|
          ChildState cs = getChildState(i);
 | 
						|
          cs.setMajorOffset(offs);
 | 
						|
          Shape ca = getChildAllocation(i);
 | 
						|
          if (ca.intersects(clip))
 | 
						|
            {
 | 
						|
              synchronized (cs)
 | 
						|
                {
 | 
						|
                  View v = cs.getChildView();
 | 
						|
                  v.paint(g, ca);
 | 
						|
                }
 | 
						|
            }
 | 
						|
          else
 | 
						|
            {
 | 
						|
              // done painting intersection
 | 
						|
              break;
 | 
						|
            }
 | 
						|
          offs += cs.getMajorSpan();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the current allocation of the child view with the specified
 | 
						|
     * index. Note that this will <em>not</em> update any location information.
 | 
						|
     *
 | 
						|
     * @param index the index of the requested child view
 | 
						|
     *
 | 
						|
     * @return the current allocation of the child view with the specified
 | 
						|
     *         index
 | 
						|
     */
 | 
						|
    protected Shape getChildAllocation(int index)
 | 
						|
    {
 | 
						|
      ChildState cs = getChildState(index);
 | 
						|
      if (! cs.isLayoutValid())
 | 
						|
          cs.run();
 | 
						|
 | 
						|
      if (getMajorAxis() == X_AXIS)
 | 
						|
        {
 | 
						|
          childAlloc.x = lastAlloc.x + (int) cs.getMajorOffset();
 | 
						|
          childAlloc.y = lastAlloc.y + (int) cs.getMinorOffset();
 | 
						|
          childAlloc.width = (int) cs.getMajorSpan();
 | 
						|
          childAlloc.height = (int) cs.getMinorSpan();
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          childAlloc.y = lastAlloc.y + (int) cs.getMajorOffset();
 | 
						|
          childAlloc.x = lastAlloc.x + (int) cs.getMinorOffset();
 | 
						|
          childAlloc.height = (int) cs.getMajorSpan();
 | 
						|
          childAlloc.width = (int) cs.getMinorSpan();
 | 
						|
        }
 | 
						|
      return childAlloc;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Sets the current allocation for this view.
 | 
						|
     *
 | 
						|
     * @param a the allocation to set
 | 
						|
     */
 | 
						|
    protected void setAllocation(Shape a)
 | 
						|
    {
 | 
						|
      if (a instanceof Rectangle)
 | 
						|
        lastAlloc.setBounds((Rectangle) a);
 | 
						|
      else
 | 
						|
        lastAlloc.setBounds(a.getBounds());
 | 
						|
 | 
						|
      setSize(lastAlloc.width, lastAlloc.height);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the index of the view at the specified offset along the major
 | 
						|
     * layout axis.
 | 
						|
     *
 | 
						|
     * @param targetOffset the requested offset
 | 
						|
     *
 | 
						|
     * @return the index of the view at the specified offset along the major
 | 
						|
     * layout axis
 | 
						|
     */
 | 
						|
    protected int getViewIndexAtVisualOffset(float targetOffset)
 | 
						|
    {
 | 
						|
      int n = getViewCount();
 | 
						|
      if (n > 0)
 | 
						|
        {
 | 
						|
          if (lastValidOffset == null)
 | 
						|
            lastValidOffset = getChildState(0);
 | 
						|
          if (targetOffset > majorSpan)
 | 
						|
            return 0;
 | 
						|
          else if (targetOffset > lastValidOffset.getMajorOffset())
 | 
						|
            return updateChildOffsets(targetOffset);
 | 
						|
          else
 | 
						|
            {
 | 
						|
              float offs = 0f;
 | 
						|
              for (int i = 0; i < n; i++)
 | 
						|
                {
 | 
						|
                  ChildState cs = getChildState(i);
 | 
						|
                  float nextOffs = offs + cs.getMajorSpan();
 | 
						|
                  if (targetOffset < nextOffs)
 | 
						|
                    return i;
 | 
						|
                  offs = nextOffs;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
      return n - 1;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Updates all the child view offsets up to the specified targetOffset.
 | 
						|
     *
 | 
						|
     * @param targetOffset the offset up to which the child view offsets are
 | 
						|
     *        updated
 | 
						|
     *
 | 
						|
     * @return the index of the view at the specified offset
 | 
						|
     */
 | 
						|
    private int updateChildOffsets(float targetOffset)
 | 
						|
    {
 | 
						|
      int n = getViewCount();
 | 
						|
      int targetIndex = n - 1;
 | 
						|
      int pos = lastValidOffset.getChildView().getStartOffset();
 | 
						|
      int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
 | 
						|
      float start = lastValidOffset.getMajorOffset();
 | 
						|
      float lastOffset = start;
 | 
						|
      for (int i = startIndex; i < n; i++)
 | 
						|
        {
 | 
						|
          ChildState cs = getChildState(i);
 | 
						|
          cs.setMajorOffset(lastOffset);
 | 
						|
          lastOffset += cs.getMajorSpan();
 | 
						|
          if (targetOffset < lastOffset)
 | 
						|
            {
 | 
						|
              targetIndex = i;
 | 
						|
              lastValidOffset = cs;
 | 
						|
              break;
 | 
						|
            }
 | 
						|
        }
 | 
						|
      return targetIndex;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Updates the offsets of the child views up to the specified index.
 | 
						|
     *
 | 
						|
     * @param index the index up to which the offsets are updated
 | 
						|
     */
 | 
						|
    private void updateChildOffsetsToIndex(int index)
 | 
						|
    {
 | 
						|
      int pos = lastValidOffset.getChildView().getStartOffset();
 | 
						|
      int startIndex = getViewIndexAtPosition(pos, Position.Bias.Forward);
 | 
						|
      float lastOffset = lastValidOffset.getMajorOffset();
 | 
						|
      for (int i = startIndex; i <= index; i++)
 | 
						|
        {
 | 
						|
          ChildState cs = getChildState(i);
 | 
						|
          cs.setMajorOffset(lastOffset);
 | 
						|
          lastOffset += cs.getMajorSpan();
 | 
						|
        }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Represents the layout state of a child view.
 | 
						|
   */
 | 
						|
  public class ChildState
 | 
						|
    implements Runnable
 | 
						|
  {
 | 
						|
 | 
						|
    /**
 | 
						|
     * The child view for this state record.
 | 
						|
     */
 | 
						|
    private View childView;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Indicates if the minor axis requirements of this child view are valid
 | 
						|
     * or not.
 | 
						|
     */
 | 
						|
    private boolean minorValid;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Indicates if the major axis requirements of this child view are valid
 | 
						|
     * or not.
 | 
						|
     */
 | 
						|
    private boolean majorValid;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Indicates if the current child size is valid. This is package private
 | 
						|
     * to avoid synthetic accessor method.
 | 
						|
     */
 | 
						|
    boolean childSizeValid;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The child views minimumSpan. This is package private to avoid accessor
 | 
						|
     * method.
 | 
						|
     */
 | 
						|
    float minimum;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The child views preferredSpan. This is package private to avoid accessor
 | 
						|
     * method.
 | 
						|
     */
 | 
						|
    float preferred;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The current span of the child view along the major axis.
 | 
						|
     */
 | 
						|
    private float majorSpan;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The current offset of the child view along the major axis.
 | 
						|
     */
 | 
						|
    private float majorOffset;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The current span of the child view along the minor axis.
 | 
						|
     */
 | 
						|
    private float minorSpan;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The current offset of the child view along the major axis.
 | 
						|
     */
 | 
						|
    private float minorOffset;
 | 
						|
 | 
						|
    /**
 | 
						|
     * The child views maximumSpan.
 | 
						|
     */
 | 
						|
    private float maximum;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates a new <code>ChildState</code> object for the specified child
 | 
						|
     * view.
 | 
						|
     *
 | 
						|
     * @param view the child view for which to create the state record
 | 
						|
     */
 | 
						|
    public ChildState(View view)
 | 
						|
    {
 | 
						|
      childView = view;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the child view for which this <code>ChildState</code> represents
 | 
						|
     * the layout state.
 | 
						|
     *
 | 
						|
     * @return the child view for this child state object
 | 
						|
     */
 | 
						|
    public View getChildView()
 | 
						|
    {
 | 
						|
      return childView;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns <code>true</code> if the current layout information is valid,
 | 
						|
     * <code>false</code> otherwise.
 | 
						|
     *
 | 
						|
     * @return <code>true</code> if the current layout information is valid,
 | 
						|
     *         <code>false</code> otherwise
 | 
						|
     */
 | 
						|
    public boolean isLayoutValid()
 | 
						|
    {
 | 
						|
      return minorValid && majorValid && childSizeValid;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Performs the layout update for the child view managed by this
 | 
						|
     * <code>ChildState</code>.
 | 
						|
     */
 | 
						|
    public void run()
 | 
						|
    {
 | 
						|
      Document doc = getDocument();
 | 
						|
      if (doc instanceof AbstractDocument)
 | 
						|
        {
 | 
						|
          AbstractDocument abstractDoc = (AbstractDocument) doc;
 | 
						|
          abstractDoc.readLock();
 | 
						|
        }
 | 
						|
 | 
						|
      try
 | 
						|
        {
 | 
						|
 | 
						|
          if (!(minorValid &&  majorValid && childSizeValid)
 | 
						|
              && childView.getParent() == AsyncBoxView.this)
 | 
						|
            {
 | 
						|
              synchronized(AsyncBoxView.this)
 | 
						|
              {
 | 
						|
                changing = this;
 | 
						|
              }
 | 
						|
              update();
 | 
						|
              synchronized(AsyncBoxView.this)
 | 
						|
              {
 | 
						|
                changing = null;
 | 
						|
              }
 | 
						|
              // Changing the major axis may cause the minor axis
 | 
						|
              // requirements to have changed, so we need to do this again.
 | 
						|
              update();
 | 
						|
            }
 | 
						|
        }
 | 
						|
      finally
 | 
						|
        {
 | 
						|
          if (doc instanceof AbstractDocument)
 | 
						|
            {
 | 
						|
              AbstractDocument abstractDoc = (AbstractDocument) doc;
 | 
						|
              abstractDoc.readUnlock();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Performs the actual update after the run methods has made its checks
 | 
						|
     * and locked the document.
 | 
						|
     */
 | 
						|
    private void update()
 | 
						|
    {
 | 
						|
      int majorAxis = getMajorAxis();
 | 
						|
      boolean minorUpdated = false;
 | 
						|
      synchronized (this)
 | 
						|
        {
 | 
						|
          if (! minorValid)
 | 
						|
            {
 | 
						|
              int minorAxis = getMinorAxis();
 | 
						|
              minimum = childView.getMinimumSpan(minorAxis);
 | 
						|
              preferred = childView.getPreferredSpan(minorAxis);
 | 
						|
              maximum = childView.getMaximumSpan(minorAxis);
 | 
						|
              minorValid = true;
 | 
						|
              minorUpdated = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
      if (minorUpdated)
 | 
						|
        minorRequirementChange(this);
 | 
						|
 | 
						|
      boolean majorUpdated = false;
 | 
						|
      float delta = 0.0F;
 | 
						|
      synchronized (this)
 | 
						|
        {
 | 
						|
          if (! majorValid)
 | 
						|
            {
 | 
						|
              float oldSpan = majorSpan;
 | 
						|
              majorSpan = childView.getPreferredSpan(majorAxis);
 | 
						|
              delta = majorSpan - oldSpan;
 | 
						|
              majorValid = true;
 | 
						|
              majorUpdated = true;
 | 
						|
            }
 | 
						|
        }
 | 
						|
      if (majorUpdated)
 | 
						|
        {
 | 
						|
          majorRequirementChange(this, delta);
 | 
						|
          locator.childChanged(this);
 | 
						|
        }
 | 
						|
 | 
						|
      synchronized (this)
 | 
						|
        {
 | 
						|
          if (! childSizeValid)
 | 
						|
            {
 | 
						|
              float w;
 | 
						|
              float h;
 | 
						|
              if (majorAxis == X_AXIS)
 | 
						|
                {
 | 
						|
                  w = majorSpan;
 | 
						|
                  h = getMinorSpan();
 | 
						|
                }
 | 
						|
              else
 | 
						|
                {
 | 
						|
                  w = getMinorSpan();
 | 
						|
                  h = majorSpan;
 | 
						|
                }
 | 
						|
              childSizeValid = true;
 | 
						|
              childView.setSize(w, h);
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the span of the child view along the minor layout axis.
 | 
						|
     *
 | 
						|
     * @return the span of the child view along the minor layout axis
 | 
						|
     */
 | 
						|
    public float getMinorSpan()
 | 
						|
    {
 | 
						|
      float retVal;
 | 
						|
      if (maximum < minorSpan)
 | 
						|
        retVal = maximum;
 | 
						|
      else
 | 
						|
        retVal = Math.max(minimum, minorSpan);
 | 
						|
      return retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the offset of the child view along the minor layout axis.
 | 
						|
     *
 | 
						|
     * @return the offset of the child view along the minor layout axis
 | 
						|
     */
 | 
						|
    public float getMinorOffset()
 | 
						|
    {
 | 
						|
      float retVal;
 | 
						|
      if (maximum < minorSpan)
 | 
						|
        {
 | 
						|
          float align = childView.getAlignment(getMinorAxis());
 | 
						|
          retVal = ((minorSpan - maximum) * align);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        retVal = 0f;
 | 
						|
 | 
						|
      return retVal;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the span of the child view along the major layout axis.
 | 
						|
     *
 | 
						|
     * @return the span of the child view along the major layout axis
 | 
						|
     */
 | 
						|
 | 
						|
    public float getMajorSpan()
 | 
						|
    {
 | 
						|
      return majorSpan;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Returns the offset of the child view along the major layout axis.
 | 
						|
     *
 | 
						|
     * @return the offset of the child view along the major layout axis
 | 
						|
     */
 | 
						|
    public float getMajorOffset()
 | 
						|
    {
 | 
						|
      return majorOffset;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Sets the offset of the child view along the major layout axis. This
 | 
						|
     * should only be called by the ChildLocator of that child view.
 | 
						|
     *
 | 
						|
     * @param offset the offset to set
 | 
						|
     */
 | 
						|
    public void setMajorOffset(float offset)
 | 
						|
    {
 | 
						|
      majorOffset = offset;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Mark the preferences changed for that child. This forwards to
 | 
						|
     * {@link AsyncBoxView#preferenceChanged}.
 | 
						|
     *
 | 
						|
     * @param width <code>true</code> if the width preference has changed
 | 
						|
     * @param height <code>true</code> if the height preference has changed
 | 
						|
     */
 | 
						|
    public void preferenceChanged(boolean width, boolean height)
 | 
						|
    {
 | 
						|
      if (getMajorAxis() == X_AXIS)
 | 
						|
        {
 | 
						|
          if (width)
 | 
						|
            majorValid = false;
 | 
						|
          if (height)
 | 
						|
            minorValid = false;
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
          if (width)
 | 
						|
            minorValid = false;
 | 
						|
          if (height)
 | 
						|
            majorValid = false;
 | 
						|
        }
 | 
						|
      childSizeValid = false;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flushes the requirements changes upwards asynchronously.
 | 
						|
   */
 | 
						|
  private class FlushTask implements Runnable
 | 
						|
  {
 | 
						|
    /**
 | 
						|
     * Starts the flush task. This obtains a readLock on the document
 | 
						|
     * and then flushes all the updates using
 | 
						|
     * {@link AsyncBoxView#flushRequirementChanges()} after updating the
 | 
						|
     * requirements.
 | 
						|
     */
 | 
						|
    public void run()
 | 
						|
    {
 | 
						|
      try
 | 
						|
        {
 | 
						|
          // Acquire a lock on the document.
 | 
						|
          Document doc = getDocument();
 | 
						|
          if (doc instanceof AbstractDocument)
 | 
						|
            {
 | 
						|
              AbstractDocument abstractDoc = (AbstractDocument) doc;
 | 
						|
              abstractDoc.readLock();
 | 
						|
            }
 | 
						|
 | 
						|
          int n = getViewCount();
 | 
						|
          if (minorChanged && (n > 0))
 | 
						|
            {
 | 
						|
              LayoutQueue q = getLayoutQueue();
 | 
						|
              ChildState min = getChildState(0);
 | 
						|
              ChildState pref = getChildState(0);
 | 
						|
              for (int i = 1; i < n; i++)
 | 
						|
                {
 | 
						|
                  ChildState cs = getChildState(i);
 | 
						|
                  if (cs.minimum > min.minimum)
 | 
						|
                    min = cs;
 | 
						|
                  if (cs.preferred > pref.preferred)
 | 
						|
                    pref = cs;
 | 
						|
                }
 | 
						|
              synchronized (AsyncBoxView.this)
 | 
						|
              {
 | 
						|
                minReq = min;
 | 
						|
                prefReq = pref;
 | 
						|
              }
 | 
						|
            }
 | 
						|
 | 
						|
          flushRequirementChanges();
 | 
						|
        }
 | 
						|
      finally
 | 
						|
      {
 | 
						|
        // Release the lock on the document.
 | 
						|
        Document doc = getDocument();
 | 
						|
        if (doc instanceof AbstractDocument)
 | 
						|
          {
 | 
						|
            AbstractDocument abstractDoc = (AbstractDocument) doc;
 | 
						|
            abstractDoc.readUnlock();
 | 
						|
          }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * The major layout axis.
 | 
						|
   */
 | 
						|
  private int majorAxis;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The top inset.
 | 
						|
   */
 | 
						|
  private float topInset;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The bottom inset.
 | 
						|
   */
 | 
						|
  private float bottomInset;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The left inset.
 | 
						|
   */
 | 
						|
  private float leftInset;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates if the major span should be treated as beeing estimated or not.
 | 
						|
   */
 | 
						|
  private boolean estimatedMajorSpan;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The right inset.
 | 
						|
   */
 | 
						|
  private float rightInset;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The children and their layout statistics.
 | 
						|
   */
 | 
						|
  private ArrayList childStates;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The currently changing child state. May be null if there is no child state
 | 
						|
   * updating at the moment. This is package private to avoid a synthetic
 | 
						|
   * accessor method inside ChildState.
 | 
						|
   */
 | 
						|
  ChildState changing;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Represents the minimum requirements. This is used in
 | 
						|
   * {@link #getMinimumSpan(int)}.
 | 
						|
   */
 | 
						|
  ChildState minReq;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Represents the minimum requirements. This is used in
 | 
						|
   * {@link #getPreferredSpan(int)}.
 | 
						|
   */
 | 
						|
  ChildState prefReq;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates that the major axis requirements have changed.
 | 
						|
   */
 | 
						|
  private boolean majorChanged;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Indicates that the minor axis requirements have changed. This is package
 | 
						|
   * private to avoid synthetic accessor method.
 | 
						|
   */
 | 
						|
  boolean minorChanged;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current span along the major layout axis. This is package private to
 | 
						|
   * avoid synthetic accessor method.
 | 
						|
   */
 | 
						|
  float majorSpan;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The current span along the minor layout axis. This is package private to
 | 
						|
   * avoid synthetic accessor method.
 | 
						|
   */
 | 
						|
  float minorSpan;
 | 
						|
 | 
						|
  /**
 | 
						|
   * This tasked is placed on the layout queue to flush updates up to the
 | 
						|
   * parent view.
 | 
						|
   */
 | 
						|
  private Runnable flushTask;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The child locator for this view.
 | 
						|
   */
 | 
						|
  protected ChildLocator locator;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new <code>AsyncBoxView</code> that represents the specified
 | 
						|
   * element and layouts its children along the specified axis.
 | 
						|
   *
 | 
						|
   * @param elem the element
 | 
						|
   * @param axis the layout axis
 | 
						|
   */
 | 
						|
  public AsyncBoxView(Element elem, int axis)
 | 
						|
  {
 | 
						|
    super(elem);
 | 
						|
    majorAxis = axis;
 | 
						|
    childStates = new ArrayList();
 | 
						|
    flushTask = new FlushTask();
 | 
						|
    locator = new ChildLocator();
 | 
						|
    minorSpan = Short.MAX_VALUE;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the major layout axis.
 | 
						|
   *
 | 
						|
   * @return the major layout axis
 | 
						|
   */
 | 
						|
  public int getMajorAxis()
 | 
						|
  {
 | 
						|
    return majorAxis;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the minor layout axis, that is the axis orthogonal to the major
 | 
						|
   * layout axis.
 | 
						|
   *
 | 
						|
   * @return the minor layout axis
 | 
						|
   */
 | 
						|
  public int getMinorAxis()
 | 
						|
  {
 | 
						|
    return majorAxis == X_AXIS ? Y_AXIS : X_AXIS;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the view at the specified <code>index</code>.
 | 
						|
   *
 | 
						|
   * @param index the index of the requested child view
 | 
						|
   *
 | 
						|
   * @return the view at the specified <code>index</code>
 | 
						|
   */
 | 
						|
  public View getView(int index)
 | 
						|
  {
 | 
						|
    View view = null;
 | 
						|
    synchronized(childStates)
 | 
						|
      {
 | 
						|
        if ((index >= 0) && (index < childStates.size()))
 | 
						|
          {
 | 
						|
            ChildState cs = (ChildState) childStates.get(index);
 | 
						|
            view = cs.getChildView();
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return view;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the number of child views.
 | 
						|
   *
 | 
						|
   * @return the number of child views
 | 
						|
   */
 | 
						|
  public int getViewCount()
 | 
						|
  {
 | 
						|
    synchronized(childStates)
 | 
						|
    {
 | 
						|
      return childStates.size();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the view index of the child view that represents the specified
 | 
						|
   * model position.
 | 
						|
   *
 | 
						|
   * @param pos the model position for which we search the view index
 | 
						|
   * @param bias the bias
 | 
						|
   *
 | 
						|
   * @return the view index of the child view that represents the specified
 | 
						|
   *         model position
 | 
						|
   */
 | 
						|
  public int getViewIndex(int pos, Position.Bias bias)
 | 
						|
  {
 | 
						|
    int retVal = -1;
 | 
						|
 | 
						|
    if (bias == Position.Bias.Backward)
 | 
						|
      pos = Math.max(0, pos - 1);
 | 
						|
 | 
						|
    // TODO: A possible optimization would be to implement a binary search
 | 
						|
    // here.
 | 
						|
    int numChildren = childStates.size();
 | 
						|
    if (numChildren > 0)
 | 
						|
      {
 | 
						|
        for (int i = 0; i < numChildren; ++i)
 | 
						|
          {
 | 
						|
            View child = ((ChildState) childStates.get(i)).getChildView();
 | 
						|
            if (child.getStartOffset() <= pos && child.getEndOffset() > pos)
 | 
						|
              {
 | 
						|
                retVal = i;
 | 
						|
                break;
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return retVal;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the top inset.
 | 
						|
   *
 | 
						|
   * @return the top inset
 | 
						|
   */
 | 
						|
  public float getTopInset()
 | 
						|
  {
 | 
						|
    return topInset;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the top inset.
 | 
						|
   *
 | 
						|
   * @param top the top inset
 | 
						|
   */
 | 
						|
  public void setTopInset(float top)
 | 
						|
  {
 | 
						|
    topInset = top;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the bottom inset.
 | 
						|
   *
 | 
						|
   * @return the bottom inset
 | 
						|
   */
 | 
						|
  public float getBottomInset()
 | 
						|
  {
 | 
						|
    return bottomInset;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the bottom inset.
 | 
						|
   *
 | 
						|
   * @param bottom the bottom inset
 | 
						|
   */
 | 
						|
  public void setBottomInset(float bottom)
 | 
						|
  {
 | 
						|
    bottomInset = bottom;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the left inset.
 | 
						|
   *
 | 
						|
   * @return the left inset
 | 
						|
   */
 | 
						|
  public float getLeftInset()
 | 
						|
  {
 | 
						|
    return leftInset;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the left inset.
 | 
						|
   *
 | 
						|
   * @param left the left inset
 | 
						|
   */
 | 
						|
  public void setLeftInset(float left)
 | 
						|
  {
 | 
						|
    leftInset = left;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the right inset.
 | 
						|
   *
 | 
						|
   * @return the right inset
 | 
						|
   */
 | 
						|
  public float getRightInset()
 | 
						|
  {
 | 
						|
    return rightInset;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the right inset.
 | 
						|
   *
 | 
						|
   * @param right the right inset
 | 
						|
   */
 | 
						|
  public void setRightInset(float right)
 | 
						|
  {
 | 
						|
    rightInset = right;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Loads the child views of this view. This is triggered by
 | 
						|
   * {@link #setParent(View)}.
 | 
						|
   *
 | 
						|
   * @param f the view factory to build child views with
 | 
						|
   */
 | 
						|
  protected void loadChildren(ViewFactory f)
 | 
						|
  {
 | 
						|
    Element e = getElement();
 | 
						|
    int n = e.getElementCount();
 | 
						|
    if (n > 0)
 | 
						|
      {
 | 
						|
        View[] added = new View[n];
 | 
						|
        for (int i = 0; i < n; i++)
 | 
						|
          {
 | 
						|
            added[i] = f.create(e.getElement(i));
 | 
						|
          }
 | 
						|
        replace(0, 0, added);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the span along an axis that is taken up by the insets.
 | 
						|
   *
 | 
						|
   * @param axis the axis
 | 
						|
   *
 | 
						|
   * @return the span along an axis that is taken up by the insets
 | 
						|
   *
 | 
						|
   * @since 1.4
 | 
						|
   */
 | 
						|
  protected float getInsetSpan(int axis)
 | 
						|
  {
 | 
						|
    float span;
 | 
						|
    if (axis == X_AXIS)
 | 
						|
      span = leftInset + rightInset;
 | 
						|
    else
 | 
						|
      span = topInset + bottomInset;
 | 
						|
    return span;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the <code>estimatedMajorSpan</code> property that determines if
 | 
						|
   * the major span should be treated as beeing estimated.
 | 
						|
   *
 | 
						|
   * @param estimated if the major span should be treated as estimated or not
 | 
						|
   *
 | 
						|
   * @since 1.4
 | 
						|
   */
 | 
						|
  protected void setEstimatedMajorSpan(boolean estimated)
 | 
						|
  {
 | 
						|
    estimatedMajorSpan = estimated;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Determines whether the major span should be treated as estimated or as
 | 
						|
   * beeing accurate.
 | 
						|
   *
 | 
						|
   * @return <code>true</code> if the major span should be treated as
 | 
						|
   *         estimated, <code>false</code> if the major span should be treated
 | 
						|
   *         as accurate
 | 
						|
   *
 | 
						|
   * @since 1.4
 | 
						|
   */
 | 
						|
  protected boolean getEstimatedMajorSpan()
 | 
						|
  {
 | 
						|
    return estimatedMajorSpan;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Receives notification from the child states that the requirements along
 | 
						|
   * the minor axis have changed.
 | 
						|
   *
 | 
						|
   * @param cs the child state from which this notification is messaged
 | 
						|
   */
 | 
						|
  protected synchronized void minorRequirementChange(ChildState cs)
 | 
						|
  {
 | 
						|
    minorChanged = true;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Receives notification from the child states that the requirements along
 | 
						|
   * the major axis have changed.
 | 
						|
   *
 | 
						|
   * @param cs the child state from which this notification is messaged
 | 
						|
   */
 | 
						|
  protected void majorRequirementChange(ChildState cs, float delta)
 | 
						|
  {
 | 
						|
    if (! estimatedMajorSpan)
 | 
						|
      majorSpan += delta;
 | 
						|
    majorChanged = true;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the parent for this view. This calls loadChildren if
 | 
						|
   * <code>parent</code> is not <code>null</code> and there have not been any
 | 
						|
   * child views initializes.
 | 
						|
   *
 | 
						|
   * @param parent the new parent view; <code>null</code> if this view is
 | 
						|
   *        removed from the view hierarchy
 | 
						|
   *
 | 
						|
   * @see View#setParent(View)
 | 
						|
   */
 | 
						|
  public void setParent(View parent)
 | 
						|
  {
 | 
						|
    super.setParent(parent);
 | 
						|
    if ((parent != null) && (getViewCount() == 0))
 | 
						|
      {
 | 
						|
        ViewFactory f = getViewFactory();
 | 
						|
        loadChildren(f);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Sets the size of this view. This is ususally called before {@link #paint}
 | 
						|
   * is called to make sure the view has a valid layout.
 | 
						|
   *
 | 
						|
   * This implementation queues layout requests for every child view if the
 | 
						|
   * minor axis span has changed. (The major axis span is requested to never
 | 
						|
   * change for this view).
 | 
						|
   *
 | 
						|
   * @param width the width of the view
 | 
						|
   * @param height the height of the view
 | 
						|
   */
 | 
						|
  public void setSize(float width, float height)
 | 
						|
  {
 | 
						|
    float targetSpan;
 | 
						|
    if (majorAxis == X_AXIS)
 | 
						|
      targetSpan = height - getTopInset() - getBottomInset();
 | 
						|
    else
 | 
						|
      targetSpan = width - getLeftInset() - getRightInset();
 | 
						|
 | 
						|
    if (targetSpan != minorSpan)
 | 
						|
      {
 | 
						|
        minorSpan = targetSpan;
 | 
						|
 | 
						|
        int n = getViewCount();
 | 
						|
        LayoutQueue q = getLayoutQueue();
 | 
						|
        for (int i = 0; i < n; i++)
 | 
						|
          {
 | 
						|
            ChildState cs = getChildState(i);
 | 
						|
            cs.childSizeValid = false;
 | 
						|
            q.addTask(cs);
 | 
						|
          }
 | 
						|
        q.addTask(flushTask);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Replaces child views with new child views.
 | 
						|
   *
 | 
						|
   * This creates ChildState objects for all the new views and adds layout
 | 
						|
   * requests for them to the layout queue.
 | 
						|
   *
 | 
						|
   * @param offset the offset at which to remove/insert
 | 
						|
   * @param length the number of child views to remove
 | 
						|
   * @param views the new child views to insert
 | 
						|
   */
 | 
						|
  public void replace(int offset, int length, View[] views)
 | 
						|
  {
 | 
						|
    synchronized(childStates)
 | 
						|
      {
 | 
						|
        LayoutQueue q = getLayoutQueue();
 | 
						|
        for (int i = 0; i < length; i++)
 | 
						|
          childStates.remove(offset);
 | 
						|
 | 
						|
        for (int i = views.length - 1; i >= 0; i--)
 | 
						|
          childStates.add(offset, createChildState(views[i]));
 | 
						|
 | 
						|
        // We need to go through the new child states _after_ they have been
 | 
						|
        // added to the childStates list, otherwise the layout tasks may find
 | 
						|
        // an incomplete child list. That means we have to loop through
 | 
						|
        // them again, but what else can we do?
 | 
						|
        if (views.length != 0)
 | 
						|
          {
 | 
						|
            for (int i = 0; i < views.length; i++)
 | 
						|
              {
 | 
						|
                ChildState cs = (ChildState) childStates.get(i + offset);
 | 
						|
                cs.getChildView().setParent(this);
 | 
						|
                q.addTask(cs);
 | 
						|
              }
 | 
						|
            q.addTask(flushTask);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Paints the view. This requests the {@link ChildLocator} to paint the views
 | 
						|
   * after setting the allocation on it.
 | 
						|
   *
 | 
						|
   * @param g the graphics context to use
 | 
						|
   * @param s the allocation for this view
 | 
						|
   */
 | 
						|
  public void paint(Graphics g, Shape s)
 | 
						|
  {
 | 
						|
    synchronized (locator)
 | 
						|
      {
 | 
						|
        locator.setAllocation(s);
 | 
						|
        locator.paintChildren(g);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the preferred span of this view along the specified layout axis.
 | 
						|
   *
 | 
						|
   * @return the preferred span of this view along the specified layout axis
 | 
						|
   */
 | 
						|
  public float getPreferredSpan(int axis)
 | 
						|
  {
 | 
						|
    float retVal;
 | 
						|
    if (majorAxis == axis)
 | 
						|
      retVal = majorSpan;
 | 
						|
 | 
						|
    else if (prefReq != null)
 | 
						|
      {
 | 
						|
        View child = prefReq.getChildView();
 | 
						|
        retVal = child.getPreferredSpan(axis);
 | 
						|
      }
 | 
						|
 | 
						|
    // If we have no layout information yet, then return insets + 30 as
 | 
						|
    // an estimation.
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (axis == X_AXIS)
 | 
						|
          retVal = getLeftInset() + getRightInset() + 30;
 | 
						|
        else
 | 
						|
          retVal = getTopInset() + getBottomInset() + 30;
 | 
						|
      }
 | 
						|
    return retVal;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Maps a model location to view coordinates.
 | 
						|
   *
 | 
						|
   * @param pos the model location
 | 
						|
   * @param a the current allocation of this view
 | 
						|
   * @param b the bias
 | 
						|
   *
 | 
						|
   * @return the view allocation for the specified model location
 | 
						|
   */
 | 
						|
  public Shape modelToView(int pos, Shape a, Bias b)
 | 
						|
    throws BadLocationException
 | 
						|
  {
 | 
						|
    int index = getViewIndexAtPosition(pos, b);
 | 
						|
    Shape ca = locator.getChildAllocation(index, a);
 | 
						|
 | 
						|
    ChildState cs = getChildState(index);
 | 
						|
    synchronized (cs)
 | 
						|
      {
 | 
						|
        View cv = cs.getChildView();
 | 
						|
        Shape v = cv.modelToView(pos, ca, b);
 | 
						|
        return v;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Maps view coordinates to a model location.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate (relative to <code>a</code>)
 | 
						|
   * @param y the y coordinate (relative to <code>a</code>)
 | 
						|
   * @param b holds the bias of the model location on method exit
 | 
						|
   *
 | 
						|
   * @return the model location for the specified view location
 | 
						|
   */
 | 
						|
  public int viewToModel(float x, float y, Shape a, Bias[] b)
 | 
						|
  {
 | 
						|
    int pos;
 | 
						|
    int index;
 | 
						|
    Shape ca;
 | 
						|
 | 
						|
    synchronized (locator)
 | 
						|
      {
 | 
						|
        index = locator.getViewIndexAtPoint(x, y, a);
 | 
						|
        ca = locator.getChildAllocation(index, a);
 | 
						|
      }
 | 
						|
 | 
						|
    ChildState cs = getChildState(index);
 | 
						|
    synchronized (cs)
 | 
						|
      {
 | 
						|
        View v = cs.getChildView();
 | 
						|
        pos = v.viewToModel(x, y, ca, b);
 | 
						|
      }
 | 
						|
    return pos;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the child allocation for the child view with the specified
 | 
						|
   * <code>index</code>.
 | 
						|
   *
 | 
						|
   * @param index the index of the child view
 | 
						|
   * @param a the current allocation of this view
 | 
						|
   *
 | 
						|
   * @return the allocation of the child view
 | 
						|
   */
 | 
						|
  public Shape getChildAllocation(int index, Shape a)
 | 
						|
  {
 | 
						|
    Shape ca = locator.getChildAllocation(index, a);
 | 
						|
    return ca;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the maximum span of this view along the specified axis.
 | 
						|
   * This is implemented to return the <code>preferredSpan</code> for the
 | 
						|
   * major axis (that means the box can't be resized along the major axis) and
 | 
						|
   * {@link Short#MAX_VALUE} for the minor axis.
 | 
						|
   *
 | 
						|
   * @param axis the axis
 | 
						|
   *
 | 
						|
   * @return the maximum span of this view along the specified axis
 | 
						|
   */
 | 
						|
  public float getMaximumSpan(int axis)
 | 
						|
  {
 | 
						|
    float max;
 | 
						|
    if (axis == majorAxis)
 | 
						|
      max = getPreferredSpan(axis);
 | 
						|
    else
 | 
						|
      max = Short.MAX_VALUE;
 | 
						|
    return max;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the minimum span along the specified axis.
 | 
						|
   */
 | 
						|
  public float getMinimumSpan(int axis)
 | 
						|
  {
 | 
						|
    float min;
 | 
						|
    if (axis == majorAxis)
 | 
						|
      min = getPreferredSpan(axis);
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (minReq != null)
 | 
						|
          {
 | 
						|
            View child = minReq.getChildView();
 | 
						|
            min = child.getMinimumSpan(axis);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            // No layout information yet. Return insets + 5 as some kind of
 | 
						|
            // estimation.
 | 
						|
            if (axis == X_AXIS)
 | 
						|
              min = getLeftInset() + getRightInset() + 5;
 | 
						|
            else
 | 
						|
              min = getTopInset() + getBottomInset() + 5;
 | 
						|
          }
 | 
						|
      }
 | 
						|
    return min;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Receives notification that one of the child views has changed its
 | 
						|
   * layout preferences along one or both axis.
 | 
						|
   *
 | 
						|
   * This queues a layout request for that child view if necessary.
 | 
						|
   *
 | 
						|
   * @param view the view that has changed its preferences
 | 
						|
   * @param width <code>true</code> if the width preference has changed
 | 
						|
   * @param height <code>true</code> if the height preference has changed
 | 
						|
   */
 | 
						|
  public synchronized void preferenceChanged(View view, boolean width,
 | 
						|
                                             boolean height)
 | 
						|
  {
 | 
						|
    if (view == null)
 | 
						|
      getParent().preferenceChanged(this, width, height);
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (changing != null)
 | 
						|
          {
 | 
						|
            View cv = changing.getChildView();
 | 
						|
            if (cv == view)
 | 
						|
              {
 | 
						|
                changing.preferenceChanged(width, height);
 | 
						|
                return;
 | 
						|
              }
 | 
						|
          }
 | 
						|
        int index = getViewIndexAtPosition(view.getStartOffset(),
 | 
						|
                                           Position.Bias.Forward);
 | 
						|
        ChildState cs = getChildState(index);
 | 
						|
        cs.preferenceChanged(width, height);
 | 
						|
        LayoutQueue q = getLayoutQueue();
 | 
						|
        q.addTask(cs);
 | 
						|
        q.addTask(flushTask);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Updates the layout for this view. This is implemented to trigger
 | 
						|
   * {@link ChildLocator#childChanged} for the changed view, if there is
 | 
						|
   * any.
 | 
						|
   *
 | 
						|
   * @param ec the element change, may be <code>null</code> if there were
 | 
						|
   *        no changes to the element of this view
 | 
						|
   * @param e the document event
 | 
						|
   * @param a the current allocation of this view
 | 
						|
   */
 | 
						|
  protected void updateLayout(DocumentEvent.ElementChange ec,
 | 
						|
                              DocumentEvent e, Shape a)
 | 
						|
  {
 | 
						|
    if (ec != null)
 | 
						|
      {
 | 
						|
        int index = Math.max(ec.getIndex() - 1, 0);
 | 
						|
        ChildState cs = getChildState(index);
 | 
						|
        locator.childChanged(cs);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the <code>ChildState</code> object associated with the child view
 | 
						|
   * at the specified <code>index</code>.
 | 
						|
   *
 | 
						|
   * @param index the index of the child view for which to query the state
 | 
						|
   *
 | 
						|
   * @return the child state for the specified child view
 | 
						|
   */
 | 
						|
  protected ChildState getChildState(int index) {
 | 
						|
    synchronized (childStates)
 | 
						|
      {
 | 
						|
        return (ChildState) childStates.get(index);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the <code>LayoutQueue</code> used for layouting the box view.
 | 
						|
   * This simply returns {@link LayoutQueue#getDefaultQueue()}.
 | 
						|
   *
 | 
						|
   * @return the <code>LayoutQueue</code> used for layouting the box view
 | 
						|
   */
 | 
						|
  protected LayoutQueue getLayoutQueue()
 | 
						|
  {
 | 
						|
    return LayoutQueue.getDefaultQueue();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the child view index of the view that represents the specified
 | 
						|
   * position in the document model.
 | 
						|
   *
 | 
						|
   * @param pos the position in the model
 | 
						|
   * @param b the bias
 | 
						|
   *
 | 
						|
   * @return the child view index of the view that represents the specified
 | 
						|
   *         position in the document model
 | 
						|
   */
 | 
						|
  protected synchronized int getViewIndexAtPosition(int pos, Position.Bias b)
 | 
						|
  {
 | 
						|
    if (b == Position.Bias.Backward)
 | 
						|
      pos = Math.max(0, pos - 1);
 | 
						|
    Element elem = getElement();
 | 
						|
    return elem.getElementIndex(pos);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a <code>ChildState</code> object for the specified view.
 | 
						|
   *
 | 
						|
   * @param v the view for which to create a child state object
 | 
						|
   *
 | 
						|
   * @return the created child state
 | 
						|
   */
 | 
						|
  protected ChildState createChildState(View v)
 | 
						|
  {
 | 
						|
    return new ChildState(v);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Flushes the requirements changes upwards to the parent view. This is
 | 
						|
   * called from the layout thread.
 | 
						|
   */
 | 
						|
  protected synchronized void flushRequirementChanges()
 | 
						|
  {
 | 
						|
    if (majorChanged || minorChanged)
 | 
						|
      {
 | 
						|
        View p = getParent();
 | 
						|
        if (p != null)
 | 
						|
          {
 | 
						|
            boolean horizontal;
 | 
						|
            boolean vertical;
 | 
						|
            if (majorAxis == X_AXIS)
 | 
						|
              {
 | 
						|
                horizontal = majorChanged;
 | 
						|
                vertical = minorChanged;
 | 
						|
              }
 | 
						|
            else
 | 
						|
              {
 | 
						|
                vertical = majorChanged;
 | 
						|
                horizontal = minorChanged;
 | 
						|
              }
 | 
						|
 | 
						|
            p.preferenceChanged(this, horizontal, vertical);
 | 
						|
            majorChanged = false;
 | 
						|
            minorChanged = false;
 | 
						|
 | 
						|
            Component c = getContainer();
 | 
						|
            if (c != null)
 | 
						|
              c.repaint();
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
}
 |