mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			495 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			495 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
/* ComponentView.java --
 | 
						|
   Copyright (C) 2002, 2004, 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.Container;
 | 
						|
import java.awt.Dimension;
 | 
						|
import java.awt.Graphics;
 | 
						|
import java.awt.Rectangle;
 | 
						|
import java.awt.Shape;
 | 
						|
 | 
						|
import javax.swing.SwingUtilities;
 | 
						|
 | 
						|
/**
 | 
						|
 * A {@link View} implementation that is able to render arbitrary
 | 
						|
 * {@link Component}s. This uses the attribute
 | 
						|
 * {@link StyleConstants#ComponentAttribute} to determine the
 | 
						|
 * <code>Component</code> that should be rendered. This <code>Component</code>
 | 
						|
 * becomes a direct child of the <code>JTextComponent</code> that contains
 | 
						|
 * this <code>ComponentView</code>, so this view must not be shared between
 | 
						|
 * multiple <code>JTextComponent</code>s.
 | 
						|
 *
 | 
						|
 * @author Roman Kennke (kennke@aicas.com)
 | 
						|
 * @author original author unknown
 | 
						|
 */
 | 
						|
public class ComponentView extends View
 | 
						|
{
 | 
						|
 | 
						|
  /**
 | 
						|
   * A special container that sits between the component and the hosting
 | 
						|
   * container. This is used to propagate invalidate requests and cache
 | 
						|
   * the component's layout sizes.
 | 
						|
   */
 | 
						|
  private class Interceptor
 | 
						|
    extends Container
 | 
						|
  {
 | 
						|
    Dimension min;
 | 
						|
    Dimension pref;
 | 
						|
    Dimension max;
 | 
						|
    float alignX;
 | 
						|
    float alignY;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Creates a new instance that hosts the specified component.
 | 
						|
     */
 | 
						|
    Interceptor(Component c)
 | 
						|
    {
 | 
						|
      setLayout(null);
 | 
						|
      add(c);
 | 
						|
      cacheComponentSizes();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Intercepts the normal invalidate call and propagates the invalidate
 | 
						|
     * request up using the View's preferenceChanged().
 | 
						|
     */
 | 
						|
    public void invalidate()
 | 
						|
    {
 | 
						|
      super.invalidate();
 | 
						|
      if (getParent() != null)
 | 
						|
        preferenceChanged(null, true, true);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * This is overridden to simply cache the layout sizes.
 | 
						|
     */
 | 
						|
    public void doLayout()
 | 
						|
    {
 | 
						|
      cacheComponentSizes();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to also reshape the component itself.
 | 
						|
     */
 | 
						|
    public void reshape(int x, int y, int w, int h)
 | 
						|
    {
 | 
						|
      super.reshape(x, y, w, h);
 | 
						|
      if (getComponentCount() > 0)
 | 
						|
        getComponent(0).setSize(w, h);
 | 
						|
      cacheComponentSizes();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to also show the component.
 | 
						|
     */
 | 
						|
    public void show()
 | 
						|
    {
 | 
						|
      super.show();
 | 
						|
      if (getComponentCount() > 0)
 | 
						|
        getComponent(0).setVisible(true);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to also hide the component.
 | 
						|
     */
 | 
						|
    public void hide()
 | 
						|
    {
 | 
						|
      super.hide();
 | 
						|
      if (getComponentCount() > 0)
 | 
						|
        getComponent(0).setVisible(false);
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to return the cached value.
 | 
						|
     */
 | 
						|
    public Dimension getMinimumSize()
 | 
						|
    {
 | 
						|
      maybeValidate();
 | 
						|
      return min;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to return the cached value.
 | 
						|
     */
 | 
						|
    public Dimension getPreferredSize()
 | 
						|
    {
 | 
						|
      maybeValidate();
 | 
						|
      return pref;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to return the cached value.
 | 
						|
     */
 | 
						|
    public Dimension getMaximumSize()
 | 
						|
    {
 | 
						|
      maybeValidate();
 | 
						|
      return max;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to return the cached value.
 | 
						|
     */
 | 
						|
    public float getAlignmentX()
 | 
						|
    {
 | 
						|
      maybeValidate();
 | 
						|
      return alignX;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Overridden to return the cached value.
 | 
						|
     */
 | 
						|
    public float getAlignmentY()
 | 
						|
    {
 | 
						|
      maybeValidate();
 | 
						|
      return alignY;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Validates the container only when necessary.
 | 
						|
     */
 | 
						|
    private void maybeValidate()
 | 
						|
    {
 | 
						|
      if (! isValid())
 | 
						|
        validate();
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Fetches the component layout sizes into the cache.
 | 
						|
     */
 | 
						|
    private void cacheComponentSizes()
 | 
						|
    {
 | 
						|
      if (getComponentCount() > 0)
 | 
						|
        {
 | 
						|
          Component c = getComponent(0);
 | 
						|
          min = c.getMinimumSize();
 | 
						|
          pref = c.getPreferredSize();
 | 
						|
          max = c.getMaximumSize();
 | 
						|
          alignX = c.getAlignmentX();
 | 
						|
          alignY = c.getAlignmentY();
 | 
						|
        }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * The component that is displayed by this view.
 | 
						|
   */
 | 
						|
  private Component comp;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The intercepting container.
 | 
						|
   */
 | 
						|
  private Interceptor interceptor;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new instance of <code>ComponentView</code> for the specified
 | 
						|
   * <code>Element</code>.
 | 
						|
   *
 | 
						|
   * @param elem the element that this <code>View</code> is rendering
 | 
						|
   */
 | 
						|
  public ComponentView(Element elem)
 | 
						|
  {
 | 
						|
    super(elem);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates the <code>Component</code> that this <code>View</code> is
 | 
						|
   * rendering. The <code>Component</code> is determined using
 | 
						|
   * the {@link StyleConstants#ComponentAttribute} of the associated
 | 
						|
   * <code>Element</code>.
 | 
						|
   *
 | 
						|
   * @return the component that is rendered
 | 
						|
   */
 | 
						|
  protected Component createComponent()
 | 
						|
  {
 | 
						|
    return StyleConstants.getComponent(getElement().getAttributes());
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the alignment of this <code>View</code> along the specified axis.
 | 
						|
   *
 | 
						|
   * @param axis either {@link View#X_AXIS} or {@link View#Y_AXIS}
 | 
						|
   *
 | 
						|
   * @return the alignment of this <code>View</code> along the specified axis
 | 
						|
   */
 | 
						|
  public float getAlignment(int axis)
 | 
						|
  {
 | 
						|
    float align = 0.0F;
 | 
						|
    // I'd rather throw an IllegalArgumentException for illegal axis,
 | 
						|
    // but the Harmony testsuite indicates fallback to super behaviour.
 | 
						|
    if (interceptor != null && (axis == X_AXIS || axis == Y_AXIS))
 | 
						|
      {
 | 
						|
        if (axis == X_AXIS)
 | 
						|
          align = interceptor.getAlignmentX();
 | 
						|
        else if (axis == Y_AXIS)
 | 
						|
          align = interceptor.getAlignmentY();
 | 
						|
        else
 | 
						|
          assert false : "Must not reach here";
 | 
						|
      }
 | 
						|
    else
 | 
						|
      align = super.getAlignment(axis);
 | 
						|
    return align;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the <code>Component</code> that is rendered by this
 | 
						|
   * <code>ComponentView</code>.
 | 
						|
   *
 | 
						|
   * @return the <code>Component</code> that is rendered by this
 | 
						|
   *         <code>ComponentView</code>
 | 
						|
   */
 | 
						|
  public final Component getComponent()
 | 
						|
  {
 | 
						|
    return comp;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the maximum span of this <code>View</code> along the specified
 | 
						|
   * axis.
 | 
						|
   *
 | 
						|
   * This will return {@link Component#getMaximumSize()} for the specified
 | 
						|
   * axis.
 | 
						|
   *
 | 
						|
   * @return the maximum span of this <code>View</code> along the specified
 | 
						|
   *         axis
 | 
						|
   */
 | 
						|
  public float getMaximumSpan(int axis)
 | 
						|
  {
 | 
						|
    if (axis != X_AXIS && axis != Y_AXIS)
 | 
						|
      throw new IllegalArgumentException("Illegal axis");
 | 
						|
    float span = 0;
 | 
						|
    if (interceptor != null)
 | 
						|
      {
 | 
						|
        if (axis == X_AXIS)
 | 
						|
          span = interceptor.getMaximumSize().width;
 | 
						|
        else if (axis == Y_AXIS)
 | 
						|
          span = interceptor.getMaximumSize().height;
 | 
						|
        else
 | 
						|
          assert false : "Must not reach here";
 | 
						|
      }
 | 
						|
    return span;
 | 
						|
  }
 | 
						|
 | 
						|
  public float getMinimumSpan(int axis)
 | 
						|
  {
 | 
						|
    if (axis != X_AXIS && axis != Y_AXIS)
 | 
						|
      throw new IllegalArgumentException("Illegal axis");
 | 
						|
    float span = 0;
 | 
						|
    if (interceptor != null)
 | 
						|
      {
 | 
						|
        if (axis == X_AXIS)
 | 
						|
          span = interceptor.getMinimumSize().width;
 | 
						|
        else if (axis == Y_AXIS)
 | 
						|
          span = interceptor.getMinimumSize().height;
 | 
						|
        else
 | 
						|
          assert false : "Must not reach here";
 | 
						|
      }
 | 
						|
    return span;
 | 
						|
  }
 | 
						|
 | 
						|
  public float getPreferredSpan(int axis)
 | 
						|
  {
 | 
						|
    if (axis != X_AXIS && axis != Y_AXIS)
 | 
						|
      throw new IllegalArgumentException("Illegal axis");
 | 
						|
    float span = 0;
 | 
						|
    if (interceptor != null)
 | 
						|
      {
 | 
						|
        if (axis == X_AXIS)
 | 
						|
          span = interceptor.getPreferredSize().width;
 | 
						|
        else if (axis == Y_AXIS)
 | 
						|
          span = interceptor.getPreferredSize().height;
 | 
						|
        else
 | 
						|
          assert false : "Must not reach here";
 | 
						|
      }
 | 
						|
    return span;
 | 
						|
  }
 | 
						|
 | 
						|
  public Shape modelToView(int pos, Shape a, Position.Bias b)
 | 
						|
    throws BadLocationException
 | 
						|
  {
 | 
						|
    int p0 = getStartOffset();
 | 
						|
    int p1 = getEndOffset();
 | 
						|
    if (pos >= p0 && pos <= p1)
 | 
						|
      {
 | 
						|
        Rectangle viewRect = a.getBounds();
 | 
						|
        if (pos == p1)
 | 
						|
          viewRect.x += viewRect.width;
 | 
						|
        viewRect.width = 0;
 | 
						|
        return viewRect;
 | 
						|
      }
 | 
						|
    else
 | 
						|
      throw new BadLocationException("Illegal position", pos);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * The real painting behavour is performed by normal component painting,
 | 
						|
   * triggered by the text component that hosts this view. This method does
 | 
						|
   * not paint by itself. However, it sets the size of the component according
 | 
						|
   * to the allocation that is passed here.
 | 
						|
   *
 | 
						|
   * @param g the graphics context
 | 
						|
   * @param a the allocation of the child
 | 
						|
   */
 | 
						|
  public void paint(Graphics g, Shape a)
 | 
						|
  {
 | 
						|
    if (interceptor != null)
 | 
						|
      {
 | 
						|
        Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
 | 
						|
        interceptor.setBounds(r.x, r.y, r.width, r.height);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * This sets up the component when the view is added to its parent, or
 | 
						|
   * cleans up the view when it is removed from its parent.
 | 
						|
   *
 | 
						|
   * When this view is added to a parent view, the component of this view
 | 
						|
   * is added to the container that hosts this view. When <code>p</code> is
 | 
						|
   * <code>null</code>, then the view is removed from it's parent and we have
 | 
						|
   * to also remove the component from it's parent container.
 | 
						|
   *
 | 
						|
   * @param p the parent view or <code>null</code> if this view is removed
 | 
						|
   *        from it's parent
 | 
						|
   */
 | 
						|
  public void setParent(final View p)
 | 
						|
  {
 | 
						|
    super.setParent(p);
 | 
						|
    if (SwingUtilities.isEventDispatchThread())
 | 
						|
      setParentImpl();
 | 
						|
    else
 | 
						|
      SwingUtilities.invokeLater
 | 
						|
      (new Runnable()
 | 
						|
       {
 | 
						|
         public void run()
 | 
						|
         {
 | 
						|
           Document doc = getDocument();
 | 
						|
           try
 | 
						|
             {
 | 
						|
               if (doc instanceof AbstractDocument)
 | 
						|
                 ((AbstractDocument) doc).readLock();
 | 
						|
               setParentImpl();
 | 
						|
               Container host = getContainer();
 | 
						|
               if (host != null)
 | 
						|
                 {
 | 
						|
                   preferenceChanged(null, true, true);
 | 
						|
                   host.repaint();
 | 
						|
                 }
 | 
						|
             }
 | 
						|
           finally
 | 
						|
             {
 | 
						|
               if (doc instanceof AbstractDocument)
 | 
						|
                 ((AbstractDocument) doc).readUnlock();
 | 
						|
             }
 | 
						|
         }
 | 
						|
       });
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * The implementation of {@link #setParent}. This is package private to
 | 
						|
   * avoid a synthetic accessor method.
 | 
						|
   */
 | 
						|
  void setParentImpl()
 | 
						|
  {
 | 
						|
    View p = getParent();
 | 
						|
    if (p != null)
 | 
						|
      {
 | 
						|
        Container c = getContainer();
 | 
						|
        if (c != null)
 | 
						|
          {
 | 
						|
            if (interceptor == null)
 | 
						|
              {
 | 
						|
                // Create component and put it inside the interceptor.
 | 
						|
                Component created = createComponent();
 | 
						|
                if (created != null)
 | 
						|
                  {
 | 
						|
                    comp = created;
 | 
						|
                    interceptor = new Interceptor(comp);
 | 
						|
                  }
 | 
						|
              }
 | 
						|
            if (interceptor != null)
 | 
						|
              {
 | 
						|
                // Add the interceptor to the hosting container.
 | 
						|
                if (interceptor.getParent() == null)
 | 
						|
                  c.add(interceptor, this);
 | 
						|
              }
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        if (interceptor != null)
 | 
						|
          {
 | 
						|
            Container parent = interceptor.getParent();
 | 
						|
            if (parent != null)
 | 
						|
              parent.remove(interceptor);
 | 
						|
          }
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Maps coordinates from the <code>View</code>'s space into a position
 | 
						|
   * in the document model.
 | 
						|
   *
 | 
						|
   * @param x the x coordinate in the view space
 | 
						|
   * @param y the y coordinate in the view space
 | 
						|
   * @param a the allocation of this <code>View</code>
 | 
						|
   * @param b the bias to use
 | 
						|
   *
 | 
						|
   * @return the position in the document that corresponds to the screen
 | 
						|
   *         coordinates <code>x, y</code>
 | 
						|
   */
 | 
						|
  public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
 | 
						|
  {
 | 
						|
    int pos;
 | 
						|
    // I'd rather do the following. The harmony testsuite indicates
 | 
						|
    // that a simple cast is performed.
 | 
						|
    //Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
 | 
						|
    Rectangle r = (Rectangle) a;
 | 
						|
    if (x < r.x + r.width / 2)
 | 
						|
      {
 | 
						|
        b[0] = Position.Bias.Forward;
 | 
						|
        pos = getStartOffset();
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        b[0] = Position.Bias.Backward;
 | 
						|
        pos = getEndOffset();
 | 
						|
      }
 | 
						|
    return pos;
 | 
						|
  }
 | 
						|
}
 |