| /* FieldView.java -- |
| Copyright (C) 2004, 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.Container; |
| import java.awt.FontMetrics; |
| import java.awt.Graphics; |
| import java.awt.Insets; |
| import java.awt.Rectangle; |
| import java.awt.Shape; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| |
| import javax.swing.BoundedRangeModel; |
| import javax.swing.JTextField; |
| import javax.swing.SwingUtilities; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import javax.swing.event.DocumentEvent; |
| |
| public class FieldView extends PlainView |
| { |
| BoundedRangeModel horizontalVisibility; |
| |
| /** Caches the preferred span of the X axis. It is invalidated by |
| * setting it to -1f. This is done when text in the document |
| * is inserted, removed or changed. The value is corrected as |
| * soon as calculateHorizontalSpan() is called. |
| */ |
| float cachedSpan = -1f; |
| |
| public FieldView(Element elem) |
| { |
| super(elem); |
| |
| } |
| |
| /** Checks whether the given container is a JTextField. If so |
| * it retrieves the textfield's horizontalVisibility instance. |
| * |
| * <p>This method should be only called when the view's container |
| * is valid. Naturally that would be the setParent() method however |
| * that method is not overridden in the RI and that is why we chose |
| * paint() instead.</p> |
| */ |
| private void checkContainer() |
| { |
| Container c = getContainer(); |
| |
| if (c instanceof JTextField) |
| { |
| horizontalVisibility = ((JTextField) c).getHorizontalVisibility(); |
| |
| // Provokes a repaint when the BoundedRangeModel's values change |
| // (which is what the RI does). |
| horizontalVisibility.addChangeListener(new ChangeListener(){ |
| public void stateChanged(ChangeEvent event) { |
| getContainer().repaint(); |
| }; |
| }); |
| |
| // It turned out that the span calculated at this point is wrong |
| // and needs to be recalculated (e.g. a different font setting is |
| // not taken into account). |
| calculateHorizontalSpan(); |
| |
| // Initializes the BoundedRangeModel properly. |
| updateVisibility(); |
| } |
| |
| } |
| |
| private void updateVisibility() |
| { |
| JTextField tf = (JTextField) getContainer(); |
| Insets insets = tf.getInsets(); |
| |
| int width = tf.getWidth() - insets.left - insets.right; |
| |
| horizontalVisibility.setMaximum(Math.max((int) ((cachedSpan != -1f) |
| ? cachedSpan |
| : calculateHorizontalSpan()), |
| width)); |
| |
| horizontalVisibility.setExtent(width - 1); |
| } |
| |
| protected FontMetrics getFontMetrics() |
| { |
| Component container = getContainer(); |
| return container.getFontMetrics(container.getFont()); |
| } |
| |
| /** |
| * Vertically centers the single line of text within the |
| * bounds of the input shape. The returned Rectangle is centered |
| * vertically within <code>shape</code> and has a height of the |
| * preferred span along the Y axis. Horizontal adjustment is done according |
| * to the horizontalAligment property of the component that is rendered. |
| * |
| * @param shape the shape within which the line is beeing centered |
| */ |
| protected Shape adjustAllocation(Shape shape) |
| { |
| // Return null when the original allocation is null (like the RI). |
| if (shape == null) |
| return null; |
| |
| Rectangle rectIn = shape.getBounds(); |
| // vertical adjustment |
| int height = (int) getPreferredSpan(Y_AXIS); |
| int y = rectIn.y + (rectIn.height - height) / 2; |
| // horizontal adjustment |
| JTextField textField = (JTextField) getContainer(); |
| int width = (int) ((cachedSpan != -1f) ? cachedSpan : calculateHorizontalSpan()); |
| int x; |
| if (horizontalVisibility != null && horizontalVisibility.getExtent() < width) |
| x = rectIn.x - horizontalVisibility.getValue(); |
| else |
| switch (textField.getHorizontalAlignment()) |
| { |
| case JTextField.CENTER: |
| x = rectIn.x + (rectIn.width - width) / 2; |
| break; |
| case JTextField.RIGHT: |
| x = rectIn.x + (rectIn.width - width - 1); |
| break; |
| case JTextField.TRAILING: |
| if (textField.getComponentOrientation().isLeftToRight()) |
| x = rectIn.x + (rectIn.width - width - 1); |
| else |
| x = rectIn.x; |
| break; |
| case JTextField.LEADING: |
| if (textField.getComponentOrientation().isLeftToRight()) |
| x = rectIn.x; |
| else |
| x = rectIn.x + (rectIn.width - width - 1); |
| break; |
| case JTextField.LEFT: |
| default: |
| x = rectIn.x; |
| break; |
| } |
| |
| return new Rectangle(x, y, width, height); |
| } |
| |
| public float getPreferredSpan(int axis) |
| { |
| if (axis != X_AXIS && axis != Y_AXIS) |
| throw new IllegalArgumentException(); |
| |
| |
| if (axis == Y_AXIS) |
| return super.getPreferredSpan(axis); |
| |
| if (cachedSpan != -1f) |
| return cachedSpan; |
| |
| return calculateHorizontalSpan(); |
| } |
| |
| /** Calculates and sets the horizontal span and stores the value |
| * in cachedSpan. |
| */ |
| private float calculateHorizontalSpan() |
| { |
| Segment s = getLineBuffer(); |
| Element elem = getElement(); |
| |
| try |
| { |
| elem.getDocument().getText(elem.getStartOffset(), |
| elem.getEndOffset() - 1, |
| s); |
| |
| return cachedSpan = Utilities.getTabbedTextWidth(s, getFontMetrics(), 0, this, s.offset); |
| } |
| catch (BadLocationException e) |
| { |
| // Should never happen |
| AssertionError ae = new AssertionError(); |
| ae.initCause(e); |
| throw ae; |
| } |
| } |
| |
| public int getResizeWeight(int axis) |
| { |
| return axis = axis == X_AXIS ? 1 : 0; |
| } |
| |
| public Shape modelToView(int pos, Shape a, Position.Bias bias) |
| throws BadLocationException |
| { |
| Shape newAlloc = adjustAllocation(a); |
| return super.modelToView(pos, newAlloc, bias); |
| } |
| |
| public void paint(Graphics g, Shape s) |
| { |
| if (horizontalVisibility == null) |
| checkContainer(); |
| |
| Shape newAlloc = adjustAllocation(s); |
| |
| Shape clip = g.getClip(); |
| if (clip != null) |
| { |
| // Reason for this: The allocation area is always determined by the |
| // size of the component (and its insets) regardless of whether |
| // parts of the component are invisible or not (e.g. when the |
| // component is part of a JScrollPane and partly moved out of |
| // the user-visible range). However the clip of the Graphics |
| // instance may be adjusted properly to that condition but |
| // does not handle insets. By calculating the intersection |
| // we get the correct clip to paint the text in all cases. |
| Rectangle r = s.getBounds(); |
| Rectangle cb = clip.getBounds(); |
| SwingUtilities.computeIntersection(r.x, r.y, r.width, r.height, cb); |
| |
| g.setClip(cb); |
| } |
| else |
| g.setClip(s); |
| |
| super.paint(g, newAlloc); |
| g.setClip(clip); |
| |
| } |
| |
| public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| cachedSpan = -1f; |
| |
| if (horizontalVisibility != null) |
| updateVisibility(); |
| |
| Shape newAlloc = adjustAllocation(shape); |
| |
| super.insertUpdate(ev, newAlloc, vf); |
| getContainer().repaint(); |
| } |
| |
| public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| cachedSpan = -1f; |
| |
| if (horizontalVisibility != null) |
| updateVisibility(); |
| |
| Shape newAlloc = adjustAllocation(shape); |
| super.removeUpdate(ev, newAlloc, vf); |
| getContainer().repaint(); |
| } |
| |
| public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) |
| { |
| cachedSpan = -1f; |
| |
| if (horizontalVisibility != null) |
| updateVisibility(); |
| |
| Shape newAlloc = adjustAllocation(shape); |
| super.changedUpdate(ev, newAlloc, vf); |
| getContainer().repaint(); |
| } |
| |
| public int viewToModel(float fx, float fy, Shape a, Position.Bias[] bias) |
| { |
| return super.viewToModel(fx, fy, adjustAllocation(a), bias); |
| } |
| |
| } |