| /* View.java -- |
| Copyright (C) 2002, 2004, 2005, 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.Container; |
| import java.awt.Graphics; |
| import java.awt.Rectangle; |
| import java.awt.Shape; |
| |
| import javax.swing.SwingConstants; |
| import javax.swing.SwingUtilities; |
| import javax.swing.event.DocumentEvent; |
| |
| public abstract class View implements SwingConstants |
| { |
| public static final int BadBreakWeight = 0; |
| public static final int ExcellentBreakWeight = 2000; |
| public static final int ForcedBreakWeight = 3000; |
| public static final int GoodBreakWeight = 1000; |
| |
| public static final int X_AXIS = 0; |
| public static final int Y_AXIS = 1; |
| |
| private float width, height; |
| private Element elt; |
| private View parent; |
| |
| /** |
| * Creates a new <code>View</code> instance. |
| * |
| * @param elem an <code>Element</code> value |
| */ |
| public View(Element elem) |
| { |
| elt = elem; |
| } |
| |
| public abstract void paint(Graphics g, Shape s); |
| |
| /** |
| * Sets the parent for this view. This is the first method that is beeing |
| * called on a view to setup the view hierarchy. This is also the last method |
| * beeing called when the view is disconnected from the view hierarchy, in |
| * this case <code>parent</code> is null. |
| * |
| * If <code>parent</code> is <code>null</code>, a call to this method also |
| * calls <code>setParent</code> on the children, thus disconnecting them from |
| * the view hierarchy. That means that super must be called when this method |
| * is overridden. |
| * |
| * @param parent the parent to set, <code>null</code> when this view is |
| * beeing disconnected from the view hierarchy |
| */ |
| public void setParent(View parent) |
| { |
| if (parent == null) |
| { |
| int numChildren = getViewCount(); |
| for (int i = 0; i < numChildren; i++) |
| getView(i).setParent(null); |
| } |
| |
| this.parent = parent; |
| } |
| |
| public View getParent() |
| { |
| return parent; |
| } |
| |
| public Container getContainer() |
| { |
| View parent = getParent(); |
| if (parent == null) |
| return null; |
| else |
| return parent.getContainer(); |
| } |
| |
| public Document getDocument() |
| { |
| return getElement().getDocument(); |
| } |
| |
| public Element getElement() |
| { |
| return elt; |
| } |
| |
| /** |
| * Returns the preferred span along the specified axis. Normally the view is |
| * rendered with the span returned here if that is possible. |
| * |
| * @param axis the axis |
| * |
| * @return the preferred span along the specified axis |
| */ |
| public abstract float getPreferredSpan(int axis); |
| |
| /** |
| * Returns the resize weight of this view. A value of <code>0</code> or less |
| * means this view is not resizeable. Positive values make the view |
| * resizeable. The default implementation returns <code>0</code> |
| * unconditionally. |
| * |
| * @param axis the axis |
| * |
| * @return the resizability of this view along the specified axis |
| */ |
| public int getResizeWeight(int axis) |
| { |
| return 0; |
| } |
| |
| /** |
| * Returns the maximum span along the specified axis. The default |
| * implementation will forward to |
| * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)} |
| * returns a value > 0, in which case this returns {@link Integer#MIN_VALUE}. |
| * |
| * @param axis the axis |
| * |
| * @return the maximum span along the specified axis |
| */ |
| public float getMaximumSpan(int axis) |
| { |
| float max = Integer.MAX_VALUE; |
| if (getResizeWeight(axis) <= 0) |
| max = getPreferredSpan(axis); |
| return max; |
| } |
| |
| /** |
| * Returns the minimum span along the specified axis. The default |
| * implementation will forward to |
| * {@link #getPreferredSpan(int)} unless {@link #getResizeWeight(int)} |
| * returns a value > 0, in which case this returns <code>0</code>. |
| * |
| * @param axis the axis |
| * |
| * @return the minimum span along the specified axis |
| */ |
| public float getMinimumSpan(int axis) |
| { |
| float min = 0; |
| if (getResizeWeight(axis) <= 0) |
| min = getPreferredSpan(axis); |
| return min; |
| } |
| |
| public void setSize(float width, float height) |
| { |
| // The default implementation does nothing. |
| } |
| |
| /** |
| * Returns the alignment of this view along the baseline of the parent view. |
| * An alignment of <code>0.0</code> will align this view with the left edge |
| * along the baseline, an alignment of <code>0.5</code> will align it |
| * centered to the baseline, an alignment of <code>1.0</code> will align |
| * the right edge along the baseline. |
| * |
| * The default implementation returns 0.5 unconditionally. |
| * |
| * @param axis the axis |
| * |
| * @return the alignment of this view along the parents baseline for the |
| * specified axis |
| */ |
| public float getAlignment(int axis) |
| { |
| return 0.5f; |
| } |
| |
| public AttributeSet getAttributes() |
| { |
| return getElement().getAttributes(); |
| } |
| |
| public boolean isVisible() |
| { |
| return true; |
| } |
| |
| public int getViewCount() |
| { |
| return 0; |
| } |
| |
| public View getView(int index) |
| { |
| return null; |
| } |
| |
| public ViewFactory getViewFactory() |
| { |
| View parent = getParent(); |
| return parent != null ? parent.getViewFactory() : null; |
| } |
| |
| /** |
| * Replaces a couple of child views with new child views. If |
| * <code>length == 0</code> then this is a simple insertion, if |
| * <code>views == null</code> this only removes some child views. |
| * |
| * @param offset the offset at which to replace |
| * @param length the number of child views to be removed |
| * @param views the new views to be inserted, may be <code>null</code> |
| */ |
| public void replace(int offset, int length, View[] views) |
| { |
| // Default implementation does nothing. |
| } |
| |
| public void insert(int offset, View view) |
| { |
| View[] array = { view }; |
| replace(offset, 1, array); |
| } |
| |
| public void append(View view) |
| { |
| View[] array = { view }; |
| int offset = getViewCount(); |
| replace(offset, 0, array); |
| } |
| |
| public void removeAll() |
| { |
| replace(0, getViewCount(), new View[0]); |
| } |
| |
| public void remove(int index) |
| { |
| replace(index, 1, null); |
| } |
| |
| public View createFragment(int p0, int p1) |
| { |
| // The default implementation doesn't support fragmentation. |
| return this; |
| } |
| |
| public int getStartOffset() |
| { |
| return getElement().getStartOffset(); |
| } |
| |
| public int getEndOffset() |
| { |
| return getElement().getEndOffset(); |
| } |
| |
| public Shape getChildAllocation(int index, Shape a) |
| { |
| return null; |
| } |
| |
| /** |
| * @since 1.4 |
| */ |
| public int getViewIndex(float x, float y, Shape allocation) |
| { |
| return -1; |
| } |
| |
| /** |
| * @since 1.4 |
| */ |
| public String getToolTipText(float x, float y, Shape allocation) |
| { |
| int index = getViewIndex(x, y, allocation); |
| |
| if (index < -1) |
| return null; |
| |
| Shape childAllocation = getChildAllocation(index, allocation); |
| |
| if (childAllocation.getBounds().contains(x, y)) |
| return getView(index).getToolTipText(x, y, childAllocation); |
| |
| return null; |
| } |
| |
| /** |
| * @since 1.3 |
| */ |
| public Graphics getGraphics() |
| { |
| return getContainer().getGraphics(); |
| } |
| |
| public void preferenceChanged(View child, boolean width, boolean height) |
| { |
| if (parent != null) |
| parent.preferenceChanged(this, width, height); |
| } |
| |
| public int getBreakWeight(int axis, float pos, float len) |
| { |
| return BadBreakWeight; |
| } |
| |
| public View breakView(int axis, int offset, float pos, float len) |
| { |
| return this; |
| } |
| |
| /** |
| * @since 1.3 |
| */ |
| public int getViewIndex(int pos, Position.Bias b) |
| { |
| return -1; |
| } |
| |
| /** |
| * Receive notification about an insert update to the text model. |
| * |
| * The default implementation of this method does the following: |
| * <ul> |
| * <li>Call {@link #updateChildren} if the element that this view is |
| * responsible for has changed. This makes sure that the children can |
| * correctly represent the model.<li> |
| * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to |
| * the child views.<li> |
| * <li>Call {@link #updateLayout}. Gives the view a chance to either |
| * repair its layout, reschedule layout or do nothing at all.</li> |
| * </ul> |
| * |
| * @param ev the DocumentEvent that describes the change |
| * @param shape the shape of the view |
| * @param vf the ViewFactory for creating child views |
| */ |
| public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| Element el = getElement(); |
| DocumentEvent.ElementChange ec = ev.getChange(el); |
| if (ec != null) |
| updateChildren(ec, ev, vf); |
| forwardUpdate(ec, ev, shape, vf); |
| updateLayout(ec, ev, shape); |
| } |
| |
| /** |
| * Receive notification about a remove update to the text model. |
| * |
| * The default implementation of this method does the following: |
| * <ul> |
| * <li>Call {@link #updateChildren} if the element that this view is |
| * responsible for has changed. This makes sure that the children can |
| * correctly represent the model.<li> |
| * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to |
| * the child views.<li> |
| * <li>Call {@link #updateLayout}. Gives the view a chance to either |
| * repair its layout, reschedule layout or do nothing at all.</li> |
| * </ul> |
| * |
| * @param ev the DocumentEvent that describes the change |
| * @param shape the shape of the view |
| * @param vf the ViewFactory for creating child views |
| */ |
| public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| Element el = getElement(); |
| DocumentEvent.ElementChange ec = ev.getChange(el); |
| if (ec != null) |
| { |
| if (! updateChildren(ec, ev, vf)) |
| ec = null; |
| } |
| forwardUpdate(ec, ev, shape, vf); |
| updateLayout(ec, ev, shape); |
| } |
| |
| /** |
| * Receive notification about a change update to the text model. |
| * |
| * The default implementation of this method does the following: |
| * <ul> |
| * <li>Call {@link #updateChildren} if the element that this view is |
| * responsible for has changed. This makes sure that the children can |
| * correctly represent the model.<li> |
| * <li>Call {@link #forwardUpdate}. This forwards the DocumentEvent to |
| * the child views.<li> |
| * <li>Call {@link #updateLayout}. Gives the view a chance to either |
| * repair its layout, reschedule layout or do nothing at all.</li> |
| * </ul> |
| * |
| * @param ev the DocumentEvent that describes the change |
| * @param shape the shape of the view |
| * @param vf the ViewFactory for creating child views |
| */ |
| public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| Element el = getElement(); |
| DocumentEvent.ElementChange ec = ev.getChange(el); |
| if (ec != null) |
| updateChildren(ec, ev, vf); |
| forwardUpdate(ec, ev, shape, vf); |
| updateLayout(ec, ev, shape); |
| } |
| |
| /** |
| * Updates the list of children that is returned by {@link #getView} |
| * and {@link #getViewCount}. |
| * |
| * Element that are specified as beeing added in the ElementChange record are |
| * assigned a view for using the ViewFactory. Views of Elements that |
| * are specified as beeing removed are removed from the list. |
| * |
| * @param ec the ElementChange record that describes the change of the |
| * element |
| * @param ev the DocumentEvent describing the change of the document model |
| * @param vf the ViewFactory to use for creating new views |
| * |
| * @return whether or not the child views represent the child elements of |
| * the element that this view is responsible for. Some views may |
| * create views that are responsible only for parts of the element |
| * that they are responsible for and should then return false. |
| * |
| * @since 1.3 |
| */ |
| protected boolean updateChildren(DocumentEvent.ElementChange ec, |
| DocumentEvent ev, |
| ViewFactory vf) |
| { |
| Element[] added = ec.getChildrenAdded(); |
| Element[] removed = ec.getChildrenRemoved(); |
| int index = ec.getIndex(); |
| |
| View[] newChildren = new View[added.length]; |
| for (int i = 0; i < added.length; ++i) |
| newChildren[i] = vf.create(added[i]); |
| replace(index, removed.length, newChildren); |
| |
| return true; |
| } |
| |
| /** |
| * Forwards the DocumentEvent to child views that need to get notified |
| * of the change to the model. This calles {@link #forwardUpdateToView} |
| * for each View that must be forwarded to. |
| * |
| * If <code>ec</code> is not <code>null</code> (this means there have been |
| * structural changes to the element that this view is responsible for) this |
| * method should recognize this and don't notify newly added child views. |
| * |
| * @param ec the ElementChange describing the element changes (may be |
| * <code>null</code> if there were no changes) |
| * @param ev the DocumentEvent describing the changes to the model |
| * @param shape the current allocation of the view |
| * @param vf the ViewFactory used to create new Views |
| * |
| * @since 1.3 |
| */ |
| protected void forwardUpdate(DocumentEvent.ElementChange ec, |
| DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| int count = getViewCount(); |
| if (count > 0) |
| { |
| // Determine start index. |
| int startOffset = ev.getOffset(); |
| int startIndex = getViewIndex(startOffset, Position.Bias.Backward); |
| |
| // For REMOVE events we have to forward the event to the last element, |
| // for the case that an Element has been removed that represente |
| // the offset. |
| if (startIndex == -1 && ev.getType() == DocumentEvent.EventType.REMOVE |
| && startOffset >= getEndOffset()) |
| { |
| startIndex = getViewCount() - 1; |
| } |
| |
| // When startIndex is on a view boundary, forward event to the |
| // previous view too. |
| if (startIndex >= 0) |
| { |
| View v = getView(startIndex); |
| if (v != null) |
| { |
| if (v.getStartOffset() == startOffset && startOffset > 0) |
| startIndex = Math.max(0, startIndex - 1); |
| } |
| } |
| startIndex = Math.max(0, startIndex); |
| |
| // Determine end index. |
| int endIndex = startIndex; |
| if (ev.getType() != DocumentEvent.EventType.REMOVE) |
| { |
| endIndex = getViewIndex(startOffset + ev.getLength(), |
| Position.Bias.Forward); |
| if (endIndex < 0) |
| endIndex = getViewCount() - 1; |
| } |
| |
| // Determine hole that comes from added elements (we don't forward |
| // the event to newly added views. |
| int startAdded = endIndex + 1; |
| int endAdded = startAdded; |
| Element[] added = (ec != null) ? ec.getChildrenAdded() : null; |
| if (added != null && added.length > 0) |
| { |
| startAdded = ec.getIndex(); |
| endAdded = startAdded + added.length - 1; |
| } |
| |
| // Forward event to all views between startIndex and endIndex, |
| // and leave out all views in the hole. |
| for (int i = startIndex; i <= endIndex; i++) |
| { |
| // Skip newly added child views. |
| if (! (i >= startAdded && i <= endAdded)) |
| { |
| View child = getView(i); |
| if (child != null) |
| { |
| Shape childAlloc = getChildAllocation(i, shape); |
| forwardUpdateToView(child, ev, childAlloc, vf); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Forwards an update event to the given child view. This calls |
| * {@link #insertUpdate}, {@link #removeUpdate} or {@link #changedUpdate}, |
| * depending on the type of document event. |
| * |
| * @param view the View to forward the event to |
| * @param ev the DocumentEvent to forward |
| * @param shape the current allocation of the View |
| * @param vf the ViewFactory used to create new Views |
| * |
| * @since 1.3 |
| */ |
| protected void forwardUpdateToView(View view, DocumentEvent ev, Shape shape, |
| ViewFactory vf) |
| { |
| DocumentEvent.EventType type = ev.getType(); |
| if (type == DocumentEvent.EventType.INSERT) |
| view.insertUpdate(ev, shape, vf); |
| else if (type == DocumentEvent.EventType.REMOVE) |
| view.removeUpdate(ev, shape, vf); |
| else if (type == DocumentEvent.EventType.CHANGE) |
| view.changedUpdate(ev, shape, vf); |
| } |
| |
| /** |
| * Updates the layout. |
| * |
| * @param ec the ElementChange that describes the changes to the element |
| * @param ev the DocumentEvent that describes the changes to the model |
| * @param shape the current allocation for this view |
| * |
| * @since 1.3 |
| */ |
| protected void updateLayout(DocumentEvent.ElementChange ec, |
| DocumentEvent ev, Shape shape) |
| { |
| if (ec != null && shape != null) |
| preferenceChanged(null, true, true); |
| Container c = getContainer(); |
| if (c != null) |
| c.repaint(); |
| } |
| |
| /** |
| * Maps a position in the document into the coordinate space of the View. |
| * The output rectangle usually reflects the font height but has a width |
| * of zero. |
| * |
| * @param pos the position of the character in the model |
| * @param a the area that is occupied by the view |
| * @param b either {@link Position.Bias#Forward} or |
| * {@link Position.Bias#Backward} depending on the preferred |
| * direction bias. If <code>null</code> this defaults to |
| * <code>Position.Bias.Forward</code> |
| * |
| * @return a rectangle that gives the location of the document position |
| * inside the view coordinate space |
| * |
| * @throws BadLocationException if <code>pos</code> is invalid |
| * @throws IllegalArgumentException if b is not one of the above listed |
| * valid values |
| */ |
| public abstract Shape modelToView(int pos, Shape a, Position.Bias b) |
| throws BadLocationException; |
| |
| /** |
| * Maps a region in the document into the coordinate space of the View. |
| * |
| * @param p1 the beginning position inside the document |
| * @param b1 the direction bias for the beginning position |
| * @param p2 the end position inside the document |
| * @param b2 the direction bias for the end position |
| * @param a the area that is occupied by the view |
| * |
| * @return a rectangle that gives the span of the document region |
| * inside the view coordinate space |
| * |
| * @throws BadLocationException if <code>p1</code> or <code>p2</code> are |
| * invalid |
| * @throws IllegalArgumentException if b1 or b2 is not one of the above |
| * listed valid values |
| */ |
| public Shape modelToView(int p1, Position.Bias b1, |
| int p2, Position.Bias b2, Shape a) |
| throws BadLocationException |
| { |
| if (b1 != Position.Bias.Forward && b1 != Position.Bias.Backward) |
| throw new IllegalArgumentException |
| ("b1 must be either Position.Bias.Forward or Position.Bias.Backward"); |
| if (b2 != Position.Bias.Forward && b2 != Position.Bias.Backward) |
| throw new IllegalArgumentException |
| ("b2 must be either Position.Bias.Forward or Position.Bias.Backward"); |
| |
| Shape s1 = modelToView(p1, a, b1); |
| // Special case for p2 == end index. |
| Shape s2; |
| if (p2 != getEndOffset()) |
| { |
| s2 = modelToView(p2, a, b2); |
| } |
| else |
| { |
| try |
| { |
| s2 = modelToView(p2, a, b2); |
| } |
| catch (BadLocationException ex) |
| { |
| // Assume the end rectangle to be at the right edge of the |
| // view. |
| Rectangle aRect = a instanceof Rectangle ? (Rectangle) a |
| : a.getBounds(); |
| s2 = new Rectangle(aRect.x + aRect.width - 1, aRect.y, 1, |
| aRect.height); |
| } |
| } |
| |
| // Need to modify the rectangle, so we create a copy in all cases. |
| Rectangle r1 = s1.getBounds(); |
| Rectangle r2 = s2 instanceof Rectangle ? (Rectangle) s2 |
| : s2.getBounds(); |
| |
| // For multiline view, let the resulting rectangle span the whole view. |
| if (r1.y != r2.y) |
| { |
| Rectangle aRect = a instanceof Rectangle ? (Rectangle) a |
| : a.getBounds(); |
| r1.x = aRect.x; |
| r1.width = aRect.width; |
| } |
| |
| return SwingUtilities.computeUnion(r2.x, r2.y, r2.width, r2.height, r1); |
| } |
| |
| /** |
| * Maps a position in the document into the coordinate space of the View. |
| * The output rectangle usually reflects the font height but has a width |
| * of zero. |
| * |
| * This method is deprecated and calls |
| * {@link #modelToView(int, Position.Bias, int, Position.Bias, Shape)} with |
| * a bias of {@link Position.Bias#Forward}. |
| * |
| * @param pos the position of the character in the model |
| * @param a the area that is occupied by the view |
| * |
| * @return a rectangle that gives the location of the document position |
| * inside the view coordinate space |
| * |
| * @throws BadLocationException if <code>pos</code> is invalid |
| * |
| * @deprecated Use {@link #modelToView(int, Shape, Position.Bias)} instead. |
| */ |
| public Shape modelToView(int pos, Shape a) throws BadLocationException |
| { |
| return modelToView(pos, a, Position.Bias.Forward); |
| } |
| |
| /** |
| * 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 abstract int viewToModel(float x, float y, Shape a, Position.Bias[] b); |
| |
| /** |
| * Maps coordinates from the <code>View</code>'s space into a position |
| * in the document model. This method is deprecated and only there for |
| * compatibility. |
| * |
| * @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> |
| * |
| * @return the position in the document that corresponds to the screen |
| * coordinates <code>x, y</code> |
| * |
| * @deprecated Use {@link #viewToModel(float, float, Shape, Position.Bias[])} |
| * instead. |
| */ |
| public int viewToModel(float x, float y, Shape a) |
| { |
| return viewToModel(x, y, a, new Position.Bias[0]); |
| } |
| |
| /** |
| * Dumps the complete View hierarchy. This method can be used for debugging |
| * purposes. |
| */ |
| protected void dump() |
| { |
| // Climb up the hierarchy to the parent. |
| View parent = getParent(); |
| if (parent != null) |
| parent.dump(); |
| else |
| dump(0); |
| } |
| |
| /** |
| * Dumps the view hierarchy below this View with the specified indentation |
| * level. |
| * |
| * @param indent the indentation level to be used for this view |
| */ |
| void dump(int indent) |
| { |
| for (int i = 0; i < indent; ++i) |
| System.out.print('.'); |
| System.out.println(this + "(" + getStartOffset() + "," + getEndOffset() + ": " + getElement()); |
| |
| int count = getViewCount(); |
| for (int i = 0; i < count; ++i) |
| getView(i).dump(indent + 1); |
| } |
| |
| /** |
| * Returns the document position that is (visually) nearest to the given |
| * document position <code>pos</code> in the given direction <code>d</code>. |
| * |
| * @param pos the document position |
| * @param b the bias for <code>pos</code> |
| * @param a the allocation for this view |
| * @param d the direction, must be either {@link SwingConstants#NORTH}, |
| * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or |
| * {@link SwingConstants#EAST} |
| * @param biasRet an array of {@link Position.Bias} that can hold at least |
| * one element, which is filled with the bias of the return position |
| * on method exit |
| * |
| * @return the document position that is (visually) nearest to the given |
| * document position <code>pos</code> in the given direction |
| * <code>d</code> |
| * |
| * @throws BadLocationException if <code>pos</code> is not a valid offset in |
| * the document model |
| * @throws IllegalArgumentException if <code>d</code> is not a valid direction |
| */ |
| public int getNextVisualPositionFrom(int pos, Position.Bias b, |
| Shape a, int d, |
| Position.Bias[] biasRet) |
| throws BadLocationException |
| { |
| int ret = pos; |
| Rectangle r; |
| View parent; |
| |
| switch (d) |
| { |
| case EAST: |
| // TODO: take component orientation into account? |
| // Note: If pos is below zero the implementation will return |
| // pos + 1 regardless of whether that value is a correct offset |
| // in the document model. However this is what the RI does. |
| ret = Math.min(pos + 1, getEndOffset()); |
| break; |
| case WEST: |
| // TODO: take component orientation into account? |
| ret = Math.max(pos - 1, getStartOffset()); |
| break; |
| case NORTH: |
| // Try to find a suitable offset by examining the area above. |
| parent = getParent(); |
| r = parent.modelToView(pos, a, b).getBounds(); |
| ret = parent.viewToModel(r.x, r.y - 1, a, biasRet); |
| break; |
| case SOUTH: |
| // Try to find a suitable offset by examining the area below. |
| parent = getParent(); |
| r = parent.modelToView(pos, a, b).getBounds(); |
| ret = parent.viewToModel(r.x + r.width, r.y + r.height, a, biasRet); |
| break; |
| default: |
| throw new IllegalArgumentException("Illegal value for d"); |
| } |
| |
| return ret; |
| } |
| } |