classpath-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[cp-patches] FYI: reworked some bits of javax.swing.text


From: Roman Kennke
Subject: [cp-patches] FYI: reworked some bits of javax.swing.text
Date: Wed, 11 May 2005 12:39:58 +0200
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.2.1) Gecko/20021204

Hi,

I have reworked and extended some pieces of javax.swing.text. Most notably it introduces insert/remove/changedUpdate() methods in javax.swing.text.View and the corresponding stuff that is needed so that these methods actually get called.

The visual result is that the JTextField is now more correctly displayed (only one line, centered/aligned in its display box etc).

2005-05-11  Roman Kennke  <address@hidden>

       * javax/swing/plaf/basic/BasicTextUI.java
       (RootView.insertUpdate): Added. Forwards the update to the real
       root view.
       (RootView.removeUpdate): Added. Forwards the update to the real
       root view.
       (RootView.changedUpdate): Added. Forwards the update to the real
       root view.
       (DocumentHandler): Added. Receives DocumentEvents and forwards
       them to the corresponding View methods.
       (installDocumentListeners): Installs Document listeners on the
       text component's document model.
       * javax/swing/plaf/text/FieldView.java
       (adjustAllocation): Added. Adjusts the view area so that the
input line is centered vertically and correctly aligned horizontally.
       (modelToView): Call adjustAllocation before call to
       super.modelToView(..).
       (paint): Call adjustAllocation before call to super.paint(..).
       (insertUpdate): Added. Calls adjustAllocation before call to
       super.insertUpdate(..).
       (removeUpdate): Added. Calls adjustAllocation before call to
       super.removeUpdate(..).
       (changedUpdate): Added. Calls adjustAllocation before call to
       super.changedUpdate(..).
       * javax/swing/text/JTextComponent.java:
       Removed RepaintListener. This is no longer needed and should
       instead be handled by the text component's View.
       * javax/swing/plaf/text/View.java
       (paint): If parent is null, we still need to make sure the
       text component is updated through calling revalidate().
       (insertUpdate): Added. Receive notification about changes in the
       text document model.
       (removeUpdate): Added. Receive notification about changes in the
       text document model.
       (changedUpdate): Added. Receive notification about changes in the
       text document model.
       (updateChildren): Added. Updates the list of child Views after
       text has been modified.
       (forwardUpdate): Added. Forwards the update notification to the
       child Views.
       (forwardUpdateToView): Added. Actually performs the forwarding of
       update events.
       (updateLayout): Added. Makes sure that the display is in sync with
       the model.

/Roman

Index: javax/swing/plaf/basic/BasicTextUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTextUI.java,v
retrieving revision 1.16
diff -u -r1.16 BasicTextUI.java
--- javax/swing/plaf/basic/BasicTextUI.java     24 Jan 2005 18:49:26 -0000      
1.16
+++ javax/swing/plaf/basic/BasicTextUI.java     11 May 2005 10:24:25 -0000
@@ -57,6 +57,8 @@
 import javax.swing.SwingUtilities;
 import javax.swing.UIDefaults;
 import javax.swing.UIManager;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
 import javax.swing.plaf.TextUI;
 import javax.swing.plaf.UIResource;
 import javax.swing.text.BadLocationException;
@@ -150,6 +152,45 @@
       
       return ((PlainView) view).modelToView(position, a, bias).getBounds();
     }
+
+    /**
+     * Notification about text insertions. These are forwarded to the
+     * real root view.
+     *
+     * @param ev the DocumentEvent describing the change
+     * @param shape the current allocation of the view's display
+     * @param vf the ViewFactory to use for creating new Views
+     */
+    public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+    {
+      view.insertUpdate(ev, shape, vf);
+    }
+
+    /**
+     * Notification about text removals. These are forwarded to the
+     * real root view.
+     *
+     * @param ev the DocumentEvent describing the change
+     * @param shape the current allocation of the view's display
+     * @param vf the ViewFactory to use for creating new Views
+     */
+    public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+    {
+      view.removeUpdate(ev, shape, vf);
+    }
+
+    /**
+     * Notification about text changes. These are forwarded to the
+     * real root view.
+     *
+     * @param ev the DocumentEvent describing the change
+     * @param shape the current allocation of the view's display
+     * @param vf the ViewFactory to use for creating new Views
+     */
+    public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+    {
+      view.changedUpdate(ev, shape, vf);
+    }
   }
 
   class UpdateHandler implements PropertyChangeListener
@@ -163,13 +204,61 @@
        }
     }
   }
