summaryrefslogtreecommitdiff
path: root/javax/swing
diff options
context:
space:
mode:
Diffstat (limited to 'javax/swing')
-rw-r--r--javax/swing/AbstractButton.java85
-rw-r--r--javax/swing/DefaultButtonModel.java2
-rw-r--r--javax/swing/JComponent.java700
-rw-r--r--javax/swing/JEditorPane.java32
-rw-r--r--javax/swing/JList.java218
-rw-r--r--javax/swing/JMenu.java182
-rw-r--r--javax/swing/JMenuItem.java16
-rw-r--r--javax/swing/JPopupMenu.java14
-rw-r--r--javax/swing/JTabbedPane.java26
-rw-r--r--javax/swing/JTable.java57
-rw-r--r--javax/swing/JTextPane.java19
-rw-r--r--javax/swing/JToolTip.java14
-rw-r--r--javax/swing/JTree.java45
-rw-r--r--javax/swing/JViewport.java4
-rw-r--r--javax/swing/RepaintManager.java205
-rw-r--r--javax/swing/ScrollPaneLayout.java38
-rw-r--r--javax/swing/SwingUtilities.java168
-rw-r--r--javax/swing/event/EventListenerList.java50
-rw-r--r--javax/swing/filechooser/FileSystemView.java12
-rw-r--r--javax/swing/plaf/basic/BasicButtonListener.java134
-rw-r--r--javax/swing/plaf/basic/BasicButtonUI.java215
-rw-r--r--javax/swing/plaf/basic/BasicFileChooserUI.java8
-rw-r--r--javax/swing/plaf/basic/BasicGraphicsUtils.java55
-rw-r--r--javax/swing/plaf/basic/BasicHTML.java26
-rw-r--r--javax/swing/plaf/basic/BasicLabelUI.java57
-rw-r--r--javax/swing/plaf/basic/BasicLookAndFeel.java17
-rw-r--r--javax/swing/plaf/basic/BasicMenuItemUI.java45
-rw-r--r--javax/swing/plaf/basic/BasicRadioButtonUI.java57
-rw-r--r--javax/swing/plaf/basic/BasicScrollBarUI.java12
-rw-r--r--javax/swing/plaf/basic/BasicScrollPaneUI.java33
-rw-r--r--javax/swing/plaf/basic/BasicSplitPaneDivider.java412
-rw-r--r--javax/swing/plaf/basic/BasicSplitPaneUI.java320
-rw-r--r--javax/swing/plaf/basic/BasicTabbedPaneUI.java310
-rw-r--r--javax/swing/plaf/basic/BasicTableHeaderUI.java8
-rw-r--r--javax/swing/plaf/basic/BasicTextUI.java473
-rw-r--r--javax/swing/plaf/basic/BasicToolTipUI.java123
-rw-r--r--javax/swing/plaf/basic/BasicTreeUI.java503
-rw-r--r--javax/swing/plaf/metal/MetalButtonUI.java72
-rw-r--r--javax/swing/plaf/metal/MetalCheckBoxIcon.java11
-rw-r--r--javax/swing/plaf/metal/MetalLookAndFeel.java19
-rw-r--r--javax/swing/plaf/metal/MetalSplitPaneDivider.java281
-rw-r--r--javax/swing/plaf/metal/MetalTabbedPaneUI.java2
-rw-r--r--javax/swing/plaf/metal/MetalToolTipUI.java70
-rw-r--r--javax/swing/plaf/metal/MetalTreeUI.java122
-rw-r--r--javax/swing/table/DefaultTableModel.java2
-rw-r--r--javax/swing/text/BoxView.java350
-rw-r--r--javax/swing/text/ComponentView.java319
-rw-r--r--javax/swing/text/CompositeView.java166
-rw-r--r--javax/swing/text/DefaultEditorKit.java365
-rw-r--r--javax/swing/text/DefaultFormatter.java2
-rw-r--r--javax/swing/text/DefaultStyledDocument.java66
-rw-r--r--javax/swing/text/FieldView.java4
-rw-r--r--javax/swing/text/FlowView.java10
-rw-r--r--javax/swing/text/GapContent.java4
-rw-r--r--javax/swing/text/GlyphView.java72
-rw-r--r--javax/swing/text/InternationalFormatter.java2
-rw-r--r--javax/swing/text/LabelView.java32
-rw-r--r--javax/swing/text/MaskFormatter.java377
-rw-r--r--javax/swing/text/ParagraphView.java59
-rw-r--r--javax/swing/text/PlainView.java12
-rw-r--r--javax/swing/text/Position.java4
-rw-r--r--javax/swing/text/SimpleAttributeSet.java14
-rw-r--r--javax/swing/text/StringContent.java304
-rw-r--r--javax/swing/text/StyleConstants.java11
-rw-r--r--javax/swing/text/StyleContext.java572
-rw-r--r--javax/swing/text/StyledEditorKit.java46
-rw-r--r--javax/swing/text/TextAction.java51
-rw-r--r--javax/swing/text/Utilities.java69
-rw-r--r--javax/swing/text/View.java78
-rw-r--r--javax/swing/text/WrappedPlainView.java175
-rw-r--r--javax/swing/text/ZoneView.java442
-rw-r--r--javax/swing/text/html/CSS.java35
-rw-r--r--javax/swing/text/html/HTMLDocument.java85
-rw-r--r--javax/swing/text/html/HTMLEditorKit.java33
-rw-r--r--javax/swing/text/html/InlineView.java52
-rw-r--r--javax/swing/text/html/MultiAttributeSet.java213
-rw-r--r--javax/swing/text/html/MultiStyle.java136
-rw-r--r--javax/swing/text/html/ParagraphView.java59
-rw-r--r--javax/swing/text/html/StyleSheet.java616
-rw-r--r--javax/swing/text/html/ViewAttributeSet.java163
-rw-r--r--javax/swing/tree/DefaultTreeCellEditor.java322
-rw-r--r--javax/swing/tree/DefaultTreeCellRenderer.java16
-rw-r--r--javax/swing/tree/VariableHeightLayoutCache.java4
83 files changed, 7013 insertions, 3601 deletions
diff --git a/javax/swing/AbstractButton.java b/javax/swing/AbstractButton.java
index 63f827a1a..ed8daca27 100644
--- a/javax/swing/AbstractButton.java
+++ b/javax/swing/AbstractButton.java
@@ -187,9 +187,32 @@ public abstract class AbstractButton extends JComponent
*/
public void stateChanged(ChangeEvent ev)
{
- AbstractButton.this.fireStateChanged();
+ getEventHandler().stateChanged(ev);
+ }
+ }
+
+ /**
+ * The combined event handler for ActionEvent, ChangeEvent and
+ * ItemEvent. This combines ButtonChangeListener, ActionListener
+ */
+ private class EventHandler
+ implements ActionListener, ChangeListener, ItemListener
+ {
+ public void actionPerformed(ActionEvent ev)
+ {
+ fireActionPerformed(ev);
+ }
+
+ public void stateChanged(ChangeEvent ev)
+ {
+ fireStateChanged();
repaint();
}
+
+ public void itemStateChanged(ItemEvent ev)
+ {
+ fireItemStateChanged(ev);
+ }
}
/** The icon displayed by default. */
@@ -264,16 +287,29 @@ public abstract class AbstractButton extends JComponent
*/
int mnemonicIndex;
- /** Listener the button uses to receive ActionEvents from its model. */
+ /**
+ * Listener the button uses to receive ActionEvents from its model.
+ */
protected ActionListener actionListener;
- /** Listener the button uses to receive ItemEvents from its model. */
+ /**
+ * Listener the button uses to receive ItemEvents from its model.
+ */
protected ItemListener itemListener;
- /** Listener the button uses to receive ChangeEvents from its model. */
+ /**
+ * Listener the button uses to receive ChangeEvents from its model.
+ */
protected ChangeListener changeListener;
/**
+ * The event handler for ActionEvent, ItemEvent and ChangeEvent.
+ * This replaces the above three handlers and combines them
+ * into one for efficiency.
+ */
+ private EventHandler eventHandler;
+
+ /**
* The time in milliseconds in which clicks get coalesced into a single
* <code>ActionEvent</code>.
*/
@@ -855,10 +891,6 @@ public abstract class AbstractButton extends JComponent
*/
public AbstractButton()
{
- actionListener = createActionListener();
- changeListener = createChangeListener();
- itemListener = createItemListener();
-
horizontalAlignment = CENTER;
horizontalTextPosition = TRAILING;
verticalAlignment = CENTER;
@@ -900,15 +932,21 @@ public abstract class AbstractButton extends JComponent
if (model != null)
{
model.removeActionListener(actionListener);
+ actionListener = null;
model.removeChangeListener(changeListener);
+ changeListener = null;
model.removeItemListener(itemListener);
+ itemListener = null;
}
ButtonModel old = model;
model = newModel;
if (model != null)
{
+ actionListener = createActionListener();
model.addActionListener(actionListener);
+ changeListener = createChangeListener();
model.addChangeListener(changeListener);
+ itemListener = createItemListener();
model.addItemListener(itemListener);
}
firePropertyChange(MODEL_CHANGED_PROPERTY, old, model);
@@ -1923,13 +1961,7 @@ public abstract class AbstractButton extends JComponent
*/
protected ActionListener createActionListener()
{
- return new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- AbstractButton.this.fireActionPerformed(e);
- }
- };
+ return getEventHandler();
}
/**
@@ -1995,7 +2027,7 @@ public abstract class AbstractButton extends JComponent
*/
protected ChangeListener createChangeListener()
{
- return new ButtonChangeListener();
+ return getEventHandler();
}
/**
@@ -2021,13 +2053,7 @@ public abstract class AbstractButton extends JComponent
*/
protected ItemListener createItemListener()
{
- return new ItemListener()
- {
- public void itemStateChanged(ItemEvent e)
- {
- AbstractButton.this.fireItemStateChanged(e);
- }
- };
+ return getEventHandler();
}
/**
@@ -2490,4 +2516,17 @@ public abstract class AbstractButton extends JComponent
super.setUIProperty(propertyName, value);
}
}
+
+ /**
+ * Returns the combined event handler. The instance is created if
+ * necessary.
+ *
+ * @return the combined event handler
+ */
+ EventHandler getEventHandler()
+ {
+ if (eventHandler == null)
+ eventHandler = new EventHandler();
+ return eventHandler;
+ }
}
diff --git a/javax/swing/DefaultButtonModel.java b/javax/swing/DefaultButtonModel.java
index 51a241760..c0eaea239 100644
--- a/javax/swing/DefaultButtonModel.java
+++ b/javax/swing/DefaultButtonModel.java
@@ -425,7 +425,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable
public void setRollover(boolean r)
{
// if this call does not represent a CHANGE in state, then return
- if ((r && isRollover()) || (!r && !isRollover()))
+ if (r == isRollover())
return;
// cannot set ROLLOVER property unless button is enabled
diff --git a/javax/swing/JComponent.java b/javax/swing/JComponent.java
index 25fc9b424..0b4631f6c 100644
--- a/javax/swing/JComponent.java
+++ b/javax/swing/JComponent.java
@@ -69,6 +69,7 @@ import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.beans.VetoableChangeSupport;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.EventListener;
import java.util.Hashtable;
import java.util.Locale;
@@ -503,27 +504,6 @@ public abstract class JComponent extends Container implements Serializable
}
}
- /**
- * An explicit value for the component's preferred size; if not set by a
- * user, this is calculated on the fly by delegating to the {@link
- * ComponentUI#getPreferredSize} method on the {@link #ui} property.
- */
- Dimension preferredSize;
-
- /**
- * An explicit value for the component's minimum size; if not set by a
- * user, this is calculated on the fly by delegating to the {@link
- * ComponentUI#getMinimumSize} method on the {@link #ui} property.
- */
- Dimension minimumSize;
-
- /**
- * An explicit value for the component's maximum size; if not set by a
- * user, this is calculated on the fly by delegating to the {@link
- * ComponentUI#getMaximumSize} method on the {@link #ui} property.
- */
- Dimension maximumSize;
-
/**
* A value between 0.0 and 1.0 indicating the preferred horizontal
* alignment of the component, relative to its siblings. The values
@@ -687,7 +667,7 @@ public abstract class JComponent extends Container implements Serializable
* Indicates whether we are calling paintDoubleBuffered() from
* paintImmadiately (RepaintManager) or from paint() (AWT refresh).
*/
- static private boolean isRepainting = false;
+ static boolean isRepainting = false;
/**
* Listeners for events other than {@link PropertyChangeEvent} are
@@ -783,6 +763,13 @@ public abstract class JComponent extends Container implements Serializable
*/
public static final int WHEN_IN_FOCUSED_WINDOW = 2;
+
+ /**
+ * Used to optimize painting. This is set in paintImmediately2() to specify
+ * the exact component path to be painted by paintChildren.
+ */
+ Component paintChild;
+
/**
* Indicates if the opaque property has been set by a client program or by
* the UI.
@@ -1270,37 +1257,38 @@ public abstract class JComponent extends Container implements Serializable
}
/**
- * Get the component's maximum size. If the {@link #maximumSize} property
- * has been explicitly set, it is returned. If the {@link #maximumSize}
+ * Get the component's maximum size. If the <code>maximumSize</code> property
+ * has been explicitly set, it is returned. If the <code>maximumSize</code>
* property has not been set but the {@link #ui} property has been, the
* result of {@link ComponentUI#getMaximumSize} is returned. If neither
* property has been set, the result of {@link Container#getMaximumSize}
* is returned.
*
- * @return The maximum size of the component
+ * @return the maximum size of the component
*
- * @see #maximumSize
- * @see #setMaximumSize
+ * @see Component#setMaximumSize
+ * @see Component#getMaximumSize()
+ * @see Component#isMaximumSizeSet()
+ * @see ComponentUI#getMaximumSize(JComponent)
*/
public Dimension getMaximumSize()
{
- if (maximumSize != null)
- return maximumSize;
-
- if (ui != null)
+ Dimension size = null;
+ if (isMaximumSizeSet())
+ size = super.getMaximumSize();
+ else
{
- Dimension s = ui.getMaximumSize(this);
- if (s != null)
- return s;
+ if (ui != null)
+ size = ui.getMaximumSize(this);
+ if (size == null)
+ size = super.getMaximumSize();
}
-
- Dimension p = super.getMaximumSize();
- return p;
+ return size;
}
/**
- * Get the component's minimum size. If the {@link #minimumSize} property
- * has been explicitly set, it is returned. If the {@link #minimumSize}
+ * Get the component's minimum size. If the <code>minimumSize</code> property
+ * has been explicitly set, it is returned. If the <code>minimumSize</code>
* property has not been set but the {@link #ui} property has been, the
* result of {@link ComponentUI#getMinimumSize} is returned. If neither
* property has been set, the result of {@link Container#getMinimumSize}
@@ -1308,97 +1296,57 @@ public abstract class JComponent extends Container implements Serializable
*
* @return The minimum size of the component
*
- * @see #minimumSize
- * @see #setMinimumSize
+ * @see Component#setMinimumSize
+ * @see Component#getMinimumSize()
+ * @see Component#isMinimumSizeSet()
+ * @see ComponentUI#getMinimumSize(JComponent)
*/
public Dimension getMinimumSize()
{
- if (minimumSize != null)
- return minimumSize;
-
- if (ui != null)
+ Dimension size = null;
+ if (isMinimumSizeSet())
+ size = super.getMinimumSize();
+ else
{
- Dimension s = ui.getMinimumSize(this);
- if (s != null)
- return s;
+ if (ui != null)
+ size = ui.getMinimumSize(this);
+ if (size == null)
+ size = super.getMinimumSize();
}
-
- Dimension p = super.getMinimumSize();
- return p;
+ return size;
}
/**
- * Get the component's preferred size. If the {@link #preferredSize}
- * property has been explicitly set, it is returned. If the {@link
- * #preferredSize} property has not been set but the {@link #ui} property
- * has been, the result of {@link ComponentUI#getPreferredSize} is
+ * Get the component's preferred size. If the <code>preferredSize</code>
+ * property has been explicitly set, it is returned. If the
+ * <code>preferredSize</code> property has not been set but the {@link #ui}
+ * property has been, the result of {@link ComponentUI#getPreferredSize} is
* returned. If neither property has been set, the result of {@link
* Container#getPreferredSize} is returned.
*
* @return The preferred size of the component
*
- * @see #preferredSize
- * @see #setPreferredSize
+ * @see Component#setPreferredSize
+ * @see Component#getPreferredSize()
+ * @see Component#isPreferredSizeSet()
+ * @see ComponentUI#getPreferredSize(JComponent)
*/
public Dimension getPreferredSize()
{
- Dimension prefSize = null;
- if (preferredSize != null)
- prefSize = new Dimension(preferredSize);
-
- else if (ui != null)
+ Dimension size = null;
+ if (isPreferredSizeSet())
+ size = super.getPreferredSize();
+ else
{
- Dimension s = ui.getPreferredSize(this);
- if (s != null)
- prefSize = s;
+ if (ui != null)
+ size = ui.getPreferredSize(this);
+ if (size == null)
+ size = super.getPreferredSize();
}
-
- if (prefSize == null)
- prefSize = super.getPreferredSize();
-
- return prefSize;
+ return size;
}
/**
- * Checks if a maximum size was explicitely set on the component.
- *
- * @return <code>true</code> if a maximum size was set,
- * <code>false</code> otherwise
- *
- * @since 1.3
- */
- public boolean isMaximumSizeSet()
- {
- return maximumSize != null;
- }
-
- /**
- * Checks if a minimum size was explicitely set on the component.
- *
- * @return <code>true</code> if a minimum size was set,
- * <code>false</code> otherwise
- *
- * @since 1.3
- */
- public boolean isMinimumSizeSet()
- {
- return minimumSize != null;
- }
-
- /**
- * Checks if a preferred size was explicitely set on the component.
- *
- * @return <code>true</code> if a preferred size was set,
- * <code>false</code> otherwise
- *
- * @since 1.3
- */
- public boolean isPreferredSizeSet()
- {
- return preferredSize != null;
- }
-
- /**
* Return the value of the <code>nextFocusableComponent</code> property.
*
* @return The current value of the property, or <code>null</code>
@@ -1850,7 +1798,7 @@ public abstract class JComponent extends Container implements Serializable
&& rm.isDoubleBufferingEnabled())
{
Rectangle clip = g.getClipBounds();
- paintDoubleBuffered(clip);
+ paintDoubleBuffered(clip.x, clip.y, clip.width, clip.height);
}
else
{
@@ -1865,8 +1813,22 @@ public abstract class JComponent extends Container implements Serializable
dragBuffer = null;
}
- if (g.getClip() == null)
- g.setClip(0, 0, getWidth(), getHeight());
+ Rectangle clip = g.getClipBounds();
+ int clipX, clipY, clipW, clipH;
+ if (clip == null)
+ {
+ clipX = 0;
+ clipY = 0;
+ clipW = getWidth();
+ clipH = getHeight();
+ }
+ else
+ {
+ clipX = clip.x;
+ clipY = clip.y;
+ clipW = clip.width;
+ clipH = clip.height;
+ }
if (dragBuffer != null && dragBufferInitialized)
{
g.drawImage(dragBuffer, 0, 0, this);
@@ -1874,14 +1836,51 @@ public abstract class JComponent extends Container implements Serializable
else
{
Graphics g2 = getComponentGraphics(g);
- paintComponent(g2);
- paintBorder(g2);
+ if (! isOccupiedByChild(clipX, clipY, clipW, clipH))
+ {
+ paintComponent(g2);
+ paintBorder(g2);
+ }
paintChildren(g2);
}
}
}
/**
+ * Determines if a region of this component is completely occupied by
+ * an opaque child component, in which case we don't need to bother
+ * painting this component at all.
+ *
+ * @param x the area, x coordinate
+ * @param y the area, y coordinate
+ * @param w the area, width
+ * @param h the area, height
+ *
+ * @return <code>true</code> if the specified area is completely covered
+ * by a child component, <code>false</code> otherwise
+ */
+ private boolean isOccupiedByChild(int x, int y, int w, int h)
+ {
+ boolean occupied = false;
+ int count = getComponentCount();
+ for (int i = 0; i < count; i++)
+ {
+ Component child = getComponent(i);
+ int cx = child.getX();
+ int cy = child.getY();
+ int cw = child.getWidth();
+ int ch = child.getHeight();
+ if (child.isVisible() && x >= cx && x + w <= cx + cw && y >= cy
+ && y + h <= cy + ch)
+ {
+ occupied = child.isOpaque();
+ break;
+ }
+ }
+ return occupied;
+ }
+
+ /**
* Initializes the drag buffer by creating a new image and painting this
* component into it.
*/
@@ -1940,7 +1939,14 @@ public abstract class JComponent extends Container implements Serializable
// Need to lock the tree to avoid problems with AWT and concurrency.
synchronized (getTreeLock())
{
- for (int i = getComponentCount() - 1; i >= 0; i--)
+ // Fast forward to the child to paint, if set by
+ // paintImmediately2()
+ int i = getComponentCount() - 1;
+ if (paintChild != null && paintChild.isOpaque())
+ {
+ for (; i >= 0 && getComponent(i) != paintChild; i--);
+ }
+ for (; i >= 0; i--)
{
Component child = getComponent(i);
if (child != null && child.isLightweight()
@@ -1958,7 +1964,8 @@ public abstract class JComponent extends Container implements Serializable
Rectangle clip = g.getClipBounds(); // A copy.
SwingUtilities.computeIntersection(cx, cy, cw, ch,
clip);
- if (isCompletelyObscured(i, clip))
+ if (isCompletelyObscured(i, clip.x, clip.y,
+ clip.width, clip.height))
continue; // Continues the for-loop.
}
Graphics cg = g.create(cx, cy, cw, ch);
@@ -1984,12 +1991,15 @@ public abstract class JComponent extends Container implements Serializable
* of its siblings.
*
* @param index the index of the child component
- * @param rect the region to check
+ * @param x the region to check, x coordinate
+ * @param y the region to check, y coordinate
+ * @param w the region to check, width
+ * @param h the region to check, height
*
* @return <code>true</code> if the region is completely obscured by a
* sibling, <code>false</code> otherwise
*/
- private boolean isCompletelyObscured(int index, Rectangle rect)
+ private boolean isCompletelyObscured(int index, int x, int y, int w, int h)
{
boolean obscured = false;
for (int i = index - 1; i >= 0 && obscured == false; i--)
@@ -1998,10 +2008,10 @@ public abstract class JComponent extends Container implements Serializable
if (sib.isVisible())
{
Rectangle sibRect = sib.getBounds(rectCache);
- if (sib.isOpaque() && rect.x >= sibRect.x
- && (rect.x + rect.width) <= (sibRect.x + sibRect.width)
- && rect.y >= sibRect.y
- && (rect.y + rect.height) <= (sibRect.y + sibRect.height))
+ if (sib.isOpaque() && x >= sibRect.x
+ && (x + w) <= (sibRect.x + sibRect.width)
+ && y >= sibRect.y
+ && (y + h) <= (sibRect.y + sibRect.height))
{
obscured = true;
}
@@ -2011,6 +2021,39 @@ public abstract class JComponent extends Container implements Serializable
}
/**
+ * Checks if a component/rectangle is partially obscured by one of its
+ * siblings.
+ * Note that this doesn't check for completely obscured, this is
+ * done by isCompletelyObscured() and should probably also be checked.
+ *
+ * @param i the component index from which to start searching
+ * @param x the x coordinate of the rectangle to check
+ * @param y the y coordinate of the rectangle to check
+ * @param w the width of the rectangle to check
+ * @param h the height of the rectangle to check
+ *
+ * @return <code>true</code> if the rectangle is partially obscured
+ */
+ private boolean isPartiallyObscured(int i, int x, int y, int w, int h)
+ {
+ boolean obscured = false;
+ for (int j = i - 1; j >= 0 && ! obscured; j--)
+ {
+ Component sibl = getComponent(j);
+ if (sibl.isVisible())
+ {
+ Rectangle rect = sibl.getBounds(rectCache);
+ if (!(x + w <= rect.x)
+ || (y + h <= rect.y)
+ || (x >= rect.x + rect.width)
+ || (y >= rect.y + rect.height))
+ obscured = true;
+ }
+ }
+ return obscured;
+ }
+
+ /**
* Paint the component's body. This usually means calling {@link
* ComponentUI#update} on the {@link #ui} property of the component, if
* it is non-<code>null</code>. You may override this if you wish to
@@ -2050,7 +2093,26 @@ public abstract class JComponent extends Container implements Serializable
*/
public void paintImmediately(int x, int y, int w, int h)
{
- paintImmediately(new Rectangle(x, y, w, h));
+ // Find opaque parent and call paintImmediately2() on it.
+ if (isShowing())
+ {
+ Component c = this;
+ Component p;
+ while (c != null && ! c.isOpaque())
+ {
+ p = c.getParent();
+ if (p != null)
+ {
+ x += c.getX();
+ y += c.getY();
+ c = p;
+ }
+ }
+ if (c instanceof JComponent)
+ ((JComponent) c).paintImmediately2(x, y, w, h);
+ else
+ c.repaint(x, y, w, h);
+ }
}
/**
@@ -2073,60 +2135,177 @@ public abstract class JComponent extends Container implements Serializable
*/
public void paintImmediately(Rectangle r)
{
- // Try to find a root pane for this component.
- //Component root = findPaintRoot(r);
- Component root = findPaintRoot(r);
- // If no paint root is found, then this component is completely overlapped
- // by another component and we don't need repainting.
- if (root == null|| !root.isShowing())
- return;
- SwingUtilities.convertRectangleToAncestor(this, r, root);
- if (root instanceof JComponent)
- ((JComponent) root).paintImmediately2(r);
- else
- root.repaint(r.x, r.y, r.width, r.height);
+ paintImmediately(r.x, r.y, r.width, r.height);
}
/**
* Performs the actual work of paintImmediatly on the repaint root.
*
- * @param r the area to be repainted
+ * @param x the area to be repainted, X coordinate
+ * @param y the area to be repainted, Y coordinate
*/
- void paintImmediately2(Rectangle r)
+ void paintImmediately2(int x, int y, int w, int h)
{
- isRepainting = true;
+ // Optimization for components that are always painted on top.
+ boolean onTop = onTop() && isOpaque();
+
+ // Fetch the RepaintManager.
RepaintManager rm = RepaintManager.currentManager(this);
- if (rm.isDoubleBufferingEnabled() && isPaintingDoubleBuffered())
- paintDoubleBuffered(r);
- else
- paintSimple(r);
- isRepainting = false;
+
+ // The painting clip;
+ int paintX = x;
+ int paintY = y;
+ int paintW = w;
+ int paintH = h;
+
+ // If we should paint buffered or not.
+ boolean haveBuffer = false;
+
+ // The component that is finally triggered for painting.
+ JComponent paintRoot = this;
+
+ // Stores the component and all its parents. This will be used to limit
+ // the actually painted components in paintChildren by setting
+ // the field paintChild.
+ int pIndex = -1;
+ int pCount = 0;
+ ArrayList components = new ArrayList();
+
+ // Offset to subtract from the paintRoot rectangle when painting.
+ int offsX = 0;
+ int offsY = 0;
+
+ // The current component and its child.
+ Component child;
+ Container c;
+
+ // Find appropriate paint root.
+ for (c = this, child = null;
+ c != null && ! (c instanceof Window) && ! (c instanceof Applet);
+ child = c, c = c.getParent())
+ {
+ JComponent jc = c instanceof JComponent ? (JComponent) c : null;
+ components.add(c);
+ if (! onTop && jc != null && ! jc.isOptimizedDrawingEnabled())
+ {
+ // Check obscured state of the child.
+ // Generally, we have 3 cases here:
+ // 1. Not obscured. No need to paint from the parent.
+ // 2. Partially obscured. Paint from the parent.
+ // 3. Completely obscured. No need to paint anything.
+ if (c != this)
+ {
+ int count = c.getComponentCount();
+ int i = 0;
+ for (; i < count && c.getComponent(i) != child; i++);
+
+ if (jc.isCompletelyObscured(i, paintX, paintY, paintW, paintH))
+ return; // No need to paint anything.
+ else if (jc.isPartiallyObscured(i, paintX, paintY, paintW,
+ paintH))
+ {
+ // Paint from parent.
+ paintRoot = jc;
+ pIndex = pCount;
+ offsX = 0;
+ offsY = 0;
+ haveBuffer = false;
+ }
+ }
+ }
+ pCount++;
+ // Check if component is double buffered.
+ if (rm.isDoubleBufferingEnabled() && jc != null
+ && jc.isDoubleBuffered())
+ {
+ haveBuffer = true;
+ }
+
+ // Clip the paint region with the parent.
+ if (! onTop)
+ {
+ paintX = Math.max(0, paintX);
+ paintY = Math.max(0, paintY);
+ paintW = Math.min(c.getWidth(), paintW + paintX) - paintX;
+ paintH = Math.min(c.getHeight(), paintH + paintY) - paintY;
+ int dx = c.getX();
+ int dy = c.getY();
+ paintX += dx;
+ paintY += dy;
+ offsX += dx;
+ offsY += dy;
+ }
+ }
+ if (c != null && c.getPeer() != null && paintW > 0 && paintH > 0)
+ {
+ isRepainting = true;
+ paintX -= offsX;
+ paintY -= offsY;
+
+ // Set the painting path so that paintChildren paints only what we
+ // want.
+ if (paintRoot != this)
+ {
+ for (int i = pIndex; i > 0; i--)
+ {
+ Component paintParent = (Component) components.get(i);
+ if (paintParent instanceof JComponent)
+ ((JComponent) paintParent).paintChild =
+ (Component) components.get(i - 1);
+ }
+ }
+
+ // Actually trigger painting.
+ if (haveBuffer)
+ paintRoot.paintDoubleBuffered(paintX, paintY, paintW,
+ paintH);
+ else
+ {
+ Graphics g = paintRoot.getGraphics();
+ try
+ {
+ g.setClip(paintX, paintY, paintW, paintH);
+ paintRoot.paint(g);
+ }
+ finally
+ {
+ g.dispose();
+ }
+ }
+
+ // Reset the painting path.
+ if (paintRoot != this)
+ {
+ for (int i = pIndex; i > 0; i--)
+ {
+ Component paintParent = (Component) components.get(i);
+ if (paintParent instanceof JComponent)
+ ((JComponent) paintParent).paintChild = null;
+ }
+ }
+
+ isRepainting = false;
+ }
}
/**
- * Returns true if we must paint double buffered, that is, when this
- * component or any of it's ancestors are double buffered.
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
*
- * @return true if we must paint double buffered, that is, when this
- * component or any of it's ancestors are double buffered
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
*/
- private boolean isPaintingDoubleBuffered()
+ boolean onTop()
{
- boolean doubleBuffered = isDoubleBuffered();
- Component parent = getParent();
- while (! doubleBuffered && parent != null)
- {
- doubleBuffered = parent instanceof JComponent
- && ((JComponent) parent).isDoubleBuffered();
- parent = parent.getParent();
- }
- return doubleBuffered;
+ return false;
}
/**
* Performs double buffered repainting.
*/
- private void paintDoubleBuffered(Rectangle r)
+ private void paintDoubleBuffered(int x, int y, int w, int h)
{
RepaintManager rm = RepaintManager.currentManager(this);
@@ -2143,7 +2322,7 @@ public abstract class JComponent extends Container implements Serializable
//Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root);
Graphics g2 = buffer.getGraphics();
clipAndTranslateGraphics(root, this, g2);
- g2.clipRect(r.x, r.y, r.width, r.height);
+ g2.clipRect(x, y, w, h);
g2 = getComponentGraphics(g2);
paintingDoubleBuffered = true;
try
@@ -2164,7 +2343,7 @@ public abstract class JComponent extends Container implements Serializable
}
// Paint the buffer contents on screen.
- rm.commitBuffer(this, r);
+ rm.commitBuffer(this, x, y, w, h);
}
/**
@@ -2577,7 +2756,7 @@ public abstract class JComponent extends Container implements Serializable
KeyEvent e,
int condition,
boolean pressed)
- {
+ {
if (isEnabled())
{
Action act = null;
@@ -2591,7 +2770,7 @@ public abstract class JComponent extends Container implements Serializable
if (cmd instanceof ActionListenerProxy)
act = (Action) cmd;
else
- act = (Action) getActionMap().get(cmd);
+ act = getActionMap().get(cmd);
}
}
if (act != null && act.isEnabled())
@@ -2741,9 +2920,25 @@ public abstract class JComponent extends Container implements Serializable
*/
public void scrollRectToVisible(Rectangle r)
{
- Component p = getParent();
- if (p instanceof JComponent)
- ((JComponent) p).scrollRectToVisible(r);
+ // Search nearest JComponent.
+ int xOffs = getX();
+ int yOffs = getY();
+ Component p;
+ for (p = getParent(); p != null && ! (p instanceof JComponent);
+ p = p.getParent())
+ {
+ xOffs += p.getX();
+ yOffs += p.getY();
+ }
+ if (p != null)
+ {
+ r.x += xOffs;
+ r.y += yOffs;
+ JComponent jParent = (JComponent) p;
+ jParent.scrollRectToVisible(r);
+ r.x -= xOffs;
+ r.y -= yOffs;
+ }
}
/**
@@ -2862,57 +3057,6 @@ public abstract class JComponent extends Container implements Serializable
}
/**
- * Set the value of the {@link #maximumSize} property. The passed value is
- * copied, the later direct changes on the argument have no effect on the
- * property value.
- *
- * @param max The new value of the property
- */
- public void setMaximumSize(Dimension max)
- {
- Dimension oldMaximumSize = maximumSize;
- if (max != null)
- maximumSize = new Dimension(max);
- else
- maximumSize = null;
- firePropertyChange("maximumSize", oldMaximumSize, maximumSize);
- }
-
- /**
- * Set the value of the {@link #minimumSize} property. The passed value is
- * copied, the later direct changes on the argument have no effect on the
- * property value.
- *
- * @param min The new value of the property
- */
- public void setMinimumSize(Dimension min)
- {
- Dimension oldMinimumSize = minimumSize;
- if (min != null)
- minimumSize = new Dimension(min);
- else
- minimumSize = null;
- firePropertyChange("minimumSize", oldMinimumSize, minimumSize);
- }
-
- /**
- * Set the value of the {@link #preferredSize} property. The passed value is
- * copied, the later direct changes on the argument have no effect on the
- * property value.
- *
- * @param pref The new value of the property
- */
- public void setPreferredSize(Dimension pref)
- {
- Dimension oldPreferredSize = preferredSize;
- if (pref != null)
- preferredSize = new Dimension(pref);
- else
- preferredSize = null;
- firePropertyChange("preferredSize", oldPreferredSize, preferredSize);
- }
-
- /**
* Set the specified component to be the next component in the
* focus cycle, overriding the {@link FocusTraversalPolicy} for
* this component.
@@ -3571,130 +3715,6 @@ public abstract class JComponent extends Container implements Serializable
jc.fireAncestorEvent(ancestor, id);
}
}
-
- /**
- * Finds a suitable paint root for painting this component. This method first
- * checks if this component is overlapped using
- * {@link #findOverlapFreeParent(Rectangle)}. The returned paint root is then
- * feeded to {@link #findOpaqueParent(Component)} to find the nearest opaque
- * component for this paint root. If no paint is necessary, then we return
- * <code>null</code>.
- *
- * @param c the clip of this component
- *
- * @return the paint root or <code>null</code> if no painting is necessary
- */
- private Component findPaintRoot(Rectangle c)
- {
- Component p = findOverlapFreeParent(c);
- if (p == null)
- return null;
- Component root = findOpaqueParent(p);
- return root;
- }
-
- /**
- * Scans the containment hierarchy upwards for components that overlap the
- * this component in the specified clip. This method returns
- * <code>this</code>, if no component overlaps this component. It returns
- * <code>null</code> if another component completely covers this component
- * in the specified clip (no repaint necessary). If another component partly
- * overlaps this component in the specified clip, then the parent of this
- * component is returned (this is the component that must be used as repaint
- * root). For efficient lookup, the method
- * {@link #isOptimizedDrawingEnabled()} is used.
- *
- * @param clip the clip of this component
- *
- * @return the paint root, or <code>null</code> if no paint is necessary
- */
- private Component findOverlapFreeParent(Rectangle clip)
- {
- Rectangle currentClip = clip;
- Component found = this;
- Container parent = this;
-
- while (parent != null && !(parent instanceof Window))
- {
- Container newParent = parent.getParent();
- if (newParent == null || newParent instanceof Window)
- break;
- // If the parent is optimizedDrawingEnabled, then its children are
- // tiled and cannot have an overlapping child. Go directly to next
- // parent.
- if ((newParent instanceof JComponent
- && ((JComponent) newParent).isOptimizedDrawingEnabled()))
-
- {
- parent = newParent;
- continue;
- }
-
- // If the parent is not optimizedDrawingEnabled, we must check if the
- // parent or some neighbor overlaps the current clip.
-
- // This is the current clip converted to the parent's coordinate
- // system. TODO: We can do this more efficiently by succesively
- // cumulating the parent-child translations.
- Rectangle target = SwingUtilities.convertRectangle(found,
- currentClip,
- newParent);
-
- // We have an overlap if either:
- // - The new parent itself doesn't completely cover the clip
- // (this can be the case with viewports).
- // - If some higher-level (than the current) children of the new parent
- // intersect the target rectangle.
- Rectangle parentRect = SwingUtilities.getLocalBounds(newParent);
- boolean haveOverlap =
- ! SwingUtilities.isRectangleContainingRectangle(parentRect, target);
- if (! haveOverlap)
- {
- Component child;
- for (int i = 0; (child = newParent.getComponent(i)) != parent && !haveOverlap; i++)
- {
- Rectangle childRect = child.getBounds();
- haveOverlap = target.intersects(childRect);
- }
- }
- if (haveOverlap)
- {
- found = newParent;
- currentClip = target;
- }
- parent = newParent;
- }
- //System.err.println("overlapfree parent: " + found);
- return found;
- }
-
- /**
- * Finds the nearest component to <code>c</code> (upwards in the containment
- * hierarchy), that is opaque. If <code>c</code> itself is opaque,
- * this returns <code>c</code> itself.
- *
- * @param c the start component for the search
- * @return the nearest component to <code>c</code> (upwards in the containment
- * hierarchy), that is opaque; If <code>c</code> itself is opaque,
- * this returns <code>c</code> itself
- */
- private Component findOpaqueParent(Component c)
- {
- Component found = c;
- while (true)
- {
- if ((found instanceof JComponent) && ((JComponent) found).isOpaque())
- break;
- else if (!(found instanceof JComponent) && !found.isLightweight())
- break;
- Container p = found.getParent();
- if (p == null)
- break;
- else
- found = p;
- }
- return found;
- }
/**
* This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map
diff --git a/javax/swing/JEditorPane.java b/javax/swing/JEditorPane.java
index 4ae3c5a1c..a5efa07df 100644
--- a/javax/swing/JEditorPane.java
+++ b/javax/swing/JEditorPane.java
@@ -56,6 +56,7 @@ import javax.accessibility.AccessibleStateSet;
import javax.accessibility.AccessibleText;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
+import javax.swing.plaf.TextUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.Document;
@@ -695,10 +696,28 @@ public class JEditorPane extends JTextComponent
public Dimension getPreferredSize()
{
Dimension pref = super.getPreferredSize();
- if (getScrollableTracksViewportWidth())
- pref.width = getUI().getMinimumSize(this).width;
- if (getScrollableTracksViewportHeight())
- pref.height = getUI().getMinimumSize(this).height;
+ Container parent = getParent();
+ if (parent instanceof JViewport)
+ {
+ JViewport vp = (JViewport) getParent();
+ TextUI ui = getUI();
+ Dimension min = null;
+ if (! getScrollableTracksViewportWidth())
+ {
+ min = ui.getMinimumSize(this);
+ int vpWidth = vp.getWidth();
+ if (vpWidth != 0 && vpWidth < min.width)
+ pref.width = min.width;
+ }
+ if (! getScrollableTracksViewportHeight())
+ {
+ if (min == null)
+ min = ui.getMinimumSize(this);
+ int vpHeight = vp.getHeight();
+ if (vpHeight != 0 && vpHeight < min.height)
+ pref.height = min.height;
+ }
+ }
return pref;
}
@@ -716,8 +735,11 @@ public class JEditorPane extends JTextComponent
// Tests show that this returns true when the parent is a JViewport
// and has a height > minimum UI height.
Container parent = getParent();
+ int height = parent.getHeight();
+ TextUI ui = getUI();
return parent instanceof JViewport
- && parent.getHeight() > getUI().getMinimumSize(this).height;
+ && height >= ui.getMinimumSize(this).height
+ && height <= ui.getMaximumSize(this).height;
}
/**
diff --git a/javax/swing/JList.java b/javax/swing/JList.java
index 1cff9553a..ff1b23921 100644
--- a/javax/swing/JList.java
+++ b/javax/swing/JList.java
@@ -1946,72 +1946,74 @@ public class JList extends JComponent implements Accessible, Scrollable
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction)
{
- ListUI lui = this.getUI();
+ int unit = -1;
if (orientation == SwingConstants.VERTICAL)
{
- if (direction > 0)
+ int row = getFirstVisibleIndex();
+ if (row == -1)
+ unit = 0;
+ else if (direction > 0)
{
- // Scrolling down
- Point bottomLeft = new Point(visibleRect.x,
- visibleRect.y + visibleRect.height);
- int curIdx = lui.locationToIndex(this, bottomLeft);
- Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx);
- if (curBounds.y + curBounds.height == bottomLeft.y)
+ // Scrolling down.
+ Rectangle bounds = getCellBounds(row, row);
+ if (bounds != null)
+ unit = bounds.height - (visibleRect.y - bounds.y);
+ else
+ unit = 0;
+ }
+ else
+ {
+ // Scrolling up.
+ Rectangle bounds = getCellBounds(row, row);
+ // First row.
+ if (row == 0 && bounds.y == visibleRect.y)
+ unit = 0; // No need to scroll.
+ else if (bounds.y == visibleRect.y)
{
- // we are at the exact bottom of the current cell, so we
- // are being asked to scroll to the end of the next one
- if (curIdx + 1 < model.getSize())
- {
- // there *is* a next item in the list
- Rectangle nxtBounds = lui.getCellBounds(this, curIdx + 1, curIdx + 1);
- return nxtBounds.height;
- }
+ // Scroll to previous row.
+ Point loc = bounds.getLocation();
+ loc.y--;
+ int prev = locationToIndex(loc);
+ Rectangle prevR = getCellBounds(prev, prev);
+ if (prevR == null || prevR.y >= bounds.y)
+ unit = 0; // For multicolumn lists.
else
- {
- // no next item, no advance possible
- return 0;
- }
+ unit = prevR.height;
}
else
- {
- // we are part way through an existing cell, so we are being
- // asked to scroll to the bottom of it
- return (curBounds.y + curBounds.height) - bottomLeft.y;
- }
+ unit = visibleRect.y - bounds.y;
}
- else
+ }
+ else if (orientation == SwingConstants.HORIZONTAL && getLayoutOrientation() != VERTICAL)
+ {
+ // Horizontal scrolling.
+ int i = locationToIndex(visibleRect.getLocation());
+ if (i != -1)
{
- // scrolling up
- Point topLeft = new Point(visibleRect.x, visibleRect.y);
- int curIdx = lui.locationToIndex(this, topLeft);
- Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx);
- if (curBounds.y == topLeft.y)
+ Rectangle b = getCellBounds(i, i);
+ if (b != null)
{
- // we are at the exact top of the current cell, so we
- // are being asked to scroll to the top of the previous one
- if (curIdx > 0)
+ if (b.x != visibleRect.x)
{
- // there *is* a previous item in the list
- Rectangle nxtBounds = lui.getCellBounds(this, curIdx - 1, curIdx - 1);
- return -nxtBounds.height;
+ if (direction < 0)
+ unit = Math.abs(b.x - visibleRect.x);
+ else
+ unit = b.width + b.x - visibleRect.x;
}
else
- {
- // no previous item, no advance possible
- return 0;
- }
+ unit = b.width;
}
- else
- {
- // we are part way through an existing cell, so we are being
- // asked to scroll to the top of it
- return curBounds.y - topLeft.y;
- }
}
}
- // FIXME: handle horizontal scrolling (also wrapping?)
- return 1;
+ if (unit == -1)
+ {
+ // This fallback seems to be used by the RI for the degenerate cases
+ // not covered above.
+ Font f = getFont();
+ unit = f != null ? f.getSize() : 1;
+ }
+ return unit;
}
/**
@@ -2040,10 +2042,120 @@ public class JList extends JComponent implements Accessible, Scrollable
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction)
{
- if (orientation == VERTICAL)
- return visibleRect.height * direction;
- else
- return visibleRect.width * direction;
+ int block = -1;
+ if (orientation == SwingConstants.VERTICAL)
+ {
+ // Default block scroll. Special cases are handled below for
+ // better usability.
+ block = visibleRect.height;
+ if (direction > 0)
+ {
+ // Scroll down.
+ // Scroll so that after scrolling the last line aligns with
+ // the lower boundary of the visible area.
+ Point p = new Point(visibleRect.x,
+ visibleRect.y + visibleRect.height - 1);
+ int last = locationToIndex(p);
+ if (last != -1)
+ {
+ Rectangle lastR = getCellBounds(last, last);
+ if (lastR != null)
+ {
+ block = lastR.y - visibleRect.y;
+ if (block == 0&& last < getModel().getSize() - 1)
+ block = lastR.height;
+ }
+ }
+ }
+ else
+ {
+ // Scroll up.
+ // Scroll so that after scrolling the first line aligns with
+ // the upper boundary of the visible area.
+ Point p = new Point(visibleRect.x,
+ visibleRect.y - visibleRect.height);
+ int newFirst = locationToIndex(p);
+ if (newFirst != -1)
+ {
+ int first = getFirstVisibleIndex();
+ if (first == -1)
+ first = locationToIndex(visibleRect.getLocation());
+ Rectangle newFirstR = getCellBounds(newFirst, newFirst);
+ Rectangle firstR = getCellBounds(first, first);
+ if (newFirstR != null && firstR != null)
+ {
+ // Search first item that would left the current first
+ // item visible when scrolled to.
+ while (newFirstR.y + visibleRect.height
+ < firstR.y + firstR.height
+ && newFirstR.y < firstR.y)
+ {
+ newFirst++;
+ newFirstR = getCellBounds(newFirst, newFirst);
+ }
+ block = visibleRect.y - newFirstR.y;
+ if (block <= 0 && newFirstR.y > 0)
+ {
+ newFirst--;
+ newFirstR = getCellBounds(newFirst, newFirst);
+ if (newFirstR != null)
+ block = visibleRect.y - newFirstR.y;
+ }
+ }
+ }
+ }
+ }
+ else if (orientation == SwingConstants.HORIZONTAL
+ && getLayoutOrientation() != VERTICAL)
+ {
+ // Default block increment. Special cases are handled below for
+ // better usability.
+ block = visibleRect.width;
+ if (direction > 0)
+ {
+ // Scroll right.
+ Point p = new Point(visibleRect.x + visibleRect.width + 1,
+ visibleRect.y);
+ int last = locationToIndex(p);
+ if (last != -1)
+ {
+ Rectangle lastR = getCellBounds(last, last);
+ if (lastR != null)
+ {
+ block = lastR.x - visibleRect.x;
+ if (block < 0)
+ block += lastR.width;
+ else if (block == 0 && last < getModel().getSize() - 1)
+ block = lastR.width;
+ }
+ }
+ }
+ else
+ {
+ // Scroll left.
+ Point p = new Point(visibleRect.x - visibleRect.width,
+ visibleRect.y);
+ int first = locationToIndex(p);
+ if (first != -1)
+ {
+ Rectangle firstR = getCellBounds(first, first);
+ if (firstR != null)
+ {
+ if (firstR.x < visibleRect.x - visibleRect.width)
+ {
+ if (firstR.x + firstR.width > visibleRect.x)
+ block = visibleRect.x - firstR.x;
+ else
+ block = visibleRect.x - firstR.x - firstR.width;
+ }
+ else
+ block = visibleRect.x - firstR.x;
+ }
+ }
+ }
+ }
+
+ return block;
}
/**
diff --git a/javax/swing/JMenu.java b/javax/swing/JMenu.java
index 68f31e335..7e627f118 100644
--- a/javax/swing/JMenu.java
+++ b/javax/swing/JMenu.java
@@ -39,7 +39,14 @@ exception statement from your version. */
package javax.swing;
import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Insets;
import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
@@ -53,6 +60,8 @@ import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;
import javax.accessibility.AccessibleSelection;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.plaf.MenuItemUI;
@@ -71,6 +80,36 @@ import javax.swing.plaf.MenuItemUI;
*/
public class JMenu extends JMenuItem implements Accessible, MenuElement
{
+ /**
+ * Receives notifications when the JMenu's ButtonModel is changed and
+ * fires menuSelected or menuDeselected events when appropriate.
+ */
+ private class MenuChangeListener
+ implements ChangeListener
+ {
+ /**
+ * Indicates the last selected state.
+ */
+ private boolean selected;
+
+ /**
+ * Receives notification when the JMenu's ButtonModel changes.
+ */
+ public void stateChanged(ChangeEvent ev)
+ {
+ ButtonModel m = (ButtonModel) ev.getSource();
+ boolean s = m.isSelected();
+ if (s != selected)
+ {
+ if (s)
+ fireMenuSelected();
+ else
+ fireMenuDeselected();
+ selected = s;
+ }
+ }
+ }
+
private static final long serialVersionUID = 4227225638931828014L;
/** A Popup menu associated with this menu, which pops up when menu is selected */
@@ -87,11 +126,20 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
/* PopupListener */
protected WinListener popupListener;
- /** Location at which popup menu associated with this menu will be
- displayed */
+ /**
+ * Location at which popup menu associated with this menu will be
+ * displayed
+ */
private Point menuLocation;
/**
+ * The ChangeListener for the ButtonModel.
+ *
+ * @see MenuChangeListener
+ */
+ private ChangeListener menuChangeListener;
+
+ /**
* Creates a new JMenu object.
*/
public JMenu()
@@ -188,7 +236,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
*/
public JMenuItem add(String text)
{
- return getPopupMenu().add(text);
+ return add(new JMenuItem(text));
}
/**
@@ -200,7 +248,10 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
*/
public JMenuItem add(Action action)
{
- return getPopupMenu().add(action);
+ JMenuItem i = createActionComponent(action);
+ i.setAction(action);
+ add(i);
+ return i;
}
/**
@@ -323,7 +374,18 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
*/
public void setModel(ButtonModel model)
{
+ ButtonModel oldModel = getModel();
+ if (oldModel != null && menuChangeListener != null)
+ oldModel.removeChangeListener(menuChangeListener);
+
super.setModel(model);
+
+ if (model != null)
+ {
+ if (menuChangeListener == null)
+ menuChangeListener = new MenuChangeListener();
+ model.addChangeListener(menuChangeListener);
+ }
}
/**
@@ -372,7 +434,8 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
if (popup && isShowing())
{
// Set location as determined by getPopupLocation().
- Point loc = getPopupMenuOrigin();
+ Point loc = menuLocation == null ? getPopupMenuOrigin()
+ : menuLocation;
getPopupMenu().show(this, loc.x, loc.y);
}
else
@@ -381,26 +444,109 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
}
/**
- * Returns origin point of the popup menu
+ * Returns origin point of the popup menu. This takes the screen bounds
+ * into account and places the popup where it fits best.
*
- * @return Point containing
+ * @return the origin of the popup menu
*/
protected Point getPopupMenuOrigin()
{
- Point point;
-
- // if menu in the menu bar
+ // The menu's screen location and size.
+ Point screenLoc = getLocationOnScreen();
+ Dimension size = getSize();
+
+ // Determine the popup's size.
+ JPopupMenu popup = getPopupMenu();
+ Dimension popupSize = popup.getSize();
+ if (popupSize.width == 0 || popupSize.height == 0)
+ popupSize = popup.getPreferredSize();
+
+ // Determine screen bounds.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ Rectangle screenBounds = new Rectangle(tk.getScreenSize());
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice gd = ge.getDefaultScreenDevice();
+ GraphicsConfiguration gc = gd.getDefaultConfiguration();
+ Insets screenInsets = tk.getScreenInsets(gc);
+ screenBounds.x -= screenInsets.left;
+ screenBounds.width -= screenInsets.left + screenInsets.right;
+ screenBounds.y -= screenInsets.top;
+ screenBounds.height -= screenInsets.top + screenInsets.bottom;
+ screenLoc.x -= screenInsets.left;
+ screenLoc.y -= screenInsets.top;
+
+ Point point = new Point();
if (isTopLevelMenu())
- point = new Point(0, this.getHeight());
-
- // if submenu
+ {
+ // If menu in the menu bar.
+ int xOffset = UIManager.getInt("Menu.menuPopupOffsetX");
+ int yOffset = UIManager.getInt("Menu.menuPopupOffsetY");
+ // Determine X location.
+ if (getComponentOrientation().isLeftToRight())
+ {
+ // Prefer popup to the right.
+ point.x = xOffset;
+ // Check if it fits, otherwise place popup wherever it fits.
+ if (screenLoc.x + point.x + popupSize.width
+ > screenBounds.width + screenBounds.width
+ && screenBounds.width - size.width
+ < 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the right if there's not enough room.
+ point.x = size.width - xOffset - popupSize.width;
+ }
+ else
+ {
+ // Prefer popup to the left.
+ point.x = size.width - xOffset - popupSize.width;
+ if (screenLoc.x + point.x < screenBounds.x
+ && screenBounds.width - size.width
+ > 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the left if there's not enough room.
+ point.x = xOffset;
+ }
+ // Determine Y location. Prefer popping down.
+ point.y = size.height + yOffset;
+ if (screenLoc.y + point.y + popupSize.height >= screenBounds.height
+ && screenBounds.height - size.height
+ < 2 * (screenLoc.y - screenBounds.y))
+ // Position above if there's not enough room below.
+ point.y = - yOffset - popupSize.height;
+ }
else
{
+ // If submenu.
int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX");
int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY");
- int x = getWidth() + xOffset;
- int y = yOffset;
- point = new Point(x, y);
+ // Determine X location.
+ if (getComponentOrientation().isLeftToRight())
+ {
+ // Prefer popup to the right.
+ point.x = size.width + xOffset;
+ if (screenLoc.x + point.x + popupSize.width
+ >= screenBounds.x + screenBounds.width
+ && screenBounds.width - size.width
+ < 2 * (screenLoc.x - screenBounds.x))
+ // Position to the left if there's not enough room on the right.
+ point.x = - xOffset - popupSize.width;
+ }
+ else
+ {
+ // Prefer popup on the left side.
+ point.x = - xOffset - popupSize.width;
+ if (screenLoc.x + point.x < screenBounds.x
+ && screenBounds.width - size.width
+ > 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the right if there's not enough room.
+ point.x = size.width + xOffset;
+ }
+ // Determine Y location. Prefer popping down.
+ point.y = yOffset;
+ if (screenLoc.y + point.y + popupSize.height
+ >= screenBounds.y + screenBounds.height
+ && screenBounds.height - size.height
+ < 2 * (screenLoc.y - screenBounds.y))
+ // Pop up if there's not enough room below.
+ point.y = size.height - yOffset - popupSize.height;
}
return point;
}
@@ -442,6 +588,8 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
public void setMenuLocation(int x, int y)
{
menuLocation = new Point(x, y);
+ if (popupMenu != null)
+ popupMenu.setLocation(x, y);
}
/**
@@ -559,7 +707,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement
if (getPopupMenu() == null || getMenuComponentCount() == 0)
return null;
- return (Component) popupMenu.getComponentAtIndex(index);
+ return popupMenu.getComponentAtIndex(index);
}
/**
diff --git a/javax/swing/JMenuItem.java b/javax/swing/JMenuItem.java
index f7f93bf00..324d61cd4 100644
--- a/javax/swing/JMenuItem.java
+++ b/javax/swing/JMenuItem.java
@@ -833,4 +833,20 @@ public class JMenuItem extends AbstractButton implements Accessible,
return AccessibleRole.MENU_ITEM;
}
}
+
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return SwingUtilities.getAncestorOfClass(JInternalFrame.class, this)
+ == null;
+ }
+
}
diff --git a/javax/swing/JPopupMenu.java b/javax/swing/JPopupMenu.java
index 2e59d4767..1ae8adad0 100644
--- a/javax/swing/JPopupMenu.java
+++ b/javax/swing/JPopupMenu.java
@@ -902,6 +902,20 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement
}
}
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return true;
+ }
+
protected class AccessibleJPopupMenu extends AccessibleJComponent
{
private static final long serialVersionUID = 7423261328879849768L;
diff --git a/javax/swing/JTabbedPane.java b/javax/swing/JTabbedPane.java
index 5c8d04748..a500b9264 100644
--- a/javax/swing/JTabbedPane.java
+++ b/javax/swing/JTabbedPane.java
@@ -760,11 +760,7 @@ public class JTabbedPane extends JComponent implements Serializable,
this.tabPlacement = tabPlacement;
layoutPolicy = tabLayoutPolicy;
- changeEvent = new ChangeEvent(this);
- changeListener = createChangeListener();
-
- model = new DefaultSingleSelectionModel();
- model.addChangeListener(changeListener);
+ setModel(new DefaultSingleSelectionModel());
updateUI();
}
@@ -877,16 +873,24 @@ public class JTabbedPane extends JComponent implements Serializable,
/**
* This method changes the model property of the JTabbedPane.
*
- * @param model The new model to use with the JTabbedPane.
+ * @param m The new model to use with the JTabbedPane.
*/
- public void setModel(SingleSelectionModel model)
+ public void setModel(SingleSelectionModel m)
{
- if (model != this.model)
+ if (m != model)
{
SingleSelectionModel oldModel = this.model;
- this.model.removeChangeListener(changeListener);
- this.model = model;
- this.model.addChangeListener(changeListener);
+ if (oldModel != null && changeListener != null)
+ oldModel.removeChangeListener(changeListener);
+
+ model = m;
+
+ if (model != null)
+ {
+ if (changeListener != null)
+ changeListener = createChangeListener();
+ model.addChangeListener(changeListener);
+ }
firePropertyChange("model", oldModel, this.model);
}
}
diff --git a/javax/swing/JTable.java b/javax/swing/JTable.java
index efa1a80bc..28cc6728d 100644
--- a/javax/swing/JTable.java
+++ b/javax/swing/JTable.java
@@ -3303,10 +3303,21 @@ public class JTable
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
{
- if (orientation == SwingConstants.VERTICAL)
- return visibleRect.height * direction;
+ int block;
+ if (orientation == SwingConstants.HORIZONTAL)
+ {
+ block = visibleRect.width;
+ }
else
- return visibleRect.width * direction;
+ {
+ int rowHeight = getRowHeight();
+ if (rowHeight > 0)
+ block = Math.max(rowHeight, // Little hack for useful rounding.
+ (visibleRect.height / rowHeight) * rowHeight);
+ else
+ block = visibleRect.height;
+ }
+ return block;
}
/**
@@ -3345,24 +3356,40 @@ public class JTable
* The values greater than one means that more mouse wheel or similar
* events were generated, and hence it is better to scroll the longer
* distance.
- * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ *
+ * @author Roman Kennke (kennke@aicas.com)
*/
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
int direction)
{
- int h = (rowHeight + rowMargin);
- int delta = h * direction;
-
- // Round so that the top would start from the row boundary
- if (orientation == SwingConstants.VERTICAL)
+ int unit;
+ if (orientation == SwingConstants.HORIZONTAL)
+ unit = 100;
+ else
{
- // Completely expose the top row
- int near = ((visibleRect.y + delta + h / 2) / h) * h;
- int diff = visibleRect.y + delta - near;
- delta -= diff;
+ unit = getRowHeight();
+ // The following adjustment doesn't work for variable height rows.
+ // It fully exposes partially visible rows in the scrolling direction.
+ if (rowHeights == null)
+ {
+ if (direction > 0)
+ {
+ // Scroll down.
+ // How much pixles are exposed from the last item?
+ int exposed = (visibleRect.y + visibleRect.height) % unit;
+ if (exposed > 0 && exposed < unit - 1)
+ unit = unit - exposed - 1;
+ }
+ else
+ {
+ // Scroll up.
+ int exposed = visibleRect.y % unit;
+ if (exposed > 0 && exposed < unit)
+ unit = exposed;
+ }
+ }
}
- return delta;
- // TODO when scrollng horizontally, scroll into the column boundary.
+ return unit;
}
/**
diff --git a/javax/swing/JTextPane.java b/javax/swing/JTextPane.java
index c0a5f80cf..05968fc8c 100644
--- a/javax/swing/JTextPane.java
+++ b/javax/swing/JTextPane.java
@@ -214,20 +214,11 @@ public class JTextPane
*/
public void insertIcon(Icon icon)
{
- SimpleAttributeSet atts = new SimpleAttributeSet();
- atts.addAttribute(StyleConstants.IconAttribute, icon);
- atts.addAttribute(StyleConstants.NameAttribute,
- StyleConstants.IconElementName);
- try
- {
- getDocument().insertString(getCaret().getDot(), " ", atts);
- }
- catch (BadLocationException ex)
- {
- AssertionError err = new AssertionError("Unexpected bad location");
- err.initCause(ex);
- throw err;
- }
+ MutableAttributeSet inputAtts = getInputAttributes();
+ inputAtts.removeAttributes(inputAtts);
+ StyleConstants.setIcon(inputAtts, icon);
+ replaceSelection(" ");
+ inputAtts.removeAttributes(inputAtts);
}
/**
diff --git a/javax/swing/JToolTip.java b/javax/swing/JToolTip.java
index 836c122c6..3153894da 100644
--- a/javax/swing/JToolTip.java
+++ b/javax/swing/JToolTip.java
@@ -225,4 +225,18 @@ public class JToolTip extends JComponent implements Accessible
{
setUI((ToolTipUI) UIManager.getUI(this));
}
+
+ /**
+ * Returns <code>true</code> if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return <code>true</code> if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return true;
+ }
}
diff --git a/javax/swing/JTree.java b/javax/swing/JTree.java
index 32acca643..c76a9b783 100644
--- a/javax/swing/JTree.java
+++ b/javax/swing/JTree.java
@@ -1684,29 +1684,52 @@ public class JTree extends JComponent implements Scrollable, Accessible
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
int direction)
{
- int delta;
+ int delta = 0;
// Round so that the top would start from the row boundary
if (orientation == SwingConstants.VERTICAL)
{
- // One pixel down, otherwise picks another row too high.
- int row = getClosestRowForLocation(visibleRect.x, visibleRect.y + 1);
- row = row + direction;
- if (row < 0)
- row = 0;
-
- Rectangle newTop = getRowBounds(row);
- delta = newTop.y - visibleRect.y;
+ int row = getClosestRowForLocation(0, visibleRect.y);
+ if (row != -1)
+ {
+ Rectangle b = getRowBounds(row);
+ if (b.y != visibleRect.y)
+ {
+ if (direction < 0)
+ delta = Math.max(0, visibleRect.y - b.y);
+ else
+ delta = b.y + b.height - visibleRect.height;
+ }
+ else
+ {
+ if (direction < 0)
+ {
+ if (row != 0)
+ {
+ b = getRowBounds(row - 1);
+ delta = b.height;
+ }
+ }
+ else
+ delta = b.height;
+ }
+ }
}
else
- delta = direction * rowHeight == 0 ? 20 : rowHeight;
+ // The RI always returns 4 for HORIZONTAL scrolling.
+ delta = 4;
return delta;
}
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction)
{
- return getScrollableUnitIncrement(visibleRect, orientation, direction);
+ int block;
+ if (orientation == SwingConstants.VERTICAL)
+ block = visibleRect.height;
+ else
+ block = visibleRect.width;
+ return block;
}
public boolean getScrollableTracksViewportHeight()
diff --git a/javax/swing/JViewport.java b/javax/swing/JViewport.java
index 7cf393996..babffb7ce 100644
--- a/javax/swing/JViewport.java
+++ b/javax/swing/JViewport.java
@@ -942,10 +942,10 @@ public class JViewport extends JComponent implements Accessible
*
* @param r the rectangle to paint
*/
- void paintImmediately2(Rectangle r)
+ void paintImmediately2(int x, int y, int w, int h)
{
isPaintRoot = true;
- super.paintImmediately2(r);
+ super.paintImmediately2(x, y, w, h);
isPaintRoot = false;
}
}
diff --git a/javax/swing/RepaintManager.java b/javax/swing/RepaintManager.java
index 29bf98fbe..2d29e9076 100644
--- a/javax/swing/RepaintManager.java
+++ b/javax/swing/RepaintManager.java
@@ -38,6 +38,7 @@ exception statement from your version. */
package javax.swing;
+import java.applet.Applet;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
@@ -49,7 +50,6 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
@@ -197,19 +197,6 @@ public class RepaintManager
private WeakHashMap offscreenBuffers;
/**
- * Indicates if the RepaintManager is currently repainting an area.
- */
- private boolean repaintUnderway;
-
- /**
- * This holds buffer commit requests when the RepaintManager is working.
- * This maps Component objects (the top level components) to Rectangle
- * objects (the area of the corresponding buffer that must be blitted on
- * the component).
- */
- private HashMap commitRequests;
-
- /**
* The maximum width and height to allocate as a double buffer. Requests
* beyond this size are ignored.
*
@@ -232,8 +219,6 @@ public class RepaintManager
doubleBufferMaximumSize = new Dimension(2000,2000);
doubleBufferingEnabled = true;
offscreenBuffers = new WeakHashMap();
- repaintUnderway = false;
- commitRequests = new HashMap();
}
/**
@@ -398,8 +383,6 @@ public class RepaintManager
if (w <= 0 || h <= 0 || !component.isShowing())
return;
- Component parent = component.getParent();
-
component.computeVisibleRect(rectCache);
SwingUtilities.computeIntersection(x, y, w, h, rectCache);
@@ -557,7 +540,6 @@ public class RepaintManager
compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots);
}
- repaintUnderway = true;
for (Iterator i = repaintRoots.iterator(); i.hasNext();)
{
JComponent comp = (JComponent) i.next();
@@ -567,13 +549,11 @@ public class RepaintManager
comp.paintImmediately(damaged);
}
dirtyComponentsWork.clear();
- repaintUnderway = false;
- commitRemainingBuffers();
}
-
+
/**
* Compiles a list of components that really get repainted. This is called
- * once for each component in the dirtyComponents HashMap, each time with
+ * once for each component in the dirtyRegions HashMap, each time with
* another <code>dirty</code> parameter. This searches up the component
* hierarchy of <code>dirty</code> to find the highest parent that is also
* marked dirty and merges the dirty regions.
@@ -588,6 +568,29 @@ public class RepaintManager
Component current = dirty;
Component root = dirty;
+ // This will contain the dirty region in the root coordinate system,
+ // possibly clipped by ancestor's bounds.
+ Rectangle originalDirtyRect = (Rectangle) dirtyRegions.get(dirty);
+ rectCache.setBounds(originalDirtyRect);
+
+ // The bounds of the current component.
+ int x = dirty.getX();
+ int y = dirty.getY();
+ int w = dirty.getWidth();
+ int h = dirty.getHeight();
+
+ // Do nothing if dirty region is clipped away by the component's bounds.
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+ if (rectCache.isEmpty())
+ return;
+
+ // The cumulated offsets.
+ int dx = 0;
+ int dy = 0;
+ // The actual offset for the found root.
+ int rootDx = 0;
+ int rootDy = 0;
+
// Search the highest component that is also marked dirty.
Component parent;
while (true)
@@ -597,10 +600,29 @@ public class RepaintManager
break;
current = parent;
+ // Update the offset.
+ dx += x;
+ dy += y;
+ rectCache.x += x;
+ rectCache.y += y;
+
+ x = current.getX();
+ y = current.getY();
+ w = current.getWidth();
+ h = current.getHeight();
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+
+ // Don't paint if the dirty regions is clipped away by any of
+ // its ancestors.
+ if (rectCache.isEmpty())
+ return;
+
// We can skip to the next up when this parent is not dirty.
if (dirtyRegions.containsKey(parent))
{
root = current;
+ rootDx = dx;
+ rootDy = dy;
}
}
@@ -608,15 +630,16 @@ public class RepaintManager
// the are different.
if (root != dirty)
{
- Rectangle dirtyRect = (Rectangle) dirtyRegions.get(dirty);
- dirtyRect = SwingUtilities.convertRectangle(dirty, dirtyRect, root);
- Rectangle rootRect = (Rectangle) dirtyRegions.get(root);
- SwingUtilities.computeUnion(dirtyRect.x, dirtyRect.y, dirtyRect.width,
- dirtyRect.height, rootRect);
+ rectCache.x += rootDx - dx;
+ rectCache.y += rootDy - dy;
+ Rectangle dirtyRect = (Rectangle) dirtyRegions.get(root);
+ SwingUtilities.computeUnion(rectCache.x, rectCache.y, rectCache.width,
+ rectCache.height, dirtyRect);
}
// Adds the root to the roots set.
- roots.add(root);
+ if (! roots.contains(root))
+ roots.add(root);
}
/**
@@ -643,130 +666,40 @@ public class RepaintManager
width = Math.min(doubleBufferMaximumSize.width, width);
int height = Math.max(proposedHeight, root.getHeight());
height = Math.min(doubleBufferMaximumSize.height, height);
- buffer = root.createImage(width, height);
+ buffer = component.createImage(width, height);
offscreenBuffers.put(root, buffer);
}
return buffer;
}
/**
- * Blits the back buffer of the specified root component to the screen. If
- * the RepaintManager is currently working on a paint request, the commit
- * requests are queued up and committed at once when the paint request is
- * done (by {@link #commitRemainingBuffers}). This is package private because
- * it must get called by JComponent.
+ * Blits the back buffer of the specified root component to the screen.
+ * This is package private because it must get called by JComponent.
*
* @param comp the component to be painted
- * @param area the area to paint on screen, in comp coordinates
- */
- void commitBuffer(Component comp, Rectangle area)
- {
- // Determine the component that we finally paint the buffer upon.
- // We need to paint on the nearest heavyweight component, so that Swing
- // hierarchies inside (non-window) heavyweights get painted correctly.
- // Otherwise we would end up blitting the backbuffer behind the heavyweight
- // which is wrong.
- Component root = getHeavyweightParent(comp);
- // FIXME: Optimize this.
- Rectangle rootRect = SwingUtilities.convertRectangle(comp, area, root);
-
- // We synchronize on dirtyComponents here because that is what
- // paintDirtyRegions also synchronizes on while painting.
- synchronized (dirtyComponents)
- {
- // If the RepaintManager is not currently painting, then directly
- // blit the requested buffer on the screen.
- if (true || ! repaintUnderway)
- {
- blitBuffer(root, rootRect);
- }
-
- // Otherwise queue this request up, until all the RepaintManager work
- // is done.
- else
- {
- if (commitRequests.containsKey(root))
- SwingUtilities.computeUnion(rootRect.x, rootRect.y,
- rootRect.width, rootRect.height,
- (Rectangle) commitRequests.get(root));
- else
- commitRequests.put(root, rootRect);
- }
- }
- }
-
- /**
- * Copies the buffer to the screen. Note that the root component here is
- * not necessarily the component with which the offscreen buffer is
- * associated. The offscreen buffers are always allocated for the toplevel
- * windows. However, painted is performed on lower-level heavyweight
- * components too, if they contain Swing components.
- *
- * @param root the heavyweight component to blit upon
- * @param rootRect the rectangle in the root component's coordinate space
+ * @param x the area to paint on screen, in comp coordinates
+ * @param y the area to paint on screen, in comp coordinates
+ * @param w the area to paint on screen, in comp coordinates
+ * @param h the area to paint on screen, in comp coordinates
*/
- private void blitBuffer(Component root, Rectangle rootRect)
+ void commitBuffer(Component comp, int x, int y, int w, int h)
{
- if (! root.isShowing())
- return;
-
- // Find the Window from which we use the backbuffer.
- Component bufferRoot = root;
- Rectangle bufferRect = rootRect.getBounds();
- if (!(bufferRoot instanceof Window))
+ Component root = comp;
+ while (root != null
+ && ! (root instanceof Window || root instanceof Applet))
{
- bufferRoot = SwingUtilities.getWindowAncestor(bufferRoot);
- SwingUtilities.convertRectangleToAncestor(root, rootRect, bufferRoot);
+ x += root.getX();
+ y += root.getY();
+ root = root.getParent();
}
Graphics g = root.getGraphics();
- Image buffer = (Image) offscreenBuffers.get(bufferRoot);
+ Image buffer = (Image) offscreenBuffers.get(root);
// Make sure we have a sane clip at this point.
- g.clipRect(rootRect.x, rootRect.y, rootRect.width, rootRect.height);
- g.drawImage(buffer, rootRect.x - bufferRect.x, rootRect.y - bufferRect.y,
- root);
+ g.clipRect(x, y, w, h);
+ g.drawImage(buffer, 0, 0, root);
g.dispose();
-
- }
-
- /**
- * Finds and returns the nearest heavyweight parent for the specified
- * component. If the component isn't contained inside a heavyweight parent,
- * this returns null.
- *
- * @param comp the component
- *
- * @return the nearest heavyweight parent for the specified component or
- * null if the component has no heavyweight ancestor
- */
- private Component getHeavyweightParent(Component comp)
- {
- while (comp != null && comp.isLightweight())
- comp = comp.getParent();
- return comp;
- }
-
- /**
- * Commits the queued up back buffers to screen all at once.
- */
- private void commitRemainingBuffers()
- {
- // We synchronize on dirtyComponents here because that is what
- // paintDirtyRegions also synchronizes on while painting.
- synchronized (dirtyComponents)
- {
- Set entrySet = commitRequests.entrySet();
- Iterator i = entrySet.iterator();
- while (i.hasNext())
- {
- Map.Entry entry = (Map.Entry) i.next();
- Component root = (Component) entry.getKey();
- Rectangle area = (Rectangle) entry.getValue();
- blitBuffer(root, area);
- i.remove();
- }
- }
}
/**
diff --git a/javax/swing/ScrollPaneLayout.java b/javax/swing/ScrollPaneLayout.java
index 8ce8fd86f..2a16f26ea 100644
--- a/javax/swing/ScrollPaneLayout.java
+++ b/javax/swing/ScrollPaneLayout.java
@@ -46,6 +46,8 @@ import java.awt.LayoutManager;
import java.awt.Rectangle;
import java.io.Serializable;
+import javax.swing.border.Border;
+
/**
* ScrollPaneLayout
* @author Andrew Selkirk
@@ -277,6 +279,16 @@ public class ScrollPaneLayout
width += rowHead.getPreferredSize().width;
if (colHead != null && colHead.isVisible())
height += colHead.getPreferredSize().height;
+
+ // Add insets of viewportBorder if present.
+ Border vpBorder = sc.getViewportBorder();
+ if (vpBorder != null)
+ {
+ Insets i = vpBorder.getBorderInsets(sc);
+ width += i.left + i.right;
+ height += i.top + i.bottom;
+ }
+
Insets i = sc.getInsets();
return new Dimension(width + i.left + i.right,
height + i.left + i.right);
@@ -300,6 +312,15 @@ public class ScrollPaneLayout
!= JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
height += sc.getHorizontalScrollBar().getMinimumSize().height;
+ // Add insets of viewportBorder if present.
+ Border vpBorder = sc.getViewportBorder();
+ if (vpBorder != null)
+ {
+ i = vpBorder.getBorderInsets(sc);
+ width += i.left + i.right;
+ height += i.top + i.bottom;
+ }
+
return new Dimension(width, height);
}
@@ -342,6 +363,15 @@ public class ScrollPaneLayout
int y1 = 0, y2 = 0, y3 = 0, y4 = 0;
Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null);
+ // If there is a viewportBorder, remove its insets from the available
+ // space.
+ Border vpBorder = sc.getViewportBorder();
+ Insets vpi;
+ if (vpBorder != null)
+ vpi = vpBorder.getBorderInsets(sc);
+ else
+ vpi = new Insets(0, 0, 0, 0);
+
x1 = scrollPaneBounds.x;
y1 = scrollPaneBounds.y;
x4 = scrollPaneBounds.x + scrollPaneBounds.width;
@@ -404,7 +434,9 @@ public class ScrollPaneLayout
// now set the layout
if (viewport != null)
- viewport.setBounds(new Rectangle(x2, y2, x3 - x2, y3 - y2));
+ viewport.setBounds(new Rectangle(x2 + vpi.left, y2 + vpi.top,
+ x3 - x2 - vpi.left - vpi.right,
+ y3 - y2 - vpi.top - vpi.bottom));
if (colHead != null)
colHead.setBounds(new Rectangle(x2, y1, x3 - x2, y2 - y1));
@@ -415,7 +447,7 @@ public class ScrollPaneLayout
if (showVsb)
{
vsb.setVisible(true);
- vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2));
+ vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2 ));
}
else if (vsb != null)
vsb.setVisible(false);
@@ -423,7 +455,7 @@ public class ScrollPaneLayout
if (showHsb)
{
hsb.setVisible(true);
- hsb.setBounds(new Rectangle(x2, y3, x3 - x2, y4 - y3));
+ hsb.setBounds(new Rectangle(x2 , y3, x3 - x2, y4 - y3));
}
else if (hsb != null)
hsb.setVisible(false);
diff --git a/javax/swing/SwingUtilities.java b/javax/swing/SwingUtilities.java
index 2823367ce..6ff0b3346 100644
--- a/javax/swing/SwingUtilities.java
+++ b/javax/swing/SwingUtilities.java
@@ -40,7 +40,6 @@ package javax.swing;
import java.applet.Applet;
import java.awt.Component;
-import java.awt.ComponentOrientation;
import java.awt.Container;
import java.awt.FontMetrics;
import java.awt.Frame;
@@ -721,38 +720,35 @@ public class SwingUtilities
// Fix up the orientation-based horizontal positions.
- if (horizontalTextPosition == LEADING)
- {
- if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
- horizontalTextPosition = RIGHT;
- else
- horizontalTextPosition = LEFT;
- }
- else if (horizontalTextPosition == TRAILING)
+ boolean ltr = true;
+ if (c != null && ! c.getComponentOrientation().isLeftToRight())
+ ltr = false;
+
+ switch (horizontalTextPosition)
{
- if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
- horizontalTextPosition = LEFT;
- else
- horizontalTextPosition = RIGHT;
+ case LEADING:
+ horizontalTextPosition = ltr ? LEFT : RIGHT;
+ break;
+ case TRAILING:
+ horizontalTextPosition = ltr ? RIGHT : LEFT;
+ break;
+ default:
+ // Nothing to do in the other cases.
}
// Fix up the orientation-based alignments.
-
- if (horizontalAlignment == LEADING)
- {
- if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
- horizontalAlignment = RIGHT;
- else
- horizontalAlignment = LEFT;
- }
- else if (horizontalAlignment == TRAILING)
+ switch (horizontalAlignment)
{
- if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT)
- horizontalAlignment = LEFT;
- else
- horizontalAlignment = RIGHT;
+ case LEADING:
+ horizontalAlignment = ltr ? LEFT : RIGHT;
+ break;
+ case TRAILING:
+ horizontalAlignment = ltr ? RIGHT : LEFT;
+ break;
+ default:
+ // Nothing to do in the other cases.
}
-
+
return layoutCompoundLabelImpl(c, fm, text, icon,
verticalAlignment,
horizontalAlignment,
@@ -921,104 +917,108 @@ public class SwingUtilities
iconR.width = icon.getIconWidth();
iconR.height = icon.getIconHeight();
}
+
if (text == null || text.equals(""))
{
textIconGap = 0;
textR.width = 0;
textR.height = 0;
+ text = "";
}
else
{
+ int availableWidth = viewR.width;
+ if (horizontalTextPosition != CENTER)
+ availableWidth -= iconR.width + textIconGap;
View html = c == null ? null
: (View) c.getClientProperty(BasicHTML.propertyKey);
if (html != null)
{
textR.width = (int) html.getPreferredSpan(View.X_AXIS);
+ textR.width = Math.min(availableWidth, textR.width);
textR.height = (int) html.getPreferredSpan(View.Y_AXIS);
}
else
{
int fromIndex = 0;
textR.width = fm.stringWidth(text);
- textR.height = fm.getHeight();
- while (text.indexOf('\n', fromIndex) != -1)
+ textR.height = fm.getHeight();
+ if (textR.width > availableWidth)
{
- textR.height += fm.getHeight();
- fromIndex = text.indexOf('\n', fromIndex) + 1;
+ text = clipString(c, fm, text, availableWidth);
+ textR.width = fm.stringWidth(text);
}
}
}
- // Work out the position of text and icon, assuming the top-left coord
+ // Work out the position of text, assuming the top-left coord
// starts at (0,0). We will fix that up momentarily, after these
// "position" decisions are made and we look at alignment.
- switch (horizontalTextPosition)
+ switch (verticalTextPosition)
{
- case LEFT:
- textR.x = 0;
- iconR.x = textR.width + textIconGap;
+ case TOP:
+ textR.y = horizontalTextPosition == CENTER ?
+ - textR.height - textIconGap : 0;
break;
- case RIGHT:
- iconR.x = 0;
- textR.x = iconR.width + textIconGap;
+ case BOTTOM:
+ textR.y = horizontalTextPosition == CENTER ?
+ iconR.height + textIconGap : iconR.height - textR.height;
break;
case CENTER:
- int centerLine = Math.max(textR.width, iconR.width) / 2;
- textR.x = centerLine - textR.width/2;
- iconR.x = centerLine - iconR.width/2;
+ textR.y = iconR.height / 2 - textR.height / 2;
break;
}
- switch (verticalTextPosition)
+ switch (horizontalTextPosition)
{
- case TOP:
- textR.y = 0;
- iconR.y = (horizontalTextPosition == CENTER
- ? textR.height + textIconGap : 0);
+ case LEFT:
+ textR.x = -(textR.width + textIconGap);
break;
- case BOTTOM:
- iconR.y = 0;
- textR.y = (horizontalTextPosition == CENTER
- ? iconR.height + textIconGap
- : Math.max(iconR.height - textR.height, 0));
+ case RIGHT:
+ textR.x = iconR.width + textIconGap;
break;
case CENTER:
- int centerLine = Math.max(textR.height, iconR.height) / 2;
- textR.y = centerLine - textR.height/2;
- iconR.y = centerLine - iconR.height/2;
+ textR.x = iconR.width / 2 - textR.width / 2;
break;
}
+
// The two rectangles are laid out correctly now, but only assuming
// that their upper left corner is at (0,0). If we have any alignment other
// than TOP and LEFT, we need to adjust them.
- Rectangle u = textR.union(iconR);
- int horizontalAdjustment = viewR.x;
- int verticalAdjustment = viewR.y;
+ // These coordinates specify the rectangle that contains both the
+ // icon and text. Move it so that it fullfills the alignment properties.
+ int lx = Math.min(iconR.x, textR.x);
+ int lw = Math.max(iconR.x + iconR.width, textR.x + textR.width) - lx;
+ int ly = Math.min(iconR.y, textR.y);
+ int lh = Math.max(iconR.y + iconR.height, textR.y + textR.height) - ly;
+ int horizontalAdjustment = 0;
+ int verticalAdjustment = 0;
switch (verticalAlignment)
{
case TOP:
+ verticalAdjustment = viewR.y - ly;
break;
case BOTTOM:
- verticalAdjustment += (viewR.height - u.height);
+ verticalAdjustment = viewR.y + viewR.height - ly - lh;
break;
case CENTER:
- verticalAdjustment += ((viewR.height/2) - (u.height/2));
+ verticalAdjustment = viewR.y + viewR.height / 2 - ly - lh / 2;
break;
}
switch (horizontalAlignment)
{
case LEFT:
+ horizontalAdjustment = viewR.x - lx;
break;
case RIGHT:
- horizontalAdjustment += (viewR.width - u.width);
+ horizontalAdjustment = viewR.x + viewR.width - lx - lw;
break;
case CENTER:
- horizontalAdjustment += ((viewR.width/2) - (u.width/2));
+ horizontalAdjustment = (viewR.x + (viewR.width / 2)) - (lx + (lw / 2));
break;
}
-
iconR.x += horizontalAdjustment;
iconR.y += verticalAdjustment;
@@ -1028,6 +1028,48 @@ public class SwingUtilities
return text;
}
+ /**
+ * The method clips the specified string so that it fits into the
+ * available width. It is only called when the text really doesn't fit,
+ * so we don't need to check that again.
+ *
+ * @param c the component
+ * @param fm the font metrics
+ * @param text the text
+ * @param availableWidth the available width
+ *
+ * @return the clipped string
+ */
+ private static String clipString(JComponent c, FontMetrics fm, String text,
+ int availableWidth)
+ {
+ String dots = "...";
+ int dotsWidth = fm.stringWidth(dots);
+ char[] string = text.toCharArray();
+ int endIndex = string.length;
+ while (fm.charsWidth(string, 0, endIndex) + dotsWidth > availableWidth
+ && endIndex > 0)
+ endIndex--;
+ String clipped;
+ if (string.length >= endIndex + 3)
+ {
+ string[endIndex] = '.';
+ string[endIndex + 1] = '.';
+ string[endIndex + 2] = '.';
+ clipped = new String(string, 0, endIndex + 3);
+ }
+ else
+ {
+ char[] clippedChars = new char[string.length + 3];
+ System.arraycopy(string, 0, clippedChars, 0, string.length);
+ clippedChars[endIndex] = '.';
+ clippedChars[endIndex + 1] = '.';
+ clippedChars[endIndex + 2] = '.';
+ clipped = new String(clippedChars, 0, endIndex + 3);
+ }
+ return clipped;
+ }
+
/**
* Calls {@link java.awt.EventQueue#invokeLater} with the
* specified {@link Runnable}.
diff --git a/javax/swing/event/EventListenerList.java b/javax/swing/event/EventListenerList.java
index f76dfa3fe..1568039f0 100644
--- a/javax/swing/event/EventListenerList.java
+++ b/javax/swing/event/EventListenerList.java
@@ -37,6 +37,9 @@ exception statement from your version. */
package javax.swing.event;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.EventListener;
@@ -304,4 +307,51 @@ public class EventListenerList
}
return buf.toString();
}
+
+ /**
+ * Serializes an instance to an ObjectOutputStream.
+ *
+ * @param out the stream to serialize to
+ *
+ * @throws IOException if something goes wrong
+ */
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ out.defaultWriteObject();
+ for (int i = 0; i < listenerList.length; i += 2)
+ {
+ Class cl = (Class) listenerList[i];
+ EventListener l = (EventListener) listenerList[i + 1];
+ if (l != null && l instanceof Serializable)
+ {
+ out.writeObject(cl.getName());
+ out.writeObject(l);
+ }
+ }
+ // Write end marker.
+ out.writeObject(null);
+ }
+
+ /**
+ * Deserializes an instance from an ObjectInputStream.
+ *
+ * @param in the input stream
+ *
+ * @throws ClassNotFoundException if a serialized class can't be found
+ * @throws IOException if something goes wrong
+ */
+ private <T extends EventListener> void readObject(ObjectInputStream in)
+ throws ClassNotFoundException, IOException
+ {
+ listenerList = NO_LISTENERS;
+ in.defaultReadObject();
+ Object type;
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ while ((type = in.readObject()) != null)
+ {
+ EventListener l = (EventListener) in.readObject();
+ add(((Class<T>) Class.forName((String) type, true, cl)), (T) l);
+ }
+ }
}
diff --git a/javax/swing/filechooser/FileSystemView.java b/javax/swing/filechooser/FileSystemView.java
index 84b80dd40..26ca4860c 100644
--- a/javax/swing/filechooser/FileSystemView.java
+++ b/javax/swing/filechooser/FileSystemView.java
@@ -37,6 +37,8 @@ exception statement from your version. */
package javax.swing.filechooser;
+import gnu.classpath.NotImplementedException;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@@ -169,16 +171,12 @@ public abstract class FileSystemView
* @return A default {@link FileSystemView} appropriate for the platform.
*/
public static FileSystemView getFileSystemView()
+ throws NotImplementedException
{
if (defaultFileSystemView == null)
{
- if (File.separator.equals("/"))
- defaultFileSystemView = new UnixFileSystemView();
- // FIXME: need to implement additional views
- // else if (File.Separator.equals("\"))
- // return new Win32FileSystemView();
- // else
- // return new GenericFileSystemView();
+ // FIXME: We need to support other file systems too.
+ defaultFileSystemView = new UnixFileSystemView();
}
return defaultFileSystemView;
}
diff --git a/javax/swing/plaf/basic/BasicButtonListener.java b/javax/swing/plaf/basic/BasicButtonListener.java
index 042192b62..c99de2c70 100644
--- a/javax/swing/plaf/basic/BasicButtonListener.java
+++ b/javax/swing/plaf/basic/BasicButtonListener.java
@@ -54,15 +54,79 @@ import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
+import javax.swing.Action;
+import javax.swing.ActionMap;
import javax.swing.ButtonModel;
+import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ButtonUI;
-public class BasicButtonListener implements MouseListener, MouseMotionListener,
- FocusListener, ChangeListener, PropertyChangeListener
+public class BasicButtonListener
+ implements MouseListener, MouseMotionListener, FocusListener, ChangeListener,
+ PropertyChangeListener
{
+ /**
+ * Implements the keyboard action for Swing buttons.
+ */
+ private class ButtonAction
+ extends AbstractAction
+ {
+ /**
+ * The key for pressed action.
+ */
+ static final String PRESSED = "pressed";
+
+ /**
+ * The key for released action.
+ */
+ static final String RELEASED = "released";
+
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ Object cmd = getValue("__command__");
+ AbstractButton b = (AbstractButton) event.getSource();
+ ButtonModel m = b.getModel();
+ if (PRESSED.equals(cmd))
+ {
+ m.setArmed(true);
+ m.setPressed(true);
+ if (! b.isFocusOwner())
+ b.requestFocus();
+ }
+ else if (RELEASED.equals(cmd))
+ {
+ m.setPressed(false);
+ m.setArmed(false);
+ }
+ }
+
+ /**
+ * Indicates if this action is enabled.
+ *
+ * @param source the source of the action
+ *
+ * @return <code>true</code> when enabled, <code>false</code> otherwise
+ */
+ public boolean isEnabled(Object source)
+ {
+ boolean enabled = true;
+ if (source instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) source;
+ enabled = b.isEnabled();
+ }
+ return enabled;
+ }
+ }
+
public BasicButtonListener(AbstractButton b)
{
// Do nothing here.
@@ -86,16 +150,25 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener,
false, false);
TextLayout layout = new TextLayout(text, b.getFont(), frc);
b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
+
+ // Update HTML renderer.
+ BasicHTML.updateRenderer(b, b.getText());
}
- if (property.equals(AbstractButton.TEXT_CHANGED_PROPERTY))
+ else if (property.equals(AbstractButton.CONTENT_AREA_FILLED_CHANGED_PROPERTY))
{
- BasicHTML.updateRenderer(b, b.getText());
+ checkOpacity(b);
}
}
-
+
+ /**
+ * Checks the <code>contentAreaFilled</code> property and updates the
+ * opaque property of the button.
+ *
+ * @param b the button to check
+ */
protected void checkOpacity(AbstractButton b)
{
- // TODO: What should be done here?
+ b.setOpaque(b.isContentAreaFilled());
}
public void focusGained(FocusEvent e)
@@ -120,6 +193,26 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener,
public void installKeyboardActions(JComponent c)
{
+ ButtonUI ui = ((AbstractButton) c).getUI();
+ if (ui instanceof BasicButtonUI)
+ {
+ // Install InputMap.
+ BasicButtonUI basicUI = (BasicButtonUI) ui;
+ String prefix = basicUI.getPropertyPrefix();
+ InputMap focusInputMap =
+ (InputMap) UIManager.get(prefix + "focusInputMap");
+ SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED,
+ focusInputMap);
+
+ ActionMap am = (ActionMap) UIManager.get(prefix + "actionMap");
+ if (am == null)
+ {
+ am = createDefaultActionMap();
+ UIManager.put(prefix + "actionMap", am);
+ }
+ SwingUtilities.replaceUIActionMap(c, am);
+ }
+
c.getActionMap().put("pressed",
new AbstractAction()
{
@@ -146,31 +239,46 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener,
}
});
}
-
+
+ /**
+ * Creates and returns the default action map for Swing buttons.
+ *
+ * @return the default action map for Swing buttons
+ */
+ private ActionMap createDefaultActionMap()
+ {
+ Action action = new ButtonAction();
+ ActionMapUIResource am = new ActionMapUIResource();
+ am.put(ButtonAction.PRESSED, action);
+ am.put(ButtonAction.RELEASED, action);
+ return am;
+ }
+
public void uninstallKeyboardActions(JComponent c)
{
- c.getActionMap().put("pressed", null);
- c.getActionMap().put("released", null);
+ SwingUtilities.replaceUIActionMap(c, null);
+ SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
}
public void stateChanged(ChangeEvent e)
{
- // TODO: What should be done here, if anything?
+ // Need to repaint when the button state changes.
+ ((AbstractButton) e.getSource()).repaint();
}
public void mouseMoved(MouseEvent e)
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
public void mouseDragged(MouseEvent e)
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
public void mouseClicked(MouseEvent e)
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
/**
diff --git a/javax/swing/plaf/basic/BasicButtonUI.java b/javax/swing/plaf/basic/BasicButtonUI.java
index cdaec2543..e2493d156 100644
--- a/javax/swing/plaf/basic/BasicButtonUI.java
+++ b/javax/swing/plaf/basic/BasicButtonUI.java
@@ -42,12 +42,13 @@ import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
+import java.awt.Insets;
import java.awt.Rectangle;
+import java.beans.PropertyChangeListener;
import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.Icon;
-import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.LookAndFeel;
@@ -64,6 +65,34 @@ import javax.swing.text.View;
public class BasicButtonUI extends ButtonUI
{
/**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle viewR = new Rectangle();
+
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle iconR = new Rectangle();
+
+ /**
+ * Cached rectangle for layouting the label. Used in paint() and
+ * BasicGraphicsUtils.getPreferredButtonSize().
+ */
+ static Rectangle textR = new Rectangle();
+
+ /**
+ * The shared button UI.
+ */
+ private static BasicButtonUI sharedUI;
+
+ /**
+ * The shared BasicButtonListener.
+ */
+ private static BasicButtonListener sharedListener;
+
+ /**
* A constant used to pad out elements in the button's layout and
* preferred size calculations.
*/
@@ -87,7 +116,9 @@ public class BasicButtonUI extends ButtonUI
*/
public static ComponentUI createUI(final JComponent c)
{
- return new BasicButtonUI();
+ if (sharedUI == null)
+ sharedUI = new BasicButtonUI();
+ return sharedUI;
}
/**
@@ -154,14 +185,29 @@ public class BasicButtonUI extends ButtonUI
protected void installDefaults(AbstractButton b)
{
String prefix = getPropertyPrefix();
+ // Install colors and font.
LookAndFeel.installColorsAndFont(b, prefix + "background",
prefix + "foreground", prefix + "font");
+ // Install border.
LookAndFeel.installBorder(b, prefix + "border");
+
+ // Install margin property.
if (b.getMargin() == null || b.getMargin() instanceof UIResource)
b.setMargin(UIManager.getInsets(prefix + "margin"));
- b.setIconTextGap(UIManager.getInt(prefix + "textIconGap"));
- b.setInputMap(JComponent.WHEN_FOCUSED,
- (InputMap) UIManager.get(prefix + "focusInputMap"));
+
+ // Install rollover property.
+ Object rollover = UIManager.get(prefix + "rollover");
+ if (rollover != null)
+ LookAndFeel.installProperty(b, "rolloverEnabled", rollover);
+
+ // Fetch default textShiftOffset.
+ defaultTextShiftOffset = UIManager.getInt(prefix + "textShiftOffset");
+
+ // Make button opaque if needed.
+ if (b.isContentAreaFilled())
+ LookAndFeel.installProperty(b, "opaque", Boolean.TRUE);
+ else
+ LookAndFeel.installProperty(b, "opaque", Boolean.FALSE);
}
/**
@@ -171,21 +217,10 @@ public class BasicButtonUI extends ButtonUI
*/
protected void uninstallDefaults(AbstractButton b)
{
- if (b.getFont() instanceof UIResource)
- b.setFont(null);
- if (b.getForeground() instanceof UIResource)
- b.setForeground(null);
- if (b.getBackground() instanceof UIResource)
- b.setBackground(null);
- if (b.getBorder() instanceof UIResource)
- b.setBorder(null);
- b.setIconTextGap(defaultTextIconGap);
- if (b.getMargin() instanceof UIResource)
- b.setMargin(null);
+ // The other properties aren't uninstallable.
+ LookAndFeel.uninstallBorder(b);
}
- protected BasicButtonListener listener;
-
/**
* Creates and returns a new instance of {@link BasicButtonListener}. This
* method provides a hook to make it easy for subclasses to install a
@@ -197,7 +232,13 @@ public class BasicButtonUI extends ButtonUI
*/
protected BasicButtonListener createButtonListener(AbstractButton b)
{
- return new BasicButtonListener(b);
+ // Note: The RI always returns a new instance here. However,
+ // the BasicButtonListener class is perfectly suitable to be shared
+ // between multiple buttons, so we return a shared instance here
+ // for efficiency.
+ if (sharedListener == null)
+ sharedListener = new BasicButtonListener(b);
+ return sharedListener;
}
/**
@@ -207,12 +248,15 @@ public class BasicButtonUI extends ButtonUI
*/
protected void installListeners(AbstractButton b)
{
- listener = createButtonListener(b);
- b.addChangeListener(listener);
- b.addPropertyChangeListener(listener);
- b.addFocusListener(listener);
- b.addMouseListener(listener);
- b.addMouseMotionListener(listener);
+ BasicButtonListener listener = createButtonListener(b);
+ if (listener != null)
+ {
+ b.addChangeListener(listener);
+ b.addPropertyChangeListener(listener);
+ b.addFocusListener(listener);
+ b.addMouseListener(listener);
+ b.addMouseMotionListener(listener);
+ }
}
/**
@@ -222,21 +266,29 @@ public class BasicButtonUI extends ButtonUI
*/
protected void uninstallListeners(AbstractButton b)
{
- b.removeChangeListener(listener);
- b.removePropertyChangeListener(listener);
- b.removeFocusListener(listener);
- b.removeMouseListener(listener);
- b.removeMouseMotionListener(listener);
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ {
+ b.removeChangeListener(listener);
+ b.removePropertyChangeListener(listener);
+ b.removeFocusListener(listener);
+ b.removeMouseListener(listener);
+ b.removeMouseMotionListener(listener);
+ }
}
protected void installKeyboardActions(AbstractButton b)
{
- listener.installKeyboardActions(b);
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.installKeyboardActions(b);
}
protected void uninstallKeyboardActions(AbstractButton b)
{
- listener.uninstallKeyboardActions(b);
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.uninstallKeyboardActions(b);
}
/**
@@ -254,6 +306,9 @@ public class BasicButtonUI extends ButtonUI
{
AbstractButton b = (AbstractButton) c;
installDefaults(b);
+ // It is important to install the listeners before installing
+ // the keyboard actions, because the keyboard actions
+ // are actually installed on the listener instance.
installListeners(b);
installKeyboardActions(b);
BasicHTML.updateRenderer(b, b.getText());
@@ -378,44 +433,50 @@ public class BasicButtonUI extends ButtonUI
{
AbstractButton b = (AbstractButton) c;
- Rectangle tr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle vr = new Rectangle();
+ Insets i = c.getInsets();
+ viewR.x = i.left;
+ viewR.y = i.top;
+ viewR.width = c.getWidth() - i.left - i.right;
+ viewR.height = c.getHeight() - i.top - i.bottom;
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
Font f = c.getFont();
-
g.setFont(f);
+ Icon icon = b.getIcon();
+ String text = b.getText();
+ text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
+ text, icon,
+ b.getVerticalAlignment(),
+ b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(),
+ viewR, iconR, textR,
+ text == null ? 0
+ : b.getIconTextGap());
- if (b.isBorderPainted())
- SwingUtilities.calculateInnerArea(b, vr);
- else
- vr = SwingUtilities.getLocalBounds(b);
- String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
- b.getText(),
- currentIcon(b),
- b.getVerticalAlignment(),
- b.getHorizontalAlignment(),
- b.getVerticalTextPosition(),
- b.getHorizontalTextPosition(),
- vr, ir, tr,
- b.getIconTextGap()
- + defaultTextShiftOffset);
-
- if ((b.getModel().isArmed() && b.getModel().isPressed())
- || b.isSelected())
+ ButtonModel model = b.getModel();
+ if (model.isArmed() && model.isPressed())
paintButtonPressed(g, b);
-
- paintIcon(g, c, ir);
+
+ if (icon != null)
+ paintIcon(g, c, iconR);
if (text != null)
{
View html = (View) b.getClientProperty(BasicHTML.propertyKey);
if (html != null)
- html.paint(g, tr);
+ html.paint(g, textR);
else
- paintText(g, b, tr, text);
+ paintText(g, b, textR, text);
}
if (b.isFocusOwner() && b.isFocusPainted())
- paintFocus(g, b, vr, tr, ir);
+ paintFocus(g, b, viewR, textR, iconR);
}
/**
@@ -455,7 +516,16 @@ public class BasicButtonUI extends ButtonUI
Icon i = currentIcon(b);
if (i != null)
- i.paintIcon(c, g, iconRect.x, iconRect.y);
+ {
+ ButtonModel m = b.getModel();
+ if (m.isPressed() && m.isArmed())
+ {
+ int offs = getTextShiftOffset();
+ i.paintIcon(c, g, iconRect.x + offs, iconRect.y + offs);
+ }
+ else
+ i.paintIcon(c, g, iconRect.x, iconRect.y);
+ }
}
/**
@@ -524,4 +594,31 @@ public class BasicButtonUI extends ButtonUI
textRect.y + fm.getAscent());
}
}
+
+ /**
+ * A helper method that finds the BasicButtonListener for the specified
+ * button. This is there because this UI class is stateless and
+ * shared for all buttons, and thus can't store the listener
+ * as instance field. (We store our shared instance in sharedListener,
+ * however, subclasses may override createButtonListener() and we would
+ * be lost in this case).
+ *
+ * @param b the button
+ *
+ * @return the UI event listener
+ */
+ private BasicButtonListener getButtonListener(AbstractButton b)
+ {
+ // The listener gets installed as PropertyChangeListener,
+ // so look for it in the list of property change listeners.
+ PropertyChangeListener[] listeners = b.getPropertyChangeListeners();
+ BasicButtonListener l = null;
+ for (int i = 0; listeners != null && l == null && i < listeners.length;
+ i++)
+ {
+ if (listeners[i] instanceof BasicButtonListener)
+ l = (BasicButtonListener) listeners[i];
+ }
+ return l;
+ }
}
diff --git a/javax/swing/plaf/basic/BasicFileChooserUI.java b/javax/swing/plaf/basic/BasicFileChooserUI.java
index e641a1c10..e1f8e4b28 100644
--- a/javax/swing/plaf/basic/BasicFileChooserUI.java
+++ b/javax/swing/plaf/basic/BasicFileChooserUI.java
@@ -444,10 +444,10 @@ public class BasicFileChooserUI extends FileChooserUI
setDirectory(null);
}
lastSelected = path;
- parentPath = path.substring(0, path.lastIndexOf("/") + 1);
+ parentPath = f.getParent();
if (f.isFile())
- setFileName(path.substring(path.lastIndexOf("/") + 1));
+ setFileName(f.getName());
else if (filechooser.getFileSelectionMode() !=
JFileChooser.FILES_ONLY)
setFileName(path);
@@ -827,9 +827,9 @@ public class BasicFileChooserUI extends FileChooserUI
installComponents(fc);
installListeners(fc);
- Object path = filechooser.getCurrentDirectory();
+ File path = filechooser.getCurrentDirectory();
if (path != null)
- parentPath = path.toString().substring(path.toString().lastIndexOf("/"));
+ parentPath = path.getParent();
}
}
diff --git a/javax/swing/plaf/basic/BasicGraphicsUtils.java b/javax/swing/plaf/basic/BasicGraphicsUtils.java
index 1e84be932..4c270682d 100644
--- a/javax/swing/plaf/basic/BasicGraphicsUtils.java
+++ b/javax/swing/plaf/basic/BasicGraphicsUtils.java
@@ -748,7 +748,6 @@ public class BasicGraphicsUtils
}
}
-
/**
* Determines the preferred width and height of an AbstractButton,
* given the gap between the button&#x2019;s text and icon.
@@ -769,24 +768,31 @@ public class BasicGraphicsUtils
public static Dimension getPreferredButtonSize(AbstractButton b,
int textIconGap)
{
- Rectangle contentRect;
- Rectangle viewRect;
- Rectangle iconRect = new Rectangle();
- Rectangle textRect = new Rectangle();
- Insets insets = b.getInsets();
-
- viewRect = new Rectangle();
-
- /* java.awt.Toolkit.getFontMetrics is deprecated. However, it
- * seems not obvious how to get to the correct FontMetrics object
- * otherwise. The real problem probably is that the method
- * javax.swing.SwingUtilities.layoutCompundLabel should take a
- * LineMetrics, not a FontMetrics argument. But fixing this that
- * would change the public API.
- */
+ // These cached rectangles are use here and in BasicButtonUI.paint(),
+ // so these two methods must never be executed concurrently. Maybe
+ // we must use other Rectangle instances here. OTOH, Swing is
+ // designed to be not thread safe, and every layout and paint operation
+ // should be performed from the EventDispatchThread, so it _should_ be
+ // OK to do this optimization.
+ Rectangle viewRect = BasicButtonUI.viewR;
+ viewRect.x = 0;
+ viewRect.y = 0;
+ viewRect.width = Short.MAX_VALUE;
+ viewRect.height = Short.MAX_VALUE;
+ Rectangle iconRect = BasicButtonUI.iconR;
+ iconRect.x = 0;
+ iconRect.y = 0;
+ iconRect.width = 0;
+ iconRect.height = 0;
+ Rectangle textRect = BasicButtonUI.textR;
+ textRect.x = 0;
+ textRect.y = 0;
+ textRect.width = 0;
+ textRect.height = 0;
+
SwingUtilities.layoutCompoundLabel(
b, // for the component orientation
- b.getToolkit().getFontMetrics(b.getFont()), // see comment above
+ b.getFontMetrics(b.getFont()), // see comment above
b.getText(),
b.getIcon(),
b.getVerticalAlignment(),
@@ -804,13 +810,12 @@ public class BasicGraphicsUtils
* +------------------------+ +------------------------+
*/
- contentRect = textRect.union(iconRect);
-
- return new Dimension(insets.left
- + contentRect.width
- + insets.right + b.getHorizontalAlignment(),
- insets.top
- + contentRect.height
- + insets.bottom);
+ Rectangle contentRect =
+ SwingUtilities.computeUnion(textRect.x, textRect.y, textRect.width,
+ textRect.height, iconRect);
+
+ Insets insets = b.getInsets();
+ return new Dimension(insets.left + contentRect.width + insets.right,
+ insets.top + contentRect.height + insets.bottom);
}
}
diff --git a/javax/swing/plaf/basic/BasicHTML.java b/javax/swing/plaf/basic/BasicHTML.java
index 98c9cb277..6e26d5355 100644
--- a/javax/swing/plaf/basic/BasicHTML.java
+++ b/javax/swing/plaf/basic/BasicHTML.java
@@ -48,6 +48,7 @@ import java.io.StringReader;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
@@ -107,6 +108,7 @@ public class BasicHTML
editorKit = kit;
document = doc;
setView(view);
+ setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS));
}
/**
@@ -151,6 +153,14 @@ public class BasicHTML
}
/**
+ * Overridden to forward to real view.
+ */
+ public void setSize(float w, float h)
+ {
+ view.setSize(w, h);
+ }
+
+ /**
* Returns the real root view, regardless of the index.
*
* @param index not used here
@@ -346,6 +356,22 @@ public class BasicHTML
{
return document;
}
+
+ /**
+ * Overridden to return null, as a RootView has no attributes on its own.
+ */
+ public AttributeSet getAttributes()
+ {
+ return null;
+ }
+
+ /**
+ * Overridden to provide an element for the view.
+ */
+ public Element getElement()
+ {
+ return view.getElement();
+ }
}
/**
diff --git a/javax/swing/plaf/basic/BasicLabelUI.java b/javax/swing/plaf/basic/BasicLabelUI.java
index 304e13ad7..1ec020b1c 100644
--- a/javax/swing/plaf/basic/BasicLabelUI.java
+++ b/javax/swing/plaf/basic/BasicLabelUI.java
@@ -119,13 +119,37 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener
{
JLabel lab = (JLabel) c;
Insets insets = lab.getInsets();
- FontMetrics fm = lab.getFontMetrics(lab.getFont());
- layoutCL(lab, fm, lab.getText(), lab.getIcon(), vr, ir, tr);
- Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width, tr.height,
- ir);
- return new Dimension(insets.left + cr.width + insets.right, insets.top
- + cr.height + insets.bottom);
-
+ int insetsX = insets.left + insets.right;
+ int insetsY = insets.top + insets.bottom;
+ Icon icon = lab.getIcon();
+ String text = lab.getText();
+ Dimension ret;
+ if (icon == null && text == null)
+ ret = new Dimension(insetsX, insetsY);
+ else if (icon != null && text == null)
+ ret = new Dimension(icon.getIconWidth() + insetsX,
+ icon.getIconHeight() + insetsY);
+ else
+ {
+ FontMetrics fm = lab.getFontMetrics(lab.getFont());
+ ir.x = 0;
+ ir.y = 0;
+ ir.width = 0;
+ ir.height = 0;
+ tr.x = 0;
+ tr.y = 0;
+ tr.width = 0;
+ tr.height = 0;
+ vr.x = 0;
+ vr.y = 0;
+ vr.width = Short.MAX_VALUE;
+ vr.height = Short.MAX_VALUE;
+ layoutCL(lab, fm, text, icon, vr, ir, tr);
+ Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width,
+ tr.height, ir);
+ ret = new Dimension(cr.width + insetsX, cr.height + insetsY);
+ }
+ return ret;
}
/**
@@ -166,13 +190,20 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener
{
JLabel b = (JLabel) c;
FontMetrics fm = g.getFontMetrics();
- vr = SwingUtilities.calculateInnerArea(c, vr);
-
- if (vr.width < 0)
- vr.width = 0;
- if (vr.height < 0)
- vr.height = 0;
+ Insets i = c.getInsets();
+ vr.x = i.left;
+ vr.y = i.right;
+ vr.width = c.getWidth() - i.left + i.right;
+ vr.height = c.getHeight() - i.top + i.bottom;
+ ir.x = 0;
+ ir.y = 0;
+ ir.width = 0;
+ ir.height = 0;
+ tr.x = 0;
+ tr.y = 0;
+ tr.width = 0;
+ tr.height = 0;
Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon();
String text = layoutCL(b, fm, b.getText(), icon, vr, ir, tr);
diff --git a/javax/swing/plaf/basic/BasicLookAndFeel.java b/javax/swing/plaf/basic/BasicLookAndFeel.java
index c056a2403..76d67b002 100644
--- a/javax/swing/plaf/basic/BasicLookAndFeel.java
+++ b/javax/swing/plaf/basic/BasicLookAndFeel.java
@@ -1218,10 +1218,10 @@ public abstract class BasicLookAndFeel extends LookAndFeel
"ctrl UP", "requestFocus",
"ctrl KP_UP", "requestFocus"
}),
- "TabbedPane.background", new ColorUIResource(light),
+ "TabbedPane.background", new ColorUIResource(192, 192, 192),
"TabbedPane.contentBorderInsets", new InsetsUIResource(2, 2, 3, 3),
- "TabbedPane.darkShadow", new ColorUIResource(shadow),
- "TabbedPane.focus", new ColorUIResource(darkShadow),
+ "TabbedPane.darkShadow", new ColorUIResource(Color.black),
+ "TabbedPane.focus", new ColorUIResource(Color.black),
"TabbedPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
KeyStroke.getKeyStroke("ctrl DOWN"), "requestFocusForVisibleComponent",
KeyStroke.getKeyStroke("KP_UP"), "navigateUp",
@@ -1235,17 +1235,16 @@ public abstract class BasicLookAndFeel extends LookAndFeel
KeyStroke.getKeyStroke("DOWN"), "navigateDown"
}),
"TabbedPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
- "TabbedPane.foreground", new ColorUIResource(darkShadow),
- "TabbedPane.highlight", new ColorUIResource(highLight),
- "TabbedPane.light", new ColorUIResource(highLight),
+ "TabbedPane.foreground", new ColorUIResource(Color.black),
+ "TabbedPane.highlight", new ColorUIResource(Color.white),
+ "TabbedPane.light", new ColorUIResource(192, 192, 192),
"TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1),
- "TabbedPane.shadow", new ColorUIResource(shadow),
- "TabbedPane.tabbedPaneContentBorderInsets", new InsetsUIResource(3, 2, 1, 2),
- "TabbedPane.tabbedPaneTabPadInsets", new InsetsUIResource(1, 1, 1, 1),
+ "TabbedPane.shadow", new ColorUIResource(128, 128, 128),
"TabbedPane.tabsOpaque", Boolean.TRUE,
"TabbedPane.tabAreaInsets", new InsetsUIResource(3, 2, 0, 2),
"TabbedPane.tabInsets", new InsetsUIResource(0, 4, 1, 4),
"TabbedPane.tabRunOverlay", new Integer(2),
+ "TabbedPane.tabsOverlapBorder", Boolean.FALSE,
"TabbedPane.textIconGap", new Integer(4),
"Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
"ctrl DOWN", "selectNextRowChangeLead",
diff --git a/javax/swing/plaf/basic/BasicMenuItemUI.java b/javax/swing/plaf/basic/BasicMenuItemUI.java
index 6110aca66..85eefb9ee 100644
--- a/javax/swing/plaf/basic/BasicMenuItemUI.java
+++ b/javax/swing/plaf/basic/BasicMenuItemUI.java
@@ -449,6 +449,7 @@ public class BasicMenuItemUI extends MenuItemUI
// Layout the menu item. The result gets stored in the rectangle
// fields of this class.
+ resetRectangles(null);
layoutMenuItem(m, accelText);
// The union of the text and icon areas is the label area.
@@ -700,6 +701,8 @@ public class BasicMenuItemUI extends MenuItemUI
// Layout menu item. The result gets stored in the rectangle fields
// of this class.
+ resetRectangles(m);
+
layoutMenuItem(m, accelText);
// Paint the background.
@@ -1271,6 +1274,33 @@ public class BasicMenuItemUI extends MenuItemUI
}
/**
+ * Resets the cached layout rectangles. If <code>i</code> is not null, then
+ * the view rectangle is set to the inner area of the component, otherwise
+ * it is set to (0, 0, Short.MAX_VALUE, Short.MAX_VALUE), this is needed
+ * for layouting.
+ *
+ * @param i the component for which to initialize the rectangles
+ */
+ private void resetRectangles(JMenuItem i)
+ {
+ // Reset rectangles.
+ iconRect.setBounds(0, 0, 0, 0);
+ textRect.setBounds(0, 0, 0, 0);
+ accelRect.setBounds(0, 0, 0, 0);
+ checkIconRect.setBounds(0, 0, 0, 0);
+ arrowIconRect.setBounds(0, 0, 0, 0);
+ if (i == null)
+ viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
+ else
+ {
+ Insets insets = i.getInsets();
+ viewRect.setBounds(insets.left, insets.top,
+ i.getWidth() - insets.left - insets.right,
+ i.getHeight() - insets.top - insets.bottom);
+ }
+ }
+
+ /**
* A helper method that lays out the menu item. The layout is stored
* in the fields of this class.
*
@@ -1282,21 +1312,6 @@ public class BasicMenuItemUI extends MenuItemUI
int width = m.getWidth();
int height = m.getHeight();
- // Reset rectangles.
- iconRect.setBounds(0, 0, 0, 0);
- textRect.setBounds(0, 0, 0, 0);
- accelRect.setBounds(0, 0, 0, 0);
- checkIconRect.setBounds(0, 0, 0, 0);
- arrowIconRect.setBounds(0, 0, 0, 0);
- viewRect.setBounds(0, 0, width, height);
-
- // Substract insets to the view rect.
- Insets insets = m.getInsets();
- viewRect.x += insets.left;
- viewRect.y += insets.top;
- viewRect.width -= insets.left + insets.right;
- viewRect.height -= insets.top + insets.bottom;
-
// Fetch the fonts.
Font font = m.getFont();
FontMetrics fm = m.getFontMetrics(font);
diff --git a/javax/swing/plaf/basic/BasicRadioButtonUI.java b/javax/swing/plaf/basic/BasicRadioButtonUI.java
index aed4d69d6..802b25339 100644
--- a/javax/swing/plaf/basic/BasicRadioButtonUI.java
+++ b/javax/swing/plaf/basic/BasicRadioButtonUI.java
@@ -129,9 +129,22 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
{
AbstractButton b = (AbstractButton) c;
- Rectangle tr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle vr = new Rectangle();
+ Insets i = b.getInsets();
+ Rectangle tr = textR;
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ Rectangle ir = iconR;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+ Rectangle vr = viewR;
+ viewR.x = i.left;
+ viewR.y = i.right;
+ viewR.width = b.getWidth() - i.left - i.right;
+ viewR.height = b.getHeight() - i.top - i.bottom;
Font f = c.getFont();
@@ -149,13 +162,12 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
currentIcon = getDefaultIcon();
}
- SwingUtilities.calculateInnerArea(b, vr);
String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
b.getText(), currentIcon,
b.getVerticalAlignment(), b.getHorizontalAlignment(),
b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
vr, ir, tr, b.getIconTextGap() + defaultTextShiftOffset);
-
+
currentIcon.paintIcon(c, g, ir.x, ir.y);
if (text != null)
@@ -174,17 +186,25 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
// The other icon properties are ignored.
AbstractButton b = (AbstractButton) c;
- Rectangle contentRect;
- Rectangle viewRect;
- Rectangle iconRect = new Rectangle();
- Rectangle textRect = new Rectangle();
Insets insets = b.getInsets();
-
+
+ String text = b.getText();
Icon i = b.getIcon();
if (i == null)
i = getDefaultIcon();
- viewRect = new Rectangle();
+ textR.x = 0;
+ textR.y = 0;
+ textR.width = 0;
+ textR.height = 0;
+ iconR.x = 0;
+ iconR.y = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+ viewR.x = 0;
+ viewR.y = 0;
+ viewR.width = Short.MAX_VALUE;
+ viewR.height = Short.MAX_VALUE;
SwingUtilities.layoutCompoundLabel(
b, // for the component orientation
@@ -195,17 +215,14 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI
b.getHorizontalAlignment(),
b.getVerticalTextPosition(),
b.getHorizontalTextPosition(),
- viewRect, iconRect, textRect,
+ viewR, iconR, textR,
defaultTextIconGap + defaultTextShiftOffset);
- contentRect = textRect.union(iconRect);
-
- return new Dimension(insets.left
- + contentRect.width
- + insets.right + b.getHorizontalAlignment(),
- insets.top
- + contentRect.height
- + insets.bottom);
+ Rectangle r = SwingUtilities.computeUnion(textR.x, textR.y, textR.width,
+ textR.height, iconR);
+
+ return new Dimension(insets.left + r.width + insets.right,
+ insets.top + r.height + insets.bottom);
}
/**
diff --git a/javax/swing/plaf/basic/BasicScrollBarUI.java b/javax/swing/plaf/basic/BasicScrollBarUI.java
index 78e5168fc..ee246cbba 100644
--- a/javax/swing/plaf/basic/BasicScrollBarUI.java
+++ b/javax/swing/plaf/basic/BasicScrollBarUI.java
@@ -1228,8 +1228,12 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
*/
protected void scrollByBlock(int direction)
{
+ if (direction > 0)
scrollbar.setValue(scrollbar.getValue()
+ scrollbar.getBlockIncrement(direction));
+ else
+ scrollbar.setValue(scrollbar.getValue()
+ - scrollbar.getBlockIncrement(direction));
}
/**
@@ -1239,8 +1243,12 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
*/
protected void scrollByUnit(int direction)
{
- scrollbar.setValue(scrollbar.getValue()
- + scrollbar.getUnitIncrement(direction));
+ if (direction > 0)
+ scrollbar.setValue(scrollbar.getValue()
+ + scrollbar.getUnitIncrement(direction));
+ else
+ scrollbar.setValue(scrollbar.getValue()
+ - scrollbar.getUnitIncrement(direction));
}
/**
diff --git a/javax/swing/plaf/basic/BasicScrollPaneUI.java b/javax/swing/plaf/basic/BasicScrollPaneUI.java
index a0616a8c1..69e352711 100644
--- a/javax/swing/plaf/basic/BasicScrollPaneUI.java
+++ b/javax/swing/plaf/basic/BasicScrollPaneUI.java
@@ -38,8 +38,6 @@ exception statement from your version. */
package javax.swing.plaf.basic;
-import gnu.classpath.NotImplementedException;
-
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
@@ -54,7 +52,6 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
-import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
@@ -69,11 +66,13 @@ import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
+import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.ScrollPaneUI;
+import javax.swing.plaf.UIResource;
/**
* A UI delegate for the {@link JScrollPane} component.
@@ -281,7 +280,7 @@ public class BasicScrollPaneUI extends ScrollPaneUI
// Scroll non scrollables.
delta = wheel * SCROLL_NON_SCROLLABLES;
}
- scroll(bar, delta);
+ scroll(bar, wheel > 0 ? delta : -delta);
}
// If not, try to scroll horizontally
else
@@ -436,16 +435,24 @@ public class BasicScrollPaneUI extends ScrollPaneUI
"ScrollPane.foreground",
"ScrollPane.font");
LookAndFeel.installBorder(p, "ScrollPane.border");
+
+ // Install Viewport border.
+ Border vpBorder = p.getViewportBorder();
+ if (vpBorder == null || vpBorder instanceof UIResource)
+ {
+ vpBorder = UIManager.getBorder("ScrollPane.viewportBorder");
+ p.setViewportBorder(vpBorder);
+ }
+
p.setOpaque(true);
}
protected void uninstallDefaults(JScrollPane p)
{
- p.setForeground(null);
- p.setBackground(null);
- p.setFont(null);
- p.setBorder(null);
- scrollpane = null;
+ LookAndFeel.uninstallBorder(p);
+ Border vpBorder = p.getViewportBorder();
+ if (vpBorder != null && vpBorder instanceof UIResource)
+ p.setViewportBorder(null);
}
public void installUI(final JComponent c)
@@ -809,8 +816,12 @@ public class BasicScrollPaneUI extends ScrollPaneUI
public void paint(Graphics g, JComponent c)
{
- // do nothing; the normal painting-of-children algorithm, along with
- // ScrollPaneLayout, does all the relevant work.
+ Border vpBorder = scrollpane.getViewportBorder();
+ if (vpBorder != null)
+ {
+ Rectangle r = scrollpane.getViewportBorderBounds();
+ vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
+ }
}
/**
diff --git a/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/javax/swing/plaf/basic/BasicSplitPaneDivider.java
index 06d32984e..95468caa9 100644
--- a/javax/swing/plaf/basic/BasicSplitPaneDivider.java
+++ b/javax/swing/plaf/basic/BasicSplitPaneDivider.java
@@ -38,12 +38,15 @@ exception statement from your version. */
package javax.swing.plaf.basic;
+import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.LayoutManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
@@ -52,7 +55,7 @@ import java.beans.PropertyChangeListener;
import javax.swing.JButton;
import javax.swing.JSplitPane;
-import javax.swing.SwingConstants;
+import javax.swing.UIManager;
import javax.swing.border.Border;
/**
@@ -72,6 +75,207 @@ public class BasicSplitPaneDivider extends Container
implements PropertyChangeListener
{
/**
+ * The buttons used as one touch buttons.
+ */
+ private class BasicOneTouchButton
+ extends JButton
+ {
+ /**
+ * Denotes a left button.
+ */
+ static final int LEFT = 0;
+
+ /**
+ * Denotes a right button.
+ */
+ static final int RIGHT = 1;
+
+ /**
+ * The x points for the arrow.
+ */
+ private int[] xpoints;
+
+ /**
+ * The y points for the arrow.
+ */
+ private int[] ypoints;
+
+ /**
+ * Either LEFT or RIGHT.
+ */
+ private int direction;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param dir either LEFT or RIGHT
+ */
+ BasicOneTouchButton(int dir)
+ {
+ direction = dir;
+ xpoints = new int[3];
+ ypoints = new int[3];
+ }
+
+ /**
+ * Never allow borders.
+ */
+ public void setBorder(Border b)
+ {
+ }
+
+ /**
+ * Never allow focus traversal.
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Paints the one touch button.
+ */
+ public void paint(Graphics g)
+ {
+ if (splitPane != null)
+ {
+ // Fill background.
+ g.setColor(splitPane.getBackground());
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ // Draw arrow.
+ int size;
+ if (direction == LEFT)
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ size = Math.min(getHeight(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size / 2;
+ xpoints[2] = size;
+ ypoints[0] = size;
+ ypoints[1] = 0;
+ ypoints[2] = size;
+ }
+ else
+ {
+ size = Math.min(getWidth(), ONE_TOUCH_SIZE);
+ xpoints[0] = size;
+ xpoints[1] = 0;
+ xpoints[2] = size;
+ ypoints[0] = 0;
+ ypoints[1] = size / 2;
+ ypoints[2] = size;
+ }
+ }
+ else
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ size = Math.min(getHeight(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size / 2;
+ xpoints[2] = size;
+ ypoints[0] = 0;
+ ypoints[1] = size;
+ ypoints[2] = 0;
+ }
+ else
+ {
+ size = Math.min(getWidth(), ONE_TOUCH_SIZE);
+ xpoints[0] = 0;
+ xpoints[1] = size;
+ xpoints[2] = 0;
+ ypoints[0] = 0;
+ ypoints[1] = size / 2;
+ ypoints[2] = size;
+ }
+ }
+ g.setColor(Color.BLACK);
+ g.fillPolygon(xpoints, ypoints, 3);
+ }
+ }
+ }
+
+ /**
+ * Listens for actions on the one touch buttons.
+ */
+ private class OneTouchAction
+ implements ActionListener
+ {
+
+ public void actionPerformed(ActionEvent ev)
+ {
+ Insets insets = splitPane.getInsets();
+ int lastLoc = splitPane.getLastDividerLocation();
+ int currentLoc = splitPaneUI.getDividerLocation(splitPane);
+ int newLoc;
+
+ if (ev.getSource() == leftButton)
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ if (currentLoc
+ >= splitPane.getHeight() - insets.bottom - getHeight())
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = insets.top;
+ }
+ }
+ else
+ {
+ if (currentLoc
+ >= splitPane.getWidth() - insets.right - getWidth())
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = insets.left;
+ }
+ }
+ }
+ else
+ {
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ if (currentLoc == insets.top)
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = splitPane.getHeight() - insets.top - getHeight();
+ }
+ }
+ else
+ {
+ if (currentLoc == insets.left)
+ {
+ newLoc = Math.min(splitPane.getMaximumDividerLocation(),
+ lastLoc);
+ }
+ else
+ {
+ newLoc = splitPane.getWidth() - insets.left - getWidth();
+ }
+ }
+ }
+ if (currentLoc != newLoc)
+ {
+ splitPane.setDividerLocation(newLoc);
+ splitPane.setLastDividerLocation(currentLoc);
+ }
+ }
+ }
+
+ /**
* Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1
* on MacOS X 10.1.5.
*/
@@ -161,6 +365,14 @@ public class BasicSplitPaneDivider extends Container
transient int currentDividerLocation = 1;
/**
+ * Indicates if the ont touch buttons are laid out centered or at the
+ * top/left.
+ *
+ * Package private to avoid accessor method.
+ */
+ boolean centerOneTouchButtons;
+
+ /**
* Constructs a new divider.
*
* @param ui the UI delegate of the enclosing <code>JSplitPane</code>.
@@ -170,6 +382,8 @@ public class BasicSplitPaneDivider extends Container
setLayout(new DividerLayout());
setBasicSplitPaneUI(ui);
setDividerSize(splitPane.getDividerSize());
+ centerOneTouchButtons =
+ UIManager.getBoolean("SplitPane.centerOneTouchButtons");
}
/**
@@ -202,7 +416,8 @@ public class BasicSplitPaneDivider extends Container
addMouseMotionListener(mouseHandler);
hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider();
orientation = splitPane.getOrientation();
- oneTouchExpandableChanged();
+ if (splitPane.isOneTouchExpandable())
+ oneTouchExpandableChanged();
}
}
@@ -293,7 +508,12 @@ public class BasicSplitPaneDivider extends Container
*/
public Dimension getPreferredSize()
{
- return getLayout().preferredLayoutSize(this);
+ Dimension d;
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ d = new Dimension(getDividerSize(), 1);
+ else
+ d = new Dimension(1, getDividerSize());
+ return d;
}
/**
@@ -320,11 +540,9 @@ public class BasicSplitPaneDivider extends Container
else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
{
orientation = splitPane.getOrientation();
- if (splitPane.isOneTouchExpandable())
- {
- layout();
- repaint();
- }
+ invalidate();
+ if (splitPane != null)
+ splitPane.revalidate();
}
else if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
dividerSize = splitPane.getDividerSize();
@@ -345,11 +563,6 @@ public class BasicSplitPaneDivider extends Container
dividerSize = getSize();
border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height);
}
- if (splitPane.isOneTouchExpandable())
- {
- ((BasicArrowButton) rightButton).paint(g);
- ((BasicArrowButton) leftButton).paint(g);
- }
}
/**
@@ -361,31 +574,23 @@ public class BasicSplitPaneDivider extends Container
if (splitPane.isOneTouchExpandable())
{
leftButton = createLeftOneTouchButton();
- rightButton = createRightOneTouchButton();
- add(leftButton);
- add(rightButton);
+ if (leftButton != null)
+ leftButton.addActionListener(new OneTouchAction());
- leftButton.addMouseListener(mouseHandler);
- rightButton.addMouseListener(mouseHandler);
+ rightButton = createRightOneTouchButton();
+ if (rightButton != null)
+ rightButton.addActionListener(new OneTouchAction());
- // Set it to 1.
- currentDividerLocation = 1;
- }
- else
- {
+ // Only add them when both are non-null.
if (leftButton != null && rightButton != null)
- {
- leftButton.removeMouseListener(mouseHandler);
- rightButton.removeMouseListener(mouseHandler);
-
- remove(leftButton);
- remove(rightButton);
- leftButton = null;
- rightButton = null;
+ {
+ add(leftButton);
+ add(rightButton);
}
}
- layout();
- repaint();
+ invalidate();
+ if (splitPane != null)
+ splitPane.revalidate();
}
/**
@@ -396,12 +601,9 @@ public class BasicSplitPaneDivider extends Container
*/
protected JButton createLeftOneTouchButton()
{
- int dir = SwingConstants.WEST;
- if (orientation == JSplitPane.VERTICAL_SPLIT)
- dir = SwingConstants.NORTH;
- JButton button = new BasicArrowButton(dir);
- button.setBorder(null);
-
+ JButton button = new BasicOneTouchButton(BasicOneTouchButton.LEFT);
+ button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ button.setRequestFocusEnabled(false);
return button;
}
@@ -413,11 +615,9 @@ public class BasicSplitPaneDivider extends Container
*/
protected JButton createRightOneTouchButton()
{
- int dir = SwingConstants.EAST;
- if (orientation == JSplitPane.VERTICAL_SPLIT)
- dir = SwingConstants.SOUTH;
- JButton button = new BasicArrowButton(dir);
- button.setBorder(null);
+ JButton button = new BasicOneTouchButton(BasicOneTouchButton.RIGHT);
+ button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ button.setRequestFocusEnabled(false);
return button;
}
@@ -521,25 +721,6 @@ public class BasicSplitPaneDivider extends Container
*/
public void mousePressed(MouseEvent e)
{
- if (splitPane.isOneTouchExpandable())
- {
- if (e.getSource() == leftButton)
- {
- currentDividerLocation--;
- if (currentDividerLocation < 0)
- currentDividerLocation = 0;
- moveDividerTo(currentDividerLocation);
- return;
- }
- else if (e.getSource() == rightButton)
- {
- currentDividerLocation++;
- if (currentDividerLocation > 2)
- currentDividerLocation = 2;
- moveDividerTo(currentDividerLocation);
- return;
- }
- }
isDragging = true;
currentDividerLocation = 1;
if (orientation == JSplitPane.HORIZONTAL_SPLIT)
@@ -797,10 +978,64 @@ public class BasicSplitPaneDivider extends Container
*/
public void layoutContainer(Container c)
{
- if (splitPane.isOneTouchExpandable())
+ if (leftButton != null && rightButton != null
+ && c == BasicSplitPaneDivider.this)
{
- changeButtonOrientation();
- positionButtons();
+ if (splitPane.isOneTouchExpandable())
+ {
+ Insets insets = getInsets();
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ {
+ int size = getWidth() - insets.left - insets.right;
+ size = Math.max(size, 0);
+ size = Math.min(size, ONE_TOUCH_SIZE);
+ int x, y;
+ if (centerOneTouchButtons)
+ {
+ y = insets.top;
+ x = (getWidth() - size) / 2;
+ }
+ else
+ {
+ x = insets.left;
+ y = 0;
+ }
+
+ leftButton.setBounds(x, y + ONE_TOUCH_OFFSET, size,
+ size * 2);
+ rightButton.setBounds(x, y + ONE_TOUCH_OFFSET
+ + ONE_TOUCH_SIZE * 2, size, size * 2);
+ }
+ else
+ {
+ int size = getHeight() - insets.top - insets.bottom;
+ size = Math.max(size, 0);
+ size = Math.min(size, ONE_TOUCH_SIZE);
+ int x, y;
+ if (centerOneTouchButtons)
+ {
+ x = insets.left;
+ y = (getHeight() - size) / 2;
+ }
+ else
+ {
+ x = 0;
+ y = insets.top;
+ }
+ leftButton.setBounds(x + ONE_TOUCH_OFFSET, y, size * 2,
+ size);
+ rightButton.setBounds(x + ONE_TOUCH_OFFSET
+ + ONE_TOUCH_SIZE * 2, y, size * 2,
+ size);
+ }
+ }
+ else
+ {
+ // The JDK sets this bounds for disabled one touch buttons, so
+ // do we.
+ leftButton.setBounds(-5, -5, 1, 1);
+ rightButton.setBounds(-5, -5, 1, 1);
+ }
}
}
@@ -838,50 +1073,5 @@ public class BasicSplitPaneDivider extends Container
// Do nothing.
}
- /**
- * This method changes the button orientation when the orientation of the
- * SplitPane changes.
- */
- private void changeButtonOrientation()
- {
- if (orientation == JSplitPane.HORIZONTAL_SPLIT)
- {
- ((BasicArrowButton) rightButton).setDirection(SwingConstants.EAST);
- ((BasicArrowButton) leftButton).setDirection(SwingConstants.WEST);
- }
- else
- {
- ((BasicArrowButton) rightButton).setDirection(SwingConstants.SOUTH);
- ((BasicArrowButton) leftButton).setDirection(SwingConstants.NORTH);
- }
- }
-
- /**
- * This method sizes and positions the buttons.
- */
- private void positionButtons()
- {
- int w = 0;
- int h = 0;
- if (orientation == JSplitPane.HORIZONTAL_SPLIT)
- {
- rightButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
- leftButton.setLocation(ONE_TOUCH_OFFSET,
- ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE);
- w = dividerSize - 2 * ONE_TOUCH_OFFSET;
- h = 2 * ONE_TOUCH_SIZE;
- }
- else
- {
- leftButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET);
- rightButton.setLocation(ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE,
- ONE_TOUCH_OFFSET);
- h = dividerSize - 2 * ONE_TOUCH_OFFSET;
- w = 2 * ONE_TOUCH_SIZE;
- }
- Dimension dims = new Dimension(w, h);
- leftButton.setSize(dims);
- rightButton.setSize(dims);
- }
}
}
diff --git a/javax/swing/plaf/basic/BasicSplitPaneUI.java b/javax/swing/plaf/basic/BasicSplitPaneUI.java
index 2d5955974..6ef4c08ce 100644
--- a/javax/swing/plaf/basic/BasicSplitPaneUI.java
+++ b/javax/swing/plaf/basic/BasicSplitPaneUI.java
@@ -63,6 +63,7 @@ import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.plaf.ActionMapUIResource;
@@ -107,13 +108,34 @@ public class BasicSplitPaneUI extends SplitPaneUI
protected int[] sizes = new int[3];
/**
+ * This is used to determine if we are vertical or horizontal layout.
+ * In the JDK, the BasicVerticalLayoutManager seems to have no more
+ * methods implemented (as of JDK5), so we keep this state here.
+ */
+ private int axis;
+
+ /**
* Creates a new instance. This is package private because the reference
* implementation has no public constructor either. Still, we need to
* call it from BasicVerticalLayoutManager.
*/
BasicHorizontalLayoutManager()
{
- // Nothing to do here.
+ this(SwingConstants.HORIZONTAL);
+ }
+
+ /**
+ * Creates a new instance for a specified axis. This is provided for
+ * compatibility, since the BasicVerticalLayoutManager seems to have
+ * no more implementation in the RI, according to the specs. So
+ * we handle all the axis specific stuff here.
+ *
+ * @param a the axis, either SwingConstants#HORIZONTAL,
+ * or SwingConstants#VERTICAL
+ */
+ BasicHorizontalLayoutManager(int a)
+ {
+ axis = a;
}
/**
@@ -167,7 +189,12 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
protected int getAvailableSize(Dimension containerSize, Insets insets)
{
- return containerSize.width - insets.left - insets.right;
+ int size;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = containerSize.width - insets.left - insets.right;
+ else
+ size = containerSize.height - insets.top - insets.bottom;
+ return size;
}
/**
@@ -180,9 +207,15 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
protected int getInitialLocation(Insets insets)
{
+ int loc = 0;
if (insets != null)
- return insets.left;
- return 0;
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ loc = insets.left;
+ else
+ loc = insets.top;
+ }
+ return loc;
}
/**
@@ -195,7 +228,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public float getLayoutAlignmentX(Container target)
{
- return target.getAlignmentX();
+ return 0.0f;
}
/**
@@ -208,7 +241,7 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public float getLayoutAlignmentY(Container target)
{
- return target.getAlignmentY();
+ return 0.0f;
}
/**
@@ -220,10 +253,19 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
protected int getPreferredSizeOfComponent(Component c)
{
+ int size = 0;
Dimension dims = c.getPreferredSize();
- if (dims != null)
- return dims.width;
- return 0;
+ if (axis == SwingConstants.HORIZONTAL)
+ {
+ if (dims != null)
+ size = dims.width;
+ }
+ else
+ {
+ if (dims != null)
+ size = dims.height;
+ }
+ return size;
}
/**
@@ -235,7 +277,12 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
protected int getSizeOfComponent(Component c)
{
- return c.getWidth();
+ int size;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = c.getHeight();
+ else
+ size = c.getWidth();
+ return size;
}
/**
@@ -313,27 +360,31 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public Dimension minimumLayoutSize(Container target)
{
+ Dimension dim = new Dimension();
if (target instanceof JSplitPane)
{
JSplitPane split = (JSplitPane) target;
- Insets insets = target.getInsets();
-
- int height = 0;
- int width = 0;
+ int primary = 0;
+ int secondary = 0;
for (int i = 0; i < components.length; i++)
{
- if (components[i] == null)
- continue;
- Dimension dims = components[i].getMinimumSize();
- if (dims != null)
+ if (components[i] != null)
{
- width += dims.width;
- height = Math.max(height, dims.height);
+ Dimension dims = components[i].getMinimumSize();
+ primary += axis == SwingConstants.HORIZONTAL ? dims.width
+ : dims.height;
+ int sec = axis == SwingConstants.HORIZONTAL ? dims.height
+ : dims.width;
+ secondary = Math.max(sec, secondary);
}
}
- return new Dimension(width, height);
+ int width = axis == SwingConstants.HORIZONTAL ? primary : secondary;
+ int height = axis == SwingConstants.VERTICAL ? secondary : primary;
+
+ Insets i = splitPane.getInsets();
+ dim.setSize(width + i.left + i.right, height + i.top + i.bottom);
}
- return null;
+ return dim;
}
/**
@@ -347,28 +398,31 @@ public class BasicSplitPaneUI extends SplitPaneUI
*/
public Dimension preferredLayoutSize(Container target)
{
+ Dimension dim = new Dimension();
if (target instanceof JSplitPane)
{
JSplitPane split = (JSplitPane) target;
- Insets insets = target.getInsets();
-
- int height = 0;
- int width = 0;
+ int primary = 0;
+ int secondary = 0;
for (int i = 0; i < components.length; i++)
{
- if (components[i] == null)
- continue;
- Dimension dims = components[i].getPreferredSize();
- if (dims != null)
+ if (components[i] != null)
{
- width += dims.width;
- if (!(components[i] instanceof BasicSplitPaneDivider))
- height = Math.max(height, dims.height);
+ Dimension dims = components[i].getPreferredSize();
+ primary += axis == SwingConstants.HORIZONTAL ? dims.width
+ : dims.height;
+ int sec = axis == SwingConstants.HORIZONTAL ? dims.height
+ : dims.width;
+ secondary = Math.max(sec, secondary);
}
}
- return new Dimension(width, height);
+ int width = axis == SwingConstants.HORIZONTAL ? primary : secondary;
+ int height = axis == SwingConstants.VERTICAL ? secondary : primary;
+
+ Insets i = splitPane.getInsets();
+ dim.setSize(width + i.left + i.right, height + i.top + i.bottom);
}
- return null;
+ return dim;
}
/**
@@ -425,11 +479,23 @@ public class BasicSplitPaneUI extends SplitPaneUI
protected void setComponentToSize(Component c, int size, int location,
Insets insets, Dimension containerSize)
{
- int w = size;
- int h = containerSize.height - insets.top - insets.bottom;
- int x = location;
- int y = insets.top;
- c.setBounds(x, y, w, h);
+ if (insets != null)
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ c.setBounds(location, insets.top, size,
+ containerSize.height - insets.top - insets.bottom);
+ else
+ c.setBounds(insets.left, location,
+ containerSize.width - insets.left - insets.right,
+ size);
+ }
+ else
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ c.setBounds(location, 0, size, containerSize.height);
+ else
+ c.setBounds(0, location, containerSize.width, size);
+ }
}
/**
@@ -462,7 +528,6 @@ public class BasicSplitPaneUI extends SplitPaneUI
resetSizeAt(1);
}
components[2] = divider;
- resetSizeAt(2);
}
/**
@@ -485,10 +550,13 @@ public class BasicSplitPaneUI extends SplitPaneUI
int minimumSizeOfComponent(int index)
{
Dimension dims = components[index].getMinimumSize();
+ int size = 0;
if (dims != null)
- return dims.width;
- else
- return 0;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = dims.width;
+ else
+ size = dims.height;
+ return size;
}
} //end BasicHorizontalLayoutManager
@@ -504,163 +572,11 @@ public class BasicSplitPaneUI extends SplitPaneUI
extends BasicHorizontalLayoutManager
{
/**
- * This method returns the height of the container minus the top and
- * bottom inset.
- *
- * @param containerSize The size of the container.
- * @param insets The insets of the container.
- *
- * @return The height minus top and bottom inset.
- */
- protected int getAvailableSize(Dimension containerSize, Insets insets)
- {
- return containerSize.height - insets.top - insets.bottom;
- }
-
- /**
- * This method returns the top inset.
- *
- * @param insets The Insets to use.
- *
- * @return The top inset.
- */
- protected int getInitialLocation(Insets insets)
- {
- return insets.top;
- }
-
- /**
- * This method returns the preferred height of the component.
- *
- * @param c The component to measure.
- *
- * @return The preferred height of the component.
+ * Creates a new instance.
*/
- protected int getPreferredSizeOfComponent(Component c)
+ public BasicVerticalLayoutManager()
{
- Dimension dims = c.getPreferredSize();
- if (dims != null)
- return dims.height;
- return 0;
- }
-
- /**
- * This method returns the current height of the component.
- *
- * @param c The component to measure.
- *
- * @return The current height of the component.
- */
- protected int getSizeOfComponent(Component c)
- {
- return c.getHeight();
- }
-
- /**
- * This method returns the minimum layout size. The minimum height is the
- * sum of all the components' minimum heights. The minimum width is the
- * maximum of all the components' minimum widths.
- *
- * @param container The container to measure.
- *
- * @return The minimum size.
- */
- public Dimension minimumLayoutSize(Container container)
- {
- if (container instanceof JSplitPane)
- {
- JSplitPane split = (JSplitPane) container;
- Insets insets = container.getInsets();
-
- int height = 0;
- int width = 0;
- for (int i = 0; i < components.length; i++)
- {
- if (components[i] == null)
- continue;
- Dimension dims = components[i].getMinimumSize();
- if (dims != null)
- {
- height += dims.height;
- width = Math.max(width, dims.width);
- }
- }
- return new Dimension(width, height);
- }
- return null;
- }
-
- /**
- * This method returns the preferred layout size. The preferred height is
- * the sum of all the components' preferred heights. The preferred width
- * is the maximum of all the components' preferred widths.
- *
- * @param container The container to measure.
- *
- * @return The preferred size.
- */
- public Dimension preferredLayoutSize(Container container)
- {
- if (container instanceof JSplitPane)
- {
- JSplitPane split = (JSplitPane) container;
- Insets insets = container.getInsets();
-
- int height = 0;
- int width = 0;
- for (int i = 0; i < components.length; i++)
- {
- if (components[i] == null)
- continue;
- Dimension dims = components[i].getPreferredSize();
- if (dims != null)
- {
- height += dims.height;
- width = Math.max(width, dims.width);
- }
- }
- return new Dimension(width, height);
- }
- return null;
- }
-
- /**
- * This method sets the bounds of the given component. The y coordinate is
- * the location given. The x coordinate is the left inset. The height is
- * the size given. The width is the container size minus the left and
- * right inset.
- *
- * @param c The component to set bounds for.
- * @param size The height.
- * @param location The y coordinate.
- * @param insets The insets to use.
- * @param containerSize The container's size.
- */
- protected void setComponentToSize(Component c, int size, int location,
- Insets insets, Dimension containerSize)
- {
- int y = location;
- int x = insets.left;
- int h = size;
- int w = containerSize.width - insets.left - insets.right;
- c.setBounds(x, y, w, h);
- }
-
- /**
- * This method returns the minimum height of the component at the given
- * index.
- *
- * @param index The index of the component to check.
- *
- * @return The minimum height of the given component.
- */
- int minimumSizeOfComponent(int index)
- {
- Dimension dims = components[index].getMinimumSize();
- if (dims != null)
- return dims.height;
- else
- return 0;
+ super(SwingConstants.VERTICAL);
}
}
@@ -1007,8 +923,10 @@ public class BasicSplitPaneUI extends SplitPaneUI
nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider();
splitPane.add(divider, JSplitPane.DIVIDER);
- // There is no need to add the nonContinuousLayoutDivider
- splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize"));
+ // There is no need to add the nonContinuousLayoutDivider.
+ dividerSize = UIManager.getInt("SplitPane.dividerSize");
+ splitPane.setDividerSize(dividerSize);
+ divider.setDividerSize(dividerSize);
splitPane.setOpaque(true);
}
diff --git a/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/javax/swing/plaf/basic/BasicTabbedPaneUI.java
index 11f25167d..21dcf0d29 100644
--- a/javax/swing/plaf/basic/BasicTabbedPaneUI.java
+++ b/javax/swing/plaf/basic/BasicTabbedPaneUI.java
@@ -76,7 +76,6 @@ import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
-import javax.swing.plaf.PanelUI;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.UIResource;
import javax.swing.text.View;
@@ -252,7 +251,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
{
public void mouseReleased(MouseEvent e)
{
- // Nothing to do here.
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
}
/**
@@ -264,6 +272,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
public void mousePressed(MouseEvent e)
{
Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
int placement = tabPane.getTabPlacement();
if (s == incrButton)
@@ -298,47 +316,61 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
if(!decrButton.isEnabled())
return;
- // The scroll location may be zero but the offset
- // greater than zero because of an adjustement to
- // make a partially visible tab completely visible.
- if (currentScrollLocation > 0)
- currentScrollLocation--;
+ // The scroll location may be zero but the offset
+ // greater than zero because of an adjustement to
+ // make a partially visible tab completely visible.
+ if (currentScrollLocation > 0)
+ currentScrollLocation--;
- // Set the offset back to 0 and recompute it.
- currentScrollOffset = 0;
-
- switch (placement)
- {
- case JTabbedPane.TOP:
- case JTabbedPane.BOTTOM:
- // Take the tab area inset into account.
- if (currentScrollLocation > 0)
- currentScrollOffset = getTabAreaInsets(placement).left;
- // Recompute scroll offset.
- for (int i = 0; i < currentScrollLocation; i++)
- currentScrollOffset += rects[i].width;
- break;
- default:
- // Take the tab area inset into account.
- if (currentScrollLocation > 0)
- currentScrollOffset = getTabAreaInsets(placement).top;
+ // Set the offset back to 0 and recompute it.
+ currentScrollOffset = 0;
+
+ switch (placement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ // Take the tab area inset into account.
+ if (currentScrollLocation > 0)
+ currentScrollOffset = getTabAreaInsets(placement).left;
+ // Recompute scroll offset.
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].width;
+ break;
+ default:
+ // Take the tab area inset into account.
+ if (currentScrollLocation > 0)
+ currentScrollOffset = getTabAreaInsets(placement).top;
+
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].height;
+ }
- for (int i = 0; i < currentScrollLocation; i++)
- currentScrollOffset += rects[i].height;
- }
+ updateViewPosition();
+ updateButtons();
- updateViewPosition();
- updateButtons();
-
- tabPane.repaint();
- } else if (tabPane.isEnabled())
+ tabPane.repaint();
+ }
+ else if (tabPane.isEnabled())
{
int index = tabForCoordinate(tabPane, e.getX(), e.getY());
+ if (!tabPane.isEnabledAt(index))
+ return;
+
if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT
&& s == panel)
+ {
scrollTab(index, placement);
+
+ tabPane.setSelectedIndex(index);
+ tabPane.repaint();
+ }
+ else
+ {
+ tabPane.setSelectedIndex(index);
+ tabPane.revalidate();
+ tabPane.repaint();
+ }
- tabPane.setSelectedIndex(index);
}
}
@@ -347,11 +379,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
* Receives notification when the mouse pointer has entered the tabbed
* pane.
*
- * @param ev the mouse event
+ * @param e the mouse event
*/
- public void mouseEntered(MouseEvent ev)
+ public void mouseEntered(MouseEvent e)
{
- int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY());
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
+ int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
setRolloverTab(tabIndex);
}
@@ -359,10 +402,21 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
* Receives notification when the mouse pointer has exited the tabbed
* pane.
*
- * @param ev the mouse event
+ * @param e the mouse event
*/
- public void mouseExited(MouseEvent ev)
+ public void mouseExited(MouseEvent e)
{
+ Object s = e.getSource();
+
+ // Event may originate from the viewport in
+ // SCROLL_TAB_LAYOUT mode. It is redisptached
+ // through the tabbed pane then.
+ if (tabPane != e.getSource())
+ {
+ redispatchEvent(e);
+ e.setSource(s);
+ }
+
setRolloverTab(-1);
}
@@ -374,9 +428,37 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
*/
public void mouseMoved(MouseEvent ev)
{
+ Object s = ev.getSource();
+
+ if (tabPane != ev.getSource())
+ {
+ ev.setSource(tabPane);
+ tabPane.dispatchEvent(ev);
+
+ ev.setSource(s);
+ }
+
int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY());
setRolloverTab(tabIndex);
}
+
+ /** Modifies the mouse event to originate from
+ * the tabbed pane and redispatches it.
+ *
+ * @param me
+ */
+ void redispatchEvent(MouseEvent me)
+ {
+ me.setSource(tabPane);
+ Point viewPos = viewport.getViewPosition();
+ viewPos.x -= viewport.getX();
+ viewPos.y -= viewport.getY();
+ me.translatePoint(-viewPos.x, -viewPos.y);
+ tabPane.dispatchEvent(me);
+
+ me.translatePoint(viewPos.x, viewPos.y);
+ }
+
}
/**
@@ -396,20 +478,56 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
*/
public void propertyChange(PropertyChangeEvent e)
{
- if (e.getPropertyName().equals("tabLayoutPolicy"))
- {
- currentScrollLocation = currentScrollOffset = 0;
-
- layoutManager = createLayoutManager();
-
- tabPane.setLayout(layoutManager);
- }
- else if (e.getPropertyName().equals("tabPlacement")
- && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ out:
{
- incrButton = createIncreaseButton();
- decrButton = createDecreaseButton();
+ if (e.getPropertyName().equals("tabLayoutPolicy"))
+ {
+ currentScrollLocation = currentScrollOffset = 0;
+
+ layoutManager = createLayoutManager();
+
+ tabPane.setLayout(layoutManager);
+ }
+ else if (e.getPropertyName().equals("tabPlacement")
+ && tabPane.getTabLayoutPolicy()
+ == JTabbedPane.SCROLL_TAB_LAYOUT)
+ {
+ incrButton = createIncreaseButton();
+ decrButton = createDecreaseButton();
+
+ // If the tab placement value was changed of a tabbed pane
+ // in SCROLL_TAB_LAYOUT mode we investigate the change to
+ // implement the following behavior which was observed in
+ // the RI:
+ // The scrolling offset will be reset if we change to
+ // a direction which is orthogonal to the current
+ // direction and stays the same if it is parallel.
+
+ int oldPlacement = ((Integer) e.getOldValue()).intValue();
+ int newPlacement = ((Integer) e.getNewValue()).intValue();
+ switch (newPlacement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ if (oldPlacement == JTabbedPane.TOP
+ || oldPlacement == JTabbedPane.BOTTOM)
+ break out;
+
+ currentScrollOffset = getTabAreaInsets(newPlacement).left;
+ break;
+ default:
+ if (oldPlacement == JTabbedPane.LEFT
+ || oldPlacement == JTabbedPane.RIGHT)
+ break out;
+
+ currentScrollOffset = getTabAreaInsets(newPlacement).top;
+ }
+
+ updateViewPosition();
+ updateButtons();
+ }
}
+
tabPane.revalidate();
tabPane.repaint();
}
@@ -784,6 +902,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
default:
tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount,
maxTabHeight);
+
compX = insets.left + contentBorderInsets.left;
compY = tabAreaHeight + insets.top + contentBorderInsets.top;
}
@@ -838,7 +957,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
int max)
{
- Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
if (tabPlacement == SwingUtilities.TOP
|| tabPlacement == SwingUtilities.BOTTOM)
{
@@ -1325,7 +1443,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
{
super.layoutContainer(pane);
int tabCount = tabPane.getTabCount();
- Point p = null;
if (tabCount == 0)
return;
int tabPlacement = tabPane.getTabPlacement();
@@ -1512,7 +1629,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
*/
public void updateUI()
{
- setUI((PanelUI) new ScrollingPanelUI());
+ setUI(new ScrollingPanelUI());
}
}
@@ -1892,15 +2009,19 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
final void updateViewPosition()
{
Point p = viewport.getViewPosition();
-
+
+ // The unneeded coordinate must be set to zero
+ // in order to correctly handle placement changes.
switch (tabPane.getTabPlacement())
{
case JTabbedPane.LEFT:
case JTabbedPane.RIGHT:
+ p.x = 0;
p.y = currentScrollOffset;
break;
default:
p.x = currentScrollOffset;
+ p.y = 0;
}
viewport.setViewPosition(p);
@@ -2331,7 +2452,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
paintTabBorder(g, tabPlacement, tabIndex, rect.x, rect.y, rect.width,
rect.height, isSelected);
-
// Layout label.
FontMetrics fm = getFontMetrics();
Icon icon = getIconForTab(tabIndex);
@@ -2369,7 +2489,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
Rectangle tabRect, Rectangle iconRect,
Rectangle textRect, boolean isSelected)
{
- SwingUtilities.layoutCompoundLabel(metrics, title, icon,
+ // Reset the icon and text rectangles, as the result is not specified
+ // when the locations are not (0,0).
+ textRect.x = 0;
+ textRect.y = 0;
+ textRect.width = 0;
+ textRect.height = 0;
+ iconRect.x = 0;
+ iconRect.y = 0;
+ iconRect.width = 0;
+ iconRect.height = 0;
+ SwingUtilities.layoutCompoundLabel(tabPane, metrics, title, icon,
SwingConstants.CENTER,
SwingConstants.CENTER,
SwingConstants.CENTER,
@@ -2764,7 +2894,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
int width = tabPane.getWidth();
int height = tabPane.getHeight();
Insets insets = tabPane.getInsets();
- Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
// Calculate coordinates of content area.
int x = insets.left;
@@ -2869,8 +2998,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
int endgap = rects[selectedIndex].y + rects[selectedIndex].height
- currentScrollOffset;
- int diff = 0;
-
if (tabPlacement == SwingConstants.LEFT && startgap >= 0)
{
g.drawLine(x, y, x, startgap);
@@ -2957,8 +3084,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
int endgap = rects[selectedIndex].y + rects[selectedIndex].height
- currentScrollOffset;
- int diff = 0;
-
if (tabPlacement == SwingConstants.RIGHT && startgap >= 0)
{
g.setColor(shadow);
@@ -2988,8 +3113,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
}
/**
- * This method returns the tab bounds for the given index.
- *
+ * <p>This method returns the bounds of a tab for the given index
+ * and shifts it by the current scrolling offset if the tabbed
+ * pane is in scrolling tab layout mode.</p>
+ *
+ * <p>Subclassses should retrievs a tab's bounds by this method
+ * if they want to find out whether the tab is currently visible.</p>
+ *
* @param pane The JTabbedPane.
* @param i The index to look for.
*
@@ -3000,6 +3130,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
// Need to re-layout container if tab does not exist.
if (i >= rects.length)
layoutManager.layoutContainer(pane);
+
+ // Properly shift coordinates if scrolling has taken
+ // place.
+ if (pane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ {
+ Rectangle r = new Rectangle(rects[i]);
+
+ switch(pane.getTabPlacement())
+ {
+ case SwingConstants.TOP:
+ case SwingConstants.BOTTOM:
+ r.x -= currentScrollOffset;
+ break;
+ default:
+ r.y -= currentScrollOffset;
+ }
+
+ return r;
+ }
+
return rects[i];
}
@@ -3048,7 +3198,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
}
/**
- * This method returns the tab bounds in the given rectangle.
+ * <p>This method returns the tab bounds in the given rectangle.</p>
+ *
+ * <p>The returned rectangle will be shifted by the current scroll
+ * offset if the tabbed pane is in scrolling tab layout mode.</p>.
*
* @param tabIndex The index to get bounds for.
* @param dest The rectangle to store bounds in.
@@ -3324,21 +3477,20 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
Icon icon = getIconForTab(tabIndex);
Insets insets = getTabInsets(tabPlacement, tabIndex);
- int width = 0;
+ int width = insets.bottom + insets.right + 3;
if (icon != null)
{
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
- layoutLabel(tabPlacement, getFontMetrics(), tabIndex,
- tabPane.getTitleAt(tabIndex), icon, vr, ir, tr,
- tabIndex == tabPane.getSelectedIndex());
- width = tr.union(ir).width;
+ width += icon.getIconWidth() + textIconGap;
}
- else
- width = metrics.stringWidth(tabPane.getTitleAt(tabIndex));
- width += insets.left + insets.right;
+ View v = getTextViewForTab(tabIndex);
+ if (v != null)
+ width += v.getPreferredSpan(View.X_AXIS);
+ else
+ {
+ String label = tabPane.getTitleAt(tabIndex);
+ width += metrics.stringWidth(label);
+ }
return width;
}
@@ -3377,7 +3529,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
{
Insets insets = getTabAreaInsets(tabPlacement);
int tabAreaHeight = horizRunCount * maxTabHeight
- - (horizRunCount - 1) * tabRunOverlay;
+ - (horizRunCount - 1)
+ * getTabRunOverlay(tabPlacement);
tabAreaHeight += insets.top + insets.bottom;
@@ -3399,7 +3552,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
{
Insets insets = getTabAreaInsets(tabPlacement);
int tabAreaWidth = vertRunCount * maxTabWidth
- - (vertRunCount - 1) * tabRunOverlay;
+ - (vertRunCount - 1)
+ * getTabRunOverlay(tabPlacement);
tabAreaWidth += insets.left + insets.right;
diff --git a/javax/swing/plaf/basic/BasicTableHeaderUI.java b/javax/swing/plaf/basic/BasicTableHeaderUI.java
index abe7cab43..8a8eeb837 100644
--- a/javax/swing/plaf/basic/BasicTableHeaderUI.java
+++ b/javax/swing/plaf/basic/BasicTableHeaderUI.java
@@ -38,8 +38,6 @@ exception statement from your version. */
package javax.swing.plaf.basic;
-import gnu.classpath.NotImplementedException;
-
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
@@ -415,9 +413,8 @@ public class BasicTableHeaderUI extends TableHeaderUI
}
protected void installKeyboardActions()
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ // AFAICS, the RI does nothing here.
}
/**
@@ -448,9 +445,8 @@ public class BasicTableHeaderUI extends TableHeaderUI
}
protected void uninstallKeyboardActions()
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ // AFAICS, the RI does nothing here.
}
/**
diff --git a/javax/swing/plaf/basic/BasicTextUI.java b/javax/swing/plaf/basic/BasicTextUI.java
index 8e9c8c949..dc30347f5 100644
--- a/javax/swing/plaf/basic/BasicTextUI.java
+++ b/javax/swing/plaf/basic/BasicTextUI.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package javax.swing.plaf.basic;
+import gnu.classpath.SystemProperties;
+
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
@@ -71,6 +73,7 @@ import javax.swing.plaf.InputMapUIResource;
import javax.swing.plaf.TextUI;
import javax.swing.plaf.UIResource;
import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.DefaultCaret;
@@ -120,6 +123,140 @@ public abstract class BasicTextUI extends TextUI
}
}
+ private static class FocusHandler
+ implements FocusListener
+ {
+ public void focusGained(FocusEvent e)
+ {
+ // Nothing to do here.
+ }
+ public void focusLost(FocusEvent e)
+ {
+ JTextComponent textComponent = (JTextComponent) e.getComponent();
+ // Integrates Swing text components with the system clipboard:
+ // The idea is that if one wants to copy text around X11-style
+ // (select text and middle-click in the target component) the focus
+ // will move to the new component which gives the old focus owner the
+ // possibility to paste its selection into the clipboard.
+ if (!e.isTemporary()
+ && textComponent.getSelectionStart()
+ != textComponent.getSelectionEnd())
+ {
+ SecurityManager sm = System.getSecurityManager();
+ try
+ {
+ if (sm != null)
+ sm.checkSystemClipboardAccess();
+
+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
+ if (cb != null)
+ {
+ StringSelection selection = new StringSelection(
+ textComponent.getSelectedText());
+ cb.setContents(selection, selection);
+ }
+ }
+ catch (SecurityException se)
+ {
+ // Not allowed to access the clipboard: Ignore and
+ // do not access it.
+ }
+ catch (HeadlessException he)
+ {
+ // There is no AWT: Ignore and do not access the
+ // clipboard.
+ }
+ catch (IllegalStateException ise)
+ {
+ // Clipboard is currently unavaible.
+ }
+ }
+ }
+ }
+
+ /**
+ * This FocusListener triggers repaints on focus shift.
+ */
+ private static FocusListener focusListener;
+
+ /**
+ * Receives notifications when properties of the text component change.
+ */
+ private class Handler
+ implements PropertyChangeListener, DocumentListener
+ {
+ /**
+ * Notifies when a property of the text component changes.
+ *
+ * @param event the PropertyChangeEvent describing the change
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getPropertyName().equals("document"))
+ {
+ // Document changed.
+ Object oldValue = event.getOldValue();
+ if (oldValue != null)
+ {
+ Document oldDoc = (Document) oldValue;
+ oldDoc.removeDocumentListener(handler);
+ }
+ Object newValue = event.getNewValue();
+ if (newValue != null)
+ {
+ Document newDoc = (Document) newValue;
+ newDoc.addDocumentListener(handler);
+ }
+ modelChanged();
+ }
+
+ BasicTextUI.this.propertyChange(event);
+ }
+
+ /**
+ * Notification about a document change event.
+ *
+ * @param ev the DocumentEvent describing the change
+ */
+ public void changedUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.changedUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ /**
+ * Notification about a document insert event.
+ *
+ * @param ev the DocumentEvent describing the insertion
+ */
+ public void insertUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.insertUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ /**
+ * Notification about a document removal event.
+ *
+ * @param ev the DocumentEvent describing the removal
+ */
+ public void removeUpdate(DocumentEvent ev)
+ {
+ // Updates are forwarded to the View even if 'getVisibleEditorRect'
+ // method returns null. This means the View classes have to be
+ // aware of that possibility.
+ rootView.removeUpdate(ev, getVisibleEditorRect(),
+ rootView.getViewFactory());
+ }
+
+ }
+
/**
* This view forms the root of the View hierarchy. However, it delegates
* most calls to another View which is the real root of the hierarchy.
@@ -225,20 +362,10 @@ public abstract class BasicTextUI extends TextUI
return textComponent;
}
- /**
- * Returns the preferred span along the specified <code>axis</code>.
- * This is delegated to the real root view.
- *
- * @param axis the axis for which the preferred span is queried
- *
- * @return the preferred span along the axis
- */
- public float getPreferredSpan(int axis)
+ public void setSize(float w, float h)
{
if (view != null)
- return view.getPreferredSpan(axis);
-
- return Integer.MAX_VALUE;
+ view.setSize(w, h);
}
/**
@@ -312,7 +439,8 @@ public abstract class BasicTextUI extends TextUI
*/
public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
- view.insertUpdate(ev, shape, vf);
+ if (view != null)
+ view.insertUpdate(ev, shape, vf);
}
/**
@@ -325,7 +453,8 @@ public abstract class BasicTextUI extends TextUI
*/
public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
- view.removeUpdate(ev, shape, vf);
+ if (view != null)
+ view.removeUpdate(ev, shape, vf);
}
/**
@@ -338,7 +467,8 @@ public abstract class BasicTextUI extends TextUI
*/
public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
{
- view.changedUpdate(ev, shape, vf);
+ if (view != null)
+ view.changedUpdate(ev, shape, vf);
}
/**
@@ -400,116 +530,73 @@ public abstract class BasicTextUI extends TextUI
{
return textComponent.getDocument();
}
- }
- /**
- * Receives notifications when properties of the text component change.
- */
- private class PropertyChangeHandler implements PropertyChangeListener
- {
/**
- * Notifies when a property of the text component changes.
- *
- * @param event the PropertyChangeEvent describing the change
+ * Returns the attributes, which is null for the RootView.
*/
- public void propertyChange(PropertyChangeEvent event)
+ public AttributeSet getAttributes()
{
- if (event.getPropertyName().equals("document"))
- {
- // Document changed.
- Object oldValue = event.getOldValue();
- if (oldValue != null)
- {
- Document oldDoc = (Document) oldValue;
- oldDoc.removeDocumentListener(documentHandler);
- }
- Object newValue = event.getNewValue();
- if (newValue != null)
- {
- Document newDoc = (Document) newValue;
- newDoc.addDocumentListener(documentHandler);
- }
- modelChanged();
- }
-
- BasicTextUI.this.propertyChange(event);
+ return null;
}
- }
- /**
- * Listens for changes on the underlying model and forwards notifications
- * to the View. This also updates the caret position of the text component.
- *
- * 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
+ * Overridden to forward to the view.
*/
- public void changedUpdate(DocumentEvent ev)
+ public float getPreferredSpan(int axis)
{
- // Updates are forwarded to the View even if 'getVisibleEditorRect'
- // method returns null. This means the View classes have to be
- // aware of that possibility.
- rootView.changedUpdate(ev, getVisibleEditorRect(),
- rootView.getViewFactory());
+ // The RI returns 10 in the degenerate case.
+ float span = 10;
+ if (view != null)
+ span = view.getPreferredSpan(axis);
+ return span;
}
/**
- * Notification about a document insert event.
- *
- * @param ev the DocumentEvent describing the insertion
+ * Overridden to forward to the real view.
*/
- public void insertUpdate(DocumentEvent ev)
+ public float getMinimumSpan(int axis)
{
- // Updates are forwarded to the View even if 'getVisibleEditorRect'
- // method returns null. This means the View classes have to be
- // aware of that possibility.
- rootView.insertUpdate(ev, getVisibleEditorRect(),
- rootView.getViewFactory());
+ // The RI returns 10 in the degenerate case.
+ float span = 10;
+ if (view != null)
+ span = view.getMinimumSpan(axis);
+ return span;
}
/**
- * Notification about a document removal event.
- *
- * @param ev the DocumentEvent describing the removal
+ * Overridden to return Integer.MAX_VALUE.
*/
- public void removeUpdate(DocumentEvent ev)
+ public float getMaximumSpan(int axis)
{
- // Updates are forwarded to the View even if 'getVisibleEditorRect'
- // method returns null. This means the View classes have to be
- // aware of that possibility.
- rootView.removeUpdate(ev, getVisibleEditorRect(),
- rootView.getViewFactory());
+ // The RI returns Integer.MAX_VALUE here, regardless of the real view's
+ // maximum size.
+ return Integer.MAX_VALUE;
}
}
/**
* The EditorKit used by this TextUI.
*/
- // FIXME: should probably be non-static.
- static EditorKit kit = new DefaultEditorKit();
+ private static EditorKit kit;
/**
- * The root view.
+ * The combined event handler for text components.
+ *
+ * This is package private to avoid accessor methods.
*/
- RootView rootView = new RootView();
+ Handler handler;
/**
- * The text component that we handle.
+ * The root view.
+ *
+ * This is package private to avoid accessor methods.
*/
- JTextComponent textComponent;
+ RootView rootView;
/**
- * Receives notification when the model changes.
+ * The text component that we handle.
*/
- private PropertyChangeHandler updateHandler = new PropertyChangeHandler();
-
- /** The DocumentEvent handler. */
- DocumentHandler documentHandler = new DocumentHandler();
+ JTextComponent textComponent;
/**
* Creates a new <code>BasicTextUI</code> instance.
@@ -558,17 +645,31 @@ public abstract class BasicTextUI extends TextUI
public void installUI(final JComponent c)
{
textComponent = (JTextComponent) c;
+
+ if (rootView == null)
+ rootView = new RootView();
+
installDefaults();
- textComponent.addPropertyChangeListener(updateHandler);
+ installFixedDefaults();
+
+ // These listeners must be installed outside of installListeners(),
+ // because overriding installListeners() doesn't prevent installing
+ // these in the RI, but overriding isntallUI() does.
+ if (handler == null)
+ handler = new Handler();
+ textComponent.addPropertyChangeListener(handler);
Document doc = textComponent.getDocument();
if (doc == null)
{
+ // The Handler takes care of installing the necessary listeners
+ // on the document here.
doc = getEditorKit(textComponent).createDefaultDocument();
textComponent.setDocument(doc);
}
else
{
- doc.addDocumentListener(documentHandler);
+ // Must install the document listener.
+ doc.addDocumentListener(handler);
modelChanged();
}
@@ -586,7 +687,6 @@ public abstract class BasicTextUI extends TextUI
LookAndFeel.installColorsAndFont(textComponent, prefix + ".background",
prefix + ".foreground", prefix + ".font");
LookAndFeel.installBorder(textComponent, prefix + ".border");
- textComponent.setMargin(UIManager.getInsets(prefix + ".margin"));
// Some additional text component only properties.
Color color = textComponent.getCaretColor();
@@ -600,7 +700,7 @@ public abstract class BasicTextUI extends TextUI
color = textComponent.getDisabledTextColor();
if (color == null || color instanceof UIResource)
{
- color = UIManager.getColor(prefix + ".inactiveBackground");
+ color = UIManager.getColor(prefix + ".inactiveForeground");
textComponent.setDisabledTextColor(color);
}
color = textComponent.getSelectedTextColor();
@@ -623,6 +723,15 @@ public abstract class BasicTextUI extends TextUI
textComponent.setMargin(margin);
}
+ }
+
+ /**
+ * Installs defaults that can't be overridden by overriding
+ * installDefaults().
+ */
+ private void installFixedDefaults()
+ {
+ String prefix = getPropertyPrefix();
Caret caret = textComponent.getCaret();
if (caret == null || caret instanceof UIResource)
{
@@ -638,64 +747,18 @@ public abstract class BasicTextUI extends TextUI
}
/**
- * This FocusListener triggers repaints on focus shift.
- */
- private FocusListener focuslistener = new FocusListener() {
- public void focusGained(FocusEvent e)
- {
- textComponent.repaint();
- }
- public void focusLost(FocusEvent e)
- {
- textComponent.repaint();
-
- // Integrates Swing text components with the system clipboard:
- // The idea is that if one wants to copy text around X11-style
- // (select text and middle-click in the target component) the focus
- // will move to the new component which gives the old focus owner the
- // possibility to paste its selection into the clipboard.
- if (!e.isTemporary()
- && textComponent.getSelectionStart()
- != textComponent.getSelectionEnd())
- {
- SecurityManager sm = System.getSecurityManager();
- try
- {
- if (sm != null)
- sm.checkSystemClipboardAccess();
-
- Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
- if (cb != null)
- {
- StringSelection selection = new StringSelection(
- textComponent.getSelectedText());
- cb.setContents(selection, selection);
- }
- }
- catch (SecurityException se)
- {
- // Not allowed to access the clipboard: Ignore and
- // do not access it.
- }
- catch (HeadlessException he)
- {
- // There is no AWT: Ignore and do not access the
- // clipboard.
- }
- catch (IllegalStateException ise)
- {
- // Clipboard is currently unavaible.
- }
- }
- }
- };
-
- /**
* Install all listeners on the text component.
*/
protected void installListeners()
{
- textComponent.addFocusListener(focuslistener);
+ //
+ if (SystemProperties.getProperty("gnu.swing.text.no-xlike-clipboard")
+ == null)
+ {
+ if (focusListener == null)
+ focusListener = new FocusHandler();
+ textComponent.addFocusListener(focusListener);
+ }
}
/**
@@ -834,10 +897,12 @@ public abstract class BasicTextUI extends TextUI
*/
public void uninstallUI(final JComponent component)
{
- super.uninstallUI(component);
+ textComponent.removePropertyChangeListener(handler);
+ textComponent.getDocument().removeDocumentListener(handler);
rootView.setView(null);
uninstallDefaults();
+ uninstallFixedDefaults();
uninstallListeners();
uninstallKeyboardActions();
@@ -850,7 +915,29 @@ public abstract class BasicTextUI extends TextUI
*/
protected void uninstallDefaults()
{
- // Do nothing here.
+ if (textComponent.getCaretColor() instanceof UIResource)
+ textComponent.setCaretColor(null);
+ if (textComponent.getSelectionColor() instanceof UIResource)
+ textComponent.setSelectionColor(null);
+ if (textComponent.getDisabledTextColor() instanceof UIResource)
+ textComponent.setDisabledTextColor(null);
+ if (textComponent.getSelectedTextColor() instanceof UIResource)
+ textComponent.setSelectedTextColor(null);
+ LookAndFeel.uninstallBorder(textComponent);
+ if (textComponent.getMargin() instanceof UIResource)
+ textComponent.setMargin(null);
+ }
+
+ /**
+ * Uninstalls additional fixed defaults that were installed
+ * by installFixedDefaults().
+ */
+ private void uninstallFixedDefaults()
+ {
+ if (textComponent.getCaret() instanceof UIResource)
+ textComponent.setCaret(null);
+ if (textComponent.getHighlighter() instanceof UIResource)
+ textComponent.setHighlighter(null);
}
/**
@@ -859,7 +946,10 @@ public abstract class BasicTextUI extends TextUI
*/
protected void uninstallListeners()
{
- textComponent.removeFocusListener(focuslistener);
+ // Don't nullify the focusListener field, as it is static and shared
+ // between components.
+ if (focusListener != null)
+ textComponent.removeFocusListener(focusListener);
}
/**
@@ -891,14 +981,33 @@ public abstract class BasicTextUI extends TextUI
*/
public Dimension getPreferredSize(JComponent c)
{
- View v = getRootView(textComponent);
-
- float w = v.getPreferredSpan(View.X_AXIS);
- float h = v.getPreferredSpan(View.Y_AXIS);
-
+ Dimension d = c.getSize();
Insets i = c.getInsets();
- return new Dimension((int) w + i.left + i.right,
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ float w;
+ float h;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ if (d.width > (i.left + i.right) && d.height > (i.top + i.bottom))
+ {
+ rootView.setSize(d.width - i.left - i.right,
+ d.height - i.top - i.bottom);
+ }
+ w = rootView.getPreferredSpan(View.X_AXIS);
+ h = rootView.getPreferredSpan(View.Y_AXIS);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ Dimension size = new Dimension((int) w + i.left + i.right,
(int) h + i.top + i.bottom);
+ return size;
}
/**
@@ -912,8 +1021,27 @@ public abstract class BasicTextUI extends TextUI
*/
public Dimension getMaximumSize(JComponent c)
{
- // Sun's implementation returns Integer.MAX_VALUE here, so do we.
- return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ Dimension d = new Dimension();
+ Insets i = c.getInsets();
+ Document doc = textComponent.getDocument();
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ // Check for overflow here.
+ d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS)
+ + i.left + i.right, Integer.MAX_VALUE);
+ d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS)
+ + i.top + i.bottom, Integer.MAX_VALUE);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return d;
}
/**
@@ -924,8 +1052,26 @@ public abstract class BasicTextUI extends TextUI
*/
public Dimension getMinimumSize(JComponent c)
{
+ Dimension d = new Dimension();
+ Document doc = textComponent.getDocument();
+ // We need to lock here, since we require the view hierarchy to _not_
+ // change in between.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ d.width = (int) rootView.getMinimumSpan(View.X_AXIS);
+ d.height = (int) rootView.getMinimumSpan(View.Y_AXIS);
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
Insets i = c.getInsets();
- return new Dimension(i.left + i.right, i.top + i.bottom);
+ d.width += i.left + i.right;
+ d.height += i.top + i.bottom;
+ return d;
}
/**
@@ -996,7 +1142,6 @@ public abstract class BasicTextUI extends TextUI
g.setColor(oldColor);
}
-
rootView.paint(g, getVisibleEditorRect());
if (caret != null && textComponent.hasFocus())
@@ -1104,6 +1249,8 @@ public abstract class BasicTextUI extends TextUI
*/
public EditorKit getEditorKit(JTextComponent t)
{
+ if (kit == null)
+ kit = new DefaultEditorKit();
return kit;
}
@@ -1224,7 +1371,7 @@ public abstract class BasicTextUI extends TextUI
*/
public int viewToModel(JTextComponent t, Point pt)
{
- return viewToModel(t, pt, null);
+ return viewToModel(t, pt, new Position.Bias[1]);
}
/**
diff --git a/javax/swing/plaf/basic/BasicToolTipUI.java b/javax/swing/plaf/basic/BasicToolTipUI.java
index 5cec2e333..94e7bc322 100644
--- a/javax/swing/plaf/basic/BasicToolTipUI.java
+++ b/javax/swing/plaf/basic/BasicToolTipUI.java
@@ -40,19 +40,20 @@ package javax.swing.plaf.basic;
import java.awt.Color;
import java.awt.Dimension;
+import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
-import java.awt.Toolkit;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import javax.swing.JToolTip;
import javax.swing.LookAndFeel;
-import javax.swing.SwingConstants;
-import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.ToolTipUI;
+import javax.swing.text.View;
/**
* This is the Basic Look and Feel UI class for JToolTip.
@@ -60,6 +61,28 @@ import javax.swing.plaf.ToolTipUI;
public class BasicToolTipUI extends ToolTipUI
{
+ /**
+ * Receives notification when a property of the JToolTip changes.
+ * This updates the HTML renderer if appropriate.
+ */
+ private class PropertyChangeHandler
+ implements PropertyChangeListener
+ {
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String prop = e.getPropertyName();
+ if (prop.equals("tiptext") || prop.equals("font")
+ || prop.equals("foreground"))
+ {
+ JToolTip tip = (JToolTip) e.getSource();
+ String text = tip.getTipText();
+ BasicHTML.updateRenderer(tip, text);
+ }
+ }
+
+ }
+
/** The shared instance of BasicToolTipUI used for all ToolTips. */
private static BasicToolTipUI shared;
@@ -67,6 +90,11 @@ public class BasicToolTipUI extends ToolTipUI
private String text;
/**
+ * Handles property changes.
+ */
+ private PropertyChangeListener propertyChangeHandler;
+
+ /**
* Creates a new BasicToolTipUI object.
*/
public BasicToolTipUI()
@@ -98,7 +126,12 @@ public class BasicToolTipUI extends ToolTipUI
*/
public Dimension getMaximumSize(JComponent c)
{
- return getPreferredSize(c);
+ Dimension d = getPreferredSize(c);
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ d.width += view.getMaximumSpan(View.X_AXIS)
+ - view.getPreferredSpan(View.X_AXIS);
+ return d;
}
/**
@@ -110,7 +143,12 @@ public class BasicToolTipUI extends ToolTipUI
*/
public Dimension getMinimumSize(JComponent c)
{
- return getPreferredSize(c);
+ Dimension d = getPreferredSize(c);
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ d.width -= view.getPreferredSpan(View.X_AXIS)
+ - view.getMinimumSpan(View.X_AXIS);
+ return d;
}
/**
@@ -123,22 +161,25 @@ public class BasicToolTipUI extends ToolTipUI
public Dimension getPreferredSize(JComponent c)
{
JToolTip tip = (JToolTip) c;
- FontMetrics fm;
- Toolkit g = tip.getToolkit();
- text = tip.getTipText();
-
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
- Insets insets = tip.getInsets();
- fm = g.getFontMetrics(tip.getFont());
- SwingUtilities.layoutCompoundLabel(tip, fm, text, null,
- SwingConstants.CENTER,
- SwingConstants.CENTER,
- SwingConstants.CENTER,
- SwingConstants.CENTER, vr, ir, tr, 0);
- return new Dimension(insets.left + tr.width + insets.right,
- insets.top + tr.height + insets.bottom);
+ String str = tip.getTipText();
+ FontMetrics fm = c.getFontMetrics(c.getFont());
+ Insets i = c.getInsets();
+ Dimension d = new Dimension(i.left + i.right, i.top + i.bottom);
+ if (str != null && ! str.equals(""))
+ {
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ d.width += (int) view.getPreferredSpan(View.X_AXIS);
+ d.height += (int) view.getPreferredSpan(View.Y_AXIS);
+ }
+ else
+ {
+ d.width += fm.stringWidth(str) + 6;
+ d.height += fm.getHeight();
+ }
+ }
+ return d;
}
/**
@@ -160,7 +201,8 @@ public class BasicToolTipUI extends ToolTipUI
*/
protected void installListeners(JComponent c)
{
- // TODO: Implement this properly.
+ propertyChangeHandler = new PropertyChangeHandler();
+ c.addPropertyChangeListener(propertyChangeHandler);
}
/**
@@ -172,6 +214,7 @@ public class BasicToolTipUI extends ToolTipUI
{
c.setOpaque(true);
installDefaults(c);
+ BasicHTML.updateRenderer(c, ((JToolTip) c).getTipText());
installListeners(c);
}
@@ -186,26 +229,25 @@ public class BasicToolTipUI extends ToolTipUI
JToolTip tip = (JToolTip) c;
String text = tip.getTipText();
- Toolkit t = tip.getToolkit();
- if (text == null)
- return;
-
- Rectangle vr = new Rectangle();
- vr = SwingUtilities.calculateInnerArea(tip, vr);
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
- FontMetrics fm = t.getFontMetrics(tip.getFont());
+ Font font = c.getFont();
+ FontMetrics fm = c.getFontMetrics(font);
int ascent = fm.getAscent();
- SwingUtilities.layoutCompoundLabel(tip, fm, text, null,
- SwingConstants.CENTER,
- SwingConstants.CENTER,
- SwingConstants.CENTER,
- SwingConstants.CENTER, vr, ir, tr, 0);
+ Insets i = c.getInsets();
+ Dimension size = c.getSize();
+ Rectangle paintR = new Rectangle(i.left, i.top,
+ size.width - i.left - i.right,
+ size.height - i.top - i.bottom);
Color saved = g.getColor();
+ Font oldFont = g.getFont();
g.setColor(Color.BLACK);
- g.drawString(text, vr.x, vr.y + ascent);
+ View view = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ view.paint(g, paintR);
+ else
+ g.drawString(text, paintR.x + 3, paintR.y + ascent);
+ g.setFont(oldFont);
g.setColor(saved);
}
@@ -229,7 +271,11 @@ public class BasicToolTipUI extends ToolTipUI
*/
protected void uninstallListeners(JComponent c)
{
- // TODO: Implement this properly.
+ if (propertyChangeHandler != null)
+ {
+ c.removePropertyChangeListener(propertyChangeHandler);
+ propertyChangeHandler = null;
+ }
}
/**
@@ -240,6 +286,7 @@ public class BasicToolTipUI extends ToolTipUI
public void uninstallUI(JComponent c)
{
uninstallDefaults(c);
+ BasicHTML.updateRenderer(c, "");
uninstallListeners(c);
}
}
diff --git a/javax/swing/plaf/basic/BasicTreeUI.java b/javax/swing/plaf/basic/BasicTreeUI.java
index f61824bb7..ef46efdd2 100644
--- a/javax/swing/plaf/basic/BasicTreeUI.java
+++ b/javax/swing/plaf/basic/BasicTreeUI.java
@@ -38,17 +38,16 @@
package javax.swing.plaf.basic;
-import gnu.classpath.NotImplementedException;
import gnu.javax.swing.tree.GnuPath;
import java.awt.Color;
import java.awt.Component;
+import java.awt.Container;
import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Label;
+import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -275,13 +274,6 @@ public class BasicTreeUI
TreeModelListener treeModelListener;
/**
- * This timer fires the editing action after about 1200 ms if not reset during
- * that time. It handles the editing start with the single mouse click (and
- * not the double mouse click) on the selected tree node.
- */
- Timer startEditTimer;
-
- /**
* The zero size icon, used for expand controls, if they are not visible.
*/
static Icon nullIcon;
@@ -428,6 +420,7 @@ public class BasicTreeUI
{
if (largeModel != this.largeModel)
{
+ completeEditing();
tree.removeComponentListener(componentListener);
this.largeModel = largeModel;
tree.addComponentListener(componentListener);
@@ -451,6 +444,7 @@ public class BasicTreeUI
*/
protected void setRowHeight(int rowHeight)
{
+ completeEditing();
if (rowHeight == 0)
rowHeight = getMaxHeight(tree);
treeState.setRowHeight(rowHeight);
@@ -544,6 +538,7 @@ public class BasicTreeUI
*/
protected void setRootVisible(boolean newValue)
{
+ completeEditing();
tree.setRootVisible(newValue);
}
@@ -590,8 +585,7 @@ public class BasicTreeUI
*/
protected void setCellEditor(TreeCellEditor editor)
{
- cellEditor = editor;
- createdCellEditor = true;
+ updateCellEditor();
}
/**
@@ -611,7 +605,7 @@ public class BasicTreeUI
*/
protected void setEditable(boolean newValue)
{
- tree.setEditable(newValue);
+ updateCellEditor();
}
/**
@@ -632,6 +626,7 @@ public class BasicTreeUI
*/
protected void setSelectionModel(TreeSelectionModel newLSM)
{
+ completeEditing();
if (newLSM != null)
{
treeSelectionModel = newLSM;
@@ -787,12 +782,13 @@ public class BasicTreeUI
*/
public boolean stopEditing(JTree tree)
{
- if (isEditing(tree))
+ boolean ret = false;
+ if (editingComponent != null && cellEditor.stopCellEditing())
{
completeEditing(false, false, true);
- finish();
+ ret = true;
}
- return ! isEditing(tree);
+ return ret;
}
/**
@@ -805,8 +801,8 @@ public class BasicTreeUI
// There is no need to send the cancel message to the editor,
// as the cancellation event itself arrives from it. This would
// only be necessary when cancelling the editing programatically.
- completeEditing(false, false, false);
- finish();
+ if (editingComponent != null)
+ completeEditing(false, true, false);
}
/**
@@ -818,7 +814,9 @@ public class BasicTreeUI
*/
public void startEditingAtPath(JTree tree, TreePath path)
{
- startEditing(path, null);
+ tree.scrollPathToVisible(path);
+ if (path != null && tree.isVisible(path))
+ startEditing(path, null);
}
/**
@@ -842,6 +840,7 @@ public class BasicTreeUI
preferredSize = new Dimension();
largeModel = tree.isLargeModel();
preferredSize = new Dimension();
+ stopEditingInCompleteEditing = true;
setModel(tree.getModel());
}
@@ -1136,6 +1135,7 @@ public class BasicTreeUI
*/
protected void updateExpandedDescendants(TreePath path)
{
+ completeEditing();
Enumeration expanded = tree.getExpandedDescendants(path);
while (expanded.hasMoreElements())
treeState.setExpandedState((TreePath) expanded.nextElement(), true);
@@ -1167,9 +1167,33 @@ public class BasicTreeUI
*/
protected void updateCellEditor()
{
- if (tree.isEditable() && cellEditor == null)
- setCellEditor(createDefaultCellEditor());
- createdCellEditor = true;
+ completeEditing();
+ TreeCellEditor newEd = null;
+ if (tree != null && tree.isEditable())
+ {
+ newEd = tree.getCellEditor();
+ if (newEd == null)
+ {
+ newEd = createDefaultCellEditor();
+ if (newEd != null)
+ {
+ tree.setCellEditor(newEd);
+ createdCellEditor = true;
+ }
+ }
+ }
+ // Update listeners.
+ if (newEd != cellEditor)
+ {
+ if (cellEditor != null && cellEditorListener != null)
+ cellEditor.removeCellEditorListener(cellEditorListener);
+ cellEditor = newEd;
+ if (cellEditorListener == null)
+ cellEditorListener = createCellEditorListener();
+ if (cellEditor != null && cellEditorListener != null)
+ cellEditor.addCellEditorListener(cellEditorListener);
+ createdCellEditor = false;
+ }
}
/**
@@ -1719,6 +1743,10 @@ public class BasicTreeUI
*/
protected void completeEditing()
{
+ if (tree.getInvokesStopCellEditing() && stopEditingInCompleteEditing
+ && editingComponent != null)
+ cellEditor.stopCellEditing();
+
completeEditing(false, true, false);
}
@@ -1736,28 +1764,35 @@ public class BasicTreeUI
boolean messageTree)
{
// Make no attempt to complete the non existing editing session.
- if (!isEditing(tree))
- return;
-
- if (messageStop)
- {
- getCellEditor().stopCellEditing();
- stopEditingInCompleteEditing = true;
- }
-
- if (messageCancel)
+ if (stopEditingInCompleteEditing && editingComponent != null)
{
- getCellEditor().cancelCellEditing();
- stopEditingInCompleteEditing = true;
- }
+ Component comp = editingComponent;
+ TreePath p = editingPath;
+ editingComponent = null;
+ editingPath = null;
+ if (messageStop)
+ cellEditor.stopCellEditing();
+ else if (messageCancel)
+ cellEditor.cancelCellEditing();
+
+ tree.remove(comp);
+
+ if (editorHasDifferentSize)
+ {
+ treeState.invalidatePathBounds(p);
+ updateSize();
+ }
+ else
+ {
+ // Need to refresh the tree.
+ Rectangle b = getPathBounds(tree, p);
+ tree.repaint(0, b.y, tree.getWidth(), b.height);
+ }
- if (messageTree)
- {
- TreeCellEditor editor = getCellEditor();
- if (editor != null)
+ if (messageTree)
{
- Object value = editor.getCellEditorValue();
- treeModel.valueForPathChanged(tree.getLeadSelectionPath(), value);
+ Object value = cellEditor.getCellEditorValue();
+ treeModel.valueForPathChanged(p, value);
}
}
}
@@ -1772,47 +1807,105 @@ public class BasicTreeUI
*/
protected boolean startEditing(TreePath path, MouseEvent event)
{
- updateCellEditor();
- TreeCellEditor ed = getCellEditor();
+ // Maybe cancel editing.
+ if (isEditing(tree) && tree.getInvokesStopCellEditing()
+ && ! stopEditing(tree))
+ return false;
- if (ed != null && (event == EDIT || ed.shouldSelectCell(event))
- && ed.isCellEditable(event))
+ completeEditing();
+ TreeCellEditor ed = cellEditor;
+ if (ed != null && tree.isPathEditable(path))
{
- Rectangle bounds = getPathBounds(tree, path);
-
- // Extend the right boundary till the tree width.
- bounds.width = tree.getWidth() - bounds.x;
-
- editingPath = path;
- editingRow = tree.getRowForPath(editingPath);
-
- Object value = editingPath.getLastPathComponent();
-
- stopEditingInCompleteEditing = false;
- boolean expanded = tree.isExpanded(editingPath);
- isEditing = true;
- editingComponent = ed.getTreeCellEditorComponent(tree, value, true,
- expanded,
- isLeaf(editingRow),
- editingRow);
-
- // Remove all previous components (if still present). Only one
- // container with the editing component inside is allowed in the tree.
- tree.removeAll();
-
- // The editing component must be added to its container. We add the
- // container, not the editing component itself.
- Component container = editingComponent.getParent();
- container.setBounds(bounds);
- tree.add(container);
- editingComponent.requestFocus();
+ if (ed.isCellEditable(event))
+ {
+ editingRow = getRowForPath(tree, path);
+ Object value = path.getLastPathComponent();
+ boolean isSelected = tree.isPathSelected(path);
+ boolean isExpanded = tree.isExpanded(editingPath);
+ boolean isLeaf = treeModel.isLeaf(value);
+ editingComponent = ed.getTreeCellEditorComponent(tree, value,
+ isSelected,
+ isExpanded,
+ isLeaf,
+ editingRow);
+
+ Rectangle bounds = getPathBounds(tree, path);
+
+ Dimension size = editingComponent.getPreferredSize();
+ int rowHeight = getRowHeight();
+ if (size.height != bounds.height && rowHeight > 0)
+ size.height = rowHeight;
+
+ if (size.width != bounds.width || size.height != bounds.height)
+ {
+ editorHasDifferentSize = true;
+ treeState.invalidatePathBounds(path);
+ updateSize();
+ }
+ else
+ editorHasDifferentSize = false;
+
+ // The editing component must be added to its container. We add the
+ // container, not the editing component itself.
+ tree.add(editingComponent);
+ editingComponent.setBounds(bounds.x, bounds.y, size.width,
+ size.height);
+ editingComponent.validate();
+ editingPath = path;
+
+ if (ed.shouldSelectCell(event))
+ {
+ stopEditingInCompleteEditing = false;
+ tree.setSelectionRow(editingRow);
+ stopEditingInCompleteEditing = true;
+ }
+
+ editorRequestFocus(editingComponent);
+ // Register MouseInputHandler to redispatch initial mouse events
+ // correctly.
+ if (event instanceof MouseEvent)
+ {
+ Point p = SwingUtilities.convertPoint(tree, event.getX(), event.getY(),
+ editingComponent);
+ Component active =
+ SwingUtilities.getDeepestComponentAt(editingComponent, p.x, p.y);
+ if (active != null)
+ {
+ MouseInputHandler ih = new MouseInputHandler(tree, active, event);
+
+ }
+ }
- return true;
+ return true;
+ }
+ else
+ editingComponent = null;
}
return false;
}
/**
+ * Requests focus on the editor. The method is necessary since the
+ * DefaultTreeCellEditor returns a container that contains the
+ * actual editor, and we want to request focus on the editor, not the
+ * container.
+ */
+ private void editorRequestFocus(Component c)
+ {
+ if (c instanceof Container)
+ {
+ // TODO: Maybe do something more reasonable here, like queriying the
+ // FocusTraversalPolicy.
+ Container cont = (Container) c;
+ if (cont.getComponentCount() > 0)
+ cont.getComponent(0).requestFocus();
+ }
+ else if (c.isFocusable())
+ c.requestFocus();
+
+ }
+
+ /**
* If the <code>mouseX</code> and <code>mouseY</code> are in the expand or
* collapse region of the row, this will toggle the row.
*
@@ -2180,7 +2273,7 @@ public class BasicTreeUI
*/
public void editingStopped(ChangeEvent e)
{
- stopEditing(tree);
+ completeEditing(false, false, true);
}
/**
@@ -2191,7 +2284,7 @@ public class BasicTreeUI
*/
public void editingCanceled(ChangeEvent e)
{
- cancelEditing(tree);
+ completeEditing(false, false, false);
}
} // CellEditorHandler
@@ -2347,9 +2440,15 @@ public class BasicTreeUI
* events.
*/
public class MouseHandler
- extends MouseAdapter
- implements MouseMotionListener
+ extends MouseAdapter
+ implements MouseMotionListener
{
+
+ /**
+ * If the cell has been selected on mouse press.
+ */
+ private boolean selectedOnPress;
+
/**
* Constructor
*/
@@ -2365,76 +2464,15 @@ public class BasicTreeUI
*/
public void mousePressed(MouseEvent e)
{
- // Any mouse click cancels the previous waiting edit action, initiated
- // by the single click on the selected node.
- if (startEditTimer != null)
+ if (! e.isConsumed())
{
- startEditTimer.stop();
- startEditTimer = null;
+ handleEvent(e);
+ selectedOnPress = true;
}
-
- if (tree != null && tree.isEnabled())
+ else
{
- // Always end the current editing session if clicked on the
- // tree and outside the bounds of the editing component.
- if (isEditing(tree))
- if (!stopEditing(tree))
- // Return if we have failed to cancel the editing session.
- return;
-
- int x = e.getX();
- int y = e.getY();
- TreePath path = getClosestPathForLocation(tree, x, y);
-
- if (path != null)
- {
- Rectangle bounds = getPathBounds(tree, path);
- if (SwingUtilities.isLeftMouseButton(e))
- checkForClickInExpandControl(path, x, y);
-
- if (x > bounds.x && x <= (bounds.x + bounds.width))
- {
- TreePath currentLead = tree.getLeadSelectionPath();
- if (currentLead != null && currentLead.equals(path)
- && e.getClickCount() == 1 && tree.isEditable())
- {
- // Schedule the editing session.
- final TreePath editPath = path;
-
- // The code below handles the required click-pause-click
- // functionality which must be present in the tree UI.
- // If the next click comes after the
- // time longer than the double click interval AND
- // the same node stays focused for the WAIT_TILL_EDITING
- // duration, the timer starts the editing session.
- if (startEditTimer != null)
- startEditTimer.stop();
-
- startEditTimer = new Timer(WAIT_TILL_EDITING,
- new ActionListener()
- {
- public void actionPerformed(ActionEvent e)
- {
- startEditing(editPath, EDIT);
- }
- });
-
- startEditTimer.setRepeats(false);
- startEditTimer.start();
- }
- else
- {
- if (e.getClickCount() == 2)
- toggleExpandState(path);
- else
- selectPathForEvent(path, e);
- }
- }
- }
+ selectedOnPress = false;
}
-
- // We need to request the focus.
- tree.requestFocusInWindow();
}
/**
@@ -2446,9 +2484,8 @@ public class BasicTreeUI
* @param e is the mouse event that occured
*/
public void mouseDragged(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
/**
@@ -2458,9 +2495,8 @@ public class BasicTreeUI
* @param e the mouse event that occured
*/
public void mouseMoved(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
/**
@@ -2469,9 +2505,46 @@ public class BasicTreeUI
* @param e is the mouse event that occured
*/
public void mouseReleased(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ if (! e.isConsumed() && ! selectedOnPress)
+ handleEvent(e);
+ }
+
+ /**
+ * Handles press and release events.
+ *
+ * @param e the mouse event
+ */
+ private void handleEvent(MouseEvent e)
+ {
+ if (tree != null && tree.isEnabled())
+ {
+ // Maybe stop editing.
+ if (isEditing(tree) && tree.getInvokesStopCellEditing()
+ && ! stopEditing(tree))
+ return;
+
+ // Explicitly request focus.
+ tree.requestFocusInWindow();
+
+ int x = e.getX();
+ int y = e.getY();
+ TreePath path = getClosestPathForLocation(tree, x, y);
+ if (path != null)
+ {
+ Rectangle b = getPathBounds(tree, path);
+ if (y <= b.y + b.height)
+ {
+ if (SwingUtilities.isLeftMouseButton(e))
+ checkForClickInExpandControl(path, x, y);
+ if (x > b.x && x <= b.x + b.width)
+ {
+ if (! startEditing(path, e))
+ selectPathForEvent(path, e);
+ }
+ }
+ }
+ }
}
}
@@ -2501,6 +2574,9 @@ public class BasicTreeUI
{
this.source = source;
this.destination = destination;
+ source.addMouseListener(this);
+ source.addMouseMotionListener(this);
+ dispatch(e);
}
/**
@@ -2510,9 +2586,8 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseClicked(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ dispatch(e);
}
/**
@@ -2521,9 +2596,8 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mousePressed(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ // Nothing to do here.
}
/**
@@ -2532,9 +2606,9 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseReleased(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ dispatch(e);
+ removeFromSource();
}
/**
@@ -2543,9 +2617,9 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseEntered(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ if (! SwingUtilities.isLeftMouseButton(e))
+ removeFromSource();
}
/**
@@ -2554,9 +2628,9 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseExited(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ if (! SwingUtilities.isLeftMouseButton(e))
+ removeFromSource();
}
/**
@@ -2568,9 +2642,8 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseDragged(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ dispatch(e);
}
/**
@@ -2580,18 +2653,37 @@ public class BasicTreeUI
* @param e mouse event that occured
*/
public void mouseMoved(MouseEvent e)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ removeFromSource();
}
/**
* Removes event from the source
*/
protected void removeFromSource()
- throws NotImplementedException
{
- // TODO: Implement this properly.
+ if (source != null)
+ {
+ source.removeMouseListener(this);
+ source.removeMouseMotionListener(this);
+ }
+ source = null;
+ destination = null;
+ }
+
+ /**
+ * Redispatches mouse events to the destination.
+ *
+ * @param e the mouse event to redispatch
+ */
+ private void dispatch(MouseEvent e)
+ {
+ if (destination != null)
+ {
+ MouseEvent e2 = SwingUtilities.convertMouseEvent(source, e,
+ destination);
+ destination.dispatchEvent(e2);
+ }
}
}
@@ -2627,24 +2719,42 @@ public class BasicTreeUI
public Rectangle getNodeDimensions(Object cell, int row, int depth,
boolean expanded, Rectangle size)
{
- if (size == null || cell == null)
- return null;
-
- String s = cell.toString();
- Font f = tree.getFont();
- FontMetrics fm = tree.getToolkit().getFontMetrics(f);
-
- if (s != null)
+ Dimension prefSize;
+ if (editingComponent != null && editingRow == row)
+ {
+ // Editing, ask editor for preferred size.
+ prefSize = editingComponent.getPreferredSize();
+ int rowHeight = getRowHeight();
+ if (rowHeight > 0 && rowHeight != prefSize.height)
+ prefSize.height = rowHeight;
+ }
+ else
+ {
+ // Not editing, ask renderer for preferred size.
+ Component rend =
+ currentCellRenderer.getTreeCellRendererComponent(tree, cell,
+ tree.isRowSelected(row),
+ expanded,
+ treeModel.isLeaf(cell),
+ row, false);
+ // Make sure the layout is valid.
+ rendererPane.add(rend);
+ rend.validate();
+ prefSize = rend.getPreferredSize();
+ }
+ if (size != null)
{
- TreePath path = treeState.getPathForRow(row);
size.x = getRowX(row, depth);
- size.width = SwingUtilities.computeStringWidth(fm, s);
- size.width = size.width + getCurrentControlIcon(path).getIconWidth()
- + gap + getNodeIcon(path).getIconWidth();
- size.height = getMaxHeight(tree);
- size.y = size.height * row;
+ // FIXME: This should be handled by the layout cache.
+ size.y = prefSize.height * row;
+ size.width = prefSize.width;
+ size.height = prefSize.height;
}
-
+ else
+ // FIXME: The y should be handled by the layout cache.
+ size = new Rectangle(getRowX(row, depth), prefSize.height * row, prefSize.width,
+ prefSize.height);
+
return size;
}
@@ -2706,6 +2816,9 @@ public class BasicTreeUI
if (treeState != null)
treeState.invalidateSizes();
}
+ else if (property.equals(JTree.EDITABLE_PROPERTY))
+ setEditable(((Boolean) event.getNewValue()).booleanValue());
+
}
}
@@ -2714,7 +2827,7 @@ public class BasicTreeUI
* properties of the model change.
*/
public class SelectionModelPropertyChangeHandler
- implements PropertyChangeListener
+ implements PropertyChangeListener
{
/**
@@ -2732,9 +2845,8 @@ public class BasicTreeUI
* the property that has changed.
*/
public void propertyChange(PropertyChangeEvent event)
- throws NotImplementedException
{
- // TODO: What should be done here, if anything?
+ treeSelectionModel.resetRowSelection();
}
}
@@ -2804,6 +2916,7 @@ public class BasicTreeUI
*/
public void treeCollapsed(TreeExpansionEvent event)
{
+ completeEditing();
validCachedPreferredSize = false;
treeState.setExpandedState(event.getPath(), false);
// The maximal cell height may change
@@ -3269,8 +3382,7 @@ public class BasicTreeUI
*/
public void valueChanged(TreeSelectionEvent event)
{
- if (tree.isEditing())
- tree.cancelEditing();
+ completeEditing();
TreePath op = event.getOldLeadSelectionPath();
TreePath np = event.getNewLeadSelectionPath();
@@ -3808,25 +3920,6 @@ public class BasicTreeUI
}
/**
- * Finish the editing session.
- */
- void finish()
- {
- treeState.invalidatePathBounds(treeState.getPathForRow(editingRow));
- editingPath = null;
- editingRow = - 1;
- stopEditingInCompleteEditing = false;
- isEditing = false;
- Rectangle bounds = editingComponent.getParent().getBounds();
- tree.removeAll();
- validCachedPreferredSize = false;
- // Repaint the region, where was the editing component.
- tree.repaint(bounds);
- editingComponent = null;
- tree.requestFocus();
- }
-
- /**
* Returns the amount to indent the given row
*
* @return amount to indent the given row.
diff --git a/javax/swing/plaf/metal/MetalButtonUI.java b/javax/swing/plaf/metal/MetalButtonUI.java
index 8addfc66c..be9607927 100644
--- a/javax/swing/plaf/metal/MetalButtonUI.java
+++ b/javax/swing/plaf/metal/MetalButtonUI.java
@@ -54,7 +54,6 @@ import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.UIResource;
-import javax.swing.plaf.basic.BasicButtonListener;
import javax.swing.plaf.basic.BasicButtonUI;
/**
@@ -66,24 +65,46 @@ public class MetalButtonUI
extends BasicButtonUI
{
- /** The color used to draw the focus rectangle around the text and/or icon. */
+ /**
+ * The shared button UI.
+ */
+ private static MetalButtonUI sharedUI;
+
+ /**
+ * The color used to draw the focus rectangle around the text and/or icon.
+ */
protected Color focusColor;
- /** The background color for the button when it is pressed. */
+ /**
+ * The background color for the button when it is pressed.
+ */
protected Color selectColor;
- /** The color for disabled button labels. */
+ /**
+ * The color for disabled button labels.
+ */
protected Color disabledTextColor;
/**
+ * Returns a UI delegate for the specified component.
+ *
+ * @param c the component (should be a subclass of {@link AbstractButton}).
+ *
+ * @return A new instance of <code>MetalButtonUI</code>.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ if (sharedUI == null)
+ sharedUI = new MetalButtonUI();
+ return sharedUI;
+ }
+
+ /**
* Creates a new instance.
*/
public MetalButtonUI()
{
super();
- focusColor = UIManager.getColor(getPropertyPrefix() + "focus");
- selectColor = UIManager.getColor(getPropertyPrefix() + "select");
- disabledTextColor = UIManager.getColor(getPropertyPrefix() + "disabledText");
}
/**
@@ -93,6 +114,7 @@ public class MetalButtonUI
*/
protected Color getFocusColor()
{
+ focusColor = UIManager.getColor(getPropertyPrefix() + "focus");
return focusColor;
}
@@ -103,6 +125,7 @@ public class MetalButtonUI
*/
protected Color getSelectColor()
{
+ selectColor = UIManager.getColor(getPropertyPrefix() + "select");
return selectColor;
}
@@ -113,22 +136,12 @@ public class MetalButtonUI
*/
protected Color getDisabledTextColor()
{
+ disabledTextColor = UIManager.getColor(getPropertyPrefix()
+ + "disabledText");
return disabledTextColor;
}
/**
- * Returns a UI delegate for the specified component.
- *
- * @param c the component (should be a subclass of {@link AbstractButton}).
- *
- * @return A new instance of <code>MetalButtonUI</code>.
- */
- public static ComponentUI createUI(JComponent c)
- {
- return new MetalButtonUI();
- }
-
- /**
* Installs the default settings for the specified button.
*
* @param button the button.
@@ -137,33 +150,20 @@ public class MetalButtonUI
*/
public void installDefaults(AbstractButton button)
{
+ // This is overridden to be public, for whatever reason.
super.installDefaults(button);
- button.setRolloverEnabled(UIManager.getBoolean(
- getPropertyPrefix() + "rollover"));
}
-
+
/**
* Removes the defaults added by {@link #installDefaults(AbstractButton)}.
*/
public void uninstallDefaults(AbstractButton button)
{
+ // This is overridden to be public, for whatever reason.
super.uninstallDefaults(button);
- button.setRolloverEnabled(false);
}
/**
- * Returns a button listener for the specified button.
- *
- * @param button the button.
- *
- * @return A button listener.
- */
- protected BasicButtonListener createButtonListener(AbstractButton button)
- {
- return new MetalButtonListener(button);
- }
-
- /**
* Paints the background of the button to indicate that it is in the
* "pressed" state.
*
@@ -175,7 +175,7 @@ public class MetalButtonUI
if (b.isContentAreaFilled())
{
Rectangle area = b.getVisibleRect();
- g.setColor(selectColor);
+ g.setColor(getSelectColor());
g.fillRect(area.x, area.y, area.width, area.height);
}
}
diff --git a/javax/swing/plaf/metal/MetalCheckBoxIcon.java b/javax/swing/plaf/metal/MetalCheckBoxIcon.java
index fb8280e44..30ee93162 100644
--- a/javax/swing/plaf/metal/MetalCheckBoxIcon.java
+++ b/javax/swing/plaf/metal/MetalCheckBoxIcon.java
@@ -1,5 +1,5 @@
/* MetalCheckBoxIcon.java -- An icon for JCheckBoxes in the Metal L&F
- Copyright (C) 2005 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -42,8 +42,8 @@ import java.awt.Component;
import java.awt.Graphics;
import java.io.Serializable;
+import javax.swing.AbstractButton;
import javax.swing.Icon;
-import javax.swing.JCheckBox;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.plaf.UIResource;
@@ -134,8 +134,9 @@ public class MetalCheckBoxIcon
MetalUtils.paintGradient(g, x, y, getIconWidth(), getIconHeight(),
SwingConstants.VERTICAL, "CheckBox.gradient");
border.paintBorder(c, g, x, y, getIconWidth(), getIconHeight());
- JCheckBox cb = (JCheckBox) c;
- if (cb.isSelected())
- drawCheck(c, g, x, y);
+
+ AbstractButton b = (AbstractButton) c;
+ if (b.isSelected())
+ drawCheck(b, g, x, y);
}
}
diff --git a/javax/swing/plaf/metal/MetalLookAndFeel.java b/javax/swing/plaf/metal/MetalLookAndFeel.java
index da16159c9..ff26aa232 100644
--- a/javax/swing/plaf/metal/MetalLookAndFeel.java
+++ b/javax/swing/plaf/metal/MetalLookAndFeel.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package javax.swing.plaf.metal;
+import gnu.classpath.SystemProperties;
+
import java.awt.Color;
import java.awt.Font;
@@ -81,16 +83,15 @@ public class MetalLookAndFeel extends BasicLookAndFeel
*/
public MetalLookAndFeel()
{
- createDefaultTheme();
+ // Nothing to do here.
}
/**
- * Sets the current theme to a new instance of {@link OceanTheme}.
+ * Sets the current theme to a new instance of {@link DefaultMetalTheme}.
*/
protected void createDefaultTheme()
{
- if (theme == null)
- setCurrentTheme(new OceanTheme());
+ getCurrentTheme();
}
/**
@@ -149,6 +150,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel
public UIDefaults getDefaults()
{
+ createDefaultTheme();
if (LAF_defaults == null)
{
LAF_defaults = super.getDefaults();
@@ -1354,7 +1356,14 @@ public class MetalLookAndFeel extends BasicLookAndFeel
public static MetalTheme getCurrentTheme()
{
if (theme == null)
- theme = new OceanTheme();
+ {
+ // swing.metalTheme property documented here:
+ // http://java.sun.com/j2se/1.5.0/docs/guide/swing/1.5/index.html
+ if ("steel".equals(SystemProperties.getProperty("swing.metalTheme")))
+ theme = new DefaultMetalTheme();
+ else
+ theme = new OceanTheme();
+ }
return theme;
}
diff --git a/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/javax/swing/plaf/metal/MetalSplitPaneDivider.java
index 6081c355c..a3069daa9 100644
--- a/javax/swing/plaf/metal/MetalSplitPaneDivider.java
+++ b/javax/swing/plaf/metal/MetalSplitPaneDivider.java
@@ -38,18 +38,14 @@ exception statement from your version. */
package javax.swing.plaf.metal;
import java.awt.Color;
-import java.awt.Component;
-import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
-import java.awt.LayoutManager;
-import java.awt.Point;
+import java.awt.Insets;
+import javax.swing.JButton;
import javax.swing.JSplitPane;
-import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
-import javax.swing.plaf.basic.BasicArrowButton;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
/**
@@ -59,6 +55,143 @@ import javax.swing.plaf.basic.BasicSplitPaneDivider;
*/
class MetalSplitPaneDivider extends BasicSplitPaneDivider
{
+ /**
+ * The button pixel data, as indices into the colors array below.
+ * This is the version for 'left' buttons.
+ *
+ * This is slightly different from the icon in Sun's version, it is
+ * one pixel smaller and is more consistent with BUTTON_SPRITE_R.
+ */
+ static final byte[][] BUTTON_SPRITE_L = {{ 0, 0, 0, 2, 0, 0, 0, 0 },
+ { 0, 0, 2, 1, 1, 0, 0, 0 },
+ { 0, 2, 1, 1, 1, 1, 0, 0 },
+ { 2, 1, 1, 1, 1, 1, 1, 0 },
+ { 0, 3, 3, 3, 3, 3, 3, 3 }};
+
+ /**
+ * The button pixel data, as indices into the colors array below.
+ * This is the version for 'right' buttons.
+ */
+ static final byte[][] BUTTON_SPRITE_R = {{ 2, 2, 2, 2, 2, 2, 2, 2 },
+ { 0, 1, 1, 1, 1, 1, 1, 3 },
+ { 0, 0, 1, 1, 1, 1, 3, 0 },
+ { 0, 0, 0, 1, 1, 3, 0, 0 },
+ { 0, 0, 0, 0, 3, 0, 0, 0 }};
+
+ private class MetalOneTouchButton
+ extends JButton
+ {
+ /**
+ * Denotes a left button.
+ */
+ static final int LEFT = 0;
+
+ /**
+ * Denotes a right button.
+ */
+ static final int RIGHT = 1;
+
+ /**
+ * The colors for the button sprite.
+ */
+ private Color[] colors;
+
+ /**
+ * Either LEFT or RIGHT.
+ */
+ private int direction;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param dir either LEFT or RIGHT
+ */
+ MetalOneTouchButton(int dir)
+ {
+ direction = dir;
+ colors = new Color[4];
+ }
+
+ /**
+ * Never allow borders.
+ */
+ public void setBorder(Border b)
+ {
+ }
+
+ /**
+ * Never allow focus traversal.
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Paints the one touch button.
+ */
+ public void paint(Graphics g)
+ {
+ if (splitPane != null)
+ {
+ // Update colors here to reflect dynamic changes to the theme.
+ colors[0] = getBackground();
+ colors[1] = MetalLookAndFeel.getPrimaryControlDarkShadow();
+ colors[2] = MetalLookAndFeel.getPrimaryControlInfo();
+ colors[3] = MetalLookAndFeel.getPrimaryControlHighlight();
+
+ // Fill background.
+ g.setColor(getBackground());
+ g.fillRect(0, 0, getWidth(), getHeight());
+
+ // Pressed buttons have slightly different color mapping.
+ if (getModel().isPressed())
+ colors[1] = colors[2];
+
+ byte[][] sprite;
+ if (direction == LEFT)
+ sprite = BUTTON_SPRITE_L;
+ else
+ sprite = BUTTON_SPRITE_R;
+
+ if (orientation == JSplitPane.VERTICAL_SPLIT)
+ {
+ // Draw the sprite as it is.
+ for (int y = 0; y < sprite.length; y++)
+ {
+ byte[] line = sprite[y];
+ for (int x = 0; x < line.length; x++)
+ {
+ int c = line[x];
+ if (c != 0)
+ {
+ g.setColor(colors[c]);
+ g.fillRect(x + 1, y + 1, 1, 1);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Draw the sprite with swapped X and Y axis.
+ for (int y = 0; y < sprite.length; y++)
+ {
+ byte[] line = sprite[y];
+ for (int x = 0; x < line.length; x++)
+ {
+ int c = line[x];
+ if (c != 0)
+ {
+ g.setColor(colors[c]);
+ g.fillRect(y + 1, x + 1, 1, 1);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
/** The dark color in the pattern. */
Color dark;
@@ -79,7 +212,6 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider
public MetalSplitPaneDivider(MetalSplitPaneUI ui, Color light, Color dark)
{
super(ui);
- setLayout(new MetalDividerLayout());
this.splitPane = super.splitPane;
this.orientation = super.orientation;
this.light = light;
@@ -106,126 +238,27 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider
if (border != null)
border.paintBorder(this, g, 0, 0, s.width, s.height);
- MetalUtils.fillMetalPattern(splitPane, g, 2, 2, s.width - 4, s.height - 4,
+ Insets i = getInsets();
+ MetalUtils.fillMetalPattern(splitPane, g, i.left + 2, i.top + 2,
+ s.width - i.left - i.right - 4,
+ s.height - i.top - i.bottom - 4,
light, dark);
- if (splitPane.isOneTouchExpandable())
- {
- ((BasicArrowButton) rightButton).paint(g);
- ((BasicArrowButton) leftButton).paint(g);
- }
+ super.paint(g);
}
-
- /**
- * This helper class acts as the Layout Manager for the divider.
- */
- public class MetalDividerLayout implements LayoutManager
- {
- /** The right button. */
- BasicArrowButton rb;
-
- /** The left button. */
- BasicArrowButton lb;
-
- /**
- * Creates a new DividerLayout object.
- */
- public MetalDividerLayout()
- {
- // Nothing to do here
- }
-
- /**
- * This method is called when a Component is added.
- *
- * @param string The constraints string.
- * @param c The Component to add.
- */
- public void addLayoutComponent(String string, Component c)
- {
- // Nothing to do here, constraints are set depending on
- // orientation in layoutContainer
- }
-
- /**
- * This method is called to lay out the container.
- *
- * @param c The container to lay out.
- */
- public void layoutContainer(Container c)
- {
- // The only components we care about setting up are the
- // one touch buttons.
- if (splitPane.isOneTouchExpandable())
- {
- if (c.getComponentCount() == 2)
- {
- Component c1 = c.getComponent(0);
- Component c2 = c.getComponent(1);
- if ((c1 instanceof BasicArrowButton)
- && (c2 instanceof BasicArrowButton))
- {
- lb = (BasicArrowButton) c1;
- rb = (BasicArrowButton) c2;
- }
- }
- if (rb != null && lb != null)
- {
- Point p = getLocation();
- lb.setSize(lb.getPreferredSize());
- rb.setSize(rb.getPreferredSize());
- lb.setLocation(p.x, p.y);
-
- if (orientation == JSplitPane.HORIZONTAL_SPLIT)
- {
- rb.setDirection(SwingConstants.EAST);
- lb.setDirection(SwingConstants.WEST);
- rb.setLocation(p.x, p.y + lb.getHeight());
- }
- else
- {
- rb.setDirection(SwingConstants.SOUTH);
- lb.setDirection(SwingConstants.NORTH);
- rb.setLocation(p.x + lb.getWidth(), p.y);
- }
- }
- }
- }
-
- /**
- * This method returns the minimum layout size.
- *
- * @param c The container to calculate for.
- *
- * @return The minimum layout size.
- */
- public Dimension minimumLayoutSize(Container c)
- {
- return preferredLayoutSize(c);
- }
- /**
- * This method returns the preferred layout size.
- *
- * @param c The container to calculate for.
- *
- * @return The preferred layout size.
- */
- public Dimension preferredLayoutSize(Container c)
- {
- int dividerSize = getDividerSize();
- return new Dimension(dividerSize, dividerSize);
- }
+ protected JButton createLeftOneTouchButton()
+ {
+ JButton b = new MetalOneTouchButton(MetalOneTouchButton.LEFT);
+ b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ b.setRequestFocusEnabled(false);
+ return b;
+ }
- /**
- * This method is called when a component is removed.
- *
- * @param c The component to remove.
- */
- public void removeLayoutComponent(Component c)
- {
- // Nothing to do here. If buttons are removed
- // they will not be layed out when layoutContainer is
- // called.
- }
+ protected JButton createRightOneTouchButton()
+ {
+ JButton b = new MetalOneTouchButton(MetalOneTouchButton.RIGHT);
+ b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ b.setRequestFocusEnabled(false);
+ return b;
}
}
diff --git a/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/javax/swing/plaf/metal/MetalTabbedPaneUI.java
index 20135fc85..53eaa3cac 100644
--- a/javax/swing/plaf/metal/MetalTabbedPaneUI.java
+++ b/javax/swing/plaf/metal/MetalTabbedPaneUI.java
@@ -1159,7 +1159,7 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI
g.drawLine(x + 1, y + 1, x + 1, rect.y + 1);
if (rect.y + rect.height < y + h - 2)
{
- g.drawLine(x + y, rect.y + rect.height + 1, x + 1, y + h + 2);
+ g.drawLine(x + 1, rect.y + rect.height + 1, x + 1, y + h + 2);
}
}
}
diff --git a/javax/swing/plaf/metal/MetalToolTipUI.java b/javax/swing/plaf/metal/MetalToolTipUI.java
index d1040347f..6647cc02d 100644
--- a/javax/swing/plaf/metal/MetalToolTipUI.java
+++ b/javax/swing/plaf/metal/MetalToolTipUI.java
@@ -43,9 +43,6 @@ import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
-import java.awt.Insets;
-import java.awt.Rectangle;
-import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
@@ -54,8 +51,6 @@ import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JToolTip;
import javax.swing.KeyStroke;
-import javax.swing.SwingConstants;
-import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.plaf.ComponentUI;
@@ -192,32 +187,14 @@ public class MetalToolTipUI
*/
public Dimension getPreferredSize(JComponent c)
{
- if (isAcceleratorHidden())
- return super.getPreferredSize(c);
- else
+ Dimension d = super.getPreferredSize(c);
+ String acc = getAcceleratorString();
+ if (acc != null && ! acc.equals(""))
{
- Insets insets = c.getInsets();
- JToolTip tt = (JToolTip) c;
- String tipText = tt.getTipText();
- if (tipText != null)
- {
- FontMetrics fm = c.getFontMetrics(c.getFont());
- int prefH = fm.getHeight() + insets.top + insets.bottom;
- int prefW = fm.stringWidth(tipText) + insets.left + insets.right;
-
- // this seems to be the first opportunity we have to get the
- // accelerator string from the component (if it has one)
- acceleratorString = fetchAcceleratorString(c);
- if (acceleratorString != null)
- {
- prefW += padSpaceBetweenStrings;
- fm = c.getFontMetrics(acceleratorFont);
- prefW += fm.stringWidth(acceleratorString);
- }
- return new Dimension(prefW, prefH);
- }
- else return new Dimension(0, 0);
+ FontMetrics fm = c.getFontMetrics(c.getFont());
+ d.width += fm.stringWidth(acc);
}
+ return d;
}
/**
@@ -228,39 +205,8 @@ public class MetalToolTipUI
*/
public void paint(Graphics g, JComponent c)
{
- JToolTip tip = (JToolTip) c;
-
- String text = tip.getTipText();
- Toolkit t = tip.getToolkit();
- if (text == null)
- return;
-
- Rectangle vr = new Rectangle();
- vr = SwingUtilities.calculateInnerArea(tip, vr);
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
- FontMetrics fm = t.getFontMetrics(tip.getFont());
- int ascent = fm.getAscent();
- SwingUtilities.layoutCompoundLabel(tip, fm, text, null,
- SwingConstants.CENTER, SwingConstants.LEFT,
- SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0);
- Color saved = g.getColor();
- g.setColor(Color.BLACK);
-
- g.drawString(text, vr.x, vr.y + ascent);
-
- // paint accelerator
- if (acceleratorString != null)
- {
- g.setFont(acceleratorFont);
- g.setColor(acceleratorForeground);
- fm = t.getFontMetrics(acceleratorFont);
- int width = fm.stringWidth(acceleratorString);
- g.drawString(acceleratorString, vr.x + vr.width - width
- - padSpaceBetweenStrings / 2, vr.y + vr.height - fm.getDescent());
- }
-
- g.setColor(saved);
+ super.paint(g, c);
+ // Somehow paint accelerator. Keep care for possible HTML rendering.
}
/**
diff --git a/javax/swing/plaf/metal/MetalTreeUI.java b/javax/swing/plaf/metal/MetalTreeUI.java
index 3ea37c82f..ed1e5b4d8 100644
--- a/javax/swing/plaf/metal/MetalTreeUI.java
+++ b/javax/swing/plaf/metal/MetalTreeUI.java
@@ -38,14 +38,15 @@ exception statement from your version. */
package javax.swing.plaf.metal;
-import gnu.classpath.NotImplementedException;
-
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import javax.swing.JComponent;
import javax.swing.JTree;
+import javax.swing.UIManager;
import javax.swing.tree.TreePath;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.basic.BasicTreeUI;
@@ -56,6 +57,68 @@ import javax.swing.plaf.basic.BasicTreeUI;
public class MetalTreeUI extends BasicTreeUI
{
/**
+ * Listens for property changes of the line style and updates the
+ * internal setting.
+ */
+ private class LineStyleListener
+ implements PropertyChangeListener
+ {
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(LINE_STYLE_PROPERTY))
+ decodeLineStyle(e.getNewValue());
+ }
+
+ }
+
+ /**
+ * The key to the lineStyle client property.
+ */
+ private static final String LINE_STYLE_PROPERTY = "JTree.lineStyle";
+
+ /**
+ * The property value indicating no line style.
+ */
+ private static final String LINE_STYLE_VALUE_NONE = "None";
+
+ /**
+ * The property value indicating angled line style.
+ */
+ private static final String LINE_STYLE_VALUE_ANGLED = "Angled";
+
+ /**
+ * The property value indicating horizontal line style.
+ */
+ private static final String LINE_STYLE_VALUE_HORIZONTAL = "Horizontal";
+
+ /**
+ * The line style for None.
+ */
+ private static final int LINE_STYLE_NONE = 0;
+
+ /**
+ * The line style for Angled.
+ */
+ private static final int LINE_STYLE_ANGLED = 1;
+
+ /**
+ * The line style for Horizontal.
+ */
+ private static final int LINE_STYLE_HORIZONTAL = 2;
+
+ /**
+ * The current line style.
+ */
+ private int lineStyle;
+
+ /**
+ * Listens for changes on the line style property and updates the
+ * internal settings.
+ */
+ private PropertyChangeListener lineStyleListener;
+
+ /**
* Constructs a new instance of <code>MetalTreeUI</code>.
*/
public MetalTreeUI()
@@ -103,8 +166,13 @@ public class MetalTreeUI extends BasicTreeUI
*/
public void installUI(JComponent c)
{
- // TODO: What to do here, if anything?
super.installUI(c);
+
+ Object lineStyleProp = c.getClientProperty(LINE_STYLE_PROPERTY);
+ decodeLineStyle(lineStyleProp);
+ if (lineStyleListener == null)
+ lineStyleListener = new LineStyleListener();
+ c.addPropertyChangeListener(lineStyleListener);
}
/**
@@ -124,8 +192,10 @@ public class MetalTreeUI extends BasicTreeUI
*/
public void uninstallUI(JComponent c)
{
- // TODO: What to do here?
super.uninstallUI(c);
+ if (lineStyleListener != null)
+ c.removePropertyChangeListener(lineStyleListener);
+ lineStyleListener = null;
}
/**
@@ -135,9 +205,15 @@ public class MetalTreeUI extends BasicTreeUI
* @param lineStyleFlag - String representation
*/
protected void decodeLineStyle(Object lineStyleFlag)
- throws NotImplementedException
{
- // FIXME: not implemented
+ if (lineStyleFlag == null || lineStyleFlag.equals(LINE_STYLE_VALUE_ANGLED))
+ lineStyle = LINE_STYLE_ANGLED;
+ else if (lineStyleFlag.equals(LINE_STYLE_VALUE_HORIZONTAL))
+ lineStyle = LINE_STYLE_HORIZONTAL;
+ else if (lineStyleFlag.equals(LINE_STYLE_VALUE_NONE))
+ lineStyle = LINE_STYLE_NONE;
+ else
+ lineStyle = LINE_STYLE_ANGLED;
}
/**
@@ -170,6 +246,9 @@ public class MetalTreeUI extends BasicTreeUI
// Calls BasicTreeUI's paint since it takes care of painting all
// types of icons.
super.paint(g, c);
+
+ if (lineStyle == LINE_STYLE_HORIZONTAL)
+ paintHorizontalSeparators(g, c);
}
/**
@@ -179,9 +258,28 @@ public class MetalTreeUI extends BasicTreeUI
* @param c - the current component to draw
*/
protected void paintHorizontalSeparators(Graphics g, JComponent c)
- throws NotImplementedException
{
- // FIXME: not implemented
+ g.setColor(UIManager.getColor("Tree.line"));
+ Rectangle clip = g.getClipBounds();
+ int row0 = getRowForPath(tree, getClosestPathForLocation(tree, 0, clip.y));
+ int row1 =
+ getRowForPath(tree, getClosestPathForLocation(tree, 0,
+ clip.y + clip.height - 1));
+ if (row0 >= 0 && row1 >= 0)
+ {
+ for (int i = row0; i <= row1; i++)
+ {
+ TreePath p = getPathForRow(tree, i);
+ if (p != null && p.getPathCount() == 2)
+ {
+ Rectangle r = getPathBounds(tree, getPathForRow(tree, i));
+ if (r != null)
+ {
+ g.drawLine(clip.x, r.y, clip.x + clip.width, r.y);
+ }
+ }
+ }
+ }
}
@@ -197,7 +295,8 @@ public class MetalTreeUI extends BasicTreeUI
protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
Insets insets, TreePath path)
{
- super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
+ if (lineStyle == LINE_STYLE_ANGLED)
+ super.paintVerticalPartOfLeg(g, clipBounds, insets, path);
}
/**
@@ -211,7 +310,8 @@ public class MetalTreeUI extends BasicTreeUI
boolean isExpanded, boolean hasBeenExpanded,
boolean isLeaf)
{
- super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row,
- isExpanded, hasBeenExpanded, isLeaf);
+ if (lineStyle == LINE_STYLE_ANGLED)
+ super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row,
+ isExpanded, hasBeenExpanded, isLeaf);
}
}
diff --git a/javax/swing/table/DefaultTableModel.java b/javax/swing/table/DefaultTableModel.java
index 79285903c..1b68ce2be 100644
--- a/javax/swing/table/DefaultTableModel.java
+++ b/javax/swing/table/DefaultTableModel.java
@@ -625,7 +625,7 @@ public class DefaultTableModel extends AbstractTableModel
if (columnCount > columnIdentifiers.size())
columnIdentifiers.setSize(columnCount);
- if (rowCount > dataVector.size())
+ if (dataVector != null && rowCount > dataVector.size())
{
int rowsToAdd = rowCount - dataVector.size();
addExtraRows(rowsToAdd, columnCount);
diff --git a/javax/swing/text/BoxView.java b/javax/swing/text/BoxView.java
index 7e8f19f74..962d06219 100644
--- a/javax/swing/text/BoxView.java
+++ b/javax/swing/text/BoxView.java
@@ -92,11 +92,6 @@ public class BoxView
private int[] span = new int[2];
/**
- * The SizeRequirements of the child views along the X_AXIS and Y_AXIS.
- */
- private SizeRequirements[][] childReqs = new SizeRequirements[2][];
-
- /**
* Creates a new <code>BoxView</code> for the given
* <code>Element</code> and axis. Valid values for the axis are
* {@link View#X_AXIS} and {@link View#Y_AXIS}.
@@ -227,48 +222,30 @@ public class BoxView
*/
public void replace(int offset, int length, View[] views)
{
- int numViews = 0;
- if (views != null)
- numViews = views.length;
-
- // Resize and copy data for cache arrays.
- // The spansX cache.
- int oldSize = getViewCount();
-
- int[] newSpansX = new int[oldSize - length + numViews];
- System.arraycopy(spans[X_AXIS], 0, newSpansX, 0, offset);
- System.arraycopy(spans[X_AXIS], offset + length, newSpansX,
- offset + numViews,
- oldSize - (offset + length));
- spans[X_AXIS] = newSpansX;
-
- // The spansY cache.
- int[] newSpansY = new int[oldSize - length + numViews];
- System.arraycopy(spans[Y_AXIS], 0, newSpansY, 0, offset);
- System.arraycopy(spans[Y_AXIS], offset + length, newSpansY,
- offset + numViews,
- oldSize - (offset + length));
- spans[Y_AXIS] = newSpansY;
-
- // The offsetsX cache.
- int[] newOffsetsX = new int[oldSize - length + numViews];
- System.arraycopy(offsets[X_AXIS], 0, newOffsetsX, 0, offset);
- System.arraycopy(offsets[X_AXIS], offset + length, newOffsetsX,
- offset + numViews,
- oldSize - (offset + length));
- offsets[X_AXIS] = newOffsetsX;
-
- // The offsetsY cache.
- int[] newOffsetsY = new int[oldSize - length + numViews];
- System.arraycopy(offsets[Y_AXIS], 0, newOffsetsY, 0, offset);
- System.arraycopy(offsets[Y_AXIS], offset + length, newOffsetsY,
- offset + numViews,
- oldSize - (offset + length));
- offsets[Y_AXIS] = newOffsetsY;
+ int oldNumChildren = getViewCount();
// Actually perform the replace.
super.replace(offset, length, views);
+ // Resize and copy data for cache arrays.
+ int newItems = views != null ? views.length : 0;
+ int delta = newItems - length;
+ int src = offset + length;
+ int numMove = oldNumChildren - src;
+ int dst = src + delta;
+ offsets[X_AXIS] = replaceLayoutArray(offsets[X_AXIS], offset,
+ oldNumChildren, delta, src, dst,
+ numMove);
+ spans[X_AXIS] = replaceLayoutArray(spans[X_AXIS], offset,
+ oldNumChildren, delta, src, dst,
+ numMove);
+ offsets[Y_AXIS] = replaceLayoutArray(offsets[Y_AXIS], offset,
+ oldNumChildren, delta, src, dst,
+ numMove);
+ spans[Y_AXIS] = replaceLayoutArray(spans[Y_AXIS], offset,
+ oldNumChildren, delta, src, dst,
+ numMove);
+
// Invalidate layout information.
layoutValid[X_AXIS] = false;
requirementsValid[X_AXIS] = false;
@@ -277,6 +254,34 @@ public class BoxView
}
/**
+ * Helper method. This updates the layout cache arrays in response
+ * to a call to {@link #replace(int, int, View[])}.
+ *
+ * @param oldArray the old array
+ *
+ * @return the replaced array
+ */
+ private int[] replaceLayoutArray(int[] oldArray, int offset, int numChildren,
+ int delta, int src, int dst, int numMove)
+
+ {
+ int[] newArray;
+ if (numChildren + delta > oldArray.length)
+ {
+ int newLength = Math.max(2 * oldArray.length, numChildren + delta);
+ newArray = new int[newLength];
+ System.arraycopy(oldArray, 0, newArray, 0, offset);
+ System.arraycopy(oldArray, src, newArray, dst, numMove);
+ }
+ else
+ {
+ newArray = oldArray;
+ System.arraycopy(newArray, src, newArray, dst, numMove);
+ }
+ return newArray;
+ }
+
+ /**
* Renders the <code>Element</code> that is associated with this
* <code>View</code>.
*
@@ -373,9 +378,9 @@ public class BoxView
}
/**
- * This method is obsolete and no longer in use. It is replaced by
- * {@link #calculateMajorAxisRequirements(int, SizeRequirements)} and
- * {@link #calculateMinorAxisRequirements(int, SizeRequirements)}.
+ * Calculates size requirements for a baseline layout. This is not
+ * used by the BoxView itself, but by subclasses that wish to perform
+ * a baseline layout, like the FlowView's rows.
*
* @param axis the axis that is examined
* @param sr the <code>SizeRequirements</code> object to hold the result,
@@ -387,50 +392,94 @@ public class BoxView
protected SizeRequirements baselineRequirements(int axis,
SizeRequirements sr)
{
- updateChildRequirements(axis);
+ // Create new instance if sr == null.
+ if (sr == null)
+ sr = new SizeRequirements();
+ sr.alignment = 0.5F;
+
+ // Calculate overall ascent and descent.
+ int totalAscentMin = 0;
+ int totalAscentPref = 0;
+ int totalAscentMax = 0;
+ int totalDescentMin = 0;
+ int totalDescentPref = 0;
+ int totalDescentMax = 0;
+
+ int count = getViewCount();
+ for (int i = 0; i < count; i++)
+ {
+ View v = getView(i);
+ float align = v.getAlignment(axis);
+ int span = (int) v.getPreferredSpan(axis);
+ int ascent = (int) (align * span);
+ int descent = span - ascent;
+
+ totalAscentPref = Math.max(ascent, totalAscentPref);
+ totalDescentPref = Math.max(descent, totalDescentPref);
+ if (v.getResizeWeight(axis) > 0)
+ {
+ // If the view is resizable, then use the min and max size
+ // of the view.
+ span = (int) v.getMinimumSpan(axis);
+ ascent = (int) (align * span);
+ descent = span - ascent;
+ totalAscentMin = Math.max(ascent, totalAscentMin);
+ totalDescentMin = Math.max(descent, totalDescentMin);
+
+ span = (int) v.getMaximumSpan(axis);
+ ascent = (int) (align * span);
+ descent = span - ascent;
+ totalAscentMax = Math.max(ascent, totalAscentMax);
+ totalDescentMax = Math.max(descent, totalDescentMax);
+ }
+ else
+ {
+ // If the view is not resizable, use the preferred span.
+ totalAscentMin = Math.max(ascent, totalAscentMin);
+ totalDescentMin = Math.max(descent, totalDescentMin);
+ totalAscentMax = Math.max(ascent, totalAscentMax);
+ totalDescentMax = Math.max(descent, totalDescentMax);
+ }
+ }
- SizeRequirements res = sr;
- if (res == null)
- res = new SizeRequirements();
+ // Preferred overall span is the sum of the preferred ascent and descent.
+ // With overflow check.
+ sr.preferred = (int) Math.min((long) totalAscentPref
+ + (long) totalDescentPref,
+ Integer.MAX_VALUE);
- float minLeft = 0;
- float minRight = 0;
- float prefLeft = 0;
- float prefRight = 0;
- float maxLeft = 0;
- float maxRight = 0;
- for (int i = 0; i < childReqs[axis].length; i++)
+ // Align along the baseline.
+ if (sr.preferred > 0)
+ sr.alignment = (float) totalAscentPref / sr.preferred;
+
+ if (sr.alignment == 0)
{
- float myMinLeft = childReqs[axis][i].minimum * childReqs[axis][i].alignment;
- float myMinRight = childReqs[axis][i].minimum - myMinLeft;
- minLeft = Math.max(myMinLeft, minLeft);
- minRight = Math.max(myMinRight, minRight);
- float myPrefLeft = childReqs[axis][i].preferred * childReqs[axis][i].alignment;
- float myPrefRight = childReqs[axis][i].preferred - myPrefLeft;
- prefLeft = Math.max(myPrefLeft, prefLeft);
- prefRight = Math.max(myPrefRight, prefRight);
- float myMaxLeft = childReqs[axis][i].maximum * childReqs[axis][i].alignment;
- float myMaxRight = childReqs[axis][i].maximum - myMaxLeft;
- maxLeft = Math.max(myMaxLeft, maxLeft);
- maxRight = Math.max(myMaxRight, maxRight);
+ // Nothing above the baseline, use the descent.
+ sr.minimum = totalDescentMin;
+ sr.maximum = totalDescentMax;
}
- int minSize = (int) (minLeft + minRight);
- int prefSize = (int) (prefLeft + prefRight);
- int maxSize = (int) (maxLeft + maxRight);
- float align = prefLeft / (prefRight + prefLeft);
- if (Float.isNaN(align))
- align = 0;
-
- res.alignment = align;
- res.maximum = maxSize;
- res.preferred = prefSize;
- res.minimum = minSize;
- return res;
+ else if (sr.alignment == 1.0F)
+ {
+ // Nothing below the baseline, use the descent.
+ sr.minimum = totalAscentMin;
+ sr.maximum = totalAscentMax;
+ }
+ else
+ {
+ sr.minimum = Math.max((int) (totalAscentMin / sr.alignment),
+ (int) (totalDescentMin / (1.0F - sr.alignment)));
+ sr.maximum = Math.min((int) (totalAscentMax / sr.alignment),
+ (int) (totalDescentMax / (1.0F - sr.alignment)));
+ }
+ return sr;
}
/**
- * Calculates the layout of the children of this <code>BoxView</code> along
- * the specified axis.
+ * Calculates the baseline layout of the children of this
+ * <code>BoxView</code> along the specified axis.
+ *
+ * This is not used by the BoxView itself, but by subclasses that wish to
+ * perform a baseline layout, like the FlowView's rows.
*
* @param span the target span
* @param axis the axis that is examined
@@ -440,13 +489,36 @@ public class BoxView
protected void baselineLayout(int span, int axis, int[] offsets,
int[] spans)
{
- updateChildRequirements(axis);
- updateRequirements(axis);
+ int totalAscent = (int) (span * getAlignment(axis));
+ int totalDescent = span - totalAscent;
- // Calculate the spans and offsets using the SizeRequirements uility
- // methods.
- SizeRequirements.calculateAlignedPositions(span, requirements[axis],
- childReqs[axis], offsets, spans);
+ int count = getViewCount();
+ for (int i = 0; i < count; i++)
+ {
+ View v = getView(i);
+ float align = v.getAlignment(axis);
+ int viewSpan;
+ if (v.getResizeWeight(axis) > 0)
+ {
+ // If possible, then resize for best fit.
+ int min = (int) v.getMinimumSpan(axis);
+ int max = (int) v.getMaximumSpan(axis);
+ if (align == 0.0F)
+ viewSpan = Math.max(Math.min(max, totalDescent), min);
+ else if (align == 1.0F)
+ viewSpan = Math.max(Math.min(max, totalAscent), min);
+ else
+ {
+ int fit = (int) Math.min(totalAscent / align,
+ totalDescent / (1.0F - align));
+ viewSpan = Math.max(Math.min(max, fit), min);
+ }
+ }
+ else
+ viewSpan = (int) v.getPreferredSpan(axis);
+ offsets[i] = totalAscent - (int) (viewSpan * align);
+ spans[i] = viewSpan;
+ }
}
/**
@@ -509,7 +581,7 @@ public class BoxView
res.minimum = 0;
res.preferred = 0;
- res.maximum = 0;
+ res.maximum = Integer.MAX_VALUE;
res.alignment = 0.5F;
int n = getViewCount();
for (int i = 0; i < n; i++)
@@ -589,24 +661,54 @@ public class BoxView
{
View result = null;
int count = getViewCount();
- Rectangle copy = new Rectangle(r);
-
- for (int i = 0; i < count; ++i)
+ if (myAxis == X_AXIS)
{
- copy.setBounds(r);
- // The next call modifies copy.
- childAllocation(i, copy);
- if (copy.contains(x, y))
+ // Border case. Requested point is left from the box.
+ if (x < r.x + offsets[X_AXIS][0])
{
- // Modify r on success.
- r.setBounds(copy);
- result = getView(i);
- break;
+ childAllocation(0, r);
+ result = getView(0);
+ }
+ else
+ {
+ // Search views inside box.
+ for (int i = 0; i < count && result == null; i++)
+ {
+ if (x < r.x + offsets[X_AXIS][i])
+ {
+ childAllocation(i - 1, r);
+ result = getView(i - 1);
+ }
+ }
}
}
-
- if (result == null && count > 0)
- return getView(count - 1);
+ else // Same algorithm for Y_AXIS.
+ {
+ // Border case. Requested point is above the box.
+ if (y < r.y + offsets[Y_AXIS][0])
+ {
+ childAllocation(0, r);
+ result = getView(0);
+ }
+ else
+ {
+ // Search views inside box.
+ for (int i = 0; i < count && result == null; i++)
+ {
+ if (y < r.y + offsets[Y_AXIS][i])
+ {
+ childAllocation(i - 1, r);
+ result = getView(i - 1);
+ }
+ }
+ }
+ }
+ // Not found, other border case: point is right from or below the box.
+ if (result == null)
+ {
+ childAllocation(count - 1, r);
+ result = getView(count - 1);
+ }
return result;
}
@@ -808,7 +910,9 @@ public class BoxView
*/
public int getWidth()
{
- return span[X_AXIS] + getLeftInset() - getRightInset();
+ // The RI returns the following here, however, I'd think that is a bug.
+ // return span[X_AXIS] + getLeftInset() - getRightInset();
+ return span[X_AXIS] + getLeftInset() + getRightInset();
}
/**
@@ -818,7 +922,9 @@ public class BoxView
*/
public int getHeight()
{
- return span[Y_AXIS] + getTopInset() - getBottomInset();
+ // The RI returns the following here, however, I'd think that is a bug.
+ // return span[Y_AXIS] + getTopInset() - getBottomInset();
+ return span[Y_AXIS] + getTopInset() + getBottomInset();
}
/**
@@ -977,7 +1083,11 @@ public class BoxView
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias)
{
- // FIXME: What to do here?
+ if (! isAllocationValid())
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ setSize(r.width, r.height);
+ }
return super.viewToModel(x, y, a, bias);
}
@@ -988,32 +1098,6 @@ public class BoxView
}
/**
- * Updates the child requirements along the specified axis. The requirements
- * are only updated if the layout for the specified axis is marked as
- * invalid.
- *
- * @param axis the axis to be updated
- */
- private void updateChildRequirements(int axis)
- {
- if (! isLayoutValid(axis))
- {
- int numChildren = getViewCount();
- if (childReqs[axis] == null || childReqs[axis].length != numChildren)
- childReqs[axis] = new SizeRequirements[numChildren];
- for (int i = 0; i < numChildren; ++i)
- {
- View child = getView(i);
- childReqs[axis][i] =
- new SizeRequirements((int) child.getMinimumSpan(axis),
- (int) child.getPreferredSpan(axis),
- (int) child.getMaximumSpan(axis),
- child.getAlignment(axis));
- }
- }
- }
-
- /**
* Updates the view's cached requirements along the specified axis if
* necessary. The requirements are only updated if the layout for the
* specified axis is marked as invalid.
diff --git a/javax/swing/text/ComponentView.java b/javax/swing/text/ComponentView.java
index a7d237ab7..555120396 100644
--- a/javax/swing/text/ComponentView.java
+++ b/javax/swing/text/ComponentView.java
@@ -39,11 +39,11 @@ 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.SwingConstants;
import javax.swing.SwingUtilities;
/**
@@ -62,11 +62,161 @@ 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>.
*
@@ -99,13 +249,20 @@ public class ComponentView extends View
*/
public float getAlignment(int axis)
{
- float align;
- if (axis == X_AXIS)
- align = getComponent().getAlignmentX();
- else if (axis == Y_AXIS)
- align = getComponent().getAlignmentY();
+ 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
- throw new IllegalArgumentException();
+ align = super.getAlignment(axis);
return align;
}
@@ -118,8 +275,6 @@ public class ComponentView extends View
*/
public final Component getComponent()
{
- if (comp == null)
- comp = createComponent();
return comp;
}
@@ -135,49 +290,70 @@ public class ComponentView extends View
*/
public float getMaximumSpan(int axis)
{
- float span;
- if (axis == X_AXIS)
- span = getComponent().getMaximumSize().width;
- else if (axis == Y_AXIS)
- span = getComponent().getMaximumSize().height;
- else
- throw new IllegalArgumentException();
+ 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)
{
- float span;
- if (axis == X_AXIS)
- span = getComponent().getMinimumSize().width;
- else if (axis == Y_AXIS)
- span = getComponent().getMinimumSize().height;
- else
- throw new IllegalArgumentException();
+ 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)
{
- float span;
- if (axis == X_AXIS)
- span = getComponent().getPreferredSize().width;
- else if (axis == Y_AXIS)
- span = getComponent().getPreferredSize().height;
- else
- throw new IllegalArgumentException();
+ 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
{
- Element el = getElement();
- if (pos < el.getStartOffset() || pos >= el.getEndOffset())
- throw new BadLocationException("Illegal offset for this view", pos);
- Rectangle r = a.getBounds();
- Component c = getComponent();
- return new Rectangle(r.x, r.y, c.getWidth(), c.getHeight());
+ 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);
}
/**
@@ -191,8 +367,11 @@ public class ComponentView extends View
*/
public void paint(Graphics g, Shape a)
{
- Rectangle r = a.getBounds();
- getComponent().setBounds(r.x, r.y, r.width, r.height);
+ if (interceptor != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ interceptor.setBounds(r.x, r.y, r.width, r.height);
+ }
}
/**
@@ -209,15 +388,16 @@ public class ComponentView extends View
*/
public void setParent(final View p)
{
+ super.setParent(p);
if (SwingUtilities.isEventDispatchThread())
- setParentImpl(p);
+ setParentImpl();
else
SwingUtilities.invokeLater
(new Runnable()
{
public void run()
{
- setParentImpl(p);
+ setParentImpl();
}
});
}
@@ -225,23 +405,41 @@ public class ComponentView extends View
/**
* The implementation of {@link #setParent}. This is package private to
* avoid a synthetic accessor method.
- *
- * @param p the parent view to set
*/
- private void setParentImpl(View p)
+ void setParentImpl()
{
- super.setParent(p);
+ View p = getParent();
if (p != null)
{
- Component c = getComponent();
- p.getContainer().add(c);
+ 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
{
- Component c = getComponent();
- Container parent = c.getParent();
- parent.remove(c);
- comp = null;
+ if (interceptor != null)
+ {
+ Container parent = interceptor.getParent();
+ if (parent != null)
+ parent.remove(interceptor);
+ }
}
}
@@ -259,10 +457,21 @@ public class ComponentView extends View
*/
public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
{
- // The element should only have one character position and it is clear
- // that this position is the position that best matches the given screen
- // coordinates, simply because this view has only this one position.
- Element el = getElement();
- return el.getStartOffset();
+ 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;
}
}
diff --git a/javax/swing/text/CompositeView.java b/javax/swing/text/CompositeView.java
index 6f487b898..ab587a9e1 100644
--- a/javax/swing/text/CompositeView.java
+++ b/javax/swing/text/CompositeView.java
@@ -38,7 +38,6 @@ exception statement from your version. */
package javax.swing.text;
-import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Shape;
@@ -57,7 +56,12 @@ public abstract class CompositeView
/**
* The child views of this <code>CompositeView</code>.
*/
- View[] children;
+ private View[] children;
+
+ /**
+ * The number of child views.
+ */
+ private int numChildren;
/**
* The allocation of this <code>View</code> minus its insets. This is
@@ -70,7 +74,10 @@ public abstract class CompositeView
* The insets of this <code>CompositeView</code>. This is initialized
* in {@link #setInsets}.
*/
- Insets insets;
+ private short top;
+ private short bottom;
+ private short left;
+ private short right;
/**
* Creates a new <code>CompositeView</code> for the given
@@ -82,7 +89,10 @@ public abstract class CompositeView
{
super(element);
children = new View[0];
- insets = new Insets(0, 0, 0, 0);
+ top = 0;
+ bottom = 0;
+ left = 0;
+ right = 0;
}
/**
@@ -96,16 +106,22 @@ public abstract class CompositeView
*/
protected void loadChildren(ViewFactory f)
{
- Element el = getElement();
- int count = el.getElementCount();
- View[] newChildren = new View[count];
- for (int i = 0; i < count; ++i)
+ if (f != null)
{
- Element child = el.getElement(i);
- View view = f.create(child);
- newChildren[i] = view;
+ Element el = getElement();
+ int count = el.getElementCount();
+ View[] newChildren = new View[count];
+ for (int i = 0; i < count; ++i)
+ {
+ Element child = el.getElement(i);
+ View view = f.create(child);
+ newChildren[i] = view;
+ }
+ // I'd have called replace(0, getViewCount(), newChildren) here
+ // in order to replace all existing views. However according to
+ // Harmony's tests this is not what the RI does.
+ replace(0, 0, newChildren);
}
- replace(0, getViewCount(), newChildren);
}
/**
@@ -129,7 +145,7 @@ public abstract class CompositeView
*/
public int getViewCount()
{
- return children.length;
+ return numChildren;
}
/**
@@ -156,24 +172,42 @@ public abstract class CompositeView
*/
public void replace(int offset, int length, View[] views)
{
- // Check for null views to add.
- for (int i = 0; i < views.length; ++i)
- if (views[i] == null)
- throw new NullPointerException("Added views must not be null");
-
- int endOffset = offset + length;
+ // Make sure we have an array. The Harmony testsuite indicates that we
+ // have to do something like this.
+ if (views == null)
+ views = new View[0];
// First we set the parent of the removed children to null.
+ int endOffset = offset + length;
for (int i = offset; i < endOffset; ++i)
- children[i].setParent(null);
+ {
+ if (children[i].getParent() == this)
+ children[i].setParent(null);
+ children[i] = null;
+ }
- View[] newChildren = new View[children.length - length + views.length];
- System.arraycopy(children, 0, newChildren, 0, offset);
- System.arraycopy(views, 0, newChildren, offset, views.length);
- System.arraycopy(children, offset + length, newChildren,
- offset + views.length,
- children.length - (offset + length));
- children = newChildren;
+ // Update the children array.
+ int delta = views.length - length;
+ int src = offset + length;
+ int numMove = numChildren - src;
+ int dst = src + delta;
+ if (numChildren + delta > children.length)
+ {
+ // Grow array.
+ int newLength = Math.max(2 * children.length, numChildren + delta);
+ View[] newChildren = new View[newLength];
+ System.arraycopy(children, 0, newChildren, 0, offset);
+ System.arraycopy(views, 0, newChildren, offset, views.length);
+ System.arraycopy(children, src, newChildren, dst, numMove);
+ children = newChildren;
+ }
+ else
+ {
+ // Patch existing array.
+ System.arraycopy(children, src, children, dst, numMove);
+ System.arraycopy(views, 0, children, offset, views.length);
+ }
+ numChildren += delta;
// Finally we set the parent of the added children to this.
for (int i = 0; i < views.length; ++i)
@@ -258,27 +292,6 @@ public abstract class CompositeView
}
/**
- * A helper method for {@link #modelToView(int, Position.Bias, int,
- * Position.Bias, Shape)}. This creates a default location when there is
- * no child view that can take responsibility for mapping the position to
- * view coordinates. Depending on the specified bias this will be the
- * left or right edge of this view's allocation.
- *
- * @param a the allocation for this view
- * @param bias the bias
- *
- * @return a default location
- */
- private Shape createDefaultLocation(Shape a, Position.Bias bias)
- {
- Rectangle alloc = a.getBounds();
- Rectangle location = new Rectangle(alloc.x, alloc.y, 1, alloc.height);
- if (bias == Position.Bias.Forward)
- location.x = alloc.x + alloc.width;
- return location;
- }
-
- /**
* Maps a region in the document into the coordinate space of the View.
*
* @param p1 the beginning position inside the document
@@ -394,7 +407,7 @@ public abstract class CompositeView
*/
public int getViewIndex(int pos, Position.Bias b)
{
- if (b == Position.Bias.Backward && pos != 0)
+ if (b == Position.Bias.Backward)
pos -= 1;
int i = -1;
if (pos >= getStartOffset() && pos < getEndOffset())
@@ -528,10 +541,10 @@ public abstract class CompositeView
insideAllocation = inside;
}
}
- inside.x = alloc.x + insets.left;
- inside.y = alloc.y + insets.top;
- inside.width = alloc.width - insets.left - insets.right;
- inside.height = alloc.height - insets.top - insets.bottom;
+ inside.x = alloc.x + left;
+ inside.y = alloc.y + top;
+ inside.width = alloc.width - left - right;
+ inside.height = alloc.height - top - bottom;
return inside;
}
@@ -546,39 +559,26 @@ public abstract class CompositeView
*/
protected void setParagraphInsets(AttributeSet attributes)
{
- Float l = (Float) attributes.getAttribute(StyleConstants.LeftIndent);
- short left = 0;
- if (l != null)
- left = l.shortValue();
- Float r = (Float) attributes.getAttribute(StyleConstants.RightIndent);
- short right = 0;
- if (r != null)
- right = r.shortValue();
- Float t = (Float) attributes.getAttribute(StyleConstants.SpaceAbove);
- short top = 0;
- if (t != null)
- top = t.shortValue();
- Float b = (Float) attributes.getAttribute(StyleConstants.SpaceBelow);
- short bottom = 0;
- if (b != null)
- bottom = b.shortValue();
- setInsets(top, left, bottom, right);
+ top = (short) StyleConstants.getSpaceAbove(attributes);
+ bottom = (short) StyleConstants.getSpaceBelow(attributes);
+ left = (short) StyleConstants.getLeftIndent(attributes);
+ right = (short) StyleConstants.getRightIndent(attributes);
}
/**
* Sets the insets of this <code>CompositeView</code>.
*
- * @param top the top inset
- * @param left the left inset
- * @param bottom the bottom inset
- * @param right the right inset
+ * @param t the top inset
+ * @param l the left inset
+ * @param b the bottom inset
+ * @param r the right inset
*/
- protected void setInsets(short top, short left, short bottom, short right)
+ protected void setInsets(short t, short l, short b, short r)
{
- insets.top = top;
- insets.left = left;
- insets.bottom = bottom;
- insets.right = right;
+ top = t;
+ left = l;
+ bottom = b;
+ right = r;
}
/**
@@ -588,7 +588,7 @@ public abstract class CompositeView
*/
protected short getLeftInset()
{
- return (short) insets.left;
+ return left;
}
/**
@@ -598,7 +598,7 @@ public abstract class CompositeView
*/
protected short getRightInset()
{
- return (short) insets.right;
+ return right;
}
/**
@@ -608,7 +608,7 @@ public abstract class CompositeView
*/
protected short getTopInset()
{
- return (short) insets.top;
+ return top;
}
/**
@@ -618,7 +618,7 @@ public abstract class CompositeView
*/
protected short getBottomInset()
{
- return (short) insets.bottom;
+ return bottom;
}
/**
diff --git a/javax/swing/text/DefaultEditorKit.java b/javax/swing/text/DefaultEditorKit.java
index 8602e69f8..aa69deca5 100644
--- a/javax/swing/text/DefaultEditorKit.java
+++ b/javax/swing/text/DefaultEditorKit.java
@@ -38,7 +38,6 @@ exception statement from your version. */
package javax.swing.text;
-import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
@@ -312,19 +311,21 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- int offs = t.getDocument().getLength();
- Caret c = t.getCaret();
- c.setDot(0);
- c.moveDot(offs);
-
- try
- {
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- catch(BadLocationException ble)
- {
- // Can't happen.
- }
+ if (t != null)
+ {
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.setDot(0);
+ c.moveDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
}
}
@@ -339,15 +340,18 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- c.moveDot(0);
- try
- {
- c.setMagicCaretPosition(t.modelToView(0).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ Caret c = t.getCaret();
+ c.moveDot(0);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(0).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -363,16 +367,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- int offs = t.getDocument().getLength();
- Caret c = t.getCaret();
- c.moveDot(offs);
- try
- {
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.moveDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -389,17 +396,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- try
+ if (t != null)
{
- int offs = Utilities.getRowStart(t, c.getDot());
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ Caret c = t.getCaret();
+ try
+ {
+ int offs = Utilities.getRowStart(t, c.getDot());
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
- catch(BadLocationException ble)
- {
- // Can't happen.
- }
-
}
}
@@ -414,17 +423,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- try
- {
- int offs = Utilities.getRowEnd(t, c.getDot());
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ Caret c = t.getCaret();
+ try
+ {
+ int offs = Utilities.getRowEnd(t, c.getDot());
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
-
}
}
@@ -438,20 +449,21 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- try
- {
- int offs1 = Utilities.getRowStart(t, c.getDot());
- int offs2 = Utilities.getRowEnd(t, c.getDot());
-
- c.setDot(offs2);
- c.moveDot(offs1);
-
- c.setMagicCaretPosition(t.modelToView(offs2).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ Caret c = t.getCaret();
+ try
+ {
+ int offs1 = Utilities.getRowStart(t, c.getDot());
+ int offs2 = Utilities.getRowEnd(t, c.getDot());
+ c.setDot(offs2);
+ c.moveDot(offs1);
+ c.setMagicCaretPosition(t.modelToView(offs2).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -466,51 +478,52 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- int dot = c.getDot();
-
- try
+ if (t != null)
{
- int wordStart = Utilities.getWordStart(t, dot);
-
- if (dot == wordStart)
- {
- // Current cursor position is on the first character in a word.
- c.setDot(wordStart);
- c.moveDot(Utilities.getWordEnd(t, wordStart));
- }
- else
+ Caret c = t.getCaret();
+ int dot = c.getDot();
+ try
{
- // Current cursor position is not on the first character
- // in a word.
- int nextWord = Utilities.getNextWord(t, dot);
- int previousWord = Utilities.getPreviousWord(t, dot);
- int previousWordEnd = Utilities.getWordEnd(t, previousWord);
-
- // Cursor position is in the space between two words. In such a
- // situation just select the space.
- if (dot >= previousWordEnd && dot <= nextWord)
+ int wordStart = Utilities.getWordStart(t, dot);
+
+ if (dot == wordStart)
{
- c.setDot(previousWordEnd);
- c.moveDot(nextWord);
+ // Current cursor position is on the first character in a word.
+ c.setDot(wordStart);
+ c.moveDot(Utilities.getWordEnd(t, wordStart));
}
else
{
- // Cursor position is inside a word. Just select it then.
- c.setDot(previousWord);
- c.moveDot(previousWordEnd);
+ // Current cursor position is not on the first character
+ // in a word.
+ int nextWord = Utilities.getNextWord(t, dot);
+ int previousWord = Utilities.getPreviousWord(t, dot);
+ int previousWordEnd = Utilities.getWordEnd(t, previousWord);
+
+ // Cursor position is in the space between two words. In such a
+ // situation just select the space.
+ if (dot >= previousWordEnd && dot <= nextWord)
+ {
+ c.setDot(previousWordEnd);
+ c.moveDot(nextWord);
+ }
+ else
+ {
+ // Cursor position is inside a word. Just select it then.
+ c.setDot(previousWord);
+ c.moveDot(previousWordEnd);
+ }
}
- }
- // If the position was updated change the magic caret position
- // as well.
- if (c.getDot() != dot)
- c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation());
-
- }
- catch(BadLocationException ble)
- {
- // Can't happen.
+ // If the position was updated change the magic caret position
+ // as well.
+ if (c.getDot() != dot)
+ c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -715,21 +728,23 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- try
- {
- int offs = Utilities.getRowEnd(t, t.getCaretPosition());
-
- if (offs > -1)
- {
- Caret c = t.getCaret();
- c.setDot(offs);
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- }
- catch (BadLocationException ble)
- {
- // Nothing to do here
- }
+ if (t != null)
+ {
+ try
+ {
+ int offs = Utilities.getRowEnd(t, t.getCaretPosition());
+ if (offs > -1)
+ {
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // Nothing to do here
+ }
+ }
}
}
@@ -744,21 +759,23 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- try
- {
- int offs = Utilities.getRowStart(t, t.getCaretPosition());
-
- if (offs > -1)
- {
- Caret c = t.getCaret();
- c.setDot(offs);
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- }
- catch (BadLocationException ble)
- {
- // Do nothing here.
- }
+ if (t != null)
+ {
+ try
+ {
+ int offs = Utilities.getRowStart(t, t.getCaretPosition());
+ if (offs > -1)
+ {
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ }
+ catch (BadLocationException ble)
+ {
+ // Do nothing here.
+ }
+ }
}
}
@@ -773,16 +790,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- Caret c = t.getCaret();
- c.setDot(0);
- try
- {
- c.setMagicCaretPosition(t.modelToView(0).getLocation());
- }
- catch(BadLocationException ble)
- {
- // Can't happen.
- }
+ if (t != null)
+ {
+ Caret c = t.getCaret();
+ c.setDot(0);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(0).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
+ }
}
}
@@ -797,16 +817,19 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- int offs = t.getDocument().getLength();
- Caret c = t.getCaret();
- c.setDot(offs);
- try
- {
- c.setMagicCaretPosition(t.modelToView(offs).getLocation());
- }
- catch(BadLocationException ble)
+ if (t != null)
{
- // Can't happen.
+ int offs = t.getDocument().getLength();
+ Caret c = t.getCaret();
+ c.setDot(offs);
+ try
+ {
+ c.setMagicCaretPosition(t.modelToView(offs).getLocation());
+ }
+ catch(BadLocationException ble)
+ {
+ // Can't happen.
+ }
}
}
}
@@ -862,7 +885,9 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- getTextComponent(event).copy();
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.copy();
}
}
@@ -893,7 +918,9 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- getTextComponent(event).cut();
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.cut();
}
}
@@ -922,7 +949,9 @@ public class DefaultEditorKit extends EditorKit
*/
public void actionPerformed(ActionEvent event)
{
- getTextComponent(event).paste();
+ JTextComponent target = getTextComponent(event);
+ if (target != null)
+ target.paste();
}
}
@@ -957,14 +986,26 @@ public class DefaultEditorKit extends EditorKit
{
// first we filter the following events:
// - control characters
- // - key events with the ALT modifier (FIXME: filter that too!)
- int cp = event.getActionCommand().codePointAt(0);
- if (Character.isISOControl(cp))
- return;
-
- JTextComponent t = getTextComponent(event);
- if (t != null && t.isEnabled() && t.isEditable())
- t.replaceSelection(event.getActionCommand());
+ // - key events with the ALT modifier
+ JTextComponent target = getTextComponent(event);
+ if ((target != null) && (event != null))
+ {
+ if ((target.isEditable()) && (target.isEnabled()))
+ {
+ String content = event.getActionCommand();
+ int mod = event.getModifiers();
+ if ((content != null) && (content.length() > 0)
+ && (mod & ActionEvent.ALT_MASK) == 0
+ && (mod & ActionEvent.CTRL_MASK) == 0)
+ {
+ char c = content.charAt(0);
+ if ((c >= 0x20) && (c != 0x7F))
+ {
+ target.replaceSelection(content);
+ }
+ }
+ }
+ }
}
}
@@ -992,7 +1033,8 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- t.replaceSelection("\n");
+ if (t != null)
+ t.replaceSelection("\n");
}
}
@@ -1047,7 +1089,8 @@ public class DefaultEditorKit extends EditorKit
public void actionPerformed(ActionEvent event)
{
JTextComponent t = getTextComponent(event);
- t.replaceSelection("\t");
+ if (t != null)
+ t.replaceSelection("\t");
}
}
diff --git a/javax/swing/text/DefaultFormatter.java b/javax/swing/text/DefaultFormatter.java
index 19994e21b..bf7c02a00 100644
--- a/javax/swing/text/DefaultFormatter.java
+++ b/javax/swing/text/DefaultFormatter.java
@@ -216,7 +216,7 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter
*/
public DefaultFormatter()
{
- commitsOnValidEdit = true;
+ commitsOnValidEdit = false;
overwriteMode = true;
allowsInvalid = true;
}
diff --git a/javax/swing/text/DefaultStyledDocument.java b/javax/swing/text/DefaultStyledDocument.java
index 5705bde17..367666053 100644
--- a/javax/swing/text/DefaultStyledDocument.java
+++ b/javax/swing/text/DefaultStyledDocument.java
@@ -497,11 +497,6 @@ public class DefaultStyledDocument extends AbstractDocument implements
private int pos;
/**
- * The ElementChange that describes the latest changes.
- */
- private DefaultDocumentEvent documentEvent;
-
- /**
* The parent of the fracture.
*/
private Element fracturedParent;
@@ -835,13 +830,9 @@ public class DefaultStyledDocument extends AbstractDocument implements
*/
public void change(int offset, int length, DefaultDocumentEvent ev)
{
- if (length == 0)
- return;
- this.offset = offset;
- this.pos = offset;
- this.length = length;
- documentEvent = ev;
+ prepareEdit(offset, length);
changeUpdate();
+ finishEdit(ev);
}
/**
@@ -1258,8 +1249,6 @@ public class DefaultStyledDocument extends AbstractDocument implements
{
int len = tag.getLength();
int dir = tag.getDirection();
- AttributeSet tagAtts = tag.getAttributes();
-
if (dir == ElementSpec.JoinNextDirection)
{
if (! edit.isFracture)
@@ -1603,56 +1592,6 @@ public class DefaultStyledDocument extends AbstractDocument implements
}
- /**
- * Recreates all the elements from the parent to the element on the top of
- * the stack, starting from startFrom with the starting offset of
- * startOffset.
- *
- * @param recreate -
- * the elements to recreate
- * @param parent -
- * the element to add the new elements to
- * @param startFrom -
- * where to start recreating from
- * @param startOffset -
- * the offset of the first element
- * @return the array of added elements
- */
- private Element[] recreateAfterFracture(Element[] recreate,
- BranchElement parent, int startFrom,
- int startOffset)
- {
- Element[] added = new Element[recreate.length - startFrom];
- int j = 0;
- for (int i = startFrom; i < recreate.length; i++)
- {
- Element curr = recreate[i];
- int len = curr.getEndOffset() - curr.getStartOffset();
- if (curr instanceof LeafElement)
- added[j] = createLeafElement(parent, curr.getAttributes(),
- startOffset, startOffset + len);
- else
- {
- BranchElement br =
- (BranchElement) createBranchElement(parent,
- curr.getAttributes());
- int bSize = curr.getElementCount();
- for (int k = 0; k < bSize; k++)
- {
- Element bCurr = curr.getElement(k);
- Element[] add = recreateAfterFracture(new Element[] { bCurr }, br, 0,
- startOffset);
- br.replace(0, 0, add);
-
- }
- added[j] = br;
- }
- startOffset += len;
- j++;
- }
-
- return added;
- }
}
@@ -1985,7 +1924,6 @@ public class DefaultStyledDocument extends AbstractDocument implements
// start and ends at an element end.
buffer.change(offset, length, ev);
- Element root = getDefaultRootElement();
// Visit all paragraph elements within the specified interval
int end = offset + length;
Element curr;
diff --git a/javax/swing/text/FieldView.java b/javax/swing/text/FieldView.java
index f41f90130..0a078e53d 100644
--- a/javax/swing/text/FieldView.java
+++ b/javax/swing/text/FieldView.java
@@ -45,8 +45,6 @@ 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;
@@ -225,7 +223,7 @@ public class FieldView extends PlainView
public int getResizeWeight(int axis)
{
- return axis = axis == X_AXIS ? 1 : 0;
+ return axis == X_AXIS ? 1 : 0;
}
public Shape modelToView(int pos, Shape a, Position.Bias bias)
diff --git a/javax/swing/text/FlowView.java b/javax/swing/text/FlowView.java
index 3de95ed7f..085b0ac45 100644
--- a/javax/swing/text/FlowView.java
+++ b/javax/swing/text/FlowView.java
@@ -329,6 +329,16 @@ public abstract class FlowView extends BoxView
{
super(el, axis);
}
+
+ /**
+ * Overridden to return the attributes of the parent
+ * (== the FlowView instance).
+ */
+ public AttributeSet getAttributes()
+ {
+ View p = getParent();
+ return p != null ? p.getAttributes() : null;
+ }
}
/**
diff --git a/javax/swing/text/GapContent.java b/javax/swing/text/GapContent.java
index 7b1502777..990e9d464 100644
--- a/javax/swing/text/GapContent.java
+++ b/javax/swing/text/GapContent.java
@@ -165,7 +165,7 @@ public class GapContent
*/
int getOffset()
{
- assert mark == 0 || mark < gapStart || mark >= gapEnd :
+ assert mark == 0 || mark <= gapStart || mark >= gapEnd :
"Invalid mark: " + mark + ", gapStart: " + gapStart
+ ", gapEnd: " + gapEnd;
@@ -1013,7 +1013,7 @@ public class GapContent
*
* @return the index of the first occurance of o in l, or -i + 1 if not found
*/
- private int search(List l, Object o)
+ int search(List l, Object o)
{
int i = Collections.binarySearch(l, o);
while (i > 0)
diff --git a/javax/swing/text/GlyphView.java b/javax/swing/text/GlyphView.java
index 65025dd08..385f50bf6 100644
--- a/javax/swing/text/GlyphView.java
+++ b/javax/swing/text/GlyphView.java
@@ -46,7 +46,6 @@ import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Toolkit;
-import java.text.BreakIterator;
import javax.swing.SwingConstants;
import javax.swing.event.DocumentEvent;
@@ -300,28 +299,19 @@ public class GlyphView extends View implements TabableView, Cloneable
g.setColor(view.getForeground());
g.setFont(view.getFont());
int ascent = g.getFontMetrics().getAscent();
- if (view.isSuperscript())
- // TODO: Adjust font for superscripting.
- Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent - height / 2,
- g, tabEx, txt.offset);
- else if (view.isSubscript())
- // TODO: Adjust font for subscripting.
- Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent + height / 2,
- g, tabEx, txt.offset);
- else
- Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx,
- txt.offset);
+ Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx,
+ txt.offset);
if (view.isStrikeThrough())
{
int strikeHeight = (int) (getAscent(view) / 2);
- g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width,
+ g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.x + width,
bounds.y + strikeHeight);
}
if (view.isUnderline())
{
int lineHeight = (int) getAscent(view);
- g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width,
+ g.drawLine(bounds.x, bounds.y + lineHeight, bounds.x + width,
bounds.y + lineHeight);
}
g.setColor(oldColor);
@@ -385,7 +375,6 @@ public class GlyphView extends View implements TabableView, Cloneable
public float getSpan(GlyphView view, int p0, int p1,
TabExpander te, float x)
{
- Element el = view.getElement();
Font font = view.getFont();
FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
Segment txt = view.getText(p0, p1);
@@ -466,7 +455,7 @@ public class GlyphView extends View implements TabableView, Cloneable
{
Rectangle b = a.getBounds();
int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x);
- return pos;
+ return pos + v.getStartOffset();
}
}
@@ -574,19 +563,24 @@ public class GlyphView extends View implements TabableView, Cloneable
float span = 0;
checkPainter();
GlyphPainter painter = getGlyphPainter();
- if (axis == X_AXIS)
+ switch (axis)
{
- Element el = getElement();
+ case X_AXIS:
TabExpander tabEx = null;
View parent = getParent();
if (parent instanceof TabExpander)
tabEx = (TabExpander) parent;
span = painter.getSpan(this, getStartOffset(), getEndOffset(),
tabEx, 0.F);
+ break;
+ case Y_AXIS:
+ span = painter.getHeight(this);
+ if (isSuperscript())
+ span += span / 3;
+ break;
+ default:
+ throw new IllegalArgumentException("Illegal axis");
}
- else
- span = painter.getHeight(this);
-
return span;
}
@@ -762,16 +756,19 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public Font getFont()
{
- Element el = getElement();
- AttributeSet atts = el.getAttributes();
- String family = StyleConstants.getFontFamily(atts);
- int size = StyleConstants.getFontSize(atts);
- int style = Font.PLAIN;
- if (StyleConstants.isBold(atts))
- style |= Font.BOLD;
- if (StyleConstants.isItalic(atts))
- style |= Font.ITALIC;
- Font font = new Font(family, style, size);
+ Document doc = getDocument();
+ Font font = null;
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument styledDoc = (StyledDocument) doc;
+ font = styledDoc.getFont(getAttributes());
+ }
+ else
+ {
+ Container c = getContainer();
+ if (c != null)
+ font = c.getFont();
+ }
return font;
}
@@ -908,10 +905,8 @@ public class GlyphView extends View implements TabableView, Cloneable
return this;
checkPainter();
- GlyphPainter painter = getGlyphPainter();
// Try to find a suitable line break.
- BreakIterator lineBreaker = BreakIterator.getLineInstance();
Segment txt = new Segment();
try
{
@@ -1050,14 +1045,21 @@ public class GlyphView extends View implements TabableView, Cloneable
*/
public float getAlignment(int axis)
{
+ checkPainter();
float align;
if (axis == Y_AXIS)
{
- checkPainter();
GlyphPainter painter = getGlyphPainter();
float height = painter.getHeight(this);
float descent = painter.getDescent(this);
- align = (height - descent) / height;
+ float ascent = painter.getAscent(this);
+ if (isSuperscript())
+ align = 1.0F;
+ else if (isSubscript())
+ align = height > 0 ? (height - (descent + (ascent / 2))) / height
+ : 0;
+ else
+ align = height > 0 ? (height - descent) / height : 0;
}
else
align = super.getAlignment(axis);
diff --git a/javax/swing/text/InternationalFormatter.java b/javax/swing/text/InternationalFormatter.java
index 8db435c18..d6f2359e6 100644
--- a/javax/swing/text/InternationalFormatter.java
+++ b/javax/swing/text/InternationalFormatter.java
@@ -285,7 +285,7 @@ public class InternationalFormatter
if (minimum != null && minimum.compareTo(o) > 0)
throw new ParseException("The value may not be less than the"
+ " specified minimum", 0);
- if (maximum != null && minimum.compareTo(o) < 0)
+ if (maximum != null && maximum.compareTo(o) < 0)
throw new ParseException("The value may not be greater than the"
+ " specified maximum", 0);
return o;
diff --git a/javax/swing/text/LabelView.java b/javax/swing/text/LabelView.java
index a00a49c24..7cfeae862 100644
--- a/javax/swing/text/LabelView.java
+++ b/javax/swing/text/LabelView.java
@@ -114,28 +114,24 @@ public class LabelView extends GlyphView
*/
protected void setPropertiesFromAttributes()
{
- Element el = getElement();
- AttributeSet atts = el.getAttributes();
- // We cannot use StyleConstants.getBackground() here, because that returns
- // BLACK as default (when background == null). What we need is the
- // background setting of the text component instead, which is what we get
- // when background == null anyway.
- background = (Color) atts.getAttribute(StyleConstants.Background);
- foreground = StyleConstants.getForeground(atts);
+ AttributeSet atts = getAttributes();
setStrikeThrough(StyleConstants.isStrikeThrough(atts));
setSubscript(StyleConstants.isSubscript(atts));
setSuperscript(StyleConstants.isSuperscript(atts));
setUnderline(StyleConstants.isUnderline(atts));
- // Determine the font.
- String family = StyleConstants.getFontFamily(atts);
- int size = StyleConstants.getFontSize(atts);
- int style = Font.PLAIN;
- if (StyleConstants.isBold(atts))
- style |= Font.BOLD;
- if (StyleConstants.isItalic(atts))
- style |= Font.ITALIC;
- font = new Font(family, style, size);
+ // Determine the font and colors.
+ Document d = getDocument();
+ if (d instanceof StyledDocument)
+ {
+ StyledDocument doc = (StyledDocument) d;
+ font = doc.getFont(atts);
+ if (atts.isDefined(StyleConstants.Background))
+ background = doc.getBackground(atts);
+ else
+ background = null;
+ foreground = doc.getForeground(atts);
+ }
valid = true;
}
@@ -258,6 +254,8 @@ public class LabelView extends GlyphView
*/
public boolean isSubscript()
{
+ if (! valid)
+ setPropertiesFromAttributes();
return subscript;
}
diff --git a/javax/swing/text/MaskFormatter.java b/javax/swing/text/MaskFormatter.java
index d12b9ea29..581cceb61 100644
--- a/javax/swing/text/MaskFormatter.java
+++ b/javax/swing/text/MaskFormatter.java
@@ -110,9 +110,7 @@ public class MaskFormatter extends DefaultFormatter
*/
public MaskFormatter (String mask) throws java.text.ParseException
{
- // Override super's default behaviour, in MaskFormatter the default
- // is not to allow invalid values
- setAllowsInvalid(false);
+ this();
setMask (mask);
}
@@ -307,60 +305,124 @@ public class MaskFormatter extends DefaultFormatter
*/
public Object stringToValue (String value) throws ParseException
{
- int vLength = value.length();
-
- // For value to be a valid it must be the same length as the mask
- // note this doesn't take into account symbols that occupy more than
- // one character, this is something we may possibly need to fix.
- if (maskLength != vLength)
- throw new ParseException ("stringToValue passed invalid value", vLength);
-
- // Check if the value is valid according to the mask and valid/invalid
- // sets.
- try
- {
- convertValue(value, false);
- }
- catch (ParseException pe)
- {
- throw new ParseException("stringToValue passed invalid value",
- pe.getErrorOffset());
- }
-
- if (!getValueContainsLiteralCharacters())
- value = stripLiterals(value);
- return super.stringToValue(value);
+ return super.stringToValue(convertStringToValue(value));
}
- /**
- * Strips the literal characters from the given String.
- * @param value the String to strip
- * @return the stripped String
- */
- String stripLiterals(String value)
+ private String convertStringToValue(String value)
+ throws ParseException
{
StringBuffer result = new StringBuffer();
- for (int i = 0; i < value.length(); i++)
+ char valueChar;
+ boolean isPlaceHolder;
+
+ int length = mask.length();
+ for (int i = 0, j = 0; j < length; j++)
{
- // Only append the characters that don't correspond to literal
- // characters in the mask.
- switch (mask.charAt(i))
+ char maskChar = mask.charAt(j);
+
+ if (i < value.length())
+ {
+ isPlaceHolder = false;
+ valueChar = value.charAt(i);
+ if (maskChar != ESCAPE_CHAR && maskChar != valueChar)
+ {
+ if (invalidChars != null
+ && invalidChars.indexOf(valueChar) != -1)
+ throw new ParseException("Invalid character: " + valueChar, i);
+ if (validChars != null
+ && validChars.indexOf(valueChar) == -1)
+ throw new ParseException("Invalid character: " + valueChar, i);
+ }
+ }
+ else if (placeHolder != null && i < placeHolder.length())
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolder.charAt(i);
+ }
+ else
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolderChar;
+ }
+
+ // This switch block on the mask character checks that the character
+ // within <code>value</code> at that point is valid according to the
+ // mask and also converts to upper/lowercase as needed.
+ switch (maskChar)
{
case NUM_CHAR:
+ if (! Character.isDigit(valueChar))
+ throw new ParseException("Number expected: " + valueChar, i);
+ result.append(valueChar);
+ i++;
+ break;
case UPPERCASE_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(Character.toUpperCase(valueChar));
+ i++;
+ break;
case LOWERCASE_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(Character.toLowerCase(valueChar));
+ i++;
+ break;
case ALPHANUM_CHAR:
+ if (! Character.isLetterOrDigit(valueChar))
+ throw new ParseException("Letter or number expected", i);
+ result.append(valueChar);
+ i++;
+ break;
case LETTER_CHAR:
+ if (! Character.isLetter(valueChar))
+ throw new ParseException("Letter expected", i);
+ result.append(valueChar);
+ i++;
+ break;
case HEX_CHAR:
+ if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
+ throw new ParseException("Hexadecimal character expected", i);
+ result.append(valueChar);
+ i++;
+ break;
case ANYTHING_CHAR:
- result.append(value.charAt(i));
+ result.append(valueChar);
+ i++;
+ break;
+ case ESCAPE_CHAR:
+ // Escape character, check the next character to make sure that
+ // the literals match
+ j++;
+ if (j < length)
+ {
+ maskChar = mask.charAt(j);
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ {
+ result.append(maskChar);
+ }
+ i++;
+ }
+ else if (! isPlaceHolder)
+ throw new ParseException("Bad match at trailing escape: ", i);
break;
default:
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ {
+ result.append(maskChar);
+ }
+ i++;
}
}
return result.toString();
}
-
+
/**
* Returns a String representation of the Object value based on the mask.
*
@@ -368,21 +430,10 @@ public class MaskFormatter extends DefaultFormatter
* @throws ParseException if value is invalid for this mask and valid/invalid
* character sets
*/
- public String valueToString (Object value) throws ParseException
+ public String valueToString(Object value) throws ParseException
{
- String result = super.valueToString(value);
- int rLength = result.length();
-
- // If value is longer than the mask, truncate it. Note we may need to
- // account for symbols that are more than one character long.
- if (rLength > maskLength)
- result = result.substring(0, maskLength);
-
- // Verify the validity and convert to upper/lowercase as needed.
- result = convertValue(result, true);
- if (rLength < maskLength)
- return pad(result, rLength);
- return result;
+ String string = value != null ? value.toString() : "";
+ return convertValueToString(string);
}
/**
@@ -390,194 +441,116 @@ public class MaskFormatter extends DefaultFormatter
* sure that it is valid. If <code>convert</code> is true, it also
* converts letters to upper/lowercase as required by the mask.
* @param value the String to convert
- * @param convert true if we should convert letters to upper/lowercase
* @return the converted String
* @throws ParseException if the given String isn't valid for the mask
*/
- String convertValue(String value, boolean convert) throws ParseException
+ private String convertValueToString(String value)
+ throws ParseException
{
- StringBuffer result = new StringBuffer(value);
- char markChar;
- char resultChar;
- boolean literal;
-
- // this boolean is specifically to avoid calling the isCharValid method
- // when neither invalidChars or validChars has been set
- boolean checkCharSets = (invalidChars != null || validChars != null);
+ StringBuffer result = new StringBuffer();
+ char valueChar;
+ boolean isPlaceHolder;
- for (int i = 0, j = 0; i < value.length(); i++, j++)
+ int length = mask.length();
+ for (int i = 0, j = 0; j < length; j++)
{
- literal = false;
- resultChar = result.charAt(i);
+ char maskChar = mask.charAt(j);
+ if (i < value.length())
+ {
+ isPlaceHolder = false;
+ valueChar = value.charAt(i);
+ if (maskChar != ESCAPE_CHAR && valueChar != maskChar)
+ {
+ if (invalidChars != null
+ && invalidChars.indexOf(valueChar) != -1)
+ throw new ParseException("Invalid character: " + valueChar,
+ i);
+ if (validChars != null && validChars.indexOf(valueChar) == -1)
+ throw new ParseException("Invalid character: " + valueChar +" maskChar: " + maskChar,
+ i);
+ }
+ }
+ else if (placeHolder != null && i < placeHolder.length())
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolder.charAt(i);
+ }
+ else
+ {
+ isPlaceHolder = true;
+ valueChar = placeHolderChar;
+ }
+
// This switch block on the mask character checks that the character
// within <code>value</code> at that point is valid according to the
// mask and also converts to upper/lowercase as needed.
- switch (mask.charAt(j))
+ switch (maskChar)
{
case NUM_CHAR:
- if (!Character.isDigit(resultChar))
- throw new ParseException("Number expected", i);
+ if ( ! isPlaceHolder && ! Character.isDigit(valueChar))
+ throw new ParseException("Number expected: " + valueChar, i);
+ result.append(valueChar);
+ i++;
break;
case UPPERCASE_CHAR:
- if (!Character.isLetter(resultChar))
+ if (! Character.isLetter(valueChar))
throw new ParseException("Letter expected", i);
- if (convert)
- result.setCharAt(i, Character.toUpperCase(resultChar));
+ result.append(Character.toUpperCase(valueChar));
+ i++;
break;
case LOWERCASE_CHAR:
- if (!Character.isLetter(resultChar))
+ if (! Character.isLetter(valueChar))
throw new ParseException("Letter expected", i);
- if (convert)
- result.setCharAt(i, Character.toLowerCase(resultChar));
+ result.append(Character.toLowerCase(valueChar));
+ i++;
break;
case ALPHANUM_CHAR:
- if (!Character.isLetterOrDigit(resultChar))
+ if (! Character.isLetterOrDigit(valueChar))
throw new ParseException("Letter or number expected", i);
+ result.append(valueChar);
+ i++;
break;
case LETTER_CHAR:
- if (!Character.isLetter(resultChar))
+ if (! Character.isLetter(valueChar))
throw new ParseException("Letter expected", i);
+ result.append(valueChar);
+ i++;
break;
case HEX_CHAR:
- if (hexString.indexOf(resultChar) == -1)
+ if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder)
throw new ParseException("Hexadecimal character expected", i);
+ result.append(valueChar);
+ i++;
break;
case ANYTHING_CHAR:
+ result.append(valueChar);
+ i++;
break;
case ESCAPE_CHAR:
// Escape character, check the next character to make sure that
// the literals match
j++;
- literal = true;
- if (resultChar != mask.charAt(j))
- throw new ParseException ("Invalid character: "+resultChar, i);
+ if (j < length)
+ {
+ maskChar = mask.charAt(j);
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ i++;
+ result.append(maskChar);
+ }
break;
default:
- literal = true;
- if (!getValueContainsLiteralCharacters() && convert)
- throw new ParseException ("Invalid character: "+resultChar, i);
- else if (resultChar != mask.charAt(j))
- throw new ParseException ("Invalid character: "+resultChar, i);
+ if (! isPlaceHolder && getValueContainsLiteralCharacters()
+ && valueChar != maskChar)
+ throw new ParseException ("Invalid character: "+ valueChar, i);
+ if (getValueContainsLiteralCharacters())
+ i++;
+ result.append(maskChar);
}
- // If necessary, check if the character is valid.
- if (!literal && checkCharSets && !isCharValid(resultChar))
- throw new ParseException("invalid character: "+resultChar, i);
-
- }
- return result.toString();
- }
-
- /**
- * Convenience method used by many other methods to check if a character is
- * valid according to the mask, the validChars, and the invalidChars. To
- * be valid a character must:
- * 1. be allowed by the mask
- * 2. be present in any non-null validChars String
- * 3. not be present in any non-null invalidChars String
- * @param testChar the character to test
- * @return true if the character is valid
- */
- boolean isCharValid(char testChar)
- {
- char lower = Character.toLowerCase(testChar);
- char upper = Character.toUpperCase(testChar);
- // If validChars isn't null, the character must appear in it.
- if (validChars != null)
- if (validChars.indexOf(lower) == -1 && validChars.indexOf(upper) == -1)
- return false;
- // If invalidChars isn't null, the character must not appear in it.
- if (invalidChars != null)
- if (invalidChars.indexOf(lower) != -1
- || invalidChars.indexOf(upper) != -1)
- return false;
- return true;
- }
-
- /**
- * Pads the value with literals, the placeholder String and/or placeholder
- * character as appropriate.
- * @param value the value to pad
- * @param currLength the current length of the value
- * @return the padded String
- */
- String pad (String value, int currLength)
- {
- StringBuffer result = new StringBuffer(value);
- int index = currLength;
- while (result.length() < maskLength)
- {
- // The character used to pad may be a literal, a character from the
- // place holder string, or the place holder character. getPadCharAt
- // will find the proper one for us.
- result.append (getPadCharAt(index));
- index++;
}
return result.toString();
}
- /**
- * Returns the character with which to pad the value at the given index
- * position. If the mask has a literal at this position, this is returned
- * otherwise if the place holder string is initialized and is longer than
- * <code>i</code> characters then the character at position <code>i</code>
- * from this String is returned. Else, the place holder character is
- * returned.
- * @param i the index at which we want to pad the value
- * @return the character with which we should pad the value
- */
- char getPadCharAt(int i)
- {
- boolean escaped = false;
- int target = i;
- char maskChar;
- int holderLength = placeHolder == null ? -1 : placeHolder.length();
- // We must iterate through the mask from the beginning, because the given
- // index doesn't account for escaped characters. For example, with the
- // mask "1A'A''A1" index 2 refers to the literalized A, not to the
- // single quotation.
- for (int n = 0; n < mask.length(); n++)
- {
- maskChar = mask.charAt(n);
- if (maskChar == ESCAPE_CHAR && !escaped)
- {
- target++;
- escaped = true;
- }
- else if (escaped == true)
- {
- // Check if target == n which means we've come to the character
- // we want to return and since it is a literal (because escaped
- // is true), we return it.
- if (target == n)
- return maskChar;
- escaped = false;
- }
- if (target == n)
- {
- // We've come to the character we want to return. It wasn't
- // escaped so if it isn't a literal we should return either
- // the character from place holder string or the place holder
- // character, depending on whether or not the place holder
- // string is long enough.
- switch (maskChar)
- {
- case NUM_CHAR:
- case UPPERCASE_CHAR:
- case LOWERCASE_CHAR:
- case ALPHANUM_CHAR:
- case LETTER_CHAR:
- case HEX_CHAR:
- case ANYTHING_CHAR:
- if (holderLength > i)
- return placeHolder.charAt(i);
- else
- return placeHolderChar;
- default:
- return maskChar;
- }
- }
- }
- // This shouldn't happen
- throw new AssertionError("MaskFormatter.getMaskCharAt failed");
- }
}
diff --git a/javax/swing/text/ParagraphView.java b/javax/swing/text/ParagraphView.java
index c4857863d..b0b4b246e 100644
--- a/javax/swing/text/ParagraphView.java
+++ b/javax/swing/text/ParagraphView.java
@@ -40,6 +40,7 @@ package javax.swing.text;
import java.awt.Shape;
+import javax.swing.SizeRequirements;
import javax.swing.event.DocumentEvent;
/**
@@ -64,11 +65,39 @@ public class ParagraphView extends FlowView implements TabExpander
super(el, X_AXIS);
}
+ /**
+ * Overridden to adjust when we are the first line, and firstLineIndent
+ * is not 0.
+ */
+ public short getLeftInset()
+ {
+ short leftInset = super.getLeftInset();
+ View parent = getParent();
+ if (parent != null)
+ {
+ if (parent.getView(0) == this)
+ leftInset += firstLineIndent;
+ }
+ return leftInset;
+ }
+
public float getAlignment(int axis)
{
float align;
if (axis == X_AXIS)
- align = 0.0F; // TODO: Implement according to justification
+ switch (justification)
+ {
+ case StyleConstants.ALIGN_RIGHT:
+ align = 1.0F;
+ break;
+ case StyleConstants.ALIGN_CENTER:
+ case StyleConstants.ALIGN_JUSTIFIED:
+ align = 0.5F;
+ break;
+ case StyleConstants.ALIGN_LEFT:
+ default:
+ align = 0.0F;
+ }
else
align = super.getAlignment(axis);
return align;
@@ -107,6 +136,27 @@ public class ParagraphView extends FlowView implements TabExpander
return index;
}
+
+ /**
+ * Overridden to perform a baseline layout. The normal BoxView layout
+ * isn't completely suitable for rows.
+ */
+ protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+ int[] spans)
+ {
+ baselineLayout(targetSpan, axis, offsets, spans);
+ }
+
+ /**
+ * Overridden to perform a baseline layout. The normal BoxView layout
+ * isn't completely suitable for rows.
+ */
+ protected SizeRequirements calculateMinorAxisRequirements(int axis,
+ SizeRequirements r)
+ {
+ return baselineRequirements(axis, r);
+ }
+
protected void loadChildren(ViewFactory vf)
{
// Do nothing here. The children are added while layouting.
@@ -192,11 +242,14 @@ public class ParagraphView extends FlowView implements TabExpander
*
* @param ev the document event
* @param a the allocation of this view
- * @param fv the view factory to use for creating new child views
+ * @param vf the view factory to use for creating new child views
*/
- public void changedUpdate(DocumentEvent ev, Shape a, ViewFactory fv)
+ public void changedUpdate(DocumentEvent ev, Shape a, ViewFactory vf)
{
setPropertiesFromAttributes();
+ layoutChanged(X_AXIS);
+ layoutChanged(Y_AXIS);
+ super.changedUpdate(ev, a, vf);
}
/**
diff --git a/javax/swing/text/PlainView.java b/javax/swing/text/PlainView.java
index 5d0ce4a37..e048d5f71 100644
--- a/javax/swing/text/PlainView.java
+++ b/javax/swing/text/PlainView.java
@@ -280,7 +280,6 @@ public class PlainView extends View implements TabExpander
// FIXME: Text may be scrolled.
Document document = textComponent.getDocument();
Element root = getElement();
- int y = rect.y + metrics.getAscent();
int height = metrics.getHeight();
// For layered highlighters we need to paint the layered highlights
@@ -292,7 +291,16 @@ public class PlainView extends View implements TabExpander
int count = root.getElementCount();
- for (int i = 0; i < count; i++)
+ // Determine first and last line inside the clip.
+ Rectangle clip = g.getClipBounds();
+ SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, rect.height,
+ clip);
+ int line0 = (clip.y - rect.y) / height;
+ line0 = Math.max(0, Math.min(line0, count - 1));
+ int line1 = (clip.y + clip.height - rect.y) / height;
+ line1 = Math.max(0, Math.min(line1, count - 1));
+ int y = rect.y + metrics.getAscent() + height * line0;
+ for (int i = line0; i <= line1; i++)
{
if (hl != null)
{
diff --git a/javax/swing/text/Position.java b/javax/swing/text/Position.java
index bb1449e18..d02eb834d 100644
--- a/javax/swing/text/Position.java
+++ b/javax/swing/text/Position.java
@@ -42,8 +42,8 @@ public interface Position
{
static final class Bias
{
- public static final Bias Backward = new Bias("backward");
- public static final Bias Forward = new Bias("forward");
+ public static final Bias Backward = new Bias("Backward");
+ public static final Bias Forward = new Bias("Forward");
private String name;
diff --git a/javax/swing/text/SimpleAttributeSet.java b/javax/swing/text/SimpleAttributeSet.java
index 85556b5da..701fa8a7c 100644
--- a/javax/swing/text/SimpleAttributeSet.java
+++ b/javax/swing/text/SimpleAttributeSet.java
@@ -123,9 +123,17 @@ public class SimpleAttributeSet
*/
public Object clone()
{
- SimpleAttributeSet s = new SimpleAttributeSet();
- s.tab = (Hashtable) tab.clone();
- return s;
+ SimpleAttributeSet attr = null;
+ try
+ {
+ attr = (SimpleAttributeSet) super.clone();
+ attr.tab = (Hashtable) tab.clone();
+ }
+ catch (CloneNotSupportedException ex)
+ {
+ assert false;
+ }
+ return attr;
}
/**
diff --git a/javax/swing/text/StringContent.java b/javax/swing/text/StringContent.java
index 8014dc3bc..4a3f9d752 100644
--- a/javax/swing/text/StringContent.java
+++ b/javax/swing/text/StringContent.java
@@ -39,6 +39,9 @@ exception statement from your version. */
package javax.swing.text;
import java.io.Serializable;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.Vector;
@@ -57,6 +60,76 @@ import javax.swing.undo.UndoableEdit;
public final class StringContent
implements AbstractDocument.Content, Serializable
{
+ /**
+ * Stores a reference to a mark that can be resetted to the original value
+ * after a mark has been moved. This is used for undoing actions.
+ */
+ private class UndoPosRef
+ {
+ /**
+ * The mark that might need to be reset.
+ */
+ private Mark mark;
+
+ /**
+ * The original offset to reset the mark to.
+ */
+ private int undoOffset;
+
+ /**
+ * Creates a new UndoPosRef.
+ *
+ * @param m the mark
+ */
+ UndoPosRef(Mark m)
+ {
+ mark = m;
+ undoOffset = mark.mark;
+ }
+
+ /**
+ * Resets the position of the mark to the value that it had when
+ * creating this UndoPosRef.
+ */
+ void reset()
+ {
+ mark.mark = undoOffset;
+ }
+ }
+
+ /**
+ * Holds a mark into the buffer that is used by StickyPosition to find
+ * the actual offset of the position. This is pulled out of the
+ * GapContentPosition object so that the mark and position can be handled
+ * independently, and most important, so that the StickyPosition can
+ * be garbage collected while we still hold a reference to the Mark object.
+ */
+ private class Mark
+ {
+ /**
+ * The actual mark into the buffer.
+ */
+ int mark;
+
+
+ /**
+ * The number of GapContentPosition object that reference this mark. If
+ * it reaches zero, it get's deleted by
+ * {@link StringContent#garbageCollect()}.
+ */
+ int refCount;
+
+ /**
+ * Creates a new Mark object for the specified offset.
+ *
+ * @param offset the offset
+ */
+ Mark(int offset)
+ {
+ mark = offset;
+ }
+ }
+
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = 4755994433709540381L;
@@ -65,7 +138,12 @@ public final class StringContent
private int count;
- private Vector positions = new Vector();
+ /**
+ * Holds the marks for the positions.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Vector marks;
private class InsertUndo extends AbstractUndoableEdit
{
@@ -75,6 +153,8 @@ public final class StringContent
private String redoContent;
+ private Vector positions;
+
public InsertUndo(int start, int length)
{
super();
@@ -87,10 +167,10 @@ public final class StringContent
super.undo();
try
{
- StringContent.this.checkLocation(this.start, this.length);
- this.redoContent = new String(StringContent.this.content, this.start,
- this.length);
- StringContent.this.remove(this.start, this.length);
+ if (marks != null)
+ positions = getPositionsInRange(null, start, length);
+ redoContent = getString(start, length);
+ remove(start, length);
}
catch (BadLocationException b)
{
@@ -103,7 +183,13 @@ public final class StringContent
super.redo();
try
{
- StringContent.this.insertString(this.start, this.redoContent);
+ insertString(start, redoContent);
+ redoContent = null;
+ if (positions != null)
+ {
+ updateUndoPositions(positions);
+ positions = null;
+ }
}
catch (BadLocationException b)
{
@@ -115,14 +201,19 @@ public final class StringContent
private class RemoveUndo extends AbstractUndoableEdit
{
private int start;
-
+ private int len;
private String undoString;
+ Vector positions;
+
public RemoveUndo(int start, String str)
{
super();
this.start = start;
+ len = str.length();
this.undoString = str;
+ if (marks != null)
+ positions = getPositionsInRange(null, start, str.length());
}
public void undo()
@@ -131,6 +222,12 @@ public final class StringContent
try
{
StringContent.this.insertString(this.start, this.undoString);
+ if (positions != null)
+ {
+ updateUndoPositions(positions);
+ positions = null;
+ }
+ undoString = null;
}
catch (BadLocationException bad)
{
@@ -143,8 +240,10 @@ public final class StringContent
super.redo();
try
{
- int end = this.undoString.length();
- StringContent.this.remove(this.start, end);
+ undoString = getString(start, len);
+ if (marks != null)
+ positions = getPositionsInRange(null, start, len);
+ remove(this.start, len);
}
catch (BadLocationException bad)
{
@@ -155,17 +254,18 @@ public final class StringContent
private class StickyPosition implements Position
{
- private int offset = -1;
+ Mark mark;
public StickyPosition(int offset)
{
- this.offset = offset;
- }
+ // Try to make space.
+ garbageCollect();
- // This is package-private to avoid an accessor method.
- void setOffset(int offset)
- {
- this.offset = this.offset >= 0 ? offset : -1;
+ mark = new Mark(offset);
+ mark.refCount++;
+ marks.add(mark);
+
+ new WeakReference(this, queueOfDeath);
}
/**
@@ -173,11 +273,25 @@ public final class StringContent
*/
public int getOffset()
{
- return offset < 0 ? 0 : offset;
+ return mark.mark;
}
}
/**
+ * Used in {@link #remove(int,int)}.
+ */
+ private static final char[] EMPTY = new char[0];
+
+ /**
+ * Queues all references to GapContentPositions that are about to be
+ * GC'ed. This is used to remove the corresponding marks from the
+ * positionMarks array if the number of references to that mark reaches zero.
+ *
+ * This is package private to avoid accessor synthetic methods.
+ */
+ ReferenceQueue queueOfDeath;
+
+ /**
* Creates a new instance containing the string "\n". This is equivalent
* to calling {@link #StringContent(int)} with an <code>initialLength</code>
* of 10.
@@ -196,6 +310,7 @@ public final class StringContent
public StringContent(int initialLength)
{
super();
+ queueOfDeath = new ReferenceQueue();
if (initialLength < 1)
initialLength = 1;
this.content = new char[initialLength];
@@ -207,14 +322,13 @@ public final class StringContent
int offset,
int length)
{
- Vector refPos = new Vector();
- Iterator iter = this.positions.iterator();
+ Vector refPos = v == null ? new Vector() : v;
+ Iterator iter = marks.iterator();
while(iter.hasNext())
{
- Position p = (Position) iter.next();
- if ((offset <= p.getOffset())
- && (p.getOffset() <= (offset + length)))
- refPos.add(p);
+ Mark m = (Mark) iter.next();
+ if (offset <= m.mark && m.mark <= offset + length)
+ refPos.add(new UndoPosRef(m));
}
return refPos;
}
@@ -231,10 +345,10 @@ public final class StringContent
*/
public Position createPosition(int offset) throws BadLocationException
{
- if (offset < this.count || offset > this.count)
- checkLocation(offset, 0);
+ // Lazily create marks vector.
+ if (marks == null)
+ marks = new Vector();
StickyPosition sp = new StickyPosition(offset);
- this.positions.add(sp);
return sp;
}
@@ -246,7 +360,7 @@ public final class StringContent
*/
public int length()
{
- return this.count;
+ return count;
}
/**
@@ -268,27 +382,23 @@ public final class StringContent
if (str == null)
throw new NullPointerException();
char[] insert = str.toCharArray();
- char[] temp = new char[this.content.length + insert.length];
- this.count += insert.length;
- // Copy array and insert the string.
- if (where > 0)
- System.arraycopy(this.content, 0, temp, 0, where);
- System.arraycopy(insert, 0, temp, where, insert.length);
- System.arraycopy(this.content, where, temp, (where + insert.length),
- (temp.length - where - insert.length));
- if (this.content.length < temp.length)
- this.content = new char[temp.length];
- // Copy the result in the original char array.
- System.arraycopy(temp, 0, this.content, 0, temp.length);
+ replace(where, 0, insert);
+
// Move all the positions.
- Vector refPos = getPositionsInRange(this.positions, where,
- temp.length - where);
- Iterator iter = refPos.iterator();
- while (iter.hasNext())
+ if (marks != null)
{
- StickyPosition p = (StickyPosition)iter.next();
- p.setOffset(p.getOffset() + str.length());
+ Iterator iter = marks.iterator();
+ int start = where;
+ if (start == 0)
+ start = 1;
+ while (iter.hasNext())
+ {
+ Mark m = (Mark) iter.next();
+ if (m.mark >= start)
+ m.mark += str.length();
+ }
}
+
InsertUndo iundo = new InsertUndo(where, insert.length);
return iundo;
}
@@ -308,32 +418,51 @@ public final class StringContent
public UndoableEdit remove(int where, int nitems) throws BadLocationException
{
checkLocation(where, nitems + 1);
- char[] temp = new char[(this.content.length - nitems)];
- this.count = this.count - nitems;
RemoveUndo rundo = new RemoveUndo(where, new String(this.content, where,
nitems));
- // Copy array.
- System.arraycopy(this.content, 0, temp, 0, where);
- System.arraycopy(this.content, where + nitems, temp, where,
- this.content.length - where - nitems);
- this.content = new char[temp.length];
- // Then copy the result in the original char array.
- System.arraycopy(temp, 0, this.content, 0, this.content.length);
+
+ replace(where, nitems, EMPTY);
// Move all the positions.
- Vector refPos = getPositionsInRange(this.positions, where,
- this.content.length + nitems - where);
- Iterator iter = refPos.iterator();
- while (iter.hasNext())
+ if (marks != null)
{
- StickyPosition p = (StickyPosition)iter.next();
- int result = p.getOffset() - nitems;
- p.setOffset(result);
- if (result < 0)
- this.positions.remove(p);
+ Iterator iter = marks.iterator();
+ while (iter.hasNext())
+ {
+ Mark m = (Mark) iter.next();
+ if (m.mark >= where + nitems)
+ m.mark -= nitems;
+ else if (m.mark >= where)
+ m.mark = where;
+ }
}
return rundo;
}
-
+
+ private void replace(int offs, int numRemove, char[] insert)
+ {
+ int insertLength = insert.length;
+ int delta = insertLength - numRemove;
+ int src = offs + numRemove;
+ int numMove = count - src;
+ int dest = src + delta;
+ if (count + delta >= content.length)
+ {
+ // Grow data array.
+ int newLength = Math.max(2 * content.length, count + delta);
+ char[] newContent = new char[newLength];
+ System.arraycopy(content, 0, newContent, 0, offs);
+ System.arraycopy(insert, 0, newContent, offs, insertLength);
+ System.arraycopy(content, src, newContent, dest, numMove);
+ content = newContent;
+ }
+ else
+ {
+ System.arraycopy(content, src, content, dest, numMove);
+ System.arraycopy(insert, 0, content, offs, insertLength);
+ }
+ count += delta;
+ }
+
/**
* Returns a new <code>String</code> containing the characters in the
* specified range.
@@ -348,6 +477,8 @@ public final class StringContent
*/
public String getString(int where, int len) throws BadLocationException
{
+ // The RI throws a StringIndexOutOfBoundsException here, which
+ // smells like a bug. We throw a BadLocationException instead.
checkLocation(where, len);
return new String(this.content, where, len);
}
@@ -368,22 +499,28 @@ public final class StringContent
public void getChars(int where, int len, Segment txt)
throws BadLocationException
{
- checkLocation(where, len);
- txt.array = this.content;
+ if (where + len > count)
+ throw new BadLocationException("Invalid location", where + len);
+ txt.array = content;
txt.offset = where;
txt.count = len;
}
/**
- * @specnote This method is not very well specified and the positions vector
- * is implementation specific. The undo positions are managed
- * differently in this implementation, this method is only here
- * for binary compatibility.
+ * Resets the positions in the specified vector to their original offset
+ * after a undo operation is performed. For example, after removing some
+ * content, the positions in the removed range will all be set to one
+ * offset. This method restores the positions to their original offsets
+ * after an undo.
*/
protected void updateUndoPositions(Vector positions)
{
- // We do nothing here.
+ for (Iterator i = positions.iterator(); i.hasNext();)
+ {
+ UndoPosRef pos = (UndoPosRef) i.next();
+ pos.reset();
+ }
}
/**
@@ -405,6 +542,29 @@ public final class StringContent
else if ((where + len) > this.count)
throw new BadLocationException("Invalid range", this.count);
}
-
+
+ /**
+ * Polls the queue of death for GapContentPositions, updates the
+ * corresponding reference count and removes the corresponding mark
+ * if the refcount reaches zero.
+ *
+ * This is package private to avoid accessor synthetic methods.
+ */
+ void garbageCollect()
+ {
+ Reference ref = queueOfDeath.poll();
+ while (ref != null)
+ {
+ if (ref != null)
+ {
+ StickyPosition pos = (StickyPosition) ref.get();
+ Mark m = pos.mark;
+ m.refCount--;
+ if (m.refCount == 0)
+ marks.remove(m);
+ }
+ ref = queueOfDeath.poll();
+ }
+ }
}
diff --git a/javax/swing/text/StyleConstants.java b/javax/swing/text/StyleConstants.java
index c7906b8ad..4e5005c6b 100644
--- a/javax/swing/text/StyleConstants.java
+++ b/javax/swing/text/StyleConstants.java
@@ -40,6 +40,7 @@ package javax.swing.text;
import java.awt.Color;
import java.awt.Component;
+import java.util.ArrayList;
import javax.swing.Icon;
@@ -163,6 +164,12 @@ public class StyleConstants
public static final Object ResolveAttribute = new StyleConstants("resolver");
+ /**
+ * All StyleConstants keys. This is used in StyleContext to register
+ * all known keys as static attribute keys for serialization.
+ */
+ static ArrayList keys;
+
String keyname;
// Package-private to avoid accessor constructor for use by
@@ -170,6 +177,9 @@ public class StyleConstants
StyleConstants(String k)
{
keyname = k;
+ if (keys == null)
+ keys = new ArrayList();
+ keys.add(this);
}
/**
@@ -729,6 +739,7 @@ public class StyleConstants
*/
public static void setIcon(MutableAttributeSet a, Icon c)
{
+ a.addAttribute(AbstractDocument.ElementNameAttribute, IconElementName);
a.addAttribute(IconAttribute, c);
}
diff --git a/javax/swing/text/StyleContext.java b/javax/swing/text/StyleContext.java
index 1e869485c..b01d1060f 100644
--- a/javax/swing/text/StyleContext.java
+++ b/javax/swing/text/StyleContext.java
@@ -43,19 +43,23 @@ import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Toolkit;
import java.io.IOException;
+import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.lang.ref.WeakReference;
import java.util.Enumeration;
import java.util.EventListener;
import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.WeakHashMap;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.EventListenerList;
public class StyleContext
- implements Serializable, AbstractDocument.AttributeContext
+ implements Serializable, AbstractDocument.AttributeContext
{
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = 8042858831190784241L;
@@ -66,11 +70,10 @@ public class StyleContext
/** The serialization UID (compatible with JDK1.5). */
private static final long serialVersionUID = -6690628971806226374L;
- protected ChangeEvent changeEvent;
+ protected transient ChangeEvent changeEvent;
protected EventListenerList listenerList;
- AttributeSet attributes;
- String name;
+ private transient AttributeSet attributes;
public NamedStyle()
{
@@ -84,22 +87,26 @@ public class StyleContext
public NamedStyle(String name, Style parent)
{
- this.name = name;
- this.attributes = getEmptySet();
- this.changeEvent = new ChangeEvent(this);
- this.listenerList = new EventListenerList();
- setResolveParent(parent);
+ attributes = getEmptySet();
+ listenerList = new EventListenerList();
+ if (name != null)
+ setName(name);
+ if (parent != null)
+ setResolveParent(parent);
}
public String getName()
{
+ String name = null;
+ if (isDefined(StyleConstants.NameAttribute))
+ name = getAttribute(StyleConstants.NameAttribute).toString();
return name;
}
public void setName(String n)
{
- name = n;
- fireStateChanged();
+ if (n != null)
+ addAttribute(StyleConstants.NameAttribute, n);
}
public void addChangeListener(ChangeListener l)
@@ -127,6 +134,9 @@ public class StyleContext
ChangeListener[] listeners = getChangeListeners();
for (int i = 0; i < listeners.length; ++i)
{
+ // Lazily create event.
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
listeners[i].stateChanged(changeEvent);
}
}
@@ -155,7 +165,10 @@ public class StyleContext
public AttributeSet copyAttributes()
{
- return attributes.copyAttributes();
+ // The RI returns a NamedStyle as copy, so do we.
+ NamedStyle copy = new NamedStyle();
+ copy.attributes = attributes.copyAttributes();
+ return copy;
}
public Object getAttribute(Object attrName)
@@ -210,112 +223,125 @@ public class StyleContext
public void setResolveParent(AttributeSet parent)
{
if (parent != null)
- {
- attributes = StyleContext.this.addAttribute
- (attributes, ResolveAttribute, parent);
- }
- fireStateChanged();
+ addAttribute(StyleConstants.ResolveAttribute, parent);
+ else
+ removeAttribute(StyleConstants.ResolveAttribute);
}
public String toString()
{
- return ("[NamedStyle: name=" + name + ", attrs=" + attributes.toString() + "]");
- }
+ return "NamedStyle:" + getName() + " " + attributes;
+ }
+
+ private void writeObject(ObjectOutputStream s)
+ throws IOException
+ {
+ s.defaultWriteObject();
+ writeAttributeSet(s, attributes);
+ }
+
+ private void readObject(ObjectInputStream s)
+ throws ClassNotFoundException, IOException
+ {
+ s.defaultReadObject();
+ attributes = SimpleAttributeSet.EMPTY;
+ readAttributeSet(s, this);
+ }
}
public class SmallAttributeSet
implements AttributeSet
{
final Object [] attrs;
+ private AttributeSet resolveParent;
public SmallAttributeSet(AttributeSet a)
{
- if (a == null)
- attrs = new Object[0];
- else
+ int n = a.getAttributeCount();
+ int i = 0;
+ attrs = new Object[n * 2];
+ Enumeration e = a.getAttributeNames();
+ while (e.hasMoreElements())
{
- int n = a.getAttributeCount();
- int i = 0;
- attrs = new Object[n * 2];
- Enumeration e = a.getAttributeNames();
- while (e.hasMoreElements())
- {
- Object name = e.nextElement();
- attrs[i++] = name;
- attrs[i++] = a.getAttribute(name);
- }
+ Object name = e.nextElement();
+ Object value = a.getAttribute(name);
+ if (name == ResolveAttribute)
+ resolveParent = (AttributeSet) value;
+ attrs[i++] = name;
+ attrs[i++] = value;
}
}
public SmallAttributeSet(Object [] a)
{
- if (a == null)
- attrs = new Object[0];
- else
+ attrs = a;
+ for (int i = 0; i < attrs.length; i += 2)
{
- attrs = new Object[a.length];
- System.arraycopy(a, 0, attrs, 0, a.length);
+ if (attrs[i] == ResolveAttribute)
+ resolveParent = (AttributeSet) attrs[i + 1];
}
}
public Object clone()
{
- return new SmallAttributeSet(this.attrs);
+ return this;
}
public boolean containsAttribute(Object name, Object value)
{
- for (int i = 0; i < attrs.length; i += 2)
- {
- if (attrs[i].equals(name) &&
- attrs[i+1].equals(value))
- return true;
- }
- return false;
+ return value.equals(getAttribute(name));
}
public boolean containsAttributes(AttributeSet a)
{
+ boolean res = true;
Enumeration e = a.getAttributeNames();
- while (e.hasMoreElements())
+ while (e.hasMoreElements() && res)
{
Object name = e.nextElement();
- Object val = a.getAttribute(name);
- if (!containsAttribute(name, val))
- return false;
+ res = a.getAttribute(name).equals(getAttribute(name));
}
- return true;
+ return res;
}
public AttributeSet copyAttributes()
{
- return (AttributeSet) clone();
+ return this;
}
public boolean equals(Object obj)
{
- return
- (obj instanceof AttributeSet)
- && this.isEqual((AttributeSet)obj);
+ boolean eq = false;
+ if (obj instanceof AttributeSet)
+ {
+ AttributeSet atts = (AttributeSet) obj;
+ eq = getAttributeCount() == atts.getAttributeCount()
+ && containsAttributes(atts);
+ }
+ return eq;
}
public Object getAttribute(Object key)
{
- for (int i = 0; i < attrs.length; i += 2)
+ Object att = null;
+ if (key == StyleConstants.ResolveAttribute)
+ att = resolveParent;
+
+ for (int i = 0; i < attrs.length && att == null; i += 2)
{
if (attrs[i].equals(key))
- return attrs[i+1];
+ att = attrs[i + 1];
}
-
+
// Check the resolve parent, unless we're looking for the
- // ResolveAttribute, which would cause an infinite loop
- if (!(key.equals(ResolveAttribute)))
+ // ResolveAttribute, which must not be looked up
+ if (att == null)
{
- Object p = getResolveParent();
- if (p != null && p instanceof AttributeSet)
- return (((AttributeSet)p).getAttribute(key));
+ AttributeSet parent = getResolveParent();
+ if (parent != null)
+ att = parent.getAttribute(key);
}
- return null;
+ return att;
}
public int getAttributeCount()
@@ -342,7 +368,7 @@ public class StyleContext
public AttributeSet getResolveParent()
{
- return (AttributeSet) getAttribute(ResolveAttribute);
+ return resolveParent;
}
public int hashCode()
@@ -362,68 +388,95 @@ public class StyleContext
public boolean isEqual(AttributeSet attr)
{
- return getAttributeCount() == attr.getAttributeCount()
+ boolean eq;
+ // If the other one is also a SmallAttributeSet, it is only considered
+ // equal if it's the same instance.
+ if (attr instanceof SmallAttributeSet)
+ eq = attr == this;
+ else
+ eq = getAttributeCount() == attr.getAttributeCount()
&& this.containsAttributes(attr);
+ return eq;
}
public String toString()
{
- StringBuffer sb = new StringBuffer();
- sb.append("[StyleContext.SmallattributeSet:");
- for (int i = 0; i < attrs.length - 1; ++i)
+ StringBuilder sb = new StringBuilder();
+ sb.append('{');
+ for (int i = 0; i < attrs.length; i += 2)
{
- sb.append(" (");
- sb.append(attrs[i].toString());
- sb.append("=");
- sb.append(attrs[i+1].toString());
- sb.append(")");
+ if (attrs[i + 1] instanceof AttributeSet)
+ {
+ sb.append(attrs[i]);
+ sb.append("=AttributeSet,");
+ }
+ else
+ {
+ sb.append(attrs[i]);
+ sb.append('=');
+ sb.append(attrs[i + 1]);
+ sb.append(',');
+ }
}
- sb.append("]");
+ sb.append("}");
return sb.toString();
}
}
- // FIXME: official javadocs suggest that these might be more usefully
- // implemented using a WeakHashMap, but not sure if that works most
- // places or whether it really matters anyways.
- //
- // FIXME: also not sure if these tables ought to be static (singletons),
- // shared across all StyleContexts. I think so, but it's not clear in
- // docs. revert to non-shared if you think it matters.
-
/**
- * The name of the default style.
+ * Register StyleConstant keys as static attribute keys for serialization.
*/
- public static final String DEFAULT_STYLE = "default";
-
+ static
+ {
+ // Don't let problems while doing this prevent class loading.
+ try
+ {
+ for (Iterator i = StyleConstants.keys.iterator(); i.hasNext();)
+ registerStaticAttributeKey(i.next());
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+ }
+
/**
- * The default style for this style context.
+ * The name of the default style.
*/
- NamedStyle defaultStyle = new NamedStyle(DEFAULT_STYLE, null);
+ public static final String DEFAULT_STYLE = "default";
static Hashtable sharedAttributeSets = new Hashtable();
static Hashtable sharedFonts = new Hashtable();
- static StyleContext defaultStyleContext = new StyleContext();
+ static StyleContext defaultStyleContext;
static final int compressionThreshold = 9;
/**
* These attribute keys are handled specially in serialization.
*/
- private static Hashtable staticAttributeKeys = new Hashtable();
+ private static Hashtable writeAttributeKeys;
+ private static Hashtable readAttributeKeys;
+
+ private NamedStyle styles;
+
+ /**
+ * Used for searching attributes in the pool.
+ */
+ private transient MutableAttributeSet search = new SimpleAttributeSet();
+
+ /**
+ * A pool of immutable AttributeSets.
+ */
+ private transient WeakHashMap attributeSetPool = new WeakHashMap();
- EventListenerList listenerList;
- Hashtable styleTable;
-
/**
* Creates a new instance of the style context. Add the default style
* to the style table.
*/
public StyleContext()
{
- listenerList = new EventListenerList();
- styleTable = new Hashtable();
- styleTable.put(DEFAULT_STYLE, defaultStyle);
+ styles = new NamedStyle(null);
+ addStyle(DEFAULT_STYLE, null);
}
protected SmallAttributeSet createSmallAttributeSet(AttributeSet a)
@@ -438,30 +491,30 @@ public class StyleContext
public void addChangeListener(ChangeListener listener)
{
- listenerList.add(ChangeListener.class, listener);
+ styles.addChangeListener(listener);
}
public void removeChangeListener(ChangeListener listener)
{
- listenerList.remove(ChangeListener.class, listener);
+ styles.removeChangeListener(listener);
}
public ChangeListener[] getChangeListeners()
{
- return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ return styles.getChangeListeners();
}
public Style addStyle(String name, Style parent)
{
Style newStyle = new NamedStyle(name, parent);
if (name != null)
- styleTable.put(name, newStyle);
+ styles.addAttribute(name, newStyle);
return newStyle;
}
public void removeStyle(String name)
{
- styleTable.remove(name);
+ styles.removeAttribute(name);
}
/**
@@ -476,7 +529,7 @@ public class StyleContext
*/
public Style getStyle(String name)
{
- return (Style) styleTable.get(name);
+ return (Style) styles.getAttribute(name);
}
/**
@@ -485,7 +538,22 @@ public class StyleContext
*/
public Enumeration<?> getStyleNames()
{
- return styleTable.keys();
+ return styles.getAttributeNames();
+ }
+
+ private void readObject(ObjectInputStream in)
+ throws ClassNotFoundException, IOException
+ {
+ search = new SimpleAttributeSet();
+ attributeSetPool = new WeakHashMap();
+ in.defaultReadObject();
+ }
+
+ private void writeObject(ObjectOutputStream out)
+ throws IOException
+ {
+ cleanupPool();
+ out.defaultWriteObject();
}
//
@@ -577,132 +645,120 @@ public class StyleContext
public static StyleContext getDefaultStyleContext()
{
+ if (defaultStyleContext == null)
+ defaultStyleContext = new StyleContext();
return defaultStyleContext;
}
public AttributeSet addAttribute(AttributeSet old, Object name, Object value)
{
- if (old instanceof MutableAttributeSet)
+ AttributeSet ret;
+ if (old.getAttributeCount() + 1 < getCompressionThreshold())
{
- ((MutableAttributeSet)old).addAttribute(name, value);
- return old;
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.addAttribute(name, value);
+ reclaim(old);
+ ret = searchImmutableSet();
}
- else
+ else
{
- MutableAttributeSet mutable = createLargeAttributeSet(old);
- mutable.addAttribute(name, value);
- if (mutable.getAttributeCount() >= getCompressionThreshold())
- return mutable;
- else
- {
- SmallAttributeSet small = createSmallAttributeSet(mutable);
- if (sharedAttributeSets.containsKey(small))
- small = (SmallAttributeSet) sharedAttributeSets.get(small);
- else
- sharedAttributeSets.put(small,small);
- return small;
- }
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.addAttribute(name, value);
+ ret = mas;
}
+ return ret;
}
public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes)
{
- if (old instanceof MutableAttributeSet)
+ AttributeSet ret;
+ if (old.getAttributeCount() + attributes.getAttributeCount()
+ < getCompressionThreshold())
{
- ((MutableAttributeSet)old).addAttributes(attributes);
- return old;
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.addAttributes(attributes);
+ reclaim(old);
+ ret = searchImmutableSet();
}
- else
+ else
{
- MutableAttributeSet mutable = createLargeAttributeSet(old);
- mutable.addAttributes(attributes);
- if (mutable.getAttributeCount() >= getCompressionThreshold())
- return mutable;
- else
- {
- SmallAttributeSet small = createSmallAttributeSet(mutable);
- if (sharedAttributeSets.containsKey(small))
- small = (SmallAttributeSet) sharedAttributeSets.get(small);
- else
- sharedAttributeSets.put(small,small);
- return small;
- }
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.addAttributes(attributes);
+ ret = mas;
}
+ return ret;
}
public AttributeSet getEmptySet()
{
- AttributeSet e = createSmallAttributeSet(null);
- if (sharedAttributeSets.containsKey(e))
- e = (AttributeSet) sharedAttributeSets.get(e);
- else
- sharedAttributeSets.put(e, e);
- return e;
+ return SimpleAttributeSet.EMPTY;
}
public void reclaim(AttributeSet attributes)
{
- if (sharedAttributeSets.containsKey(attributes))
- sharedAttributeSets.remove(attributes);
+ cleanupPool();
}
public AttributeSet removeAttribute(AttributeSet old, Object name)
{
- if (old instanceof MutableAttributeSet)
+ AttributeSet ret;
+ if (old.getAttributeCount() - 1 <= getCompressionThreshold())
{
- ((MutableAttributeSet)old).removeAttribute(name);
- if (old.getAttributeCount() < getCompressionThreshold())
- {
- SmallAttributeSet small = createSmallAttributeSet(old);
- if (!sharedAttributeSets.containsKey(small))
- sharedAttributeSets.put(small,small);
- old = (AttributeSet) sharedAttributeSets.get(small);
- }
- return old;
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttribute(name);
+ reclaim(old);
+ ret = searchImmutableSet();
}
- else
- {
- MutableAttributeSet mutable = createLargeAttributeSet(old);
- mutable.removeAttribute(name);
- SmallAttributeSet small = createSmallAttributeSet(mutable);
- if (sharedAttributeSets.containsKey(small))
- small = (SmallAttributeSet) sharedAttributeSets.get(small);
- else
- sharedAttributeSets.put(small,small);
- return small;
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttribute(name);
+ ret = mas;
}
+ return ret;
}
public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes)
{
- return removeAttributes(old, attributes.getAttributeNames());
+ AttributeSet ret;
+ if (old.getAttributeCount() <= getCompressionThreshold())
+ {
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttributes(attributes);
+ reclaim(old);
+ ret = searchImmutableSet();
+ }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttributes(attributes);
+ ret = mas;
+ }
+ return ret;
}
public AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names)
{
- if (old instanceof MutableAttributeSet)
+ AttributeSet ret;
+ if (old.getAttributeCount() <= getCompressionThreshold())
{
- ((MutableAttributeSet)old).removeAttributes(names);
- if (old.getAttributeCount() < getCompressionThreshold())
- {
- SmallAttributeSet small = createSmallAttributeSet(old);
- if (!sharedAttributeSets.containsKey(small))
- sharedAttributeSets.put(small,small);
- old = (AttributeSet) sharedAttributeSets.get(small);
- }
- return old;
+ search.removeAttributes(search);
+ search.addAttributes(old);
+ search.removeAttributes(names);
+ reclaim(old);
+ ret = searchImmutableSet();
}
- else
- {
- MutableAttributeSet mutable = createLargeAttributeSet(old);
- mutable.removeAttributes(names);
- SmallAttributeSet small = createSmallAttributeSet(mutable);
- if (sharedAttributeSets.containsKey(small))
- small = (SmallAttributeSet) sharedAttributeSets.get(small);
- else
- sharedAttributeSets.put(small,small);
- return small;
- }
+ else
+ {
+ MutableAttributeSet mas = getMutableAttributeSet(old);
+ mas.removeAttributes(names);
+ ret = mas;
+ }
+ return ret;
}
/**
@@ -715,7 +771,7 @@ public class StyleContext
{
if (key == null)
return null;
- return staticAttributeKeys.get(key);
+ return readAttributeKeys.get(key);
}
/**
@@ -742,27 +798,25 @@ public class StyleContext
* stream
* @throws IOException - any I/O error
*/
- public static void readAttributeSet(ObjectInputStream in, MutableAttributeSet a)
+ public static void readAttributeSet(ObjectInputStream in,
+ MutableAttributeSet a)
throws ClassNotFoundException, IOException
{
- if (in == null || a == null)
- return;
-
- Object key = in.readObject();
- Object val = in.readObject();
- while (key != null && val != null)
+ int count = in.readInt();
+ for (int i = 0; i < count; i++)
{
- Object staticKey = staticAttributeKeys.get(key);
- Object staticVal = staticAttributeKeys.get(val);
-
- if (staticKey != null)
- key = staticKey;
- if (staticVal != null)
- val = staticVal;
-
+ Object key = in.readObject();
+ Object val = in.readObject();
+ if (readAttributeKeys != null)
+ {
+ Object staticKey = readAttributeKeys.get(key);
+ if (staticKey != null)
+ key = staticKey;
+ Object staticVal = readAttributeKeys.get(val);
+ if (staticVal != null)
+ val = staticVal;
+ }
a.addAttribute(key, val);
- key = in.readObject();
- val = in.readObject();
}
}
@@ -778,18 +832,35 @@ public class StyleContext
public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a)
throws IOException
{
+ int count = a.getAttributeCount();
+ out.writeInt(count);
Enumeration e = a.getAttributeNames();
while (e.hasMoreElements())
{
- Object oldKey = e.nextElement();
- Object newKey = getStaticAttribute(oldKey);
- Object key = (newKey == null) ? oldKey : newKey;
-
- out.writeObject(key);
- out.writeObject(a.getAttribute(oldKey));
+ Object key = e.nextElement();
+ // Write key.
+ if (key instanceof Serializable)
+ out.writeObject(key);
+ else
+ {
+ Object io = writeAttributeKeys.get(key);
+ if (io == null)
+ throw new NotSerializableException(key.getClass().getName()
+ + ", key: " + key);
+ out.writeObject(io);
+ }
+ // Write value.
+ Object val = a.getAttribute(key);
+ Object io = writeAttributeKeys.get(val);
+ if (val instanceof Serializable)
+ out.writeObject(io != null ? io : val);
+ else
+ {
+ if (io == null)
+ throw new NotSerializableException(val.getClass().getName());
+ out.writeObject(io);
+ }
}
- out.writeObject(null);
- out.writeObject(null);
}
/**
@@ -833,8 +904,79 @@ public class StyleContext
*/
public static void registerStaticAttributeKey(Object key)
{
- if (key != null)
- staticAttributeKeys.put(key.getClass().getName() + "." + key.toString(),
- key);
+ String io = key.getClass().getName() + "." + key.toString();
+ if (writeAttributeKeys == null)
+ writeAttributeKeys = new Hashtable();
+ if (readAttributeKeys == null)
+ readAttributeKeys = new Hashtable();
+ writeAttributeKeys.put(key, io);
+ readAttributeKeys.put(io, key);
+ }
+
+ /**
+ * Returns a string representation of this StyleContext.
+ *
+ * @return a string representation of this StyleContext
+ */
+ public String toString()
+ {
+ cleanupPool();
+ StringBuilder b = new StringBuilder();
+ Iterator i = attributeSetPool.keySet().iterator();
+ while (i.hasNext())
+ {
+ Object att = i.next();
+ b.append(att);
+ b.append('\n');
+ }
+ return b.toString();
+ }
+
+ /**
+ * Searches the AttributeSet pool and returns a pooled instance if available,
+ * or pool a new one.
+ *
+ * @return an immutable attribute set that equals the current search key
+ */
+ private AttributeSet searchImmutableSet()
+ {
+ SmallAttributeSet k = createSmallAttributeSet(search);
+ WeakReference ref = (WeakReference) attributeSetPool.get(k);
+ SmallAttributeSet a;
+ if (ref == null || (a = (SmallAttributeSet) ref.get()) == null)
+ {
+ a = k;
+ attributeSetPool.put(a, new WeakReference(a));
+ }
+ return a;
+ }
+
+ /**
+ * Cleans up the attribute set pool from entries that are no longer
+ * referenced.
+ */
+ private void cleanupPool()
+ {
+ // TODO: How else can we force cleaning up the WeakHashMap?
+ attributeSetPool.size();
+ }
+
+ /**
+ * Returns a MutableAttributeSet that holds a. If a itself is mutable,
+ * this returns a itself, otherwise it creates a new SimpleAtttributeSet
+ * via {@link #createLargeAttributeSet(AttributeSet)}.
+ *
+ * @param a the AttributeSet to create a mutable set for
+ *
+ * @return a mutable attribute set that corresponds to a
+ */
+ private MutableAttributeSet getMutableAttributeSet(AttributeSet a)
+ {
+ MutableAttributeSet mas;
+ if (a instanceof MutableAttributeSet)
+ mas = (MutableAttributeSet) a;
+ else
+ mas = createLargeAttributeSet(a);
+ return mas;
}
}
diff --git a/javax/swing/text/StyledEditorKit.java b/javax/swing/text/StyledEditorKit.java
index c4eef4463..568694387 100644
--- a/javax/swing/text/StyledEditorKit.java
+++ b/javax/swing/text/StyledEditorKit.java
@@ -142,7 +142,7 @@ public class StyledEditorKit extends DefaultEditorKit
Element el = doc.getCharacterElement(editor.getSelectionStart());
boolean isBold = StyleConstants.isBold(el.getAttributes());
SimpleAttributeSet atts = new SimpleAttributeSet();
- StyleConstants.setItalic(atts, ! isBold);
+ StyleConstants.setBold(atts, ! isBold);
setCharacterAttributes(editor, atts, false);
}
}
@@ -335,35 +335,21 @@ public class StyledEditorKit extends DefaultEditorKit
AttributeSet atts,
boolean replace)
{
- Document doc = editor.getDocument();
- if (doc instanceof StyledDocument)
- {
- StyledDocument styleDoc = (StyledDocument) editor.getDocument();
- EditorKit kit = editor.getEditorKit();
- if (!(kit instanceof StyledEditorKit))
- {
- StyledEditorKit styleKit = (StyledEditorKit) kit;
- int start = editor.getSelectionStart();
- int end = editor.getSelectionEnd();
- int dot = editor.getCaret().getDot();
- if (start == dot && end == dot)
- {
- // If there is no selection, then we only update the
- // input attributes.
- MutableAttributeSet inputAttributes =
- styleKit.getInputAttributes();
- inputAttributes.addAttributes(atts);
- }
- else
- styleDoc.setCharacterAttributes(start, end, atts, replace);
- }
- else
- throw new AssertionError("The EditorKit for StyledTextActions "
- + "is expected to be a StyledEditorKit");
- }
- else
- throw new AssertionError("The Document for StyledTextActions is "
- + "expected to be a StyledDocument.");
+ int p0 = editor.getSelectionStart();
+ int p1 = editor.getSelectionEnd();
+ if (p0 != p1)
+ {
+ StyledDocument doc = getStyledDocument(editor);
+ doc.setCharacterAttributes(p0, p1 - p0, atts, replace);
+ }
+ // Update input attributes.
+ StyledEditorKit kit = getStyledEditorKit(editor);
+ MutableAttributeSet inputAtts = kit.getInputAttributes();
+ if (replace)
+ {
+ inputAtts.removeAttributes(inputAtts);
+ }
+ inputAtts.addAttributes(atts);
}
/**
diff --git a/javax/swing/text/TextAction.java b/javax/swing/text/TextAction.java
index 28dbff00a..49c49cb9d 100644
--- a/javax/swing/text/TextAction.java
+++ b/javax/swing/text/TextAction.java
@@ -38,14 +38,15 @@ exception statement from your version. */
package javax.swing.text;
+import java.awt.Component;
+import java.awt.KeyboardFocusManager;
import java.awt.Point;
import java.awt.event.ActionEvent;
-import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Iterator;
import javax.swing.AbstractAction;
import javax.swing.Action;
-import javax.swing.SwingConstants;
/**
* TextAction
@@ -73,10 +74,16 @@ public abstract class TextAction extends AbstractAction
*/
protected final JTextComponent getTextComponent(ActionEvent event)
{
- if (event.getSource() instanceof JTextComponent)
- return (JTextComponent) event.getSource();
-
- return getFocusedComponent();
+ JTextComponent target = null;
+ if (event != null)
+ {
+ Object source = event.getSource();
+ if (source instanceof JTextComponent)
+ target = (JTextComponent) source;
+ }
+ if (target == null)
+ target = getFocusedComponent();
+ return target;
}
/**
@@ -89,16 +96,28 @@ public abstract class TextAction extends AbstractAction
*/
public static final Action[] augmentList(Action[] list1, Action[] list2)
{
- HashSet<Action> actionSet = new HashSet<Action>();
+ HashMap<Object,Action> actions = new HashMap<Object,Action>();
for (int i = 0; i < list1.length; ++i)
- actionSet.add(list1[i]);
+ {
+ Action a = list1[i];
+ Object name = a.getValue(Action.NAME);
+ actions.put(name != null ? name : "", a);
+ }
for (int i = 0; i < list2.length; ++i)
- actionSet.add(list2[i]);
+ {
+ Action a = list2[i];
+ Object name = a.getValue(Action.NAME);
+ actions.put(name != null ? name : "", a);
+ }
+ Action[] augmented = new Action[actions.size()];
+
+ int i = 0;
+ for (Iterator<Action> it = actions.values().iterator(); it.hasNext(); i++)
+ augmented[i] = it.next();
+ return augmented;
- ArrayList<Action> list = new ArrayList<Action>(actionSet);
- return list.toArray(new Action[actionSet.size()]);
}
/**
@@ -108,7 +127,13 @@ public abstract class TextAction extends AbstractAction
*/
protected final JTextComponent getFocusedComponent()
{
- return null; // TODO
+ KeyboardFocusManager kfm =
+ KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ Component focused = kfm.getPermanentFocusOwner();
+ JTextComponent textComp = null;
+ if (focused instanceof JTextComponent)
+ textComp = (JTextComponent) focused;
+ return textComp;
}
/** Abstract helper class which implements everything needed for an
diff --git a/javax/swing/text/Utilities.java b/javax/swing/text/Utilities.java
index f75906a0f..8ddf97a12 100644
--- a/javax/swing/text/Utilities.java
+++ b/javax/swing/text/Utilities.java
@@ -54,10 +54,6 @@ import javax.swing.text.Position.Bias;
*/
public class Utilities
{
- /**
- * The length of the char buffer that holds the characters to be drawn.
- */
- private static final int BUF_LENGTH = 64;
/**
* Creates a new <code>Utilities</code> object.
@@ -125,8 +121,8 @@ public class Utilities
// In case we have a tab, we just 'jump' over the tab.
// When we have no tab expander we just use the width of ' '.
if (e != null)
- pixelX = (int) e.nextTabStop((float) pixelX,
- startOffset + offset - s.offset);
+ pixelX = (int) e.nextTabStop(pixelX,
+ startOffset + offset - s.offset);
else
pixelX += metrics.charWidth(' ');
break;
@@ -176,7 +172,7 @@ public class Utilities
// In case we have a tab, we just 'jump' over the tab.
// When we have no tab expander we just use the width of 'm'.
if (e != null)
- pixelX = (int) e.nextTabStop((float) pixelX,
+ pixelX = (int) e.nextTabStop(pixelX,
startOffset + offset - s.offset);
else
pixelX += metrics.charWidth(' ');
@@ -269,7 +265,7 @@ public class Utilities
currentX += width;
}
- return pos + p0;
+ return pos;
}
/**
@@ -537,28 +533,39 @@ public class Utilities
int x0, int x, TabExpander e,
int startOffset)
{
- int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset, false);
- BreakIterator breaker = BreakIterator.getWordInstance();
- breaker.setText(s);
-
- // If startOffset and s.offset differ then we need to use
- // that difference two convert the offset between the two metrics.
- int shift = startOffset - s.offset;
-
+ int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset,
+ false);
+ int breakLoc = mark;
// If mark is equal to the end of the string, just use that position.
- if (mark >= shift + s.count)
- return mark;
-
- // Try to find a word boundary previous to the mark at which we
- // can break the text.
- int preceding = breaker.preceding(mark + 1 - shift);
-
- if (preceding != 0)
- return preceding + shift;
-
- // If preceding is 0 we couldn't find a suitable word-boundary so
- // just break it on the character boundary
- return mark;
+ if (mark < s.count - 1)
+ {
+ for (int i = s.offset + mark; i >= s.offset; i--)
+ {
+ char ch = s.array[i];
+ if (ch < 256)
+ {
+ // For ASCII simply scan backwards for whitespace.
+ if (Character.isWhitespace(ch))
+ {
+ breakLoc = i - s.offset + 1;
+ break;
+ }
+ }
+ else
+ {
+ // Only query BreakIterator for complex chars.
+ BreakIterator bi = BreakIterator.getLineInstance();
+ bi.setText(s);
+ int pos = bi.preceding(i + 1);
+ if (pos > s.offset)
+ {
+ breakLoc = breakLoc - s.offset;
+ }
+ break;
+ }
+ }
+ }
+ return breakLoc;
}
/**
@@ -706,12 +713,12 @@ public class Utilities
offset,
Bias.Forward,
direction,
- null)
+ new Position.Bias[1])
: t.getUI().getNextVisualPositionFrom(t,
offset,
Bias.Forward,
direction,
- null);
+ new Position.Bias[1]);
}
catch (BadLocationException ble)
{
diff --git a/javax/swing/text/View.java b/javax/swing/text/View.java
index 55a63f6b6..aafd76a4f 100644
--- a/javax/swing/text/View.java
+++ b/javax/swing/text/View.java
@@ -57,7 +57,6 @@ public abstract class View implements SwingConstants
public static final int X_AXIS = 0;
public static final int Y_AXIS = 1;
- private float width, height;
private Element elt;
private View parent;
@@ -307,15 +306,16 @@ public abstract class View implements SwingConstants
{
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;
+ String text = null;
+ if (index >= 0)
+ {
+ allocation = getChildAllocation(index, allocation);
+ Rectangle r = allocation instanceof Rectangle ? (Rectangle) allocation
+ : allocation.getBounds();
+ if (r.contains(x, y))
+ text = getView(index).getToolTipText(x, y, allocation);
+ }
+ return text;
}
/**
@@ -334,7 +334,10 @@ public abstract class View implements SwingConstants
public int getBreakWeight(int axis, float pos, float len)
{
- return BadBreakWeight;
+ int weight = BadBreakWeight;
+ if (len > getPreferredSpan(axis))
+ weight = GoodBreakWeight;
+ return weight;
}
public View breakView(int axis, int offset, float pos, float len)
@@ -370,12 +373,18 @@ public abstract class View implements SwingConstants
*/
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);
+ if (getViewCount() > 0)
+ {
+ 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);
+ }
}
/**
@@ -429,12 +438,18 @@ public abstract class View implements SwingConstants
*/
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);
+ if (getViewCount() > 0)
+ {
+ 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);
+ }
}
/**
@@ -465,10 +480,15 @@ public abstract class View implements SwingConstants
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);
+ View[] newChildren = null;
+ if (added != null)
+ {
+ newChildren = new View[added.length];
+ for (int i = 0; i < added.length; ++i)
+ newChildren[i] = vf.create(added[i]);
+ }
+ int numRemoved = removed != null ? removed.length : 0;
+ replace(index, numRemoved, newChildren);
return true;
}
@@ -750,7 +770,9 @@ public abstract class View implements SwingConstants
*/
public int viewToModel(float x, float y, Shape a)
{
- return viewToModel(x, y, a, new Position.Bias[0]);
+ Position.Bias[] biasRet = new Position.Bias[1];
+ biasRet[0] = Position.Bias.Forward;
+ return viewToModel(x, y, a, biasRet);
}
/**
diff --git a/javax/swing/text/WrappedPlainView.java b/javax/swing/text/WrappedPlainView.java
index 8cb2f4fb5..00e12b112 100644
--- a/javax/swing/text/WrappedPlainView.java
+++ b/javax/swing/text/WrappedPlainView.java
@@ -83,7 +83,17 @@ public class WrappedPlainView extends BoxView implements TabExpander
/** The height of the line (used while painting) **/
int lineHeight;
-
+
+ /**
+ * The base offset for tab calculations.
+ */
+ private int tabBase;
+
+ /**
+ * The tab size.
+ */
+ private int tabSize;
+
/**
* The instance returned by {@link #getLineBuffer()}.
*/
@@ -121,10 +131,13 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public float nextTabStop(float x, int tabStop)
{
- JTextComponent host = (JTextComponent)getContainer();
- float tabSizePixels = getTabSize()
- * host.getFontMetrics(host.getFont()).charWidth('m');
- return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels;
+ int next = (int) x;
+ if (tabSize != 0)
+ {
+ int numTabs = ((int) x - tabBase) / tabSize;
+ next = tabBase + (numTabs + 1) * tabSize;
+ }
+ return next;
}
/**
@@ -274,44 +287,32 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
protected int calculateBreakPosition(int p0, int p1)
{
- Container c = getContainer();
-
- int li = getLeftInset();
- int ti = getTopInset();
-
- Rectangle alloc = new Rectangle(li, ti,
- getWidth()-getRightInset()-li,
- getHeight()-getBottomInset()-ti);
-
- // Mimic a behavior observed in the RI.
- if (alloc.isEmpty())
- return 0;
-
- updateMetrics();
-
+ Segment s = new Segment();
try
{
- getDocument().getText(p0, p1 - p0, getLineBuffer());
+ getDocument().getText(p0, p1 - p0, s);
}
- catch (BadLocationException ble)
+ catch (BadLocationException ex)
{
- // this shouldn't happen
- throw new InternalError("Invalid offsets p0: " + p0 + " - p1: " + p1);
+ assert false : "Couldn't load text";
}
-
+ int width = getWidth();
+ int pos;
if (wordWrap)
- return Utilities.getBreakLocation(lineBuffer, metrics, alloc.x,
- alloc.x + alloc.width, this, p0);
+ pos = p0 + Utilities.getBreakLocation(s, metrics, tabBase,
+ tabBase + width, this, p0);
else
- return p0 + Utilities.getTabbedTextOffset(lineBuffer, metrics, alloc.x,
- alloc.x + alloc.width, this, 0,
- true);
+ pos = p0 + Utilities.getTabbedTextOffset(s, metrics, tabBase,
+ tabBase + width, this, p0,
+ false);
+ return pos;
}
void updateMetrics()
{
Container component = getContainer();
metrics = component.getFontMetrics(component.getFont());
+ tabSize = getTabSize()* metrics.charWidth('m');
}
/**
@@ -350,9 +351,15 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void insertUpdate (DocumentEvent e, Shape a, ViewFactory f)
{
- super.insertUpdate(e, a, viewFactory);
+ // Update children efficiently.
+ updateChildren(e, a);
- // No repaint needed, as this is done by the WrappedLine instances.
+ // Notify children.
+ Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a)
+ : null;
+ View v = getViewAtPosition(e.getOffset(), r);
+ if (v != null)
+ v.insertUpdate(e, r, f);
}
/**
@@ -361,9 +368,15 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void removeUpdate (DocumentEvent e, Shape a, ViewFactory f)
{
- super.removeUpdate(e, a, viewFactory);
-
- // No repaint needed, as this is done by the WrappedLine instances.
+ // Update children efficiently.
+ updateChildren(e, a);
+
+ // Notify children.
+ Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a)
+ : null;
+ View v = getViewAtPosition(e.getOffset(), r);
+ if (v != null)
+ v.removeUpdate(e, r, f);
}
/**
@@ -373,11 +386,39 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void changedUpdate (DocumentEvent e, Shape a, ViewFactory f)
{
- super.changedUpdate(e, a, viewFactory);
-
- // No repaint needed, as this is done by the WrappedLine instances.
+ // Update children efficiently.
+ updateChildren(e, a);
}
-
+
+ /**
+ * Helper method. Updates the child views in response to
+ * insert/remove/change updates. This is here to be a little more efficient
+ * than the BoxView implementation.
+ *
+ * @param ev the document event
+ * @param a the shape
+ */
+ private void updateChildren(DocumentEvent ev, Shape a)
+ {
+ Element el = getElement();
+ DocumentEvent.ElementChange ec = ev.getChange(el);
+ if (ec != null)
+ {
+ Element[] removed = ec.getChildrenRemoved();
+ Element[] added = ec.getChildrenAdded();
+ View[] addedViews = new View[added.length];
+ for (int i = 0; i < added.length; i++)
+ addedViews[i] = new WrappedLine(added[i]);
+ replace(ec.getIndex(), removed.length, addedViews);
+ if (a != null)
+ {
+ preferenceChanged(null, true, true);
+ getContainer().repaint();
+ }
+ }
+ updateMetrics();
+ }
+
class WrappedLineCreator implements ViewFactory
{
// Creates a new WrappedLine
@@ -397,6 +438,9 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void paint(Graphics g, Shape a)
{
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ tabBase = r.x;
+
JTextComponent comp = (JTextComponent)getContainer();
// Ensure metrics are up-to-date.
updateMetrics();
@@ -434,7 +478,6 @@ public class WrappedPlainView extends BoxView implements TabExpander
public WrappedLine(Element elem)
{
super(elem);
- determineNumLines();
}
/**
@@ -496,37 +539,29 @@ public class WrappedPlainView extends BoxView implements TabExpander
}
}
-
+
/**
* Calculates the number of logical lines that the Element
* needs to be displayed and updates the variable numLines
* accordingly.
*/
- void determineNumLines()
+ private int determineNumLines()
{
- numLines = 0;
+ int nLines = 0;
int end = getEndOffset();
- if (end == 0)
- return;
-
- int breakPoint;
for (int i = getStartOffset(); i < end;)
{
- numLines ++;
+ nLines++;
// careful: check that there's no off-by-one problem here
// depending on which position calculateBreakPosition returns
- breakPoint = calculateBreakPosition(i, end);
+ int breakPoint = calculateBreakPosition(i, end);
- if (breakPoint == 0)
- return;
-
- // If breakPoint is equal to the current index no further
- // line is needed and we can end the loop.
if (breakPoint == i)
- break;
+ i = breakPoint + 1;
else
i = breakPoint;
}
+ return nLines;
}
/**
@@ -571,7 +606,7 @@ public class WrappedPlainView extends BoxView implements TabExpander
// Throwing a BadLocationException is an observed behavior of the RI.
if (rect.isEmpty())
throw new BadLocationException("Unable to calculate view coordinates "
- + "when allocation area is empty.", 5);
+ + "when allocation area is empty.", pos);
Segment s = getLineBuffer();
int lineHeight = metrics.getHeight();
@@ -648,7 +683,7 @@ public class WrappedPlainView extends BoxView implements TabExpander
return currLineStart;
if (y > rect.y + rect.height)
- return end;
+ return end - 1;
// Note: rect.x and rect.width do not represent the width of painted
// text but the area where text *may* be painted. This means the width
@@ -709,22 +744,14 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
void updateDamage (Rectangle a)
{
- // If the allocation area is empty we can't do anything useful.
- // As determining the number of lines is impossible in that state we
- // reset it to an invalid value which can then be recalculated at a
- // later point.
- if (a == null || a.isEmpty())
+ int nLines = determineNumLines();
+ if (numLines != nLines)
{
- numLines = 1;
- return;
+ numLines = nLines;
+ preferenceChanged(this, false, true);
+ getContainer().repaint();
}
-
- int oldNumLines = numLines;
- determineNumLines();
-
- if (numLines != oldNumLines)
- preferenceChanged(this, false, true);
- else
+ else if (a != null)
getContainer().repaint(a.x, a.y, a.width, a.height);
}
@@ -738,7 +765,8 @@ public class WrappedPlainView extends BoxView implements TabExpander
*/
public void insertUpdate (DocumentEvent changes, Shape a, ViewFactory f)
{
- updateDamage((Rectangle)a);
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ updateDamage(r);
}
/**
@@ -760,7 +788,8 @@ public class WrappedPlainView extends BoxView implements TabExpander
// However this seems to cause no trouble and as it reduces the
// number of method calls it can stay this way.
- updateDamage((Rectangle)a);
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ updateDamage(r);
}
}
}
diff --git a/javax/swing/text/ZoneView.java b/javax/swing/text/ZoneView.java
new file mode 100644
index 000000000..6cabc6c20
--- /dev/null
+++ b/javax/swing/text/ZoneView.java
@@ -0,0 +1,442 @@
+/* ZoneView.java -- An effective BoxView subclass
+ 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.Shape;
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+import javax.swing.event.DocumentEvent;
+
+/**
+ * A View implementation that delays loading of sub views until they are
+ * needed for display or internal transformations. This can be used for
+ * editors that need to handle large documents more effectivly than the
+ * standard {@link BoxView}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.3
+ */
+public class ZoneView
+ extends BoxView
+{
+
+ /**
+ * The default zone view implementation. The specs suggest that this is
+ * a subclass of AsyncBoxView, so do we.
+ */
+ static class Zone
+ extends AsyncBoxView
+ {
+ /**
+ * The start position for this zone.
+ */
+ private Position p0;
+
+ /**
+ * The end position for this zone.
+ */
+ private Position p1;
+
+ /**
+ * Creates a new Zone for the specified element, start and end positions.
+ *
+ * @param el the element
+ * @param pos0 the start position
+ * @param pos1 the end position
+ * @param axis the major axis
+ */
+ Zone(Element el, Position pos0, Position pos1, int axis)
+ {
+ super(el, axis);
+ p0 = pos0;
+ p1 = pos1;
+ }
+
+ /**
+ * Returns the start offset of the zone.
+ *
+ * @return the start offset of the zone
+ */
+ public int getStartOffset()
+ {
+ return p0.getOffset();
+ }
+
+ /**
+ * Returns the end offset of the zone.
+ *
+ * @return the end offset of the zone
+ */
+ public int getEndOffset()
+ {
+ return p1.getOffset();
+ }
+ }
+
+ /**
+ * The maximumZoneSize.
+ */
+ private int maximumZoneSize;
+
+ /**
+ * The maximum number of loaded zones.
+ */
+ private int maxZonesLoaded;
+
+ /**
+ * A queue of loaded zones. When the number of loaded zones exceeds the
+ * maximum number of zones, the oldest zone(s) get unloaded.
+ */
+ private LinkedList loadedZones;
+
+ /**
+ * Creates a new ZoneView for the specified element and axis.
+ *
+ * @param element the element for which to create a ZoneView
+ * @param axis the major layout axis for the box
+ */
+ public ZoneView(Element element, int axis)
+ {
+ super(element, axis);
+ maximumZoneSize = 8192;
+ maxZonesLoaded = 3;
+ loadedZones = new LinkedList();
+ }
+
+ /**
+ * Sets the maximum zone size. Note that zones might still become larger
+ * then the size specified when a singe child view is larger for itself,
+ * because zones are formed on child view boundaries.
+ *
+ * @param size the maximum zone size to set
+ *
+ * @see #getMaximumZoneSize()
+ */
+ public void setMaximumZoneSize(int size)
+ {
+ maximumZoneSize = size;
+ }
+
+ /**
+ * Returns the maximum zone size. Note that zones might still become larger
+ * then the size specified when a singe child view is larger for itself,
+ * because zones are formed on child view boundaries.
+ *
+ * @return the maximum zone size
+ *
+ * @see #setMaximumZoneSize(int)
+ */
+ public int getMaximumZoneSize()
+ {
+ return maximumZoneSize;
+ }
+
+ /**
+ * Sets the maximum number of zones that are allowed to be loaded at the
+ * same time. If the new number of allowed zones is smaller then the
+ * previous settings, this unloads all zones the aren't allowed to be
+ * loaded anymore.
+ *
+ * @param num the number of zones allowed to be loaded at the same time
+ *
+ * @throws IllegalArgumentException if <code>num &lt;= 0</code>
+ *
+ * @see #getMaxZonesLoaded()
+ */
+ public void setMaxZonesLoaded(int num)
+ {
+ if (num < 1)
+ throw new IllegalArgumentException("Illegal number of zones");
+ maxZonesLoaded = num;
+ unloadOldestZones();
+ }
+
+ /**
+ * Returns the number of zones that are allowed to be loaded.
+ *
+ * @return the number of zones that are allowed to be loaded
+ *
+ * @see #setMaxZonesLoaded(int)
+ */
+ public int getMaxZonesLoaded()
+ {
+ return maxZonesLoaded;
+ }
+
+ /**
+ * Gets called after a zone has been loaded. This unloads the oldest zone(s)
+ * when the maximum number of zones is reached.
+ *
+ * @param zone the zone that has been loaded
+ */
+ protected void zoneWasLoaded(View zone)
+ {
+ loadedZones.addLast(zone);
+ unloadOldestZones();
+ }
+
+ /**
+ * This unloads the specified zone. This is implemented to simply remove
+ * all child views from that zone.
+ *
+ * @param zone the zone to be unloaded
+ */
+ protected void unloadZone(View zone)
+ {
+ zone.removeAll();
+ }
+
+ /**
+ * Returns <code>true</code> when the specified zone is loaded,
+ * <code>false</code> otherwise. The default implementation checks if
+ * the zone view has child elements.
+ *
+ * @param zone the zone view to check
+ *
+ * @return <code>true</code> when the specified zone is loaded,
+ * <code>false</code> otherwise
+ */
+ protected boolean isZoneLoaded(View zone)
+ {
+ return zone.getViewCount() > 0;
+ }
+
+ /**
+ * Creates a zone for the specified range. Subclasses can override this
+ * to provide a custom implementation for the zones.
+ *
+ * @param p0 the start of the range
+ * @param p1 the end of the range
+ *
+ * @return the zone
+ */
+ protected View createZone(int p0, int p1)
+ {
+ Document doc = getDocument();
+ Position pos0 = null;
+ Position pos1 = null;
+ try
+ {
+ pos0 = doc.createPosition(p0);
+ pos1 = doc.createPosition(p1);
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "Must not happen";
+ }
+ Zone zone = new Zone(getElement(), pos0, pos1, getAxis());
+ return zone;
+ }
+
+ // --------------------------------------------------------------------------
+ // CompositeView methods.
+ // --------------------------------------------------------------------------
+
+ /**
+ * Overridden to not load all the child views. This methods creates
+ * initial zones without actually loading them.
+ *
+ * @param vf not used
+ */
+ protected void loadChildren(ViewFactory vf)
+ {
+ int p0 = getStartOffset();
+ int p1 = getEndOffset();
+ append(createZone(p0, p1));
+ checkZoneAt(p0);
+ }
+
+ /**
+ * Returns the index of the child view at the document position
+ * <code>pos</code>.
+ *
+ * This overrides the CompositeView implementation because the ZoneView does
+ * not provide a one to one mapping from Elements to Views.
+ *
+ * @param pos the document position
+ *
+ * @return the index of the child view at the document position
+ * <code>pos</code>
+ */
+ protected int getViewIndexAtPosition(int pos)
+ {
+ int index = -1;
+ boolean found = false;
+ if (pos >= getStartOffset() && pos <= getEndOffset())
+ {
+ int upper = getViewCount() - 1;
+ int lower = 0;
+ index = (upper - lower) / 2 + lower;
+ int bias = 0;
+ do
+ {
+ View child = getView(index);
+ int childStart = child.getStartOffset();
+ int childEnd = child.getEndOffset();
+ if (pos >= childStart && pos < childEnd)
+ found = true;
+ else if (pos < childStart)
+ {
+ upper = index;
+ bias = -1;
+ }
+ else if (pos >= childEnd)
+ {
+ lower = index;
+ bias = 1;
+ }
+ if (! found)
+ {
+ int newIndex = (upper - lower) / 2 + lower;
+ if (newIndex == index)
+ index = newIndex + bias;
+ else
+ index = newIndex;
+ }
+ } while (upper != lower && ! found);
+ }
+ // If no child view actually covers the specified offset, reset index to
+ // -1.
+ if (! found)
+ index = -1;
+ return index;
+ }
+
+ // --------------------------------------------------------------------------
+ // View methods.
+ // --------------------------------------------------------------------------
+
+ public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ }
+
+ public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ }
+
+ protected boolean updateChildren(DocumentEvent.ElementChange ec,
+ DocumentEvent e, ViewFactory vf)
+ {
+ // TODO: Implement this.
+ return false;
+ }
+
+ // --------------------------------------------------------------------------
+ // Internal helper methods.
+ // --------------------------------------------------------------------------
+
+ /**
+ * A helper method to unload the oldest zones when there are more loaded
+ * zones then allowed.
+ */
+ private void unloadOldestZones()
+ {
+ int maxZones = getMaxZonesLoaded();
+ while (loadedZones.size() > maxZones)
+ {
+ View zone = (View) loadedZones.removeFirst();
+ unloadZone(zone);
+ }
+ }
+
+ /**
+ * Checks if the zone view at position <code>pos</code> should be split
+ * (its size is greater than maximumZoneSize) and tries to split it.
+ *
+ * @param pos the document position to check
+ */
+ private void checkZoneAt(int pos)
+ {
+ int viewIndex = getViewIndexAtPosition(pos); //, Position.Bias.Forward);
+ View view = getView(viewIndex);
+ int p0 = view.getStartOffset();
+ int p1 = view.getEndOffset();
+ if (p1 - p0 > maximumZoneSize)
+ splitZone(viewIndex, p0, p1);
+ }
+
+ /**
+ * Tries to break the view at the specified index and inside the specified
+ * range into pieces that are acceptable with respect to the maximum zone
+ * size.
+ *
+ * @param index the index of the view to split
+ * @param p0 the start offset
+ * @param p1 the end offset
+ */
+ private void splitZone(int index, int p0, int p1)
+ {
+ ArrayList newZones = new ArrayList();
+ int p = p0;
+ do
+ {
+ p0 = p;
+ p = Math.min(getPreferredZoneEnd(p0), p1);
+ newZones.add(createZone(p0, p));
+ } while (p < p1);
+ View[] newViews = new View[newZones.size()];
+ newViews = (View[]) newZones.toArray(newViews);
+ replace(index, 1, newViews);
+ }
+
+ /**
+ * Calculates the positions at which a zone split is performed. This
+ * tries to create zones sized close to half the maximum zone size.
+ *
+ * @param start the start offset
+ *
+ * @return the preferred end offset
+ */
+ private int getPreferredZoneEnd(int start)
+ {
+ Element el = getElement();
+ int index = el.getElementIndex(start + (maximumZoneSize / 2));
+ Element child = el.getElement(index);
+ int p0 = child.getStartOffset();
+ int p1 = child.getEndOffset();
+ int end = p1;
+ if (p0 - start > maximumZoneSize && p0 > start)
+ end = p0;
+ return end;
+ }
+}
diff --git a/javax/swing/text/html/CSS.java b/javax/swing/text/html/CSS.java
index c248e758e..20a2debbc 100644
--- a/javax/swing/text/html/CSS.java
+++ b/javax/swing/text/html/CSS.java
@@ -37,6 +37,12 @@ exception statement from your version. */
package javax.swing.text.html;
+import gnu.javax.swing.text.html.css.CSSColor;
+import gnu.javax.swing.text.html.css.FontSize;
+import gnu.javax.swing.text.html.css.FontStyle;
+import gnu.javax.swing.text.html.css.FontWeight;
+import gnu.javax.swing.text.html.css.Length;
+
import java.io.Serializable;
import java.util.HashMap;
@@ -459,4 +465,33 @@ public class CSS implements Serializable
return defaultValue;
}
}
+
+ /**
+ * Maps attribute values (String) to some converter class, based on the
+ * key.
+ *
+ * @param att the key
+ * @param v the value
+ *
+ * @return the wrapped value
+ */
+ static Object getValue(Attribute att, String v)
+ {
+ Object o;
+ if (att == Attribute.FONT_SIZE)
+ o = new FontSize(v);
+ else if (att == Attribute.FONT_WEIGHT)
+ o = new FontWeight(v);
+ else if (att == Attribute.FONT_STYLE)
+ o = new FontStyle(v);
+ else if (att == Attribute.COLOR || att == Attribute.BACKGROUND_COLOR)
+ o = new CSSColor(v);
+ else if (att == Attribute.MARGIN || att == Attribute.MARGIN_BOTTOM
+ || att == Attribute.MARGIN_LEFT || att == Attribute.MARGIN_RIGHT
+ || att == Attribute.MARGIN_TOP)
+ o = new Length(v);
+ else
+ o = v;
+ return o;
+ }
}
diff --git a/javax/swing/text/html/HTMLDocument.java b/javax/swing/text/html/HTMLDocument.java
index f7b081c6a..00372cd36 100644
--- a/javax/swing/text/html/HTMLDocument.java
+++ b/javax/swing/text/html/HTMLDocument.java
@@ -39,7 +39,6 @@ exception statement from your version. */
package javax.swing.text.html;
import gnu.classpath.NotImplementedException;
-import gnu.javax.swing.text.html.CharacterAttributeTranslator;
import gnu.javax.swing.text.html.parser.htmlAttributeSet;
import java.io.IOException;
@@ -50,8 +49,6 @@ import java.util.Stack;
import java.util.Vector;
import javax.swing.JEditorPane;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.HyperlinkEvent.EventType;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
@@ -87,8 +84,6 @@ public class HTMLDocument extends DefaultStyledDocument
boolean preservesUnknownTags = true;
int tokenThreshold = Integer.MAX_VALUE;
HTMLEditorKit.Parser parser;
- StyleSheet styleSheet;
- AbstractDocument.Content content;
/**
* Constructs an HTML document using the default buffer size and a default
@@ -96,7 +91,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public HTMLDocument()
{
- this(null);
+ this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet());
}
/**
@@ -119,14 +114,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public HTMLDocument(AbstractDocument.Content c, StyleSheet styles)
{
- this.content = c;
- if (styles == null)
- {
- styles = new StyleSheet();
- styles.importStyleSheet(getClass().getResource(HTMLEditorKit.
- DEFAULT_CSS));
- }
- this.styleSheet = styles;
+ super(c, styles);
}
/**
@@ -137,7 +125,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public StyleSheet getStyleSheet()
{
- return styleSheet;
+ return (StyleSheet) getAttributeContext();
}
/**
@@ -269,7 +257,7 @@ public class HTMLDocument extends DefaultStyledDocument
public void setBase(URL u)
{
baseURL = u;
- styleSheet.setBase(u);
+ getStyleSheet().setBase(u);
}
/**
@@ -633,13 +621,8 @@ public class HTMLDocument extends DefaultStyledDocument
// Put the old attribute set on the stack.
pushCharacterStyle();
- // Translate tag.. return if succesful.
- if(CharacterAttributeTranslator.translateTag(charAttr, t, a))
- return;
-
// Just add the attributes in <code>a</code>.
- if (a != null)
- charAttr.addAttribute(t, a.copyAttributes());
+ charAttr.addAttribute(t, a.copyAttributes());
}
/**
@@ -812,7 +795,42 @@ public class HTMLDocument extends DefaultStyledDocument
print ("AreaAction.end not implemented");
}
}
-
+
+ /**
+ * Converts HTML tags to CSS attributes.
+ */
+ class ConvertAction
+ extends TagAction
+ {
+
+ public void start(HTML.Tag tag, MutableAttributeSet atts)
+ {
+ pushCharacterStyle();
+ charAttr.addAttribute(tag, atts.copyAttributes());
+ StyleSheet styleSheet = getStyleSheet();
+ // TODO: Add other tags here.
+ if (tag == HTML.Tag.FONT)
+ {
+ String color = (String) atts.getAttribute(HTML.Attribute.COLOR);
+ if (color != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.COLOR, color);
+ String face = (String) atts.getAttribute(HTML.Attribute.FACE);
+ if (face != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_FAMILY,
+ face);
+ String size = (String) atts.getAttribute(HTML.Attribute.SIZE);
+ if (size != null)
+ styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_SIZE,
+ size);
+ }
+ }
+
+ public void end(HTML.Tag tag)
+ {
+ popCharacterStyle();
+ }
+ }
+
class BaseAction extends TagAction
{
/**
@@ -1028,7 +1046,7 @@ public class HTMLDocument extends DefaultStyledDocument
StyleAction styleAction = new StyleAction();
TitleAction titleAction = new TitleAction();
-
+ ConvertAction convertAction = new ConvertAction();
tagToAction.put(HTML.Tag.A, characterAction);
tagToAction.put(HTML.Tag.ADDRESS, characterAction);
tagToAction.put(HTML.Tag.APPLET, hiddenAction);
@@ -1051,7 +1069,7 @@ public class HTMLDocument extends DefaultStyledDocument
tagToAction.put(HTML.Tag.DL, blockAction);
tagToAction.put(HTML.Tag.DT, paragraphAction);
tagToAction.put(HTML.Tag.EM, characterAction);
- tagToAction.put(HTML.Tag.FONT, characterAction);
+ tagToAction.put(HTML.Tag.FONT, convertAction);
tagToAction.put(HTML.Tag.FORM, blockAction);
tagToAction.put(HTML.Tag.FRAME, specialAction);
tagToAction.put(HTML.Tag.FRAMESET, blockAction);
@@ -1163,7 +1181,7 @@ public class HTMLDocument extends DefaultStyledDocument
*/
public void handleText(char[] data, int pos)
{
- if (data != null && data.length > 0)
+ if (shouldInsert() && data != null && data.length > 0)
addContent(data, 0, data.length);
}
@@ -1728,4 +1746,19 @@ public void setOuterHTML(Element elem, String htmlText)
// TODO charset
getParser().parse(new StringReader(htmlText), reader, true);
}
+
+ /**
+ * Overridden to tag content with the synthetic HTML.Tag.CONTENT
+ * tag.
+ */
+ protected void insertUpdate(DefaultDocumentEvent evt, AttributeSet att)
+ {
+ if (att == null)
+ {
+ SimpleAttributeSet sas = new SimpleAttributeSet();
+ sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
+ att = sas;
+ }
+ super.insertUpdate(evt, att);
+ }
}
diff --git a/javax/swing/text/html/HTMLEditorKit.java b/javax/swing/text/html/HTMLEditorKit.java
index b852d568c..3bf380c6f 100644
--- a/javax/swing/text/html/HTMLEditorKit.java
+++ b/javax/swing/text/html/HTMLEditorKit.java
@@ -48,6 +48,8 @@ import java.awt.event.MouseMotionListener;
import java.awt.Cursor;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
@@ -64,7 +66,6 @@ import javax.swing.text.EditorKit;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.StyleConstants;
-import javax.swing.text.StyleContext;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.TextAction;
import javax.swing.text.View;
@@ -804,7 +805,7 @@ public class HTMLEditorKit
/**
* The current style sheet.
*/
- StyleSheet styleSheet;
+ private StyleSheet styleSheet;
/**
* The ViewFactory for HTMLFactory.
@@ -831,11 +832,6 @@ public class HTMLEditorKit
*/
LinkController mouseListener;
- /**
- * Style context for this editor.
- */
- StyleContext styleContext;
-
/** The content type */
String contentType = "text/html";
@@ -850,11 +846,7 @@ public class HTMLEditorKit
*/
public HTMLEditorKit()
{
- super();
- styleContext = new StyleContext();
- styleSheet = new StyleSheet();
- styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS));
- // FIXME: Set inputAttributes with default.css
+ // Nothing to do here.
}
/**
@@ -923,8 +915,7 @@ public class HTMLEditorKit
if (parser == null)
throw new IOException("Parser is null.");
- ParserCallback pc = ((HTMLDocument) doc).getReader
- (offset, popDepth, pushDepth, insertTag);
+ ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag);
// FIXME: What should ignoreCharSet be set to?
@@ -1154,8 +1145,18 @@ public class HTMLEditorKit
{
if (styleSheet == null)
{
- styleSheet = new StyleSheet();
- styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS));
+ try
+ {
+ styleSheet = new StyleSheet();
+ InputStream in = getClass().getResourceAsStream(DEFAULT_CSS);
+ InputStreamReader r = new InputStreamReader(in);
+ styleSheet.loadRules(r, null);
+ r.close();
+ }
+ catch (IOException ex)
+ {
+ // No style available.
+ }
}
return styleSheet;
}
diff --git a/javax/swing/text/html/InlineView.java b/javax/swing/text/html/InlineView.java
index 77ec86e82..31eaa129c 100644
--- a/javax/swing/text/html/InlineView.java
+++ b/javax/swing/text/html/InlineView.java
@@ -60,6 +60,11 @@ public class InlineView
{
/**
+ * The attributes used by this view.
+ */
+ private AttributeSet attributes;
+
+ /**
* Creates a new <code>InlineView</code> that renders the specified element.
*
* @param element the element for this view
@@ -115,7 +120,9 @@ public class InlineView
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
{
super.changedUpdate(e, a, f);
- setPropertiesFromAttributes();
+ StyleSheet ss = getStyleSheet();
+ attributes = ss.getViewAttributes(this);
+ preferenceChanged(null, true, true);
}
/**
@@ -126,8 +133,12 @@ public class InlineView
*/
public AttributeSet getAttributes()
{
- // FIXME: Implement this.
- return super.getAttributes();
+ if (attributes == null)
+ {
+ StyleSheet ss = getStyleSheet();
+ attributes = ss.getViewAttributes(this);
+ }
+ return attributes;
}
@@ -143,10 +154,43 @@ public class InlineView
return super.breakView(axis, offset, pos, len);
}
+ /**
+ * Loads the character style properties from the stylesheet.
+ */
protected void setPropertiesFromAttributes()
{
- // FIXME: Implement this.
super.setPropertiesFromAttributes();
+ AttributeSet atts = getAttributes();
+ Object o = atts.getAttribute(CSS.Attribute.TEXT_DECORATION);
+
+ // Check for underline.
+ boolean b = false;
+ if (o != null && o.toString().contains("underline"))
+ b = true;
+ setUnderline(b);
+
+ // Check for line-through.
+ b = false;
+ if (o != null && o.toString().contains("line-through"))
+ b = true;
+ setStrikeThrough(b);
+
+ // Check for vertical alignment (subscript/superscript).
+ o = atts.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+
+ // Subscript.
+ b = false;
+ if (o != null && o.toString().contains("sub"))
+ b = true;
+ setSubscript(b);
+
+ // Superscript.
+ b = false;
+ if (o != null && o.toString().contains("sup"))
+ b = true;
+ setSuperscript(b);
+
+ // TODO: Handle white-space: nowrap property.
}
/**
diff --git a/javax/swing/text/html/MultiAttributeSet.java b/javax/swing/text/html/MultiAttributeSet.java
new file mode 100644
index 000000000..0f1145084
--- /dev/null
+++ b/javax/swing/text/html/MultiAttributeSet.java
@@ -0,0 +1,213 @@
+/* MultiAttributeSet.java -- Multiplexes between a set of AttributeSets
+ 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.html;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+/**
+ * An AttributeSet impl that multiplexes between a set of other AttributeSets.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class MultiAttributeSet
+ implements AttributeSet
+{
+
+ /**
+ * The Enumeration for the multiplexed names.
+ */
+ private class MultiNameEnumeration
+ implements Enumeration
+ {
+ /**
+ * The index of the current AttributeSet.
+ */
+ private int index;
+
+ /**
+ * The names Enumeration of the current AttributeSet.
+ */
+ private Enumeration current;
+
+ /**
+ * Creates a new instance.
+ */
+ MultiNameEnumeration()
+ {
+ index = 0;
+ current = multi[0].getAttributeNames();
+ }
+
+ public boolean hasMoreElements()
+ {
+ return current.hasMoreElements() || index < multi.length - 1;
+ }
+
+ public Object nextElement()
+ {
+ if (! current.hasMoreElements())
+ {
+ if (index < multi.length - 1)
+ {
+ index++;
+ current = multi[index].getAttributeNames();
+ }
+ else
+ throw new NoSuchElementException();
+ }
+ return current.nextElement();
+ }
+
+ }
+
+ /**
+ * The AttributeSets to multiplex.
+ */
+ AttributeSet[] multi;
+
+ /**
+ * Provided for subclasses that need to initialize via {@link #init}.
+ */
+ MultiAttributeSet()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param m the AttributeSets to multiplex
+ */
+ MultiAttributeSet(AttributeSet[] m)
+ {
+ init(m);
+ }
+
+ /**
+ * Provided for subclasses to initialize the attribute set.
+ *
+ * @param m the attributes to multiplex
+ */
+ void init(AttributeSet[] m)
+ {
+ multi = m;
+ }
+
+ public boolean containsAttribute(Object name, Object value)
+ {
+ boolean ret = false;
+ for (int i = 0; i < multi.length && ret == false; i++)
+ {
+ if (multi[i].containsAttribute(name, value))
+ ret = true;
+ }
+ return ret;
+ }
+
+ public boolean containsAttributes(AttributeSet attributes)
+ {
+ boolean ret = true;
+ Enumeration e = attributes.getAttributeNames();
+ while (ret && e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ ret = attributes.getAttribute(key).equals(getAttribute(key));
+ }
+ return ret;
+ }
+
+ public AttributeSet copyAttributes()
+ {
+ SimpleAttributeSet copy = new SimpleAttributeSet();
+ for (int i = 0; i < multi.length; i++)
+ {
+ copy.addAttributes(multi[i]);
+ }
+ return copy;
+ }
+
+ public Object getAttribute(Object key)
+ {
+ Object ret = null;
+ for (int i = 0; i < multi.length && ret == null; i++)
+ {
+ ret = multi[i].getAttribute(key);
+ }
+ return ret;
+ }
+
+ public int getAttributeCount()
+ {
+ int n = 0;
+ for (int i = 0; i < multi.length; i++)
+ {
+ n += multi[i].getAttributeCount();
+ }
+ return n;
+ }
+
+ public Enumeration getAttributeNames()
+ {
+ return new MultiNameEnumeration();
+ }
+
+ public AttributeSet getResolveParent()
+ {
+ return null;
+ }
+
+ public boolean isDefined(Object attrName)
+ {
+ boolean ret = false;
+ for (int i = 0; i < multi.length && ! ret; i++)
+ ret = multi[i].isDefined(attrName);
+ return ret;
+ }
+
+ public boolean isEqual(AttributeSet attr)
+ {
+ return getAttributeCount() == attr.getAttributeCount()
+ && containsAttributes(attr);
+ }
+
+}
diff --git a/javax/swing/text/html/MultiStyle.java b/javax/swing/text/html/MultiStyle.java
new file mode 100644
index 000000000..3937bff75
--- /dev/null
+++ b/javax/swing/text/html/MultiStyle.java
@@ -0,0 +1,136 @@
+/* MultiStyle.java -- Multiplexes between several Styles
+ 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.html;
+
+import java.util.Enumeration;
+
+import javax.swing.event.ChangeListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+
+/**
+ * A Style implementation that is able to multiplex between several other
+ * Styles. This is used for CSS style resolving.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class MultiStyle
+ extends MultiAttributeSet
+ implements Style
+{
+
+ // FIXME: Fix the implementation to also return attributes that
+ // are added to this style, etc. However, this is not really needed
+ // now for CSS, but would be nice for correctness.
+
+ /**
+ * The name of the style.
+ */
+ private String name;
+
+ /**
+ * The attributes added to this style.
+ */
+ private SimpleAttributeSet attributes;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param n the name
+ * @param m the styles to multiplex
+ */
+ public MultiStyle(String n, AttributeSet[] m)
+ {
+ super(m);
+ name = n;
+ attributes = new SimpleAttributeSet();
+ }
+
+ /**
+ * Returns the name of the style.
+ *
+ * @return the name of the style
+ */
+ public String getName()
+ {
+ return name;
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement.
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement.
+ }
+
+ public void addAttribute(Object name, Object value)
+ {
+ attributes.addAttribute(name, value);
+ }
+
+ public void addAttributes(AttributeSet atts)
+ {
+ attributes.addAttributes(atts);
+ }
+
+ public void removeAttribute(Object name)
+ {
+ attributes.removeAttribute(name);
+ }
+
+ public void removeAttributes(Enumeration names)
+ {
+ attributes.removeAttribute(names);
+ }
+
+ public void removeAttributes(AttributeSet atts)
+ {
+ attributes.removeAttribute(atts);
+ }
+
+ public void setResolveParent(AttributeSet parent)
+ {
+ // TODO: Implement.
+ }
+
+}
diff --git a/javax/swing/text/html/ParagraphView.java b/javax/swing/text/html/ParagraphView.java
index 2339f4e66..951f70b60 100644
--- a/javax/swing/text/html/ParagraphView.java
+++ b/javax/swing/text/html/ParagraphView.java
@@ -39,12 +39,14 @@ exception statement from your version. */
package javax.swing.text.html;
import java.awt.Graphics;
+import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.SizeRequirements;
import javax.swing.text.AttributeSet;
import javax.swing.text.Document;
import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
import javax.swing.text.View;
/**
@@ -55,10 +57,20 @@ import javax.swing.text.View;
* @author Roman Kennke (kennke@aicas.com)
*/
public class ParagraphView
- extends javax.swing.text.ParagraphView
+ extends javax.swing.text.ParagraphView
{
/**
+ * The attributes used by this view.
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The stylesheet's box painter.
+ */
+ private StyleSheet.BoxPainter painter;
+
+ /**
* Creates a new ParagraphView for the specified element.
*
* @param element the element
@@ -88,8 +100,11 @@ public class ParagraphView
*/
public AttributeSet getAttributes()
{
- // FIXME: Implement this multiplexing thing.
- return super.getAttributes();
+ if (attributes == null)
+ {
+ attributes = getStyleSheet().getViewAttributes(this);
+ }
+ return attributes;
}
/**
@@ -98,7 +113,32 @@ public class ParagraphView
*/
protected void setPropertiesFromAttributes()
{
- // FIXME: Implement this.
+ super.setPropertiesFromAttributes();
+
+ // Fetch CSS attributes.
+ AttributeSet atts = getAttributes();
+ Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN);
+ if (o != null)
+ {
+ String align = o.toString();
+ if (align.equals("left"))
+ setJustification(StyleConstants.ALIGN_LEFT);
+ else if (align.equals("right"))
+ setJustification(StyleConstants.ALIGN_RIGHT);
+ else if (align.equals("center"))
+ setJustification(StyleConstants.ALIGN_CENTER);
+ else if (align.equals("justify"))
+ setJustification(StyleConstants.ALIGN_JUSTIFIED);
+ }
+
+ // Fetch StyleSheet's box painter.
+ painter = getStyleSheet().getBoxPainter(atts);
+ setInsets((short) painter.getInset(TOP, this),
+ (short) painter.getInset(LEFT, this),
+ (short) painter.getInset(BOTTOM, this),
+ (short) painter.getInset(RIGHT, this));
+
+ // TODO: Handle CSS width and height attributes somehow.
}
/**
@@ -147,15 +187,20 @@ public class ParagraphView
}
/**
- * Paints this view. This delegates to the superclass after the coordinates
- * have been updated for tab calculations.
+ * Paints this view. This paints the box using the stylesheet's
+ * box painter for this view and delegates to the super class paint()
+ * afterwards.
*
* @param g the graphics object
* @param a the current allocation of this view
*/
public void paint(Graphics g, Shape a)
{
- // FIXME: Implement the above specified behaviour.
+ if (a != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+ painter.paint(g, r.x, r.y, r.width, r.height, this);
+ }
super.paint(g, a);
}
diff --git a/javax/swing/text/html/StyleSheet.java b/javax/swing/text/html/StyleSheet.java
index 01853d163..520076652 100644
--- a/javax/swing/text/html/StyleSheet.java
+++ b/javax/swing/text/html/StyleSheet.java
@@ -38,28 +38,35 @@ exception statement from your version. */
package javax.swing.text.html;
-import gnu.javax.swing.text.html.CharacterAttributeTranslator;
+import gnu.classpath.NotImplementedException;
+import gnu.javax.swing.text.html.css.CSSColor;
+import gnu.javax.swing.text.html.css.CSSParser;
+import gnu.javax.swing.text.html.css.CSSParserCallback;
+import gnu.javax.swing.text.html.css.FontSize;
+import gnu.javax.swing.text.html.css.FontStyle;
+import gnu.javax.swing.text.html.css.FontWeight;
+import gnu.javax.swing.text.html.css.Length;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
-
import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
-import java.io.StringReader;
-
-import java.net.MalformedURLException;
import java.net.URL;
-
+import java.util.ArrayList;
import java.util.Enumeration;
-import java.util.Vector;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringTokenizer;
+import javax.swing.event.ChangeListener;
import javax.swing.text.AttributeSet;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.View;
@@ -85,6 +92,91 @@ import javax.swing.text.View;
public class StyleSheet extends StyleContext
{
+ /**
+ * Parses CSS stylesheets using the parser in gnu/javax/swing/html/css.
+ */
+ private class CSSStyleSheetParserCallback
+ implements CSSParserCallback
+ {
+ /**
+ * The selector for which the rules are currently parsed.
+ */
+ private String[] selector;
+
+ /**
+ * Called at the beginning of a statement.
+ *
+ * @param sel the selector
+ */
+ public void startStatement(String sel)
+ {
+ StringTokenizer tokens = new StringTokenizer(sel);
+ selector = new String[tokens.countTokens()];
+ for (int index = 0; tokens.hasMoreTokens(); index++)
+ selector[index] = tokens.nextToken();
+ }
+
+ /**
+ * Called at the end of a statement.
+ */
+ public void endStatement()
+ {
+ selector = null;
+ }
+
+ /**
+ * Called when a declaration is parsed.
+ *
+ * @param property the property
+ * @param value the value
+ */
+ public void declaration(String property, String value)
+ {
+ for (int i = 0; i < selector.length; i++)
+ {
+ CSSStyle style = (CSSStyle) css.get(selector[i]);
+ if (style == null)
+ {
+ style = new CSSStyle();
+ css.put(selector[i], style);
+ }
+ CSS.Attribute cssAtt = CSS.getAttribute(property);
+ Object val = CSS.getValue(cssAtt, value);
+ if (cssAtt != null)
+ style.addAttribute(cssAtt, val);
+ // else // For debugging only.
+ // System.err.println("no mapping for: " + property);
+ }
+ }
+
+ }
+
+ /**
+ * Represents a style that is defined by a CSS rule.
+ */
+ private class CSSStyle
+ extends SimpleAttributeSet
+ implements Style
+ {
+
+ public String getName()
+ {
+ // TODO: Implement this for correctness.
+ return null;
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement this for correctness.
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement this for correctness.
+ }
+
+ }
+
/** The base URL */
URL base;
@@ -93,7 +185,18 @@ public class StyleSheet extends StyleContext
/** The style sheets stored. */
StyleSheet[] styleSheet;
-
+
+ /**
+ * Maps element names (selectors) to AttributSet (the corresponding style
+ * information).
+ */
+ HashMap css = new HashMap();
+
+ /**
+ * Maps selectors to their resolved styles.
+ */
+ private HashMap resolvedStyles;
+
/**
* Constructs a StyleSheet.
*/
@@ -101,6 +204,7 @@ public class StyleSheet extends StyleContext
{
super();
baseFontSize = 4; // Default font size from CSS
+ resolvedStyles = new HashMap();
}
/**
@@ -114,10 +218,171 @@ public class StyleSheet extends StyleContext
*/
public Style getRule(HTML.Tag t, Element e)
{
- // FIXME: Not implemented.
- return null;
+ // Create list of the element and all of its parents, starting
+ // with the bottommost element.
+ ArrayList path = new ArrayList();
+ Element el;
+ AttributeSet atts;
+ for (el = e; el != null; el = el.getParentElement())
+ path.add(el);
+
+ // Create fully qualified selector.
+ StringBuilder selector = new StringBuilder();
+ int count = path.size();
+ // We append the actual element after this loop.
+ for (int i = count - 1; i > 0; i--)
+ {
+ el = (Element) path.get(i);
+ atts = el.getAttributes();
+ Object name = atts.getAttribute(StyleConstants.NameAttribute);
+ selector.append(name.toString());
+ if (atts.isDefined(HTML.Attribute.ID))
+ {
+ selector.append('#');
+ selector.append(atts.getAttribute(HTML.Attribute.ID));
+ }
+ else if (atts.isDefined(HTML.Attribute.CLASS))
+ {
+ selector.append('.');
+ selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+ }
+ selector.append(' ');
+ }
+ selector.append(t.toString());
+ el = (Element) path.get(0);
+ atts = el.getAttributes();
+ // For leaf elements, we have to fetch the tag specific attributes.
+ if (el.isLeaf())
+ {
+ Object o = atts.getAttribute(t);
+ if (o instanceof AttributeSet)
+ atts = (AttributeSet) o;
+ else
+ atts = null;
+ }
+ if (atts != null)
+ {
+ if (atts.isDefined(HTML.Attribute.ID))
+ {
+ selector.append('#');
+ selector.append(atts.getAttribute(HTML.Attribute.ID));
+ }
+ else if (atts.isDefined(HTML.Attribute.CLASS))
+ {
+ selector.append('.');
+ selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+ }
+ }
+ return getResolvedStyle(selector.toString(), path, t);
}
-
+
+ /**
+ * Fetches a resolved style. If there is no resolved style for the
+ * specified selector, the resolve the style using
+ * {@link #resolveStyle(String, List, HTML.Tag)}.
+ *
+ * @param selector the selector for which to resolve the style
+ * @param path the Element path, used in the resolving algorithm
+ * @param tag the tag for which to resolve
+ *
+ * @return the resolved style
+ */
+ private Style getResolvedStyle(String selector, List path, HTML.Tag tag)
+ {
+ Style style = (Style) resolvedStyles.get(selector);
+ if (style == null)
+ style = resolveStyle(selector, path, tag);
+ return style;
+ }
+
+ /**
+ * Resolves a style. This creates arrays that hold the tag names,
+ * class and id attributes and delegates the work to
+ * {@link #resolveStyle(String, String[], String[], String[])}.
+ *
+ * @param selector the selector
+ * @param path the Element path
+ * @param tag the tag
+ *
+ * @return the resolved style
+ */
+ private Style resolveStyle(String selector, List path, HTML.Tag tag)
+ {
+ int count = path.size();
+ String[] tags = new String[count];
+ String[] ids = new String[count];
+ String[] classes = new String[count];
+ for (int i = 0; i < count; i++)
+ {
+ Element el = (Element) path.get(i);
+ AttributeSet atts = el.getAttributes();
+ if (i == 0 && el.isLeaf())
+ {
+ Object o = atts.getAttribute(tag);
+ if (o instanceof AttributeSet)
+ atts = (AttributeSet) o;
+ else
+ atts = null;
+ }
+ if (atts != null)
+ {
+ HTML.Tag t =
+ (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
+ if (t != null)
+ tags[i] = t.toString();
+ else
+ tags[i] = null;
+ if (atts.isDefined(HTML.Attribute.CLASS))
+ classes[i] = atts.getAttribute(HTML.Attribute.CLASS).toString();
+ else
+ classes[i] = null;
+ if (atts.isDefined(HTML.Attribute.ID))
+ ids[i] = atts.getAttribute(HTML.Attribute.ID).toString();
+ else
+ ids[i] = null;
+ }
+ else
+ {
+ tags[i] = null;
+ classes[i] = null;
+ ids[i] = null;
+ }
+ }
+ tags[0] = tag.toString();
+ return resolveStyle(selector, tags, ids, classes);
+ }
+
+ /**
+ * Performs style resolving.
+ *
+ * @param selector the selector
+ * @param tags the tags
+ * @param ids the corresponding ID attributes
+ * @param classes the corresponding CLASS attributes
+ *
+ * @return the resolved style
+ */
+ private Style resolveStyle(String selector, String[] tags, String[] ids,
+ String[] classes)
+ {
+ // FIXME: This style resolver is not correct. But it works good enough for
+ // the default.css.
+ int count = tags.length;
+ ArrayList styles = new ArrayList();
+ for (int i = 0; i < count; i++)
+ {
+ Style style = (Style) css.get(tags[i]);
+ if (style != null)
+ styles.add(style);
+ // FIXME: Handle ID and CLASS attributes.
+ }
+ Style[] styleArray = new Style[styles.size()];
+ Style resolved = new MultiStyle(selector,
+ (Style[]) styles.toArray(styleArray));
+ resolvedStyles.put(selector, resolved);
+ return resolved;
+ }
+
/**
* Gets the rule that best matches the selector. selector is a space
* separated String of element names. The attributes of the returned
@@ -128,27 +393,21 @@ public class StyleSheet extends StyleContext
*/
public Style getRule(String selector)
{
- // FIXME: Not implemented.
- return null;
+ // FIXME: This is a very rudimentary implementation. Should
+ // be extended to conform to the CSS spec.
+ return (Style) css.get(selector);
}
/**
- * Adds a set if rules to the sheet. The rules are expected to be in valid
+ * Adds a set of rules to the sheet. The rules are expected to be in valid
* CSS format. This is called as a result of parsing a <style> tag
*
* @param rule - the rule to add to the sheet
*/
public void addRule(String rule)
+ throws NotImplementedException
{
- CssParser cp = new CssParser();
- try
- {
- cp.parse(base, new StringReader(rule), false, false);
- }
- catch (IOException io)
- {
- // Do nothing here.
- }
+ // FIXME: Implement.
}
/**
@@ -176,10 +435,13 @@ public class StyleSheet extends StyleContext
* parameter.
* @throws IOException - For any IO error while reading
*/
- public void loadRules(Reader in, URL ref) throws IOException
+ public void loadRules(Reader in, URL ref)
+ throws IOException
{
- CssParser cp = new CssParser();
- cp.parse(ref, in, false, false);
+ CSSStyleSheetParserCallback cb = new CSSStyleSheetParserCallback();
+ // FIXME: Handle ref.
+ CSSParser parser = new CSSParser(in, cb);
+ parser.parse();
}
/**
@@ -191,8 +453,7 @@ public class StyleSheet extends StyleContext
*/
public AttributeSet getViewAttributes(View v)
{
- // FIXME: Not implemented.
- return null;
+ return new ViewAttributeSet(v, this);
}
/**
@@ -310,7 +571,8 @@ public class StyleSheet extends StyleContext
public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key,
String value)
{
- attr.addAttribute(key, value);
+ Object val = CSS.getValue(key, value);
+ attr.addAttribute(key, val);
}
/**
@@ -340,8 +602,11 @@ public class StyleSheet extends StyleContext
*/
public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet)
{
- // FIXME: Not implemented.
- return null;
+ // FIXME: Really convert HTML to CSS here.
+ AttributeSet cssAttr = htmlAttrSet.copyAttributes();
+ MutableAttributeSet cssStyle = addStyle(null, null);
+ cssStyle.addAttributes(cssAttr);
+ return cssStyle;
}
/**
@@ -455,7 +720,31 @@ public class StyleSheet extends StyleContext
*/
public Font getFont(AttributeSet a)
{
- return super.getFont(a);
+ FontSize size = (FontSize) a.getAttribute(CSS.Attribute.FONT_SIZE);
+ int realSize = 12;
+ if (size != null)
+ realSize = size.getValue();
+
+ // Decrement size for subscript and superscript.
+ Object valign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+ if (valign != null)
+ {
+ String v = valign.toString();
+ if (v.contains("sup") || v.contains("sub"))
+ realSize -= 2;
+ }
+
+ // TODO: Convert font family.
+ String family = "SansSerif";
+
+ int style = Font.PLAIN;
+ FontWeight weight = (FontWeight) a.getAttribute(CSS.Attribute.FONT_WEIGHT);
+ if (weight != null)
+ style |= weight.getValue();
+ FontStyle fStyle = (FontStyle) a.getAttribute(CSS.Attribute.FONT_STYLE);
+ if (fStyle != null)
+ style |= fStyle.getValue();
+ return new Font(family, style, realSize);
}
/**
@@ -468,7 +757,11 @@ public class StyleSheet extends StyleContext
*/
public Color getForeground(AttributeSet a)
{
- return super.getForeground(a);
+ CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.COLOR);
+ Color color = null;
+ if (c != null)
+ color = c.getValue();
+ return color;
}
/**
@@ -481,7 +774,11 @@ public class StyleSheet extends StyleContext
*/
public Color getBackground(AttributeSet a)
{
- return super.getBackground(a);
+ CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.BACKGROUND_COLOR);
+ Color color = null;
+ if (c != null)
+ color = c.getValue();
+ return color;
}
/**
@@ -595,7 +892,7 @@ public class StyleSheet extends StyleContext
*/
public Color stringToColor(String colorName)
{
- return CharacterAttributeTranslator.getColor(colorName);
+ return CSSColor.convertValue(colorName);
}
/**
@@ -609,12 +906,12 @@ public class StyleSheet extends StyleContext
*/
public static class BoxPainter extends Object implements Serializable
{
-
- /**
- * Attribute set for painter
- */
- AttributeSet as;
-
+
+ private float leftInset;
+ private float rightInset;
+ private float topInset;
+ private float bottomInset;
+
/**
* Package-private constructor.
*
@@ -622,9 +919,21 @@ public class StyleSheet extends StyleContext
*/
BoxPainter(AttributeSet as)
{
- this.as = as;
+ Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT);
+ if (l != null)
+ leftInset = l.getValue();
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT);
+ if (l != null)
+ rightInset = l.getValue();
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_TOP);
+ if (l != null)
+ topInset = l.getValue();
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_BOTTOM);
+ if (l != null)
+ bottomInset = l.getValue();
}
+
/**
* Gets the inset needed on a given side to account for the margin, border
* and padding.
@@ -638,8 +947,25 @@ public class StyleSheet extends StyleContext
*/
public float getInset(int size, View v)
{
- // FIXME: Not implemented.
- return 0;
+ float inset;
+ switch (size)
+ {
+ case View.TOP:
+ inset = topInset;
+ break;
+ case View.BOTTOM:
+ inset = bottomInset;
+ break;
+ case View.LEFT:
+ inset = leftInset;
+ break;
+ case View.RIGHT:
+ inset = rightInset;
+ break;
+ default:
+ inset = 0.0F;
+ }
+ return inset;
}
/**
@@ -701,207 +1027,5 @@ public class StyleSheet extends StyleContext
// FIXME: Not implemented.
}
}
-
- /**
- * The parser callback for the CSSParser.
- */
- class CssParser implements CSSParser.CSSParserCallback
- {
- /**
- * A vector of all the selectors.
- * Each element is an array of all the selector tokens
- * in a single rule.
- */
- Vector selectors;
-
- /** A vector of all the selector tokens in a rule. */
- Vector selectorTokens;
- /** Name of the current property. */
- String propertyName;
-
- /** The set of CSS declarations */
- MutableAttributeSet declaration;
-
- /**
- * True if parsing a declaration, that is the Reader will not
- * contain a selector.
- */
- boolean parsingDeclaration;
-
- /** True if the attributes are coming from a linked/imported style. */
- boolean isLink;
-
- /** The base URL */
- URL base;
-
- /** The parser */
- CSSParser parser;
-
- /**
- * Constructor
- */
- CssParser()
- {
- selectors = new Vector();
- selectorTokens = new Vector();
- parser = new CSSParser();
- base = StyleSheet.this.base;
- declaration = new SimpleAttributeSet();
- }
-
- /**
- * Parses the passed in CSS declaration into an AttributeSet.
- *
- * @param s - the declaration
- * @return the set of attributes containing the property and value.
- */
- public AttributeSet parseDeclaration(String s)
- {
- try
- {
- return parseDeclaration(new StringReader(s));
- }
- catch (IOException e)
- {
- // Do nothing here.
- }
- return null;
- }
-
- /**
- * Parses the passed in CSS declaration into an AttributeSet.
- *
- * @param r - the reader
- * @return the attribute set
- * @throws IOException from the reader
- */
- public AttributeSet parseDeclaration(Reader r) throws IOException
- {
- parse(base, r, true, false);
- return declaration;
- }
-
- /**
- * Parse the given CSS stream
- *
- * @param base - the url
- * @param r - the reader
- * @param parseDec - True if parsing a declaration
- * @param isLink - True if parsing a link
- */
- public void parse(URL base, Reader r, boolean parseDec, boolean isLink) throws IOException
- {
- parsingDeclaration = parseDec;
- this.isLink = isLink;
- this.base = base;
-
- // flush out all storage
- propertyName = null;
- selectors.clear();
- selectorTokens.clear();
- declaration.removeAttributes(declaration);
-
- parser.parse(r, this, parseDec);
- }
-
- /**
- * Invoked when a valid @import is encountered,
- * will call importStyleSheet if a MalformedURLException
- * is not thrown in creating the URL.
- *
- * @param s - the string after @import
- */
- public void handleImport(String s)
- {
- if (s != null)
- {
- try
- {
- if (s.startsWith("url(") && s.endsWith(")"))
- s = s.substring(4, s.length() - 1);
- if (s.indexOf("\"") >= 0)
- s = s.replaceAll("\"","");
-
- URL url = new URL(s);
- if (url == null && base != null)
- url = new URL(base, s);
-
- importStyleSheet(url);
- }
- catch (MalformedURLException e)
- {
- // Do nothing here.
- }
- }
- }
-
- /**
- * A selector has been encountered.
- *
- * @param s - a selector (e.g. P or UL or even P,)
- */
- public void handleSelector(String s)
- {
- if (s.endsWith(","))
- s = s.substring(0, s.length() - 1);
-
- selectorTokens.addElement(s);
- addSelector();
- }
-
- /**
- * Invoked when the start of a rule is encountered.
- */
- public void startRule()
- {
- addSelector();
- }
-
- /**
- * Invoked when a property name is encountered.
- *
- * @param s - the property
- */
- public void handleProperty(String s)
- {
- propertyName = s;
- }
-
- /**
- * Invoked when a property value is encountered.
- *
- * @param s - the value
- */
- public void handleValue(String s)
- {
- // call addCSSAttribute
- // FIXME: Not implemented
- }
-
- /**
- * Invoked when the end of a rule is encountered.
- */
- public void endRule()
- {
- // FIXME: Not implemented
- // add rules
- propertyName = null;
- }
-
- /**
- * Adds the selector to the vector.
- */
- private void addSelector()
- {
- int length = selectorTokens.size();
- if (length > 0)
- {
- Object[] sel = new Object[length];
- System.arraycopy(selectorTokens.toArray(), 0, sel, 0, length);
- selectors.add(sel);
- selectorTokens.clear();
- }
- }
- }
}
diff --git a/javax/swing/text/html/ViewAttributeSet.java b/javax/swing/text/html/ViewAttributeSet.java
new file mode 100644
index 000000000..25db89fc4
--- /dev/null
+++ b/javax/swing/text/html/ViewAttributeSet.java
@@ -0,0 +1,163 @@
+/* ViewAttributeSet.java -- The AttributeSet used by HTML views
+ 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.html;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
+
+/**
+ * An AttributeSet implemenation that is used by the HTML views. This
+ * AttributeSet is created by StyleSheet.getViewAttributes() and combines
+ * the following attributes:
+ * - The original attributes of the View's element.
+ * - Any translated (HTML->CSS) attributes, as returned by
+ * StyleSheet.translateHTMLToCS().
+ * - CSS Styles as resolved by the CSS stylesheet.
+ *
+ * In addition to that, it resolves attributes to the parent views, if
+ * a CSS attribute is requested that is inheritable.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class ViewAttributeSet
+ extends MultiAttributeSet
+{
+
+ /**
+ * The view for which we are the AttributeSet.
+ */
+ private View view;
+
+ /**
+ * The stylesheet to use.
+ */
+ private StyleSheet styleSheet;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param v the view for which to do the AttributeSet
+ */
+ ViewAttributeSet(View v, StyleSheet ss)
+ {
+ styleSheet = ss;
+ view = v;
+ ArrayList atts = new ArrayList();
+
+ Element el = v.getElement();
+ AttributeSet elAtts = el.getAttributes();
+ AttributeSet htmlAtts = styleSheet.translateHTMLToCSS(elAtts);
+ if (htmlAtts.getAttributeCount() > 0)
+ atts.add(htmlAtts);
+
+ if (el.isLeaf())
+ {
+ Enumeration n = elAtts.getAttributeNames();
+ while (n.hasMoreElements())
+ {
+ Object key = n.nextElement();
+ if (key instanceof HTML.Tag)
+ {
+ AttributeSet rule = styleSheet.getRule((HTML.Tag) key, el);
+ if (rule != null)
+ atts.add(rule);
+ }
+ }
+ }
+ else
+ {
+ HTML.Tag tag =
+ (HTML.Tag) elAtts.getAttribute(StyleConstants.NameAttribute);
+ AttributeSet rule = styleSheet.getRule(tag, el);
+ if (rule != null)
+ atts.add(rule);
+ }
+
+ AttributeSet[] atts1 = new AttributeSet[atts.size()];
+ atts1 = (AttributeSet[]) atts.toArray(atts1);
+ init(atts1);
+ }
+
+ /**
+ * Fetches the attribute for the specific ckey. If the attribute
+ * can't be found and the key is a CSS.Attribute that is inherited,
+ * then the attribute is looked up in the resolve parent.
+ */
+ public Object getAttribute(Object key)
+ {
+ Object val = super.getAttribute(key);
+ if (val == null)
+ {
+ // Didn't find value. If the key is a CSS.Attribute, and is
+ // inherited, then ask the resolve parent.
+ if (key instanceof CSS.Attribute)
+ {
+ CSS.Attribute cssKey = (CSS.Attribute) key;
+ if (cssKey.isInherited())
+ {
+ AttributeSet resolveParent = getResolveParent();
+ if (resolveParent != null)
+ val = resolveParent.getAttribute(cssKey);
+ }
+ }
+ }
+ return val;
+ }
+
+ /**
+ * Returns the resolve parent of this AttributeSet. This is the AttributeSet
+ * returned by the parent view if available.
+ */
+ public AttributeSet getResolveParent()
+ {
+ AttributeSet parent = null;
+ if (view != null)
+ {
+ View parentView = view.getParent();
+ if (parentView != null)
+ parent = parentView.getAttributes();
+ }
+ return parent;
+ }
+}
diff --git a/javax/swing/tree/DefaultTreeCellEditor.java b/javax/swing/tree/DefaultTreeCellEditor.java
index b0a4d8db8..4c10bfe1a 100644
--- a/javax/swing/tree/DefaultTreeCellEditor.java
+++ b/javax/swing/tree/DefaultTreeCellEditor.java
@@ -43,7 +43,6 @@ import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
-import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
@@ -59,10 +58,10 @@ import javax.swing.Icon;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
+import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.CellEditorListener;
-import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
@@ -77,12 +76,6 @@ public class DefaultTreeCellEditor
implements ActionListener, TreeCellEditor, TreeSelectionListener
{
/**
- * The number of the fast mouse clicks, required to start the editing
- * session.
- */
- static int CLICK_COUNT_TO_START = 3;
-
- /**
* This container that appears on the tree during editing session.
* It contains the editing component displays various other editor -
* specific parts like editing icon.
@@ -99,7 +92,7 @@ public class DefaultTreeCellEditor
*/
public EditorContainer()
{
- // Do nothing here.
+ setLayout(null);
}
/**
@@ -111,12 +104,6 @@ public class DefaultTreeCellEditor
// Do nothing here.
}
- public void setBounds(Rectangle bounds)
- {
- super.setBounds(bounds);
- doLayout();
- }
-
/**
* Overrides Container.paint to paint the node's icon and use the selection
* color for the background.
@@ -126,11 +113,20 @@ public class DefaultTreeCellEditor
*/
public void paint(Graphics g)
{
+ // Paint editing icon.
if (editingIcon != null)
{
// From the previous version, the left margin is taken as half
// of the icon width.
- editingIcon.paintIcon(this, g, 0, 0);
+ int y = Math.max(0, (getHeight() - editingIcon.getIconHeight()) / 2);
+ editingIcon.paintIcon(this, g, 0, y);
+ }
+ // Paint border.
+ Color c = getBorderSelectionColor();
+ if (c != null)
+ {
+ g.setColor(c);
+ g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
super.paint(g);
}
@@ -141,27 +137,33 @@ public class DefaultTreeCellEditor
*/
public void doLayout()
{
- // The offset of the editing component.
- int eOffset;
+ if (editingComponent != null)
+ {
+ editingComponent.getPreferredSize();
+ editingComponent.setBounds(offset, 0, getWidth() - offset,
+ getHeight());
+ }
+ }
- // Move the component to the left, leaving room for the editing icon:
- if (editingIcon != null)
- eOffset = editingIcon.getIconWidth();
+ public Dimension getPreferredSize()
+ {
+ Dimension dim;
+ if (editingComponent != null)
+ {
+ dim = editingComponent.getPreferredSize();
+ dim.width += offset + 5;
+ if (renderer != null)
+ {
+ Dimension r = renderer.getPreferredSize();
+ dim.height = Math.max(dim.height, r.height);
+ }
+ if (editingIcon != null)
+ dim.height = Math.max(dim.height, editingIcon.getIconHeight());
+ dim.width = Math.max(100, dim.width);
+ }
else
- eOffset = 0;
-
- Rectangle bounds = getBounds();
- Component c = getComponent(0);
- c.setLocation(eOffset, 0);
-
- // Span the editing component near over all window width.
- c.setSize(bounds.width - eOffset, bounds.height);
- /*
- * @specnote the Sun sets some more narrow editing component width (it is
- * not documented how does it is calculated). However as our text field is
- * still not able to auto - scroll horizontally, replicating such strategy
- * would prevent adding extra characters to the text being edited.
- */
+ dim = new Dimension(0, 0);
+ return dim;
}
}
@@ -227,46 +229,15 @@ public class DefaultTreeCellEditor
*/
public Dimension getPreferredSize()
{
- String s = getText();
-
- Font f = getFont();
-
- if (f != null)
+ Dimension size = super.getPreferredSize();
+ if (renderer != null && DefaultTreeCellEditor.this.getFont() == null)
{
- FontMetrics fm = getToolkit().getFontMetrics(f);
-
- return new Dimension(SwingUtilities.computeStringWidth(fm, s),
- fm.getHeight());
+ size.height = renderer.getPreferredSize().height;
}
return renderer.getPreferredSize();
}
}
- /**
- * Listens for the events from the realEditor.
- */
- class RealEditorListener implements CellEditorListener
- {
- /**
- * The method is called when the editing has been cancelled.
- * @param event unused
- */
- public void editingCanceled(ChangeEvent event)
- {
- cancelCellEditing();
- }
-
- /**
- * The method is called after completing the editing session.
- *
- * @param event unused
- */
- public void editingStopped(ChangeEvent event)
- {
- stopCellEditing();
- }
- }
-
private EventListenerList listenerList = new EventListenerList();
/**
@@ -367,21 +338,14 @@ public class DefaultTreeCellEditor
public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer,
TreeCellEditor editor)
{
- setTree(tree);
this.renderer = renderer;
-
- if (editor == null)
- editor = createTreeCellEditor();
- else
- editor.addCellEditorListener(new RealEditorListener());
-
realEditor = editor;
-
- lastPath = tree.getLeadSelectionPath();
- tree.addTreeSelectionListener(this);
+ if (realEditor == null)
+ realEditor = createTreeCellEditor();
editingContainer = createContainer();
- setFont(UIManager.getFont("Tree.font"));
- setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor"));
+ setTree(tree);
+ Color c = UIManager.getColor("Tree.editorBorderSelectionColor");
+ setBorderSelectionColor(c);
}
/**
@@ -505,19 +469,36 @@ public class DefaultTreeCellEditor
* @return the component for editing
*/
public Component getTreeCellEditorComponent(JTree tree, Object value,
- boolean isSelected, boolean expanded,
+ boolean isSelected,
+ boolean expanded,
boolean leaf, int row)
{
- if (realEditor == null)
- realEditor = createTreeCellEditor();
-
- return realEditor.getTreeCellEditorComponent(tree, value, isSelected,
- expanded, leaf, row);
+ setTree(tree);
+ lastRow = row;
+ determineOffset(tree, value, isSelected, expanded, leaf, row);
+ if (editingComponent != null)
+ editingContainer.remove(editingComponent);
+
+ editingComponent = realEditor.getTreeCellEditorComponent(tree, value,
+ isSelected,
+ expanded, leaf,
+ row);
+ Font f = getFont();
+ if (f == null)
+ {
+ if (renderer != null)
+ f = renderer.getFont();
+ if (f == null)
+ f = tree.getFont();
+ }
+ editingContainer.setFont(f);
+ prepareForEditing();
+ return editingContainer;
}
/**
* Returns the value currently being edited (requests it from the
- * {@link realEditor}.
+ * {@link #realEditor}.
*
* @return the value currently being edited
*/
@@ -535,16 +516,48 @@ public class DefaultTreeCellEditor
* @return true if editing can be started
*/
public boolean isCellEditable(EventObject event)
- {
- if (editingComponent == null)
- configureEditingComponent(tree, renderer, realEditor);
-
- if (editingComponent != null && realEditor.isCellEditable(event))
+ {
+ boolean ret = false;
+ boolean ed = false;
+ if (event != null)
{
- prepareForEditing();
- return true;
+ if (event.getSource() instanceof JTree)
+ {
+ setTree((JTree) event.getSource());
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) event;
+ TreePath path = tree.getPathForLocation(me.getX(), me.getY());
+ ed = lastPath != null && path != null && lastPath.equals(path);
+ if (path != null)
+ {
+ lastRow = tree.getRowForPath(path);
+ Object val = path.getLastPathComponent();
+ boolean isSelected = tree.isRowSelected(lastRow);
+ boolean isExpanded = tree.isExpanded(path);
+ TreeModel m = tree.getModel();
+ boolean isLeaf = m.isLeaf(val);
+ determineOffset(tree, val, isSelected, isExpanded, isLeaf,
+ lastRow);
+ }
+ }
+ }
}
- return false;
+ if (! realEditor.isCellEditable(event))
+ ret = false;
+ else
+ {
+ if (canEditImmediately(event))
+ ret = true;
+ else if (ed && shouldStartEditingTimer(event))
+ startEditingTimer();
+ else if (timer != null && timer.isRunning())
+ timer.stop();
+ }
+ if (ret)
+ prepareForEditing();
+ return ret;
+
}
/**
@@ -567,14 +580,13 @@ public class DefaultTreeCellEditor
*/
public boolean stopCellEditing()
{
- if (editingComponent != null)
+ boolean ret = false;
+ if (realEditor.stopCellEditing())
{
- stopEditingTimer();
- tree.stopEditing();
- editingComponent = null;
- return true;
+ finish();
+ ret = true;
}
- return false;
+ return ret;
}
/**
@@ -583,21 +595,15 @@ public class DefaultTreeCellEditor
*/
public void cancelCellEditing()
{
- if (editingComponent != null)
- {
- tree.cancelEditing();
- editingComponent = null;
- }
- stopEditingTimer();
+ realEditor.cancelCellEditing();
+ finish();
}
-
- /**
- * Stop the editing timer, if it is installed and running.
- */
- private void stopEditingTimer()
+
+ private void finish()
{
- if (timer != null && timer.isRunning())
- timer.stop();
+ if (editingComponent != null)
+ editingContainer.remove(editingComponent);
+ editingComponent = null;
}
/**
@@ -640,10 +646,18 @@ public class DefaultTreeCellEditor
*/
public void valueChanged(TreeSelectionEvent e)
{
- tPath = lastPath;
- lastPath = e.getNewLeadSelectionPath();
- lastRow = tree.getRowForPath(lastPath);
- stopCellEditing();
+ if (tree != null)
+ {
+ if (tree.getSelectionCount() == 1)
+ lastPath = tree.getSelectionPath();
+ else
+ lastPath = null;
+ }
+ // TODO: We really should do the following here, but can't due
+ // to buggy DefaultTreeSelectionModel. This selection model
+ // should only fire if the selection actually changes.
+// if (timer != null)
+// timer.stop();
}
/**
@@ -653,6 +667,8 @@ public class DefaultTreeCellEditor
*/
public void actionPerformed(ActionEvent e)
{
+ if (tree != null && lastPath != null)
+ tree.startEditingAtPath(lastPath);
}
/**
@@ -664,7 +680,17 @@ public class DefaultTreeCellEditor
*/
protected void setTree(JTree newTree)
{
- tree = newTree;
+ if (tree != newTree)
+ {
+ if (tree != null)
+ tree.removeTreeSelectionListener(this);
+ tree = newTree;
+ if (tree != null)
+ tree.addTreeSelectionListener(this);
+
+ if (timer != null)
+ timer.stop();
+ }
}
/**
@@ -675,10 +701,14 @@ public class DefaultTreeCellEditor
*/
protected boolean shouldStartEditingTimer(EventObject event)
{
- if ((event instanceof MouseEvent) &&
- ((MouseEvent) event).getClickCount() == 1)
- return true;
- return false;
+ boolean ret = false;
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent me = (MouseEvent) event;
+ ret = SwingUtilities.isLeftMouseButton(me) && me.getClickCount() == 1
+ && inHitRegion(me.getX(), me.getY());
+ }
+ return ret;
}
/**
@@ -686,8 +716,12 @@ public class DefaultTreeCellEditor
*/
protected void startEditingTimer()
{
- if (timer != null)
- timer.start();
+ if (timer == null)
+ {
+ timer = new Timer(1200, this);
+ timer.setRepeats(false);
+ }
+ timer.start();
}
/**
@@ -723,7 +757,6 @@ public class DefaultTreeCellEditor
protected boolean inHitRegion(int x, int y)
{
Rectangle bounds = tree.getPathBounds(lastPath);
-
return bounds.contains(x, y);
}
@@ -739,13 +772,24 @@ public class DefaultTreeCellEditor
protected void determineOffset(JTree tree, Object value, boolean isSelected,
boolean expanded, boolean leaf, int row)
{
- renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded,
- leaf, row, true);
- Icon c = renderer.getIcon();
- if (c != null)
- offset = renderer.getIconTextGap() + c.getIconWidth();
+ if (renderer != null)
+ {
+ if (leaf)
+ editingIcon = renderer.getLeafIcon();
+ else if (expanded)
+ editingIcon = renderer.getOpenIcon();
+ else
+ editingIcon = renderer.getClosedIcon();
+ if (editingIcon != null)
+ offset = renderer.getIconTextGap() + editingIcon.getIconWidth();
+ else
+ offset = renderer.getIconTextGap();
+ }
else
- offset = 0;
+ {
+ editingIcon = null;
+ offset = 0;
+ }
}
/**
@@ -754,8 +798,8 @@ public class DefaultTreeCellEditor
*/
protected void prepareForEditing()
{
- editingContainer.removeAll();
- editingContainer.add(editingComponent);
+ if (editingComponent != null)
+ editingContainer.add(editingComponent);
}
/**
@@ -776,10 +820,10 @@ public class DefaultTreeCellEditor
*/
protected TreeCellEditor createTreeCellEditor()
{
- DefaultCellEditor editor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField(
- UIManager.getBorder("Tree.selectionBorder")));
- editor.addCellEditorListener(new RealEditorListener());
- editor.setClickCountToStart(CLICK_COUNT_TO_START);
+ Border border = UIManager.getBorder("Tree.editorBorder");
+ JTextField tf = new DefaultTreeCellEditor.DefaultTextField(border);
+ DefaultCellEditor editor = new DefaultCellEditor(tf);
+ editor.setClickCountToStart(1);
realEditor = editor;
return editor;
}
diff --git a/javax/swing/tree/DefaultTreeCellRenderer.java b/javax/swing/tree/DefaultTreeCellRenderer.java
index e120b71c1..26a30f0cc 100644
--- a/javax/swing/tree/DefaultTreeCellRenderer.java
+++ b/javax/swing/tree/DefaultTreeCellRenderer.java
@@ -547,19 +547,9 @@ public class DefaultTreeCellRenderer
*/
public Dimension getPreferredSize()
{
- Rectangle vr = new Rectangle();
- Rectangle ir = new Rectangle();
- Rectangle tr = new Rectangle();
-
- FontMetrics fm = getToolkit().getFontMetrics(getFont());
- SwingUtilities.layoutCompoundLabel((JLabel) this, fm, getText(),
- getIcon(), getVerticalAlignment(),
- getHorizontalAlignment(),
- getVerticalTextPosition(),
- getHorizontalTextPosition(), vr, ir, tr,
- getIconTextGap());
- Rectangle cr = ir.union(tr);
- return new Dimension(cr.width, cr.height);
+ Dimension size = super.getPreferredSize();
+ size.width += 3;
+ return size;
}
/**
diff --git a/javax/swing/tree/VariableHeightLayoutCache.java b/javax/swing/tree/VariableHeightLayoutCache.java
index 11509b1b0..03251eb1f 100644
--- a/javax/swing/tree/VariableHeightLayoutCache.java
+++ b/javax/swing/tree/VariableHeightLayoutCache.java
@@ -451,8 +451,8 @@ public class VariableHeightLayoutCache
{
if (y < r.y)
return r.y - y;
- else if (y > r.y + r.height)
- return y - (r.y + r.height);
+ else if (y > r.y + r.height - 1)
+ return y - (r.y + r.height - 1);
else
return 0;
}