-  
+
+  /**
+   * Listens for changes on the underlying model and forwards notifications
+   * to the View.
+   *
+   * TODO: Maybe this should somehow be handled through EditorKits
+   */
+  class DocumentHandler implements DocumentListener
+  {
+    /**
+     * Notification about a document change event.
+     *
+     * @param ev the DocumentEvent describing the change
+     */
+    public void changedUpdate(DocumentEvent ev)
+    {
+      Dimension size = textComponent.getSize();
+      rootView.changedUpdate(ev, new Rectangle(0, 0, size.width, size.height),
+                             BasicTextUI.this);
+    }
+    
+    /**
+     * Notification about a document insert event.
+     *
+     * @param ev the DocumentEvent describing the insertion
+     */
+    public void insertUpdate(DocumentEvent ev)
+    {
+      Dimension size = textComponent.getSize();
+      rootView.insertUpdate(ev, new Rectangle(0, 0, size.width, size.height),
+                            BasicTextUI.this);
+    }
+
+    /**
+     * Notification about a document removal event.
+     *
+     * @param ev the DocumentEvent describing the removal
+     */
+    public void removeUpdate(DocumentEvent ev)
+    {
+      Dimension size = textComponent.getSize();
+      rootView.removeUpdate(ev, new Rectangle(0, 0, size.width, size.height),
+                            BasicTextUI.this);
+    }
+  }
+
   static EditorKit kit = new DefaultEditorKit();
 
   RootView rootView = new RootView();
   JTextComponent textComponent;
   UpdateHandler updateHandler = new UpdateHandler();
 
+  /** The DocumentEvent handler. */
+  DocumentHandler documentHandler = new DocumentHandler();
+
   public BasicTextUI()
   {
   }
@@ -249,6 +338,17 @@
   protected void installListeners()
   {
     textComponent.addFocusListener(focuslistener);
+    installDocumentListeners();
+  }
+
+  /**
+   * Installs the document listeners on the textComponent's model.
+   */
+  private void installDocumentListeners()
+  {
+    Document doc = textComponent.getDocument();
+    if (doc != null)
+      doc.addDocumentListener(documentHandler);
   }
 
   protected String getKeymapName()
@@ -488,6 +588,7 @@
     Document doc = textComponent.getDocument();
     if (doc == null)
       return;
+    installDocumentListeners();
     Element elem = doc.getDefaultRootElement();
     if (elem == null)
       return;
Index: javax/swing/text/FieldView.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/FieldView.java,v
retrieving revision 1.3
diff -u -r1.3 FieldView.java
--- javax/swing/text/FieldView.java     21 Nov 2004 12:56:30 -0000      1.3
+++ javax/swing/text/FieldView.java     11 May 2005 10:24:25 -0000
@@ -39,10 +39,15 @@
 package javax.swing.text;
 
 import java.awt.Component;
+import java.awt.ComponentOrientation;
 import java.awt.FontMetrics;
 import java.awt.Graphics;
+import java.awt.Rectangle;
 import java.awt.Shape;
 
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+
 public class FieldView extends PlainView
 {
   public FieldView(Element elem)
@@ -56,6 +61,55 @@
     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)
+  {
+    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 halign = textField.getHorizontalAlignment();
+    int width = (int) getPreferredSpan(X_AXIS);
+    int x;
+    ComponentOrientation orientation = textField.getComponentOrientation();
+    switch (halign)
+      {
+      case JTextField.CENTER:
+        x = rectIn.x + (rectIn.width - width) / 2;
+        break;
+      case JTextField.RIGHT:
+        x = rectIn.x + (rectIn.width - width);
+        break;
+      case JTextField.TRAILING:
+        if (orientation.isLeftToRight())
+          x = rectIn.x + (rectIn.width - width);
+        else
+          x = rectIn.x;
+        break;
+      case JTextField.LEADING:
+        if (orientation.isLeftToRight())
+          x = rectIn.x;
+        else
+          x = rectIn.x + (rectIn.width - width);
+        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)
@@ -91,11 +145,32 @@
   public Shape modelToView(int pos, Shape a, Position.Bias bias)
     throws BadLocationException
   {
-    return super.modelToView(pos, a, bias);
+    Shape newAlloc = adjustAllocation(a);
+    return super.modelToView(pos, newAlloc, bias);
   }
   
   public void paint(Graphics g, Shape s)
   {
-    super.paint(g, s);
+    Shape newAlloc = adjustAllocation(s);
+    super.paint(g, newAlloc);
+  }
+
+  public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+  {
+    Shape newAlloc = adjustAllocation(shape);
+    super.insertUpdate(ev, newAlloc, vf);
   }
+
+  public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+  {
+    Shape newAlloc = adjustAllocation(shape);
+    super.removeUpdate(ev, newAlloc, vf);
+  }
+
+  public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+  {
+    Shape newAlloc = adjustAllocation(shape);
+    super.removeUpdate(ev, newAlloc, vf);
+  }
+
 }
Index: javax/swing/text/JTextComponent.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/JTextComponent.java,v
retrieving revision 1.27
diff -u -r1.27 JTextComponent.java
--- javax/swing/text/JTextComponent.java        26 Apr 2005 18:56:20 -0000      
1.27
+++ javax/swing/text/JTextComponent.java        11 May 2005 10:24:25 -0000
@@ -895,30 +895,11 @@
   private Insets margin;
   private boolean dragEnabled;
 
-  /** Issues repaint request on document changes. */
-  private DocumentListener repaintListener;
-
   /**
    * Creates a new <code>JTextComponent</code> instance.
    */
   public JTextComponent()
   {
-    repaintListener = new DocumentListener()
-      {
-       public void changedUpdate(DocumentEvent ev)
-       {
-         repaint();
-       }
-       public void insertUpdate(DocumentEvent ev)
-       {
-         repaint();
-       }
-       public void removeUpdate(DocumentEvent ev)
-       {
-         repaint();
-       }
-      };
-
     Keymap defkeymap = getKeymap(DEFAULT_KEYMAP);
     boolean creatingKeymap = false;
     if (defkeymap == null)
@@ -952,13 +933,6 @@
   {
     Document oldDoc = doc;
     doc = newDoc;
-
-    // setup document listener
-    if (oldDoc != null)
-      oldDoc.removeDocumentListener(repaintListener);
-    if (newDoc != null)
-      newDoc.addDocumentListener(repaintListener);
-
     firePropertyChange("document", oldDoc, newDoc);
     revalidate();
     repaint();
Index: javax/swing/text/View.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/text/View.java,v
retrieving revision 1.10
diff -u -r1.10 View.java
--- javax/swing/text/View.java  25 Jan 2005 07:07:25 -0000      1.10
+++ javax/swing/text/View.java  11 May 2005 10:24:25 -0000
@@ -40,9 +40,12 @@
 
 import java.awt.Container;
 import java.awt.Graphics;
+import java.awt.Rectangle;
 import java.awt.Shape;
 
+import javax.swing.JComponent;
 import javax.swing.SwingConstants;
+import javax.swing.event.DocumentEvent;
 
 public abstract class View implements SwingConstants
 {
@@ -59,6 +62,11 @@
   private View parent;
 
   /**
+   * The child views.
+   */
+  View[] children;
+
+  /**
    * Creates a new <code>View</code> instance.
    *
    * @param elem an <code>Element</code> value
@@ -66,6 +74,7 @@
   public View(Element elem)
   {
     elt = elem;
+    children = new View[0];
   }
 
   public abstract void paint(Graphics g, Shape s);
@@ -241,6 +250,8 @@
   {
     if (parent != null)
       parent.preferenceChanged(this, width, height);
+    else
+      ((JComponent) getContainer()).revalidate();
   }
 
   public int getBreakWeight(int axis, float pos, float len)
@@ -260,5 +271,194 @@
   {
     return -1;
   }
+
+  /**
+   * Receive notification about an insert update to the text model.
+   *
+   * The default implementation of this method does the following:
+   * <ul>
+   * <li>Call address@hidden #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 address@hidden #forwardUpdate}. This forwards the DocumentEvent 
to
+   * the child views.<li>
+   * <li>Call address@hidden #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)
+  {
+    System.err.println("entering insertUpdate in class: " + 
getClass().getName());
+    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 address@hidden #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 address@hidden #forwardUpdate}. This forwards the DocumentEvent 
to
+   * the child views.<li>
+   * <li>Call address@hidden #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)
+        updateChildren(ec, ev, vf);
+    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 address@hidden #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 address@hidden #forwardUpdate}. This forwards the DocumentEvent 
to
+   * the child views.<li>
+   * <li>Call address@hidden #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 address@hidden #getView}
+   * and address@hidden #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();
+    View[] newChildren = new View[children.length + added.length
+                                  - removed.length];
+    int index = ec.getIndex();
+    System.arraycopy(children, 0, newChildren, 0, index);
+    System.arraycopy(children, index, added, 0, added.length);
+    int index2 = index + removed.length;
+    int len2 = children.length - index2;
+    System.arraycopy(children, index2, newChildren, index + added.length,
+                     len2);
+    children = newChildren;
+
+    return true;
+  }
+
+  /**
+   * Forwards the DocumentEvent to child views that need to get notified
+   * of the change to the model. This calles address@hidden 
#forwardUpdateToView}
+   * for each View that must be forwarded to.
+   *
+   * @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)
+  {
+    for (int i = 0; i < children.length; i++)
+      {
+        View child = children[i];
+        forwardUpdateToView(child, ev, shape, vf);
+      }
+  }
+
+  /**
+   * Forwards an update event to the given child view. This calls
+   * address@hidden #insertUpdate}, address@hidden #removeUpdate} or 
address@hidden #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)
+  {
+    Rectangle b = shape.getBounds();
+    if (ec != null)
+      preferenceChanged(this, true, true);
+  }
 }
 

reply via email to

[Prev in Thread] Current Thread [Next in Thread]