summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/plaf/basic
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/plaf/basic')
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java422
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicBorders.java1768
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java370
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java636
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java102
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java75
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java344
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java181
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java151
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java1410
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java1104
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java592
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java470
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java586
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java96
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java1437
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java69
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java821
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicHTML.java471
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java328
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java1015
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java1786
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java542
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicListUI.java1421
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java1734
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java481
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java1339
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java678
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java1404
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java132
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java74
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java114
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java1019
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java962
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java101
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java301
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java292
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java1513
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java823
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java252
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java2339
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java561
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java1077
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java1623
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java4003
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java566
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java1410
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java116
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java118
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java92
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java1538
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java134
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java124
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java1610
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java292
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java3939
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java75
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/ComboPopup.java103
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java78
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java91
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.pngbin0 -> 454 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.pngbin0 -> 857 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.pngbin0 -> 1787 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.pngbin0 -> 5267 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.pngbin0 -> 14735 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.pngbin0 -> 3180 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.pngbin0 -> 2667 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.pngbin0 -> 8803 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.pngbin0 -> 5976 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.pngbin0 -> 7169 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.pngbin0 -> 1874 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.pngbin0 -> 4844 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.pngbin0 -> 3771 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.pngbin0 -> 13480 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.pngbin0 -> 4832 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.pngbin0 -> 6884 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.pngbin0 -> 6816 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.pngbin0 -> 3676 bytes
-rw-r--r--libjava/classpath/javax/swing/plaf/basic/package.html46
79 files changed, 47351 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java
new file mode 100644
index 000000000..1d6e8874d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicArrowButton.java
@@ -0,0 +1,422 @@
+/* BasicArrowButton.java --
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Polygon;
+
+import javax.swing.ButtonModel;
+import javax.swing.JButton;
+import javax.swing.SwingConstants;
+
+/**
+ * A button that displays an arrow (triangle) that points {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} or {@link #WEST}. This button is used by
+ * the {@link BasicComboBoxUI} class.
+ *
+ * @see BasicComboBoxUI#createArrowButton
+ */
+public class BasicArrowButton extends JButton implements SwingConstants
+{
+
+ /**
+ * The direction that the arrow points.
+ *
+ * @see #getDirection()
+ */
+ protected int direction;
+
+ /**
+ * The color the arrow is painted in if disabled and the bottom and right
+ * edges of the button.
+ * This is package-private to avoid an accessor method.
+ */
+ transient Color shadow = Color.GRAY;
+
+ /**
+ * The color the arrow is painted in if enabled and the bottom and right
+ * edges of the button.
+ * This is package-private to avoid an accessor method.
+ */
+ transient Color darkShadow = new Color(102, 102, 102);
+
+ /**
+ * The top and left edges of the button.
+ * This is package-private to avoid an accessor method.
+ */
+ transient Color highlight = Color.WHITE;
+
+ /**
+ * Creates a new <code>BasicArrowButton</code> object with an arrow pointing
+ * in the specified direction. If the <code>direction</code> is not one of
+ * the specified constants, no arrow is drawn.
+ *
+ * @param direction The direction the arrow points in (one of:
+ * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ */
+ public BasicArrowButton(int direction)
+ {
+ super();
+ setDirection(direction);
+ setFocusable(false);
+ }
+
+ /**
+ * Creates a new BasicArrowButton object with the given colors and
+ * direction.
+ *
+ * @param direction The direction to point in (one of:
+ * {@link #NORTH}, {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ * @param background The background color.
+ * @param shadow The shadow color.
+ * @param darkShadow The dark shadow color.
+ * @param highlight The highlight color.
+ */
+ public BasicArrowButton(int direction, Color background, Color shadow,
+ Color darkShadow, Color highlight)
+ {
+ this(direction);
+ setBackground(background);
+ this.shadow = shadow;
+ this.darkShadow = darkShadow;
+ this.highlight = highlight;
+ setFocusable(false);
+ }
+
+ /**
+ * Returns whether the focus can traverse to this component. This method
+ * always returns <code>false</code>.
+ *
+ * @return <code>false</code>.
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * Returns the direction of the arrow (one of: {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ *
+ * @return The direction of the arrow.
+ */
+ public int getDirection()
+ {
+ return direction;
+ }
+
+ /**
+ * Sets the direction of the arrow.
+ *
+ * @param dir The new direction of the arrow (one of: {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ */
+ public void setDirection(int dir)
+ {
+ this.direction = dir;
+ }
+
+ /**
+ * Paints the arrow button. The painting is delegated to the
+ * paintTriangle method.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ super.paint(g);
+
+ int height = getHeight();
+ int size = height / 4;
+
+ int x = (getWidth() - size) / 2;
+ int y = (height - size) / 2;
+
+ ButtonModel m = getModel();
+ if (m.isArmed())
+ {
+ x++;
+ y++;
+ }
+
+ paintTriangle(g, x, y, size, direction, isEnabled());
+ }
+
+ /**
+ * Returns the preferred size of the arrow button.
+ *
+ * @return The preferred size (always 16 x 16).
+ */
+ public Dimension getPreferredSize()
+ {
+ // since Dimension is NOT immutable, we must return a new instance
+ // every time (if we return a cached value, the caller might modify it)
+ // - tests show that the reference implementation does the same.
+ return new Dimension(16, 16);
+ }
+
+ /**
+ * Returns the minimum size of the arrow button.
+ *
+ * @return The minimum size (always 5 x 5).
+ */
+ public Dimension getMinimumSize()
+ {
+ // since Dimension is NOT immutable, we must return a new instance
+ // every time (if we return a cached value, the caller might modify it)
+ // - tests show that the reference implementation does the same.
+ return new Dimension(5, 5);
+ }
+
+ /**
+ * Returns the maximum size of the arrow button.
+ *
+ * @return The maximum size (always Integer.MAX_VALUE x Integer.MAX_VALUE).
+ */
+ public Dimension getMaximumSize()
+ {
+ // since Dimension is NOT immutable, we must return a new instance
+ // every time (if we return a cached value, the caller might modify it)
+ // - tests show that the reference implementation does the same.
+ return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Paints a triangle with the given size, location and direction. It is
+ * difficult to explain the rationale behind the positioning of the triangle
+ * relative to the given (x, y) position - by trial and error we seem to
+ * match the behaviour of the reference implementation (which is missing a
+ * specification for this method).
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the triangle's location.
+ * @param y the y-coordinate for the triangle's location.
+ * @param size the arrow size (depth).
+ * @param direction the direction of the arrow (one of: {@link #NORTH},
+ * {@link #SOUTH}, {@link #EAST} and {@link #WEST}).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ public void paintTriangle(Graphics g, int x, int y, int size, int direction,
+ boolean isEnabled)
+ {
+ Color savedColor = g.getColor();
+ switch (direction)
+ {
+ case NORTH:
+ paintTriangleNorth(g, x, y, size, isEnabled);
+ break;
+ case SOUTH:
+ paintTriangleSouth(g, x, y, size, isEnabled);
+ break;
+ case LEFT:
+ case WEST:
+ paintTriangleWest(g, x, y, size, isEnabled);
+ break;
+ case RIGHT:
+ case EAST:
+ paintTriangleEast(g, x, y, size, isEnabled);
+ break;
+ }
+ g.setColor(savedColor);
+ }
+
+ /**
+ * Paints an upward-pointing triangle. This method is called by the
+ * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method.
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the anchor point.
+ * @param y the y-coordinate for the anchor point.
+ * @param size the arrow size (depth).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ private void paintTriangleNorth(Graphics g, int x, int y, int size,
+ boolean isEnabled)
+ {
+ int tipX = x + (size - 2) / 2;
+ int tipY = y;
+ int baseX1 = tipX - (size - 1);
+ int baseX2 = tipX + (size - 1);
+ int baseY = y + (size - 1);
+ Polygon triangle = new Polygon();
+ triangle.addPoint(tipX, tipY);
+ triangle.addPoint(baseX1, baseY);
+ triangle.addPoint(baseX2, baseY);
+ if (isEnabled)
+ {
+ g.setColor(Color.DARK_GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ }
+ else
+ {
+ g.setColor(Color.GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ g.setColor(Color.WHITE);
+ g.drawLine(baseX1 + 1, baseY + 1, baseX2 + 1, baseY + 1);
+ }
+ }
+
+ /**
+ * Paints an downward-pointing triangle. This method is called by the
+ * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method.
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the anchor point.
+ * @param y the y-coordinate for the anchor point.
+ * @param size the arrow size (depth).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ private void paintTriangleSouth(Graphics g, int x, int y, int size,
+ boolean isEnabled)
+ {
+ int tipX = x + (size - 2) / 2;
+ int tipY = y + (size - 1);
+ int baseX1 = tipX - (size - 1);
+ int baseX2 = tipX + (size - 1);
+ int baseY = y;
+ Polygon triangle = new Polygon();
+ triangle.addPoint(tipX, tipY);
+ triangle.addPoint(baseX1, baseY);
+ triangle.addPoint(baseX2, baseY);
+ if (isEnabled)
+ {
+ g.setColor(Color.DARK_GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ }
+ else
+ {
+ g.setColor(Color.GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ g.setColor(Color.WHITE);
+ g.drawLine(tipX + 1, tipY, baseX2, baseY + 1);
+ g.drawLine(tipX + 1, tipY + 1, baseX2 + 1, baseY + 1);
+ }
+ }
+
+ /**
+ * Paints a right-pointing triangle. This method is called by the
+ * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method.
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the anchor point.
+ * @param y the y-coordinate for the anchor point.
+ * @param size the arrow size (depth).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ private void paintTriangleEast(Graphics g, int x, int y, int size,
+ boolean isEnabled)
+ {
+ int tipX = x + (size - 1);
+ int tipY = y + (size - 2) / 2;
+ int baseX = x;
+ int baseY1 = tipY - (size - 1);
+ int baseY2 = tipY + (size - 1);
+
+ Polygon triangle = new Polygon();
+ triangle.addPoint(tipX, tipY);
+ triangle.addPoint(baseX, baseY1);
+ triangle.addPoint(baseX, baseY2);
+ if (isEnabled)
+ {
+ g.setColor(Color.DARK_GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ }
+ else
+ {
+ g.setColor(Color.GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ g.setColor(Color.WHITE);
+ g.drawLine(baseX + 1, baseY2, tipX, tipY + 1);
+ g.drawLine(baseX + 1, baseY2 + 1, tipX + 1, tipY + 1);
+ }
+ }
+
+ /**
+ * Paints a left-pointing triangle. This method is called by the
+ * {@link #paintTriangle(Graphics, int, int, int, int, boolean)} method.
+ *
+ * @param g the graphics device.
+ * @param x the x-coordinate for the anchor point.
+ * @param y the y-coordinate for the anchor point.
+ * @param size the arrow size (depth).
+ * @param isEnabled if <code>true</code> the arrow is drawn in the enabled
+ * state, otherwise it is drawn in the disabled state.
+ */
+ private void paintTriangleWest(Graphics g, int x, int y, int size,
+ boolean isEnabled)
+ {
+ int tipX = x;
+ int tipY = y + (size - 2) / 2;
+ int baseX = x + (size - 1);
+ int baseY1 = tipY - (size - 1);
+ int baseY2 = tipY + (size - 1);
+
+ Polygon triangle = new Polygon();
+ triangle.addPoint(tipX, tipY);
+ triangle.addPoint(baseX, baseY1);
+ triangle.addPoint(baseX, baseY2);
+ if (isEnabled)
+ {
+ g.setColor(Color.DARK_GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ }
+ else
+ {
+ g.setColor(Color.GRAY);
+ g.fillPolygon(triangle);
+ g.drawPolygon(triangle);
+ g.setColor(Color.WHITE);
+ g.drawLine(baseX + 1, baseY1 + 1, baseX + 1, baseY2 + 1);
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java
new file mode 100644
index 000000000..83afc33a2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicBorders.java
@@ -0,0 +1,1768 @@
+/* BasicBorders.java --
+ Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.JButton;
+import javax.swing.JPopupMenu;
+import javax.swing.JSplitPane;
+import javax.swing.JToolBar;
+import javax.swing.UIManager;
+import javax.swing.border.AbstractBorder;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.Border;
+import javax.swing.plaf.BorderUIResource;
+import javax.swing.plaf.UIResource;
+import javax.swing.text.JTextComponent;
+
+/**
+ * Provides various borders for the Basic look and feel.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class BasicBorders
+{
+ /**
+ * A MarginBorder that gets shared by multiple components.
+ * Created on demand by the private helper function {@link
+ * #getMarginBorder()}.
+ */
+ private static MarginBorder sharedMarginBorder;
+
+
+ /**
+ * Returns a border for drawing push buttons.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;Button.shadow&#x201d;</code>,
+ * <code>&#x201c;Button.darkShadow&#x201d;</code>,
+ * <code>&#x201c;Button.light&#x201d;</code>, and
+ * <code>&#x201c;Button.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300"
+ * height="170" alt="[A screen shot of the returned border]" />
+ *
+ * @return a {@link
+ * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
+ * whose outer border is a {@link ButtonBorder} and whose
+ * inner border is a {@link MarginBorder}.
+ */
+ public static Border getButtonBorder()
+ {
+ Border outer;
+
+ /* The keys for UIDefaults have been determined by writing a
+ * test program that dumps the UIDefaults to stdout; that program
+ * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
+ * the key "light" is usually called "highlight", and "highlight"
+ * is usually called "lightHighlight".
+ */
+ outer = new ButtonBorder(UIManager.getColor("Button.shadow"),
+ UIManager.getColor("Button.darkShadow"),
+ UIManager.getColor("Button.light"),
+ UIManager.getColor("Button.highlight"));
+
+ /* While the inner border is shared between multiple buttons,
+ * we do not share the outer border because ButtonBorders store
+ * their border colors. We cannot guarantee that the colors
+ * (which come from UIDefaults) are unchanged between invocations
+ * of getButtonBorder. We could store the last colors, and share
+ * the button border if the colors are the same as in the last
+ * invocation, but it probably is not worth the effort.
+ */
+ return new BorderUIResource.CompoundBorderUIResource(
+ outer,
+ /* inner */ getMarginBorder());
+ }
+
+
+ /**
+ * Returns a border for drawing radio buttons.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;RadioButton.shadow&#x201d;</code>,
+ * <code>&#x201c;RadioButton.darkShadow&#x201d;</code>,
+ * <code>&#x201c;RadioButton.light&#x201d;</code>, and
+ * <code>&#x201c;RadioButton.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300"
+ * height="135" alt="[A screen shot of the returned border]" />
+ *
+ * @return a {@link
+ * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
+ * whose outer border is a {@link RadioButtonBorder} and whose
+ * inner border is a {@link MarginBorder}.
+ */
+ public static Border getRadioButtonBorder()
+ {
+ Border outer;
+
+ /* The keys for UIDefaults have been determined by writing a
+ * test program that dumps the UIDefaults to stdout; that program
+ * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
+ * the key "light" is usually called "highlight", and "highlight"
+ * is usually called "lightHighlight".
+ */
+ outer = new RadioButtonBorder(
+ UIManager.getColor("RadioButton.shadow"),
+ UIManager.getColor("RadioButton.darkShadow"),
+ UIManager.getColor("RadioButton.light"),
+ UIManager.getColor("RadioButton.highlight"));
+
+ /* While the inner border is shared between multiple buttons, we
+ * do not share the outer border because RadioButtonBorders, being
+ * ButtonBorders, store their border colors. We cannot guarantee
+ * that the colors (which come from UIDefaults) are unchanged
+ * between invocations of getButtonBorder. We could store the last
+ * colors, and share the button border if the colors are the same
+ * as in the last invocation, but it probably is not worth the
+ * effort.
+ */
+ return new BorderUIResource.CompoundBorderUIResource(
+ outer,
+ /* inner */ getMarginBorder());
+ }
+
+
+ /**
+ * Returns a border for drawing toggle buttons.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;ToggleButton.shadow&#x201d;</code>,
+ * <code>&#x201c;ToggleButton.darkShadow&#x201d;</code>,
+ * <code>&#x201c;ToggleButton.light&#x201d;</code>, and
+ * <code>&#x201c;ToggleButton.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png" width="270"
+ * height="135" alt="[A screen shot of the returned border]" />
+ *
+ * @return a {@link
+ * javax.swing.plaf.BorderUIResource.CompoundBorderUIResource}
+ * whose outer border is a {@link ToggleButtonBorder} and whose
+ * inner border is a {@link MarginBorder}.
+ */
+ public static Border getToggleButtonBorder()
+ {
+ Border outer;
+
+ /* The keys for UIDefaults have been determined by writing a
+ * test program that dumps the UIDefaults to stdout; that program
+ * was run on a JDK 1.4.1_01 for GNU/Linux. Note that in the API,
+ * the key "light" is usually called "highlight", and "highlight"
+ * is usually called "lightHighlight".
+ */
+ outer = new ToggleButtonBorder(
+ UIManager.getColor("ToggleButton.shadow"),
+ UIManager.getColor("ToggleButton.darkShadow"),
+ UIManager.getColor("ToggleButton.light"),
+ UIManager.getColor("ToggleButton.highlight"));
+
+ /* While the inner border is shared between multiple buttons, we
+ * do not share the outer border because ToggleButtonBorders, being
+ * ButtonBorders, store their border colors. We cannot guarantee
+ * that the colors (which come from UIDefaults) are unchanged
+ * between invocations of getButtonBorder. We could store the last
+ * colors, and share the button border if the colors are the same
+ * as in the last invocation, but it probably is not worth the
+ * effort.
+ */
+ return new BorderUIResource.CompoundBorderUIResource(
+ outer,
+ /* inner */ getMarginBorder());
+ }
+
+
+ /**
+ * Returns a border for drawing a two-pixel thick separator line
+ * below menu bars.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;MenuBar.shadow&#x201d;</code> and
+ * <code>&#x201c;MenuBar.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
+ * height="140" alt="[A screen shot of a JMenuBar with this border]" />
+ *
+ * @return a {@link MenuBarBorder}.
+ *
+ * @see javax.swing.JMenuBar
+ */
+ public static Border getMenuBarBorder()
+ {
+ /* See comment in methods above for why this border is not shared. */
+ return new MenuBarBorder(UIManager.getColor("MenuBar.shadow"),
+ UIManager.getColor("MenuBar.highlight"));
+ }
+
+
+ /**
+ * Returns a border for drawing a one-pixel thick border around
+ * split panes that are interrupted where the divider joins the
+ * border.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;SplitPane.darkShadow&#x201d;</code> and
+ * <code>&#x201c;SplitPane.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
+ *
+ * @return a {@link SplitPaneBorder}.
+ *
+ * @see javax.swing.JSplitPane
+ * @see #getSplitPaneDividerBorder()
+ */
+ public static Border getSplitPaneBorder()
+ {
+ /* See comment in methods above for why this border is not shared. */
+ return new SplitPaneBorder(UIManager.getColor("SplitPane.highlight"),
+ UIManager.getColor("SplitPane.darkShadow"));
+ }
+
+
+ /**
+ * Returns a border for drawing a one-pixel thick border around
+ * the divider of split panes.
+ *
+ * <p>The colors of the edges that are adjacent to the child components
+ * of the <code>JSplitPane</code> are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;SplitPane.darkShadow&#x201d;</code> and
+ * <code>&#x201c;SplitPane.highlight&#x201d;</code>. The color of the
+ * other two edges is the background color of the divider.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
+ * width="520" height="200" alt=
+ * "[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
+ *
+ * @return an instance of <code>SplitPaneDividerBorder</code>, which is
+ * not a public API class of this package.
+ *
+ * @see javax.swing.JSplitPane
+ * @see javax.swing.plaf.basic.BasicSplitPaneDivider
+ * @see #getSplitPaneBorder()
+ *
+ * @since 1.3
+ */
+ public static Border getSplitPaneDividerBorder()
+ {
+ /* See comment in methods above for why this border is not shared. */
+ return new SplitPaneDividerBorder();
+ }
+
+
+ /**
+ * Returns a border for drawing a border around a text field
+ * that makes the field appear as etched into the surface.
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;TextField.shadow&#x201d;</code>,
+ * <code>&#x201c;TextField.darkShadow&#x201d;</code>,
+ * <code>&#x201c;TextField.light&#x201d;</code>, and
+ * <code>&#x201c;TextField.highlight&#x201d;</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500"
+ * height="200" alt="[A screen shot of a border returned by
+ * this method]" />
+ *
+ * @return an instance of {@link FieldBorder}.
+ *
+ * @see javax.swing.JTextField
+ * @see javax.swing.text.JTextComponent
+ */
+ public static Border getTextFieldBorder()
+ {
+ /* See comment in methods above for why this border is not shared. */
+ return new FieldBorder(
+ UIManager.getColor("TextField.shadow"),
+ UIManager.getColor("TextField.darkShadow"),
+ UIManager.getColor("TextField.light"),
+ UIManager.getColor("TextField.highlight"));
+ }
+
+
+ /**
+ * Returns a two-pixel thick, green
+ * <code>LineBorderUIResource</code>. This is so ugly that look and
+ * feels better use different borders for their progress bars, or
+ * they will look really terrible.
+ *
+ * <p><img src="doc-files/BasicBorders-1.png" width="120" height="80"
+ * alt="[A screen shot of a border returned by this method]" />
+ */
+ public static Border getProgressBarBorder()
+ {
+ /* There does not seem to exist a way to parametrize the color
+ * or thickness of the border through UIDefaults.
+ */
+ return new BorderUIResource.LineBorderUIResource(Color.green, 2);
+ }
+
+
+ /**
+ * Returns a border that is composed of a raised bevel border and a
+ * one-pixel thick line border.
+ *
+ * <p><img src="doc-files/BasicBorders-2.png" width="300" height="200"
+ * alt="[A screen shot of a border returned by this method]" />
+ *
+ * <p>The colors of the border are retrieved from the
+ * <code>UIDefaults</code> of the currently active look and feel
+ * using the keys <code>&#x201c;InternalFrame.borderShadow&#x201d;</code>,
+ * <code>&#x201c;InternalFrame.borderDarkShadow&#x201d;</code>,
+ * <code>&#x201c;InternalFrame.borderLight&#x201d;</code>,
+ * <code>&#x201c;InternalFrame.borderHighlight&#x201d;</code>, and
+ * (for the inner one-pixel thick line)
+ * <code>&#x201c;InternalFrame.borderColor&#x201d;</code>.
+ */
+ public static Border getInternalFrameBorder()
+ {
+ Color shadow, darkShadow, highlight, lightHighlight, line;
+
+ /* See comment in methods above for why this border is not shared. */
+ shadow = UIManager.getColor("InternalFrame.borderShadow");
+ darkShadow = UIManager.getColor("InternalFrame.borderDarkShadow");
+ highlight = UIManager.getColor("InternalFrame.borderLight");
+ lightHighlight = UIManager.getColor("InternalFrame.borderHighlight");
+ line = UIManager.getColor("InternalFrame.borderColor");
+
+ return new BorderUIResource.CompoundBorderUIResource(
+ /* outer border */
+ new BorderUIResource.BevelBorderUIResource(
+ BevelBorder.RAISED,
+ (highlight != null) ? highlight : Color.lightGray,
+ (lightHighlight != null) ? lightHighlight : Color.white,
+ (darkShadow != null) ? darkShadow : Color.black,
+ (shadow != null) ? shadow : Color.gray),
+
+ /* inner border */
+ new BorderUIResource.LineBorderUIResource(
+ (line != null) ? line : Color.lightGray));
+ }
+
+
+ /**
+ * Returns a shared MarginBorder.
+ */
+ static Border getMarginBorder() // intentionally not public
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (sharedMarginBorder == null)
+ sharedMarginBorder = new MarginBorder();
+
+ return sharedMarginBorder;
+ }
+
+
+ /**
+ * A border whose appearance depends on the state of
+ * the enclosed button.
+ *
+ * <p><img src="doc-files/BasicBorders.ButtonBorder-1.png" width="300"
+ * height="170" alt="[A screen shot of this border]" />
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class ButtonBorder
+ extends AbstractBorder
+ implements Serializable, UIResource
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -157053874580739687L;
+
+
+ /**
+ * The color for drawing the shaded parts of the border.
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ protected Color shadow;
+
+
+ /**
+ * The color for drawing the dark shaded parts of the border.
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ protected Color darkShadow;
+
+
+ /**
+ * The color for drawing the highlighted parts of the border.
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ protected Color highlight;
+
+
+ /**
+ * The color for drawing the bright highlighted parts of the border.
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ protected Color lightHighlight;
+
+
+ /**
+ * Constructs a new border for drawing a button in the Basic
+ * look and feel.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public ButtonBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* These colors usually come from the UIDefaults of the current
+ * look and feel. Use fallback values if the colors are not
+ * supplied. The API specification is silent about what
+ * behavior is expected for null colors, so users should not
+ * rely on this fallback (which is why it is not documented in
+ * the above Javadoc).
+ */
+ this.shadow = (shadow != null) ? shadow : Color.gray;
+ this.darkShadow = (darkShadow != null) ? darkShadow : Color.black;
+ this.highlight = (highlight != null) ? highlight : Color.lightGray;
+ this.lightHighlight = (lightHighlight != null)
+ ? lightHighlight
+ : Color.white;
+ }
+
+
+ /**
+ * Paints the ButtonBorder around a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ ButtonModel bmodel = null;
+
+ if (c instanceof AbstractButton)
+ bmodel = ((AbstractButton) c).getModel();
+
+ BasicGraphicsUtils.drawBezel(
+ g, x, y, width, height,
+ /* pressed */ (bmodel != null)
+ && /* mouse button pressed */ bmodel.isPressed()
+ && /* mouse inside */ bmodel.isArmed(),
+ /* default */ (c instanceof JButton)
+ && ((JButton) c).isDefaultButton(),
+ shadow, darkShadow, highlight, lightHighlight);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * <p>Although the thickness of the actually painted border
+ * depends on the state of the enclosed component, this
+ * measurement always returns the same amount of pixels. Indeed,
+ * it would be rather confusing if a button was appearing to
+ * change its size depending on whether it is pressed or not.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * <p>Although the thickness of the actually painted border
+ * depends on the state of the enclosed component, this
+ * measurement always returns the same amount of pixels. Indeed,
+ * it would be rather confusing if a button was appearing to
+ * change its size depending on whether it is pressed or not.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ /* The exact amount has been determined using a test program
+ * that was run on the Sun reference implementation. With
+ * Apple/Sun JDK 1.3.1 on MacOS X 10.1.5, the result is
+ * [3, 3, 3, 3]. With Sun JDK 1.4.1_01 on Linux/x86, the
+ * result is [2, 3, 3, 3]. We use the values from the 1.4.1_01
+ * release.
+ */
+ if (insets == null)
+ return new Insets(2, 3, 3, 3);
+
+ insets.top = 2;
+ insets.bottom = insets.left = insets.right = 3;
+ return insets;
+ }
+ }
+
+
+ /**
+ * A border that makes its enclosed component appear as lowered
+ * into the surface. Typically used for text fields.
+ *
+ * <p><img src="doc-files/BasicBorders.FieldBorder-1.png" width="500"
+ * height="200" alt="[A screen shot of this border]" />
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class FieldBorder
+ extends AbstractBorder
+ implements UIResource
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 949220756998454908L;
+
+
+ /**
+ * The color for drawing the outer half of the top and left
+ * edges.
+ */
+ protected Color shadow;
+
+
+ /**
+ * The color for drawing the inner half of the top and left
+ * edges.
+ */
+ protected Color darkShadow;
+
+
+ /**
+ * The color for drawing the inner half of the bottom and right
+ * edges.
+ */
+ protected Color highlight;
+
+
+ /**
+ * The color for drawing the outer half of the bottom and right
+ * edges.
+ */
+ protected Color lightHighlight;
+
+
+ /**
+ * Constructs a new border for drawing a text field in the Basic
+ * look and feel.
+ *
+ * @param shadow the color for drawing the outer half
+ * of the top and left edges.
+ *
+ * @param darkShadow the color for drawing the inner half
+ * of the top and left edges.
+ *
+ * @param highlight the color for drawing the inner half
+ * of the bottom and right edges.
+ *
+ * @param lightHighlight the color for drawing the outer half
+ * of the bottom and right edges.
+ */
+ public FieldBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* These colors usually come from the UIDefaults of the current
+ * look and feel. Use fallback values if the colors are not
+ * supplied. The API specification is silent about what
+ * behavior is expected for null colors, so users should not
+ * rely on this fallback (which is why it is not documented in
+ * the above Javadoc).
+ */
+ this.shadow = (shadow != null) ? shadow : Color.gray;
+ this.darkShadow = (darkShadow != null) ? darkShadow : Color.black;
+ this.highlight = (highlight != null) ? highlight : Color.lightGray;
+ this.lightHighlight = (lightHighlight != null)
+ ? lightHighlight : Color.white;
+ }
+
+
+ /**
+ * Paints the FieldBorder around a given component.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawEtchedRect
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ BasicGraphicsUtils.drawEtchedRect(g, x, y, width, height,
+ shadow, darkShadow,
+ highlight, lightHighlight);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ * If <code>c</code> is an instance of {@link
+ * javax.swing.text.JTextComponent}, its margin is
+ * added to the border size.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param c the component whose border is to be measured.
+ * If <code>c</code> is an instance of {@link
+ * javax.swing.text.JTextComponent}, its margin is
+ * added to the border size.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ if (insets == null)
+ insets = new Insets(2, 2, 2, 2);
+ else
+ insets.top = insets.left = insets.bottom = insets.right = 2;
+
+ if (c instanceof JTextComponent)
+ {
+ Insets margin = ((JTextComponent) c).getMargin();
+ insets.top += margin.top;
+ insets.left += margin.left;
+ insets.bottom += margin.bottom;
+ insets.right += margin.right;
+ }
+
+ return insets;
+ }
+ }
+
+
+ /**
+ * An invisible, but spacing border whose margin is determined
+ * by calling the <code>getMargin()</code> method of the enclosed
+ * component. If the enclosed component has no such method,
+ * this border will not occupy any space.
+ *
+ * <p><img src="doc-files/BasicBorders.MarginBorder-1.png" width="325"
+ * height="200" alt="[An illustration that shows how MarginBorder
+ * determines its borders]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class MarginBorder
+ extends AbstractBorder
+ implements Serializable, UIResource
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -3035848353448896090L;
+
+
+ /**
+ * Constructs a new MarginBorder.
+ */
+ public MarginBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>, <code>right</code>,
+ * <code>top</code> and <code>bottom</code> fields indicate the
+ * width of the border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return getBorderInsets(c, new Insets(0, 0, 0, 0));
+ }
+
+
+ /**
+ * Determines the insets of this border by calling the
+ * <code>getMargin()</code> method of the enclosed component. The
+ * resulting margin will be stored into the the <code>left</code>,
+ * <code>right</code>, <code>top</code> and <code>bottom</code>
+ * fields of the passed <code>insets</code> parameter.
+ *
+ * <p>Unfortunately, <code>getMargin()</code> is not a method of
+ * {@link javax.swing.JComponent} or some other common superclass
+ * of things with margins. While reflection could be used to
+ * determine the existence of this method, this would be slow on
+ * many virtual machines. Therefore, the current implementation
+ * knows about {@link javax.swing.AbstractButton#getMargin()},
+ * {@link javax.swing.JPopupMenu#getMargin()}, {@link
+ * javax.swing.JToolBar#getMargin()}, and {@link
+ * javax.swing.text.JTextComponent}. If <code>c</code> is an
+ * instance of a known class, the respective
+ * <code>getMargin()</code> method is called to determine the
+ * correct margin. Otherwise, a zero-width margin is returned.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return the same object that was passed for <code>insets</code>,
+ * but with changed fields.
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ Insets margin = null;
+
+ /* This is terrible object-oriented design. See the above Javadoc
+ * for an excuse.
+ */
+ if (c instanceof AbstractButton)
+ margin = ((AbstractButton) c).getMargin();
+ else if (c instanceof JPopupMenu)
+ margin = ((JPopupMenu) c).getMargin();
+ else if (c instanceof JToolBar)
+ margin = ((JToolBar) c).getMargin();
+ else if (c instanceof JTextComponent)
+ margin = ((JTextComponent) c).getMargin();
+
+ if (margin == null)
+ insets.top = insets.left = insets.bottom = insets.right = 0;
+ else
+ {
+ insets.top = margin.top;
+ insets.left = margin.left;
+ insets.bottom = margin.bottom;
+ insets.right = margin.right;
+ }
+
+ return insets;
+ }
+ }
+
+
+ /**
+ * A border for drawing a separator line below JMenuBar.
+ *
+ * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
+ * height="140" alt="[A screen shot of a JMenuBar with this border]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class MenuBarBorder
+ extends AbstractBorder
+ implements UIResource
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -6909056571935227506L;
+
+
+ /**
+ * The shadow color, which is used for the upper line of the
+ * two-pixel thick bottom edge.
+ */
+ private Color shadow;
+
+
+ /**
+ * The highlight color, which is used for the lower line of the
+ * two-pixel thick bottom edge.
+ */
+ private Color highlight;
+
+
+ /**
+ * Constructs a new MenuBarBorder for drawing a JMenuBar in
+ * the Basic look and feel.
+ *
+ * <p><img src="doc-files/BasicBorders.MenuBarBorder-1.png" width="500"
+ * height="140" alt="[A screen shot of a JMenuBar with this
+ * border]" />
+ *
+ * @param shadow the shadow color, which is used for the upper
+ * line of the two-pixel thick bottom edge.
+ *
+ * @param highlight the shadow color, which is used for the lower
+ * line of the two-pixel thick bottom edge.
+ */
+ public MenuBarBorder(Color shadow, Color highlight)
+ {
+ /* These colors usually come from the UIDefaults of the current
+ * look and feel. Use fallback values if the colors are not
+ * supplied. The API specification is silent about what
+ * behavior is expected for null colors, so users should not
+ * rely on this fallback (which is why it is not documented in
+ * the above Javadoc).
+ */
+ this.shadow = (shadow != null) ? shadow : Color.gray;
+ this.highlight = (highlight != null) ? highlight : Color.white;
+ }
+
+
+ /**
+ * Paints the MenuBarBorder around a given component.
+ *
+ * @param c the component whose border is to be painted, usually
+ * an instance of {@link javax.swing.JMenuBar}.
+ *
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ Color oldColor;
+
+ /* To understand this code, it might be helpful to look at the
+ * image "BasicBorders.MenuBarBorder-1.png" that is included
+ * with the JavaDoc. It is located in the "doc-files"
+ * subdirectory.
+ */
+ oldColor = g.getColor();
+ y = y + height - 2;
+ try
+ {
+ g.setColor(shadow);
+ g.drawLine(x, y, x + width - 2, y);
+ g.drawLine(x, y + 1, x, y + 1);
+ g.drawLine(x + width - 2, y + 1, x + width - 2, y + 1);
+
+ g.setColor(highlight);
+ g.drawLine(x + 1, y + 1, x + width - 3, y + 1);
+ g.drawLine(x + width - 1, y, x + width - 1, y + 1);
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ /* The exact amount has been determined using a test program
+ * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
+ * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [0,0,2,0],
+ * which was expected from looking at the screen shot.
+ */
+ if (insets == null)
+ return new Insets(0, 0, 2, 0);
+
+ insets.left = insets.right = insets.top = 0;
+ insets.bottom = 2;
+ return insets;
+ }
+ }
+
+
+ /**
+ * A border for drawing radio buttons in the Basic look and feel.
+ *
+ * <p><img src="doc-files/BasicBorders.RadioButtonBorder-1.png" width="300"
+ * height="135" alt="[A screen shot of this border]" />
+ *
+ * <p>Note about the screen shot: Normally, the
+ * <code>borderPainted</code> property is <code>false</code> for
+ * JRadioButtons. For this screen shot, it has been set to
+ * <code>true</code> so the borders get drawn. Also, a
+ * concretization of the Basic look and would typically provide
+ * icons for the various states of radio buttons.
+ *
+ * <p>Note that the focus rectangle is invisible If the radio button
+ * is currently selected. While it might be debatable whether this
+ * makes a lot of sense, this behavior can be observed in the Sun
+ * reference implementation (in JDK 1.3.1 and 1.4.1). The Classpath
+ * implementation tries to exactly replicate the JDK appearance.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class RadioButtonBorder
+ extends ButtonBorder
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = 1596945751743747369L;
+
+
+ /**
+ * Constructs a new border for drawing a JRadioButton in
+ * the Basic look and feel.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public RadioButtonBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* The superclass ButtonBorder substitutes null arguments
+ * with fallback colors.
+ */
+ super(shadow, darkShadow, highlight, lightHighlight);
+ }
+
+
+ /**
+ * Paints the RadioButtonBorder around a given component.
+ *
+ * <p>The Sun implementation always seems to draw exactly
+ * the same border, irrespective of the state of the button.
+ * This is rather surprising, but GNU Classpath emulates the
+ * observable behavior.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ AbstractButton button = null;
+ ButtonModel bmodel = null;
+ boolean lowered = false;
+ boolean focused = false;
+
+ if (c instanceof AbstractButton)
+ {
+ button = (AbstractButton) c;
+ bmodel = button.getModel();
+ }
+
+ if (bmodel != null)
+ {
+ lowered = button.isSelected()
+ || (/* mouse inside */ bmodel.isArmed() && bmodel.isPressed());
+ focused = button.hasFocus() && button.isFocusPainted();
+ }
+
+ if (lowered)
+ BasicGraphicsUtils.drawLoweredBezel(g, x, y, width, height,
+ shadow, darkShadow,
+ highlight, lightHighlight);
+ else
+ BasicGraphicsUtils.drawBezel(g, x, y, width, height,
+ /* isPressed */ false,
+ /* isPefault */ focused,
+ shadow, darkShadow,
+ highlight, lightHighlight);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ /* The exact amount has been determined using a test program
+ * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
+ * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2].
+ */
+ if (insets == null)
+ return new Insets(2, 2, 2, 2);
+
+ insets.left = insets.right = insets.top = insets.bottom = 2;
+ return insets;
+ }
+ }
+
+
+ /**
+ * A one-pixel thick border for rollover buttons, for example in
+ * tool bars.
+ *
+ * @since 1.4
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class RolloverButtonBorder
+ extends ButtonBorder
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Sun JDK 1.4.1_01 on GNU/Linux 2.4.20 for x86.
+ */
+ static final long serialVersionUID = 1976364864896996846L;
+
+
+ /**
+ * Constructs a new border for drawing a roll-over button
+ * in the Basic look and feel.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public RolloverButtonBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ super(shadow, darkShadow, highlight, lightHighlight);
+ }
+
+
+ /**
+ * Paints the border around a rollover button. If <code>c</code>
+ * is not an {@link javax.swing.AbstractButton} whose model
+ * returns <code>true</code> for {@link
+ * javax.swing.ButtonModel#isRollover}, nothing gets painted at
+ * all.
+ *
+ * @param c the button whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ ButtonModel bmodel = null;
+ boolean drawPressed;
+ Color oldColor = g.getColor();
+ int x2, y2;
+
+ if (c instanceof AbstractButton)
+ bmodel = ((AbstractButton) c).getModel();
+
+ /* Draw nothing if c is not a rollover button. */
+ if ((bmodel == null) || !bmodel.isRollover())
+ return;
+
+ /* Draw nothing if the mouse is pressed, but outside the button. */
+ if (bmodel.isPressed() && !bmodel.isArmed())
+ return;
+
+ drawPressed = bmodel.isSelected() || bmodel.isPressed();
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+
+ try
+ {
+ g.setColor(drawPressed ? shadow : lightHighlight);
+ g.drawLine(x, y, x2 - 1, y); // top edge
+ g.drawLine(x, y + 1, x, y2 - 1); // left edge
+
+ g.setColor(drawPressed ? lightHighlight : shadow);
+ g.drawLine(x, y2, x2, y2); // bottom edge
+ g.drawLine(x2, y, x2, y2 - 1); // right edge
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+ }
+
+
+ /**
+ * A border for JSplitPanes in the Basic look and feel. The divider
+ * in the middle of the JSplitPane has its own border class, of which
+ * an instance can be obtained with {@link #getSplitPaneDividerBorder()}.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
+ *
+ * <p>In contrast to the other borders of the Basic look and feel,
+ * this class is not serializable. While this might be unintended,
+ * GNU Classpath follows the specification in order to be fully
+ * compatible with the Sun reference implementation.
+ *
+ * <p>In the Sun JDK, the bottom edge of the divider also gets
+ * painted if the orientation of the enclosed JSplitPane is
+ * <code>JSplitPane.VERTICAL_SPLIT</code> (at least in versions
+ * 1.3.1 and 1.4.1). GNU Classpath does not replicate this bug. A
+ * report has been filed with Sun (bug ID 4885629).
+ *
+ * <p>Note that the bottom left pixel of the border has a different
+ * color depending on the orientation of the enclosed JSplitPane.
+ * Although this is visually inconsistent, Classpath replicates the
+ * appearance of the Sun reference implementation. A bug report has
+ * been filed with Sun (review ID 188774).
+ *
+ * @see #getSplitPaneBorder()
+ * @see #getSplitPaneDividerBorder()
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class SplitPaneBorder implements Border, UIResource
+ {
+ /**
+ * Indicates that the top edge shall be not be painted
+ * by {@link #paintRect}.
+ */
+ private static final int SUPPRESS_TOP = 1;
+
+
+ /**
+ * Indicates that the left edge shall be not be painted
+ * by {@link #paintRect}.
+ */
+ private static final int SUPPRESS_LEFT = 2;
+
+
+ /**
+ * Indicates that the bottom edge shall be not be painted
+ * by {@link #paintRect}.
+ */
+ private static final int SUPPRESS_BOTTOM = 4;
+
+
+ /**
+ * Indicates that the right edge shall be not be painted
+ * by {@link #paintRect}.
+ */
+ private static final int SUPPRESS_RIGHT = 8;
+
+
+ /**
+ * The color for drawing the bottom and right edges of the border.
+ */
+ protected Color highlight;
+
+
+ /**
+ * The color for drawing the top and left edges of the border.
+ */
+ protected Color shadow;
+
+
+ /**
+ * Constructs a new border for drawing a JSplitPane in the Basic
+ * look and feel. The divider in the middle of the JSplitPane has
+ * its own border class, <code>SplitPaneDividerBorder</code>.
+ *
+ * @param shadow the shadow color.
+ * @param highlight the highlight color.
+ */
+ public SplitPaneBorder(Color highlight, Color shadow)
+ {
+ /* These colors usually come from the UIDefaults of the current
+ * look and feel. Use fallback values if the colors are not
+ * supplied. The API specification is silent about what
+ * behavior is expected for null colors, so users should not
+ * rely on this fallback (which is why it is not documented in
+ * the above Javadoc).
+ */
+ this.shadow = (shadow != null) ? shadow : Color.black;
+ this.highlight = (highlight != null) ? highlight : Color.white;
+ }
+
+
+ /**
+ * Paints the border around a <code>JSplitPane</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-1.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.HORIZONTAL_SPLIT]" />
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneBorder-2.png" width="520"
+ * height="200" alt="[A screen shot for JSplitPane.VERTICAL_SPLIT]" />
+ *
+ * @param c the <code>JSplitPane</code> whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ JSplitPane splitPane;
+ Component content;
+
+ if (!(c instanceof JSplitPane))
+ return;
+
+ splitPane = (JSplitPane) c;
+ switch (splitPane.getOrientation())
+ {
+ case JSplitPane.HORIZONTAL_SPLIT:
+ if ((content = splitPane.getLeftComponent()) != null)
+ paintRect(g, SUPPRESS_RIGHT, true, x, y, content.getBounds());
+ if ((content = splitPane.getRightComponent()) != null)
+ paintRect(g, SUPPRESS_LEFT, true, x, y, content.getBounds());
+ break;
+
+ case JSplitPane.VERTICAL_SPLIT:
+ if ((content = splitPane.getTopComponent()) != null)
+ paintRect(g, SUPPRESS_BOTTOM, false, x, y, content.getBounds());
+ if ((content = splitPane.getBottomComponent()) != null)
+ paintRect(g, SUPPRESS_TOP, false, x, y, content.getBounds());
+ break;
+ }
+ }
+
+
+ /**
+ * Paints a border around a child of a <code>JSplitPane</code>,
+ * omitting some of the edges.
+ *
+ * @param g the graphics for painting.
+ *
+ * @param suppress a bit mask indicating the set of suppressed
+ * edges, for example <code>SUPPRESS_TOP | SUPPRESS_RIGHT</code>.
+ *
+ * @param x the x coordinate of the SplitPaneBorder.
+ *
+ * @param y the y coordinate of the SplitPaneBorder.
+ *
+ * @param shadeBottomLeftPixel <code>true</code> to paint the
+ * bottom left pixel in the shadow color,
+ * <code>false</code> for the highlight color. The Basic
+ * look and feel uses the highlight color for the bottom
+ * left pixel of the border of a JSplitPane whose
+ * orientation is VERTICAL_SPLIT, and the shadow color
+ * otherwise. While this might be a strange distinction,
+ * Classpath tries to look identical to the reference
+ * implementation. A bug report has been filed with Sun;
+ * its review ID is 188774. We currently replicate the
+ * Sun behavior.
+ *
+ * @param rect the bounds of the child of JSplitPane whose
+ * border is to be painted.
+ */
+ private void paintRect(Graphics g, int suppress,
+ boolean shadeBottomLeftPixel,
+ int x, int y,
+ Rectangle rect)
+ {
+ if (rect == null)
+ return;
+
+ /* On each edge, the border exceeds the enclosed child by one
+ * pixel. See the image "BasicBorders.SplitPaneBorder-1.png" in
+ * the directory "doc-files".
+ */
+ x += rect.x - 1;
+ y += rect.y - 1;
+ int right = x + rect.width + 1;
+ int bottom = y + rect.height + 1;
+
+ Color oldColor = g.getColor();
+ try
+ {
+ g.setColor(shadow);
+ if ((suppress & SUPPRESS_TOP) == 0)
+ g.drawLine(x, y, right, y);
+ if ((suppress & SUPPRESS_LEFT) == 0)
+ g.drawLine(x, y, x, bottom);
+ else
+ g.drawLine(x, bottom, x, bottom); // one pixel
+
+ g.setColor(highlight);
+ if ((suppress & SUPPRESS_BOTTOM) == 0)
+ g.drawLine(x + (shadeBottomLeftPixel ? 1 : 0), bottom, right, bottom);
+ else if (!shadeBottomLeftPixel)
+ g.drawLine(x, bottom, x, bottom); // one pixel
+
+ if ((suppress & SUPPRESS_RIGHT) == 0)
+ g.drawLine(right, y, right, bottom);
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured, usually
+ * an instance of {@link javax.swing.JSplitPane}.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(1, 1, 1, 1);
+ }
+
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * @return <code>false</code> because this border does not
+ * paint over the pixels where the divider joins
+ * the border.
+ */
+ public boolean isBorderOpaque()
+ {
+ /* Strangely, the Sun implementation (tested with JDK 1.3.1 and
+ * 1.4.1_01) seems to always return true. It could be a bug,
+ * but without knowing the details of their implementation, it is
+ * hard to decide.
+ */
+ return false;
+ }
+ }
+
+
+ /**
+ * A border for the divider inside a JSplitPane.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
+ * width="520" height="200" alt="[A screen shot of this border]" />
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ private static class SplitPaneDividerBorder
+ implements Border, UIResource, Serializable
+ {
+ /**
+ * Constructs a new border for drawing the divider of a JSplitPane
+ * in the Basic look and feel. The outer parts of the JSplitPane have
+ * their own border class, <code>SplitPaneBorder</code>.
+ */
+ public SplitPaneDividerBorder()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Paints the border around the divider of a <code>JSplitPane</code>.
+ *
+ * <p><img src="doc-files/BasicBorders.SplitPaneDividerBorder-1.png"
+ * width="520" height="200" alt="[A picture that shows which pixels
+ * get painted in what color]" />
+ *
+ * @param c the <code>JSplitPane</code> whose divider&#x2019;s border
+ * is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ Color highlight = UIManager.getColor("SplitPane.highlight");
+ Color shadow = UIManager.getColor("SplitPane.shadow");
+ Color oldColor, dcol;
+ int x2, y2;
+ JSplitPane sp;
+
+ sp = getSplitPane(c);
+ if (sp == null)
+ return;
+
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+ oldColor = g.getColor();
+ dcol = c.getBackground();
+ try
+ {
+ switch (sp.getOrientation())
+ {
+ case JSplitPane.HORIZONTAL_SPLIT:
+ g.setColor(dcol);
+ g.drawLine(x + 1, y, x2 - 1, y);
+ g.drawLine(x + 1, y2, x2 - 1, y2);
+ g.setColor(sp.getLeftComponent() != null ? highlight : dcol);
+ g.drawLine(x, y, x, y2);
+ g.setColor(sp.getRightComponent() != null ? shadow : dcol);
+ g.drawLine(x2, y, x2, y2);
+ break;
+
+ case JSplitPane.VERTICAL_SPLIT:
+ g.setColor(dcol);
+ g.drawLine(x, y + 1, x, y2 - 1);
+ g.drawLine(x2, y + 1, x2, y2 - 1);
+ g.setColor(sp.getTopComponent() != null ? highlight : dcol);
+ g.drawLine(x, y, x2, y);
+ g.setColor(sp.getBottomComponent() != null ? shadow : dcol);
+ g.drawLine(x, y2, x2, y2);
+ break;
+ }
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured, usually
+ * an instance of {@link javax.swing.JSplitPane}.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(1, 1, 1, 1);
+ }
+
+ /**
+ * Determines whether this border fills every pixel in its area
+ * when painting.
+ *
+ * @return <code>true</code>
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+
+ /**
+ * Determines the JSplitPane whose divider is being painted.
+ *
+ * @param c an instance of BasicSplitPaneDivider.
+ *
+ * @return a <code>JSplitPane</code>, or <code>null</code> if
+ * <code>c</code> is not an instance of {@link
+ * javax.swing.plaf.basic.BasicSplitPaneDivider}.
+ */
+ private JSplitPane getSplitPane(Component c)
+ {
+ if (c instanceof BasicSplitPaneDivider)
+ return (((BasicSplitPaneDivider) c).getBasicSplitPaneUI())
+ .getSplitPane();
+ else
+ return null;
+ }
+ }
+
+
+ /**
+ * A border for toggle buttons in the Basic look and feel.
+ *
+ * <p><img src="doc-files/BasicBorders.ToggleButtonBorder-1.png"
+ * width="270" height="135" alt="[A screen shot of this border]" />
+ *
+ * <p>The Sun implementation always seems to draw exactly
+ * the same border, irrespective of the state of the button.
+ * This is rather surprising, but GNU Classpath emulates the
+ * observable behavior.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ public static class ToggleButtonBorder
+ extends ButtonBorder
+ {
+ /**
+ * Determined using the <code>serialver</code> tool
+ * of Apple/Sun JDK 1.3.1 on MacOS X 10.1.5.
+ */
+ static final long serialVersionUID = -3528666548001058394L;
+
+
+ /**
+ * Constructs a new border for drawing a JToggleButton in
+ * the Basic look and feel.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public ToggleButtonBorder(Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* The superclass ButtonBorder substitutes null arguments
+ * with fallback colors.
+ */
+ super(shadow, darkShadow, highlight, lightHighlight);
+ }
+
+
+ /**
+ * Paints the ToggleButtonBorder around a given component.
+ *
+ * <p>The Sun implementation always seems to draw exactly
+ * the same border, irrespective of the state of the button.
+ * This is rather surprising, but GNU Classpath emulates the
+ * observable behavior.
+ *
+ * @param c the component whose border is to be painted.
+ * @param g the graphics for painting.
+ * @param x the horizontal position for painting the border.
+ * @param y the vertical position for painting the border.
+ * @param width the width of the available area for painting the border.
+ * @param height the height of the available area for painting the border.
+ *
+ * @see javax.swing.plaf.basic.BasicGraphicsUtils#drawBezel
+ */
+ public void paintBorder(Component c, Graphics g,
+ int x, int y, int width, int height)
+ {
+ /* The author of this code tried various variants for setting
+ * the state of the enclosed JToggleButton, but it seems that
+ * the drawn border is always identical. Weird, because this
+ * means that the user does not see whether the JToggleButton
+ * is selected or not.
+ */
+ BasicGraphicsUtils.drawBezel(g, x, y, width, height,
+ /* pressed */ false,
+ /* default */ false,
+ shadow, darkShadow,
+ highlight, lightHighlight);
+ }
+
+
+ /**
+ * Measures the width of this border.
+ *
+ * @param c the component whose border is to be measured.
+ *
+ * @return an Insets object whose <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @see #getBorderInsets(java.awt.Component, java.awt.Insets)
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ /* There is no obvious reason for overriding this method, but we
+ * try to have exactly the same API as the Sun reference
+ * implementation.
+ */
+ return getBorderInsets(c, null);
+ }
+
+
+ /**
+ * Measures the width of this border, storing the results into a
+ * pre-existing Insets object.
+ *
+ * @param insets an Insets object for holding the result values.
+ * After invoking this method, the <code>left</code>,
+ * <code>right</code>, <code>top</code> and
+ * <code>bottom</code> fields indicate the width of the
+ * border at the respective edge.
+ *
+ * @return the same object that was passed for <code>insets</code>.
+ *
+ * @see #getBorderInsets(Component)
+ */
+ public Insets getBorderInsets(Component c, Insets insets)
+ {
+ /* The exact amount has been determined using a test program
+ * that was run on the Apple/Sun JDK 1.3.1 on MacOS X, and the
+ * Sun JDK 1.4.1_01 on GNU/Linux for x86. Both gave [2,2,2,2].
+ */
+ if (insets == null)
+ return new Insets(2, 2, 2, 2);
+
+ insets.left = insets.right = insets.top = insets.bottom = 2;
+ return insets;
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java
new file mode 100644
index 000000000..22033b6ad
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonListener.java
@@ -0,0 +1,370 @@
+/* BasicButtonListener.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import gnu.classpath.SystemProperties;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.beans.PropertyChangeEvent;
+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
+{
+ /**
+ * 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.
+ }
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // Store the TextLayout for this in a client property for speed-up
+ // painting of the label.
+ String property = e.getPropertyName();
+ AbstractButton b = (AbstractButton) e.getSource();
+ if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
+ || property.equals("font"))
+ && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
+ == null)
+ {
+ String text = b.getText();
+ if (text == null)
+ text = "";
+ FontRenderContext frc = new FontRenderContext(new AffineTransform(),
+ 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());
+ }
+ else if (property.equals(AbstractButton.CONTENT_AREA_FILLED_CHANGED_PROPERTY))
+ {
+ 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)
+ {
+ b.setOpaque(b.isContentAreaFilled());
+ }
+
+ public void focusGained(FocusEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ if (button.isFocusPainted())
+ button.repaint();
+ }
+ }
+
+ public void focusLost(FocusEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ if (button.isFocusPainted())
+ button.repaint();
+ }
+ }
+
+ 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()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ // It is important that these transitions happen in this order.
+ model.setArmed(true);
+ model.setPressed(true);
+ }
+ });
+
+ c.getActionMap().put("released",
+ new AbstractAction()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ // It is important that these transitions happen in this order.
+ model.setPressed(false);
+ model.setArmed(false);
+ }
+ });
+ }
+
+ /**
+ * 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)
+ {
+ SwingUtilities.replaceUIActionMap(c, null);
+ SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
+ }
+
+ public void stateChanged(ChangeEvent e)
+ {
+ // Need to repaint when the button state changes.
+ ((AbstractButton) e.getSource()).repaint();
+ }
+
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseDragged(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Accept a mouse press event and arm the button.
+ *
+ * @param e The mouse press event to accept
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ if (SwingUtilities.isLeftMouseButton(e))
+ {
+ // It is important that these transitions happen in this order.
+ model.setArmed(true);
+ model.setPressed(true);
+
+ if (! button.isFocusOwner() && button.isRequestFocusEnabled())
+ button.requestFocus();
+ }
+ }
+ }
+
+ /**
+ * Accept a mouse release event and set the button's
+ * "pressed" property to <code>true</code>, if the model
+ * is armed. If the model is not armed, ignore the event.
+ *
+ * @param e The mouse release event to accept
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ if (e.getButton() == MouseEvent.BUTTON1)
+ {
+ // It is important that these transitions happen in this order.
+ model.setPressed(false);
+ model.setArmed(false);
+ }
+ }
+ }
+
+ /**
+ * Accept a mouse enter event and set the button's "rollover" property to
+ * <code>true</code>, if the button's "rolloverEnabled" property is
+ * <code>true</code>. If the button is currently armed and the mouse
+ * button is not held down, this enter event will also disarm the model.
+ *
+ * @param e The mouse enter event to accept
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ if (button.isRolloverEnabled()
+ && ! SwingUtilities.isLeftMouseButton(e))
+ model.setRollover(true);
+
+ if (model.isPressed())
+ model.setArmed(true);
+ }
+ }
+
+ /**
+ * Accept a mouse exit event and set the button's model's "rollover"
+ * property to <code>false</code>, if it's "rolloverEnabled" property is
+ * <code>true</code>. Also disarm the button.
+ *
+ * @param e The mouse exit event to accept
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ if (e.getSource() instanceof AbstractButton)
+ {
+ AbstractButton button = (AbstractButton) e.getSource();
+ ButtonModel model = button.getModel();
+ if (button.isRolloverEnabled())
+ model.setRollover(false);
+ model.setArmed(false);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
new file mode 100644
index 000000000..1697c24d9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicButtonUI.java
@@ -0,0 +1,636 @@
+/* BasicButtonUI.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+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.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ButtonUI;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.text.View;
+
+/**
+ * A UI delegate for the {@link JButton} component.
+ */
+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();
+
+ /**
+ * Cached Insets instance, used in paint().
+ */
+ static Insets cachedInsets;
+
+ /**
+ * 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.
+ */
+ protected int defaultTextIconGap = 4;
+
+ /**
+ * A constant added to the defaultTextIconGap to adjust the text
+ * within this particular button.
+ */
+ protected int defaultTextShiftOffset;
+
+ private int textShiftOffset;
+
+ /**
+ * Factory method to create an instance of BasicButtonUI for a given
+ * {@link JComponent}, which should be an {@link AbstractButton}.
+ *
+ * @param c The component.
+ *
+ * @return A new UI capable of drawing the component
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ if (sharedUI == null)
+ sharedUI = new BasicButtonUI();
+ return sharedUI;
+ }
+
+ /**
+ * Returns the default gap between the button's text and icon (in pixels).
+ *
+ * @param b the button (ignored).
+ *
+ * @return The gap.
+ */
+ public int getDefaultTextIconGap(AbstractButton b)
+ {
+ return defaultTextIconGap;
+ }
+
+ /**
+ * Sets the text shift offset to zero.
+ *
+ * @see #setTextShiftOffset()
+ */
+ protected void clearTextShiftOffset()
+ {
+ textShiftOffset = 0;
+ }
+
+ /**
+ * Returns the text shift offset.
+ *
+ * @return The text shift offset.
+ *
+ * @see #clearTextShiftOffset()
+ * @see #setTextShiftOffset()
+ */
+ protected int getTextShiftOffset()
+ {
+ return textShiftOffset;
+ }
+
+ /**
+ * Sets the text shift offset to the value in {@link #defaultTextShiftOffset}.
+ *
+ * @see #clearTextShiftOffset()
+ */
+ protected void setTextShiftOffset()
+ {
+ textShiftOffset = defaultTextShiftOffset;
+ }
+
+ /**
+ * Returns the prefix for the UI defaults property for this UI class.
+ * This is &apos;Button&apos; for this class.
+ *
+ * @return the prefix for the UI defaults property
+ */
+ protected String getPropertyPrefix()
+ {
+ return "Button.";
+ }
+
+ /**
+ * Installs the default settings.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ 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"));
+
+ // 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);
+ }
+
+ /**
+ * Removes the defaults added by {@link #installDefaults(AbstractButton)}.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void uninstallDefaults(AbstractButton b)
+ {
+ // The other properties aren't uninstallable.
+ LookAndFeel.uninstallBorder(b);
+ }
+
+ /**
+ * Creates and returns a new instance of {@link BasicButtonListener}. This
+ * method provides a hook to make it easy for subclasses to install a
+ * different listener.
+ *
+ * @param b the button.
+ *
+ * @return A new listener.
+ */
+ protected BasicButtonListener createButtonListener(AbstractButton 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;
+ }
+
+ /**
+ * Installs listeners for the button.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void installListeners(AbstractButton b)
+ {
+ BasicButtonListener listener = createButtonListener(b);
+ if (listener != null)
+ {
+ b.addChangeListener(listener);
+ b.addPropertyChangeListener(listener);
+ b.addFocusListener(listener);
+ b.addMouseListener(listener);
+ b.addMouseMotionListener(listener);
+ }
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ listener.propertyChange(new PropertyChangeEvent(b, "font", null,
+ b.getFont()));
+ }
+
+ /**
+ * Uninstalls listeners for the button.
+ *
+ * @param b the button (<code>null</code> not permitted).
+ */
+ protected void uninstallListeners(AbstractButton b)
+ {
+ 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)
+ {
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.installKeyboardActions(b);
+ }
+
+ protected void uninstallKeyboardActions(AbstractButton b)
+ {
+ BasicButtonListener listener = getButtonListener(b);
+ if (listener != null)
+ listener.uninstallKeyboardActions(b);
+ }
+
+ /**
+ * Install the BasicButtonUI as the UI for a particular component.
+ * This means registering all the UI's listeners with the component,
+ * and setting any properties of the button which are particular to
+ * this look and feel.
+ *
+ * @param c The component to install the UI into
+ */
+ public void installUI(final JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof AbstractButton)
+ {
+ 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());
+ }
+ }
+
+ /**
+ * Uninstalls the UI from the component.
+ *
+ * @param c the component from which to uninstall the UI
+ */
+ public void uninstallUI(JComponent c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ uninstallKeyboardActions(b);
+ uninstallListeners(b);
+ uninstallDefaults(b);
+ BasicHTML.updateRenderer(b, "");
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
+ }
+ }
+
+ /**
+ * Calculates the minimum size for the specified component.
+ *
+ * @param c the component for which to compute the minimum size
+ *
+ * @return the minimum size for the specified component
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Dimension size = getPreferredSize(c);
+ // When the HTML view has a minimum width different from the preferred
+ // width, then substract this here accordingly. The height is not
+ // affected by that.
+ View html = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ size.width -= html.getPreferredSpan(View.X_AXIS)
+ - html.getPreferredSpan(View.X_AXIS);
+ }
+ return size;
+ }
+
+ /**
+ * Calculates the maximum size for the specified component.
+ *
+ * @param c the component for which to compute the maximum size
+ *
+ * @return the maximum size for the specified component
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension size = getPreferredSize(c);
+ // When the HTML view has a maximum width different from the preferred
+ // width, then add this here accordingly. The height is not
+ // affected by that.
+ View html = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ size.width += html.getMaximumSpan(View.X_AXIS)
+ - html.getPreferredSpan(View.X_AXIS);
+ }
+ return size;
+ }
+
+ /**
+ * Calculate the preferred size of this component, by delegating to
+ * {@link BasicGraphicsUtils#getPreferredButtonSize}.
+ *
+ * @param c The component to measure
+ *
+ * @return The preferred dimensions of the component
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Dimension d = BasicGraphicsUtils.getPreferredButtonSize(b,
+ b.getIconTextGap());
+ return d;
+ }
+
+ static Icon currentIcon(AbstractButton b)
+ {
+ Icon i = b.getIcon();
+ ButtonModel model = b.getModel();
+
+ if (model.isPressed() && b.getPressedIcon() != null && b.isEnabled())
+ i = b.getPressedIcon();
+
+ else if (model.isRollover())
+ {
+ if (b.isSelected() && b.getRolloverSelectedIcon() != null)
+ i = b.getRolloverSelectedIcon();
+ else if (b.getRolloverIcon() != null)
+ i = b.getRolloverIcon();
+ }
+
+ else if (b.isSelected() && b.isEnabled())
+ {
+ if (b.isEnabled() && b.getSelectedIcon() != null)
+ i = b.getSelectedIcon();
+ else if (b.getDisabledSelectedIcon() != null)
+ i = b.getDisabledSelectedIcon();
+ }
+
+ else if (! b.isEnabled() && b.getDisabledIcon() != null)
+ i = b.getDisabledIcon();
+
+ return i;
+ }
+
+ /**
+ * Paint the component, which is an {@link AbstractButton}, according to
+ * its current state.
+ *
+ * @param g The graphics context to paint with
+ * @param c The component to paint the state of
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+
+ Insets i = c.getInsets(cachedInsets);
+ 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());
+
+ ButtonModel model = b.getModel();
+ if (model.isArmed() && model.isPressed())
+ paintButtonPressed(g, b);
+
+ if (icon != null)
+ paintIcon(g, c, iconR);
+ if (text != null)
+ {
+ View html = (View) b.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ html.paint(g, textR);
+ else
+ paintText(g, b, textR, text);
+ }
+ if (b.isFocusOwner() && b.isFocusPainted())
+ paintFocus(g, b, viewR, textR, iconR);
+ }
+
+ /**
+ * Paint any focus decoration this {@link JComponent} might have. The
+ * component, which in this case will be an {@link AbstractButton},
+ * should only have focus decoration painted if it has the focus, and its
+ * "focusPainted" property is <code>true</code>.
+ *
+ * @param g Graphics context to paint with
+ * @param b Button to paint the focus of
+ * @param vr Visible rectangle, the area in which to paint
+ * @param tr Text rectangle, contained in visible rectangle
+ * @param ir Icon rectangle, contained in visible rectangle
+ *
+ * @see AbstractButton#isFocusPainted()
+ * @see JComponent#hasFocus()
+ */
+ protected void paintFocus(Graphics g, AbstractButton b, Rectangle vr,
+ Rectangle tr, Rectangle ir)
+ {
+ // In the BasicLookAndFeel no focus border is drawn. This can be
+ // overridden in subclasses to implement such behaviour.
+ }
+
+ /**
+ * Paint the icon for this component. Depending on the state of the
+ * component and the availability of the button's various icon
+ * properties, this might mean painting one of several different icons.
+ *
+ * @param g Graphics context to paint with
+ * @param c Component to paint the icon of
+ * @param iconRect Rectangle in which the icon should be painted
+ */
+ protected void paintIcon(Graphics g, JComponent c, Rectangle iconRect)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Icon i = currentIcon(b);
+
+ if (i != null)
+ {
+ 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);
+ }
+ }
+
+ /**
+ * Paints the background area of an {@link AbstractButton} in the pressed
+ * state. This means filling the supplied area with a darker than normal
+ * background.
+ *
+ * @param g The graphics context to paint with
+ * @param b The button to paint the state of
+ */
+ protected void paintButtonPressed(Graphics g, AbstractButton b)
+ {
+ if (b.isContentAreaFilled() && b.isOpaque())
+ {
+ Rectangle area = new Rectangle();
+ SwingUtilities.calculateInnerArea(b, area);
+ g.setColor(UIManager.getColor(getPropertyPrefix() + "shadow"));
+ g.fillRect(area.x, area.y, area.width, area.height);
+ }
+ }
+
+ /**
+ * Paints the "text" property of an {@link AbstractButton}.
+ *
+ * @param g The graphics context to paint with
+ * @param c The component to paint the state of
+ * @param textRect The area in which to paint the text
+ * @param text The text to paint
+ */
+ protected void paintText(Graphics g, JComponent c, Rectangle textRect,
+ String text)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Font f = b.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+
+ if (b.isEnabled())
+ {
+ g.setColor(b.getForeground());
+ // FIXME: Underline mnemonic.
+ BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
+ textRect.y + fm.getAscent());
+ }
+ else
+ {
+ String prefix = getPropertyPrefix();
+ g.setColor(UIManager.getColor(prefix + "disabledText"));
+ // FIXME: Underline mnemonic.
+ BasicGraphicsUtils.drawString(b, g, text, -1, textRect.x,
+ textRect.y + fm.getAscent());
+ }
+ }
+
+ /**
+ * Paints the "text" property of an {@link AbstractButton}.
+ *
+ * @param g The graphics context to paint with
+ * @param b The button to paint the state of
+ * @param textRect The area in which to paint the text
+ * @param text The text to paint
+ *
+ * @since 1.4
+ */
+ protected void paintText(Graphics g, AbstractButton b, Rectangle textRect,
+ String text)
+ {
+ paintText(g, (JComponent) b, textRect, text);
+ }
+
+ /**
+ * 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/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java
new file mode 100644
index 000000000..79ce7b834
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxMenuItemUI.java
@@ -0,0 +1,102 @@
+/* BasicCheckBoxMenuItemUI.java --
+ Copyright (C) 2002, 2004 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.plaf.basic;
+
+import java.awt.event.MouseEvent;
+
+import javax.swing.JComponent;
+import javax.swing.JMenuItem;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * DOCUMENT ME!
+ */
+public class BasicCheckBoxMenuItemUI extends BasicMenuItemUI
+{
+
+ /**
+ * Creates a new BasicCheckBoxMenuItemUI object.
+ */
+ public BasicCheckBoxMenuItemUI()
+ {
+ super();
+ }
+
+ /**
+ * Factory method to create a BasicCheckBoxMenuItemUI for the given {@link
+ * JComponent}, which should be a JCheckBoxMenuItem
+ *
+ * @param c The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicCheckBoxMenuItemUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ return new BasicCheckBoxMenuItemUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "CheckBoxMenuItem"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "CheckBoxMenuItem";
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param item DOCUMENT ME!
+ * @param e DOCUMENT ME!
+ * @param path DOCUMENT ME!
+ * @param manager DOCUMENT ME!
+ */
+ public void processMouseEvent(JMenuItem item, MouseEvent e,
+ MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // TODO: May not be implemented properly.
+ item.processMouseEvent(e, path, manager);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java
new file mode 100644
index 000000000..0a4da00b2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicCheckBoxUI.java
@@ -0,0 +1,75 @@
+/* BasicCheckBoxUI.java
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import javax.swing.JCheckBox;
+import javax.swing.JComponent;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate for the {@link JCheckBox} component.
+ */
+public class BasicCheckBoxUI extends BasicRadioButtonUI
+{
+
+ /**
+ * Returns a UI delegate (that is, an instance of this class) for the
+ * specified component.
+ *
+ * @param c the component (this should be a {@link JCheckBox}).
+ *
+ * @return A new instance of <code>BasicCheckBoxUI</code>.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicCheckBoxUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIManager} defaults table
+ * (<code>"CheckBox."</code> in this case).
+ *
+ * @return "CheckBox."
+ */
+ public String getPropertyPrefix()
+ {
+ return "CheckBox.";
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java
new file mode 100644
index 000000000..10b56431a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicColorChooserUI.java
@@ -0,0 +1,344 @@
+/* BasicColorChooserUI.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.BorderLayout;
+import java.awt.Container;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JColorChooser;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.LookAndFeel;
+import javax.swing.colorchooser.AbstractColorChooserPanel;
+import javax.swing.colorchooser.ColorChooserComponentFactory;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ColorChooserUI;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * This is the UI Class for the JColorChooser in the Basic Look and Feel.
+ */
+public class BasicColorChooserUI extends ColorChooserUI
+{
+ /**
+ * This helper class handles property changes from the JColorChooser.
+ */
+ public class PropertyHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when any of the properties of the JColorChooser
+ * change.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName() == JColorChooser.CHOOSER_PANELS_PROPERTY)
+ makeTabs(chooser.getChooserPanels());
+ else if (e.getPropertyName() == JColorChooser.PREVIEW_PANEL_PROPERTY)
+ updatePreviewPanel(chooser.getPreviewPanel());
+ else if (e.getPropertyName() == JColorChooser.SELECTION_MODEL_PROPERTY)
+ ((AbstractColorChooserPanel) pane.getSelectedComponent())
+ .updateChooser();
+
+ chooser.repaint();
+ }
+ }
+
+ /**
+ * This is a helper class that listens to the Model of the JColorChooser for
+ * color change events so it can update the preview panel.
+ */
+ private class PreviewListener implements ChangeListener
+ {
+ /**
+ * This method is called whenever the JColorChooser's color changes.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ if (pane != null)
+ {
+ AbstractColorChooserPanel panel = (AbstractColorChooserPanel) pane
+ .getSelectedComponent();
+ if (panel != null)
+ panel.updateChooser();
+ }
+ chooser.repaint();
+ }
+ }
+
+ /**
+ * This helper class listens to the JTabbedPane that is used for tab
+ * changes.
+ */
+ private class TabPaneListener implements ChangeListener
+ {
+ /**
+ * This method is called whenever a different tab is selected in the
+ * JTabbedPane.
+ *
+ * @param e The ChangeEvent.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Need to do this because we don't update all the tabs when they're not
+ // visible, so they are not informed of new colors when they're hidden.
+ AbstractColorChooserPanel comp = (AbstractColorChooserPanel) pane
+ .getSelectedComponent();
+ comp.updateChooser();
+ }
+ }
+
+ /** An array of default choosers to use in the JColorChooser. */
+ protected AbstractColorChooserPanel[] defaultChoosers;
+
+ /** The listener for the preview panel. */
+ protected ChangeListener previewListener;
+
+ /** The PropertyChangeListener for the JColorChooser. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /**
+ * The JColorChooser this is installed on.
+ */
+ protected JColorChooser chooser;
+
+ /** The JTabbedPane that is used. */
+ JTabbedPane pane;
+
+ /** The Container that holds the preview panel. */
+ private Container prevContainer;
+
+ /**
+ * Creates a new BasicColorChooserUI object.
+ */
+ public BasicColorChooserUI()
+ {
+ super();
+ }
+
+ /**
+ * This method creates a new UI Component for the given JComponent.
+ *
+ * @param c The JComponent to create an UI for.
+ *
+ * @return A new BasicColorChooserUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicColorChooserUI();
+ }
+
+ /**
+ * This method creates the default chooser panels for the JColorChooser.
+ *
+ * @return The default chooser panels.
+ */
+ protected AbstractColorChooserPanel[] createDefaultChoosers()
+ {
+ return ColorChooserComponentFactory.getDefaultChooserPanels();
+ }
+
+ /**
+ * This method installs the UI Component for the given JComponent.
+ *
+ * @param c The JComponent to install this UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JColorChooser)
+ {
+ chooser = (JColorChooser) c;
+ chooser.setLayout(new BorderLayout());
+
+ // Do this first, so we avoid doing work for property change events.
+ defaultChoosers = createDefaultChoosers();
+ chooser.setChooserPanels(defaultChoosers);
+ pane = new JTabbedPane();
+
+ pane.addChangeListener(new ChangeListener()
+ {
+ public void stateChanged(ChangeEvent e)
+ {
+ pane.repaint();
+ }
+ });
+
+ makeTabs(defaultChoosers);
+
+ chooser.add(pane, BorderLayout.NORTH);
+
+ installPreviewPanel();
+
+ installDefaults();
+ installListeners();
+ }
+ }
+
+ /**
+ * This method adds tabs to the JTabbedPane for the chooserPanels defined in
+ * the JColorChooser.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param panels The Panels that need tabs to be made for them.
+ */
+ void makeTabs(AbstractColorChooserPanel[] panels)
+ {
+ pane.removeAll();
+ for (int i = 0; i < panels.length; i++)
+ pane.addTab(panels[i].getDisplayName(), panels[i].getSmallDisplayIcon(),
+ panels[i]);
+ }
+
+ /**
+ * This method uninstalls this UI for the given JComponent.
+ *
+ * @param c The JComponent that will have this UI removed.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallDefaultChoosers();
+
+ pane = null;
+ chooser = null;
+ }
+
+ /**
+ * Uninstalls the default color choosers that have been installed by this UI.
+ */
+ protected void uninstallDefaultChoosers()
+ {
+ defaultChoosers = null;
+ }
+
+ /**
+ * This method installs the preview panel for the JColorChooser.
+ */
+ protected void installPreviewPanel()
+ {
+ updatePreviewPanel(ColorChooserComponentFactory.getPreviewPanel());
+ }
+
+ /**
+ * This is a helper method that swaps the existing preview panel with the
+ * given panel.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param preview The new preview panel.
+ */
+ void updatePreviewPanel(JComponent preview)
+ {
+ if (prevContainer == null)
+ {
+ prevContainer = new JPanel();
+ prevContainer.setLayout(new BorderLayout());
+ chooser.add(prevContainer, BorderLayout.CENTER);
+ }
+ prevContainer.removeAll();
+ prevContainer.add(preview, BorderLayout.CENTER);
+ }
+
+ /**
+ * This method installs the default properties given by the Basic Look and
+ * Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(chooser, "ColorChooser.background",
+ "ColorChooser.foreground",
+ "ColorChooser.font");
+ }
+
+ /**
+ * This method uninstalls the default properties given by the Basic Look and
+ * Feel.
+ */
+ protected void uninstallDefaults()
+ {
+ chooser.setBackground(null);
+ chooser.setForeground(null);
+ chooser.setFont(null);
+ }
+
+ /**
+ * This method installs any listeners required for this UI to function.
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+ previewListener = new PreviewListener();
+
+ chooser.addPropertyChangeListener(propertyChangeListener);
+ chooser.getSelectionModel().addChangeListener(previewListener);
+
+ pane.addChangeListener(new TabPaneListener());
+ }
+
+ /**
+ * This method creates the PropertyChangeListener used for listening to the
+ * JColorChooser.
+ *
+ * @return A PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyHandler();
+ }
+
+ /**
+ * This method uninstalls any listeners that were previously installed by
+ * the UI.
+ */
+ protected void uninstallListeners()
+ {
+ chooser.removePropertyChangeListener(propertyChangeListener);
+ chooser.getSelectionModel().removeChangeListener(previewListener);
+
+ previewListener = null;
+ propertyChangeListener = null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java
new file mode 100644
index 000000000..b04f3fa01
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxEditor.java
@@ -0,0 +1,181 @@
+/* BasicComboBoxEditor.java --
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Component;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+
+import javax.swing.ComboBoxEditor;
+import javax.swing.JTextField;
+
+/**
+ * An editor used by the {@link BasicComboBoxUI} class. This editor uses a
+ * {@link JTextField} as the editor component.
+ *
+ * @author Olga Rodimina
+ */
+public class BasicComboBoxEditor extends Object implements ComboBoxEditor,
+ FocusListener
+{
+ /** The editor component. */
+ protected JTextField editor;
+
+ /**
+ * Creates a new <code>BasicComboBoxEditor</code> instance.
+ */
+ public BasicComboBoxEditor()
+ {
+ editor = new JTextField();
+ editor.setBorder(null);
+ editor.setColumns(9);
+ }
+
+ /**
+ * Returns the component that will be used by the combo box to display and
+ * edit the currently selected item in the combo box.
+ *
+ * @return The editor component, which is a {@link JTextField} in this case.
+ */
+ public Component getEditorComponent()
+ {
+ return editor;
+ }
+
+ /**
+ * Sets item that should be edited when any editing operation is performed
+ * by the user. The value is always equal to the currently selected value
+ * in the combo box. Thus whenever a different value is selected from the
+ * combo box list then this method should be called to change editing
+ * item to the new selected item.
+ *
+ * @param item item that is currently selected in the combo box
+ */
+ public void setItem(Object item)
+ {
+ if (item == null)
+ editor.setText("");
+ else
+ editor.setText(item.toString());
+ }
+
+ /**
+ * Returns the text from the editor component.
+ *
+ * @return The text from the editor component.
+ */
+ public Object getItem()
+ {
+ return editor.getText();
+ }
+
+ /**
+ * Selects all the text in the editor component.
+ */
+ public void selectAll()
+ {
+ editor.selectAll();
+ }
+
+ /**
+ * This method is called when textfield gains focus. This will enable
+ * editing of the selected item.
+ *
+ * @param e the FocusEvent describing change in focus.
+ */
+ public void focusGained(FocusEvent e)
+ {
+ // FIXME: Need to implement
+ }
+
+ /**
+ * This method is called when textfield loses focus. If during this time any
+ * editting operation was performed by the user, then it will be cancelled
+ * and selected item will not be changed.
+ *
+ * @param e the FocusEvent describing change in focus
+ */
+ public void focusLost(FocusEvent e)
+ {
+ // FIXME: Need to implement
+ }
+
+ /**
+ * Adds an {@link ActionListener} to the editor component. If the user will
+ * edit currently selected item in the textfield and pressEnter, then action
+ * will be performed. The actionPerformed of this ActionListener should
+ * change the selected item of the comboBox to the newly editted selected
+ * item.
+ *
+ * @param l the ActionListener responsible for changing selected item of the
+ * combo box when it is editted by the user.
+ */
+ public void addActionListener(ActionListener l)
+ {
+ editor.addActionListener(l);
+ }
+
+ /**
+ * Removes the {@link ActionListener} from the editor component.
+ *
+ * @param l the listener to remove.
+ */
+ public void removeActionListener(ActionListener l)
+ {
+ editor.removeActionListener(l);
+ }
+
+ /**
+ * A subclass of {@link BasicComboBoxEditor} that implements the
+ * {@link UIResource} interface.
+ */
+ public static class UIResource extends BasicComboBoxEditor
+ implements javax.swing.plaf.UIResource
+ {
+ /**
+ * Creates a new <code>BasicComboBoxEditor.UIResource</code> instance.
+ */
+ public UIResource()
+ {
+ // Nothing to do here.
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java
new file mode 100644
index 000000000..761d7d11f
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxRenderer.java
@@ -0,0 +1,151 @@
+/* BasicComboBoxRenderer.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.io.Serializable;
+
+import javax.swing.Icon;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.border.Border;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * A renderer for a {@link JComboBox}.
+ *
+ * @author Olga Rodimina
+ */
+public class BasicComboBoxRenderer
+ extends JLabel
+ implements ListCellRenderer, Serializable
+{
+ /**
+ * A shared border instance for all renderers.
+ */
+ protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+
+ /**
+ * Creates a new <code>BasicComboBoxRenderer</code> object.
+ */
+ public BasicComboBoxRenderer()
+ {
+ setOpaque(true);
+ setBorder(noFocusBorder);
+ }
+
+ /**
+ * Returns preferredSize of the renderer
+ *
+ * @return preferredSize of the renderer
+ */
+ public Dimension getPreferredSize()
+ {
+ if (this.getText() != null && ! this.getText().equals(""))
+ return super.getPreferredSize();
+ else
+ {
+ // If the combo box option's text is empty or null, it won't size
+ // properly (ie, it'll be way too short)... so we throw in a dummy
+ // space to trick the superclass's sizing methods.
+ String oldText = this.getText();
+ this.setText(" ");
+ Dimension d = super.getPreferredSize();
+ this.setText(oldText);
+ return d;
+ }
+ }
+
+ /**
+ * Returns a component that has been configured to display the given
+ * <code>value</code>.
+ *
+ * @param list List of items for which to the background and foreground
+ * colors
+ * @param value object that should be rendered in the cell
+ * @param index index of the cell in the list of items.
+ * @param isSelected draw cell highlighted if isSelected is true
+ * @param cellHasFocus draw focus rectangle around cell if the cell has
+ * focus
+ *
+ * @return Component that will be used to draw the desired cell.
+ */
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected,
+ boolean cellHasFocus)
+ {
+ if (isSelected)
+ {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ }
+ else
+ {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+ setFont(list.getFont());
+
+ if (value instanceof Icon)
+ setIcon((Icon) value);
+ else
+ setText(value == null ? "" : value.toString());
+
+ return this;
+ }
+
+ /**
+ * A subclass of {@link BasicComboBoxRenderer} that implements the
+ * {@link javax.swing.plaf.UIResource} interface.
+ */
+ public static class UIResource extends BasicComboBoxRenderer
+ implements javax.swing.plaf.UIResource
+ {
+ /**
+ * Creates a new <code>UIResource</code> object.
+ */
+ public UIResource()
+ {
+ // Nothing to do here.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java
new file mode 100644
index 000000000..07d4f42cd
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboBoxUI.java
@@ -0,0 +1,1410 @@
+/* BasicComboBoxUI.java --
+ Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.swing.CellRendererPane;
+import javax.swing.ComboBoxEditor;
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.plaf.ComboBoxUI;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A UI delegate for the {@link JComboBox} component.
+ *
+ * @author Olga Rodimina
+ * @author Robert Schuster
+ */
+public class BasicComboBoxUI extends ComboBoxUI
+{
+ /**
+ * The arrow button that is displayed in the right side of JComboBox. This
+ * button is used to hide and show combo box's list of items.
+ */
+ protected JButton arrowButton;
+
+ /**
+ * The combo box represented by this UI delegate.
+ */
+ protected JComboBox comboBox;
+
+ /**
+ * The component that is responsible for displaying/editing the selected
+ * item of the combo box.
+ *
+ * @see BasicComboBoxEditor#getEditorComponent()
+ */
+ protected Component editor;
+
+ /**
+ * A listener listening to focus events occurring in the {@link JComboBox}.
+ */
+ protected FocusListener focusListener;
+
+ /**
+ * A flag indicating whether JComboBox currently has the focus.
+ */
+ protected boolean hasFocus;
+
+ /**
+ * A listener listening to item events fired by the {@link JComboBox}.
+ */
+ protected ItemListener itemListener;
+
+ /**
+ * A listener listening to key events that occur while {@link JComboBox} has
+ * the focus.
+ */
+ protected KeyListener keyListener;
+
+ /**
+ * List used when rendering selected item of the combo box. The selection
+ * and foreground colors for combo box renderer are configured from this
+ * list.
+ */
+ protected JList listBox;
+
+ /**
+ * ListDataListener listening to JComboBox model
+ */
+ protected ListDataListener listDataListener;
+
+ /**
+ * Popup list containing the combo box's menu items.
+ */
+ protected ComboPopup popup;
+
+ protected KeyListener popupKeyListener;
+
+ protected MouseListener popupMouseListener;
+
+ protected MouseMotionListener popupMouseMotionListener;
+
+ /**
+ * Listener listening to changes in the bound properties of JComboBox
+ */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /* Size of the largest item in the comboBox
+ * This is package-private to avoid an accessor method.
+ */
+ Dimension displaySize = new Dimension();
+
+ /**
+ * Used to render the combo box values.
+ */
+ protected CellRendererPane currentValuePane;
+
+ /**
+ * The current minimum size if isMinimumSizeDirty is false.
+ * Setup by getMinimumSize() and invalidated by the various listeners.
+ */
+ protected Dimension cachedMinimumSize;
+
+ /**
+ * Indicates whether or not the cachedMinimumSize field is valid or not.
+ */
+ protected boolean isMinimumSizeDirty = true;
+
+ /**
+ * Creates a new <code>BasicComboBoxUI</code> object.
+ */
+ public BasicComboBoxUI()
+ {
+ currentValuePane = new CellRendererPane();
+ cachedMinimumSize = new Dimension();
+ }
+
+ /**
+ * A factory method to create a UI delegate for the given
+ * {@link JComponent}, which should be a {@link JComboBox}.
+ *
+ * @param c The {@link JComponent} a UI is being created for.
+ *
+ * @return A UI delegate for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicComboBoxUI();
+ }
+
+ /**
+ * Installs the UI for the given {@link JComponent}.
+ *
+ * @param c the JComponent to install a UI for.
+ *
+ * @see #uninstallUI(JComponent)
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ if (c instanceof JComboBox)
+ {
+ isMinimumSizeDirty = true;
+ comboBox = (JComboBox) c;
+ installDefaults();
+ popup = createPopup();
+ listBox = popup.getList();
+
+ // Set editor and renderer for the combo box. Editor is used
+ // only if combo box becomes editable, otherwise renderer is used
+ // to paint the selected item; combobox is not editable by default.
+ ListCellRenderer renderer = comboBox.getRenderer();
+ if (renderer == null || renderer instanceof UIResource)
+ comboBox.setRenderer(createRenderer());
+
+ ComboBoxEditor currentEditor = comboBox.getEditor();
+ if (currentEditor == null || currentEditor instanceof UIResource)
+ {
+ currentEditor = createEditor();
+ comboBox.setEditor(currentEditor);
+ }
+
+ installComponents();
+ installListeners();
+ comboBox.setLayout(createLayoutManager());
+ comboBox.setFocusable(true);
+ installKeyboardActions();
+ comboBox.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP,
+ Boolean.TRUE);
+ }
+ }
+
+ /**
+ * Uninstalls the UI for the given {@link JComponent}.
+ *
+ * @param c The JComponent that is having this UI removed.
+ *
+ * @see #installUI(JComponent)
+ */
+ public void uninstallUI(JComponent c)
+ {
+ setPopupVisible(comboBox, false);
+ popup.uninstallingUI();
+ uninstallKeyboardActions();
+ comboBox.setLayout(null);
+ uninstallComponents();
+ uninstallListeners();
+ uninstallDefaults();
+ comboBox = null;
+ }
+
+ /**
+ * Installs the defaults that are defined in the {@link BasicLookAndFeel}
+ * for this {@link JComboBox}.
+ *
+ * @see #uninstallDefaults()
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(comboBox, "ComboBox.background",
+ "ComboBox.foreground", "ComboBox.font");
+ LookAndFeel.installBorder(comboBox, "ComboBox.border");
+ }
+
+ /**
+ * Creates and installs the listeners for this UI.
+ *
+ * @see #uninstallListeners()
+ */
+ protected void installListeners()
+ {
+ // install combo box's listeners
+ propertyChangeListener = createPropertyChangeListener();
+ comboBox.addPropertyChangeListener(propertyChangeListener);
+
+ focusListener = createFocusListener();
+ comboBox.addFocusListener(focusListener);
+
+ itemListener = createItemListener();
+ comboBox.addItemListener(itemListener);
+
+ keyListener = createKeyListener();
+ comboBox.addKeyListener(keyListener);
+
+ // install listeners that listen to combo box model
+ listDataListener = createListDataListener();
+ comboBox.getModel().addListDataListener(listDataListener);
+
+ // Install mouse and key listeners from the popup.
+ popupMouseListener = popup.getMouseListener();
+ comboBox.addMouseListener(popupMouseListener);
+
+ popupMouseMotionListener = popup.getMouseMotionListener();
+ comboBox.addMouseMotionListener(popupMouseMotionListener);
+
+ popupKeyListener = popup.getKeyListener();
+ comboBox.addKeyListener(popupKeyListener);
+ }
+
+ /**
+ * Uninstalls the defaults and sets any objects created during
+ * install to <code>null</code>.
+ *
+ * @see #installDefaults()
+ */
+ protected void uninstallDefaults()
+ {
+ if (comboBox.getFont() instanceof UIResource)
+ comboBox.setFont(null);
+
+ if (comboBox.getForeground() instanceof UIResource)
+ comboBox.setForeground(null);
+
+ if (comboBox.getBackground() instanceof UIResource)
+ comboBox.setBackground(null);
+
+ LookAndFeel.uninstallBorder(comboBox);
+ }
+
+ /**
+ * Detaches all the listeners we attached in {@link #installListeners}.
+ *
+ * @see #installListeners()
+ */
+ protected void uninstallListeners()
+ {
+ comboBox.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+
+ comboBox.removeFocusListener(focusListener);
+ listBox.removeFocusListener(focusListener);
+ focusListener = null;
+
+ comboBox.removeItemListener(itemListener);
+ itemListener = null;
+
+ comboBox.removeKeyListener(keyListener);
+ keyListener = null;
+
+ comboBox.getModel().removeListDataListener(listDataListener);
+ listDataListener = null;
+
+ if (popupMouseListener != null)
+ comboBox.removeMouseListener(popupMouseListener);
+ popupMouseListener = null;
+
+ if (popupMouseMotionListener != null)
+ comboBox.removeMouseMotionListener(popupMouseMotionListener);
+ popupMouseMotionListener = null;
+
+ if (popupKeyListener != null)
+ comboBox.removeKeyListener(popupKeyListener);
+ popupKeyListener = null;
+ }
+
+ /**
+ * Creates the popup that will contain list of combo box's items.
+ *
+ * @return popup containing list of combo box's items
+ */
+ protected ComboPopup createPopup()
+ {
+ return new BasicComboPopup(comboBox);
+ }
+
+ /**
+ * Creates a {@link KeyListener} to listen to key events.
+ *
+ * @return KeyListener that listens to key events.
+ */
+ protected KeyListener createKeyListener()
+ {
+ return new KeyHandler();
+ }
+
+ /**
+ * Creates the {@link FocusListener} that will listen to changes in this
+ * JComboBox's focus.
+ *
+ * @return the FocusListener.
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * Creates a {@link ListDataListener} to listen to the combo box's data model.
+ *
+ * @return The new listener.
+ */
+ protected ListDataListener createListDataListener()
+ {
+ return new ListDataHandler();
+ }
+
+ /**
+ * Creates an {@link ItemListener} that will listen to the changes in
+ * the JComboBox's selection.
+ *
+ * @return The ItemListener
+ */
+ protected ItemListener createItemListener()
+ {
+ return new ItemHandler();
+ }
+
+ /**
+ * Creates a {@link PropertyChangeListener} to listen to the changes in
+ * the JComboBox's bound properties.
+ *
+ * @return The PropertyChangeListener
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates and returns a layout manager for the combo box. Subclasses can
+ * override this method to provide a different layout.
+ *
+ * @return a layout manager for the combo box.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new ComboBoxLayoutManager();
+ }
+
+ /**
+ * Creates a component that will be responsible for rendering the
+ * selected component in the combo box.
+ *
+ * @return A renderer for the combo box.
+ */
+ protected ListCellRenderer createRenderer()
+ {
+ return new BasicComboBoxRenderer.UIResource();
+ }
+
+ /**
+ * Creates the component that will be responsible for displaying/editing
+ * the selected item in the combo box. This editor is used only when combo
+ * box is editable.
+ *
+ * @return A new component that will be responsible for displaying/editing
+ * the selected item in the combo box.
+ */
+ protected ComboBoxEditor createEditor()
+ {
+ return new BasicComboBoxEditor.UIResource();
+ }
+
+ /**
+ * Installs the components for this JComboBox. ArrowButton, main
+ * part of combo box (upper part) and popup list of items are created and
+ * configured here.
+ */
+ protected void installComponents()
+ {
+ // create and install arrow button
+ arrowButton = createArrowButton();
+ comboBox.add(arrowButton);
+ if (arrowButton != null)
+ configureArrowButton();
+
+ if (comboBox.isEditable())
+ addEditor();
+
+ comboBox.add(currentValuePane);
+ }
+
+ /**
+ * Uninstalls components from this {@link JComboBox}.
+ *
+ * @see #installComponents()
+ */
+ protected void uninstallComponents()
+ {
+ // Unconfigure arrow button.
+ if (arrowButton != null)
+ {
+ unconfigureArrowButton();
+ }
+
+ // Unconfigure editor.
+ if (editor != null)
+ {
+ unconfigureEditor();
+ }
+
+ comboBox.removeAll();
+ arrowButton = null;
+ }
+
+ /**
+ * Adds the current editor to the combo box.
+ */
+ public void addEditor()
+ {
+ removeEditor();
+ editor = comboBox.getEditor().getEditorComponent();
+ if (editor != null)
+ {
+ configureEditor();
+ comboBox.add(editor);
+ }
+ }
+
+ /**
+ * Removes the current editor from the combo box.
+ */
+ public void removeEditor()
+ {
+ if (editor != null)
+ {
+ unconfigureEditor();
+ comboBox.remove(editor);
+ }
+ }
+
+ /**
+ * Configures the editor for this combo box.
+ */
+ protected void configureEditor()
+ {
+ editor.setFont(comboBox.getFont());
+ if (popupKeyListener != null)
+ editor.addKeyListener(popupKeyListener);
+ if (keyListener != null)
+ editor.addKeyListener(keyListener);
+ comboBox.configureEditor(comboBox.getEditor(),
+ comboBox.getSelectedItem());
+ }
+
+ /**
+ * Unconfigures the editor for this combo box.
+ */
+ protected void unconfigureEditor()
+ {
+ if (popupKeyListener != null)
+ editor.removeKeyListener(popupKeyListener);
+ if (keyListener != null)
+ editor.removeKeyListener(keyListener);
+ }
+
+ /**
+ * Configures the arrow button.
+ *
+ * @see #configureArrowButton()
+ */
+ public void configureArrowButton()
+ {
+ if (arrowButton != null)
+ {
+ arrowButton.setEnabled(comboBox.isEnabled());
+ arrowButton.setFocusable(false);
+ arrowButton.addMouseListener(popup.getMouseListener());
+ arrowButton.addMouseMotionListener(popup.getMouseMotionListener());
+
+ // Mark the button as not closing the popup, we handle this ourselves.
+ arrowButton.putClientProperty(BasicLookAndFeel.DONT_CANCEL_POPUP,
+ Boolean.TRUE);
+ }
+ }
+
+ /**
+ * Unconfigures the arrow button.
+ *
+ * @see #configureArrowButton()
+ *
+ * @specnote The specification says this method is implementation specific
+ * and should not be used or overridden.
+ */
+ public void unconfigureArrowButton()
+ {
+ if (arrowButton != null)
+ {
+ if (popupMouseListener != null)
+ arrowButton.removeMouseListener(popupMouseListener);
+ if (popupMouseMotionListener != null)
+ arrowButton.removeMouseMotionListener(popupMouseMotionListener);
+ }
+ }
+
+ /**
+ * Creates an arrow button for this {@link JComboBox}. The arrow button is
+ * displayed at the right end of the combo box and is used to display/hide
+ * the drop down list of items.
+ *
+ * @return A new button.
+ */
+ protected JButton createArrowButton()
+ {
+ return new BasicArrowButton(BasicArrowButton.SOUTH);
+ }
+
+ /**
+ * Returns <code>true</code> if the popup is visible, and <code>false</code>
+ * otherwise.
+ *
+ * @param c The JComboBox to check
+ *
+ * @return <code>true</code> if popup part of the JComboBox is visible and
+ * <code>false</code> otherwise.
+ */
+ public boolean isPopupVisible(JComboBox c)
+ {
+ return popup.isVisible();
+ }
+
+ /**
+ * Displays/hides the {@link JComboBox}'s list of items on the screen.
+ *
+ * @param c The combo box, for which list of items should be
+ * displayed/hidden
+ * @param v true if show popup part of the jcomboBox and false to hide.
+ */
+ public void setPopupVisible(JComboBox c, boolean v)
+ {
+ if (v)
+ popup.show();
+ else
+ popup.hide();
+ }
+
+ /**
+ * JComboBox is focus traversable if it is editable and not otherwise.
+ *
+ * @param c combo box for which to check whether it is focus traversable
+ *
+ * @return true if focus tranversable and false otherwise
+ */
+ public boolean isFocusTraversable(JComboBox c)
+ {
+ if (!comboBox.isEditable())
+ return true;
+
+ return false;
+ }
+
+ /**
+ * Paints given menu item using specified graphics context
+ *
+ * @param g The graphics context used to paint this combo box
+ * @param c comboBox which needs to be painted.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ hasFocus = comboBox.hasFocus();
+ if (! comboBox.isEditable())
+ {
+ Rectangle rect = rectangleForCurrentValue();
+ paintCurrentValueBackground(g, rect, hasFocus);
+ paintCurrentValue(g, rect, hasFocus);
+ }
+ }
+
+ /**
+ * Returns preferred size for the combo box.
+ *
+ * @param c comboBox for which to get preferred size
+ *
+ * @return The preferred size for the given combo box
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return getMinimumSize(c);
+ }
+
+ /**
+ * Returns the minimum size for this {@link JComboBox} for this
+ * look and feel. Also makes sure cachedMinimimSize is setup correctly.
+ *
+ * @param c The {@link JComponent} to find the minimum size for.
+ *
+ * @return The dimensions of the minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ if (isMinimumSizeDirty)
+ {
+ Insets i = getInsets();
+ Dimension d = getDisplaySize();
+ d.width += i.left + i.right + d.height;
+ cachedMinimumSize = new Dimension(d.width, d.height + i.top + i.bottom);
+ isMinimumSizeDirty = false;
+ }
+ return new Dimension(cachedMinimumSize);
+ }
+
+ /**
+ * Returns the maximum size for this {@link JComboBox} for this
+ * look and feel.
+ *
+ * @param c The {@link JComponent} to find the maximum size for
+ *
+ * @return The maximum size (<code>Dimension(32767, 32767)</code>).
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(32767, 32767);
+ }
+
+ /**
+ * Returns the number of accessible children of the combobox.
+ *
+ * @param c the component (combobox) to check, ignored
+ *
+ * @return the number of accessible children of the combobox
+ */
+ public int getAccessibleChildrenCount(JComponent c)
+ {
+ int count = 1;
+ if (comboBox.isEditable())
+ count = 2;
+ return count;
+ }
+
+ /**
+ * Returns the accessible child with the specified index.
+ *
+ * @param c the component, this is ignored
+ * @param i the index of the accessible child to return
+ */
+ public Accessible getAccessibleChild(JComponent c, int i)
+ {
+ Accessible child = null;
+ switch (i)
+ {
+ case 0: // The popup.
+ if (popup instanceof Accessible)
+ {
+ AccessibleContext ctx = ((Accessible) popup).getAccessibleContext();
+ ctx.setAccessibleParent(comboBox);
+ child = (Accessible) popup;
+ }
+ break;
+ case 1: // The editor, if any.
+ if (comboBox.isEditable() && editor instanceof Accessible)
+ {
+ AccessibleContext ctx =
+ ((Accessible) editor).getAccessibleContext();
+ ctx.setAccessibleParent(comboBox);
+ child = (Accessible) editor;
+ }
+ break;
+ }
+ return child;
+ }
+
+ /**
+ * Returns true if the specified key is a navigation key and false otherwise
+ *
+ * @param keyCode a key for which to check whether it is navigation key or
+ * not.
+ *
+ * @return true if the specified key is a navigation key and false otherwis
+ */
+ protected boolean isNavigationKey(int keyCode)
+ {
+ return keyCode == KeyEvent.VK_UP || keyCode == KeyEvent.VK_DOWN
+ || keyCode == KeyEvent.VK_LEFT || keyCode == KeyEvent.VK_RIGHT
+ || keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_ESCAPE
+ || keyCode == KeyEvent.VK_TAB;
+ }
+
+ /**
+ * Selects next possible item relative to the current selection
+ * to be next selected item in the combo box.
+ */
+ protected void selectNextPossibleValue()
+ {
+ int index = comboBox.getSelectedIndex();
+ if (index != comboBox.getItemCount() - 1)
+ comboBox.setSelectedIndex(index + 1);
+ }
+
+ /**
+ * Selects previous item relative to current selection to be
+ * next selected item.
+ */
+ protected void selectPreviousPossibleValue()
+ {
+ int index = comboBox.getSelectedIndex();
+ if (index > 0)
+ comboBox.setSelectedIndex(index - 1);
+ }
+
+ /**
+ * Displays combo box popup if the popup is not currently shown
+ * on the screen and hides it if it is currently shown
+ */
+ protected void toggleOpenClose()
+ {
+ setPopupVisible(comboBox, ! isPopupVisible(comboBox));
+ }
+
+ /**
+ * Returns the bounds in which comboBox's selected item will be
+ * displayed.
+ *
+ * @return rectangle bounds in which comboBox's selected Item will be
+ * displayed
+ */
+ protected Rectangle rectangleForCurrentValue()
+ {
+ int w = comboBox.getWidth();
+ int h = comboBox.getHeight();
+ Insets i = comboBox.getInsets();
+ int arrowSize = h - (i.top + i.bottom);
+ if (arrowButton != null)
+ arrowSize = arrowButton.getWidth();
+ return new Rectangle(i.left, i.top, w - (i.left + i.right + arrowSize),
+ h - (i.top + i.left));
+ }
+
+ /**
+ * Returns the insets of the current border.
+ *
+ * @return Insets representing space between combo box and its border
+ */
+ protected Insets getInsets()
+ {
+ return comboBox.getInsets();
+ }
+
+ /**
+ * Paints currently selected value in the main part of the combo
+ * box (part without popup).
+ *
+ * @param g graphics context
+ * @param bounds Rectangle representing the size of the area in which
+ * selected item should be drawn
+ * @param hasFocus true if combo box has focus and false otherwise
+ */
+ public void paintCurrentValue(Graphics g, Rectangle bounds, boolean hasFocus)
+ {
+ /* Gets the component to be drawn for the current value.
+ * If there is currently no selected item we will take an empty
+ * String as replacement.
+ */
+ ListCellRenderer renderer = comboBox.getRenderer();
+ if (comboBox.getSelectedIndex() != -1)
+ {
+ Component comp;
+ if (hasFocus && ! isPopupVisible(comboBox))
+ {
+ comp = renderer.getListCellRendererComponent(listBox,
+ comboBox.getSelectedItem(), -1, true, false);
+ }
+ else
+ {
+ comp = renderer.getListCellRendererComponent(listBox,
+ comboBox.getSelectedItem(), -1, false, false);
+ Color bg = UIManager.getColor("ComboBox.disabledForeground");
+ comp.setBackground(bg);
+ }
+ comp.setFont(comboBox.getFont());
+ if (hasFocus && ! isPopupVisible(comboBox))
+ {
+ comp.setForeground(listBox.getSelectionForeground());
+ comp.setBackground(listBox.getSelectionBackground());
+ }
+ else if (comboBox.isEnabled())
+ {
+ comp.setForeground(comboBox.getForeground());
+ comp.setBackground(comboBox.getBackground());
+ }
+ else
+ {
+ Color fg = UIManager.getColor("ComboBox.disabledForeground");
+ comp.setForeground(fg);
+ Color bg = UIManager.getColor("ComboBox.disabledBackground");
+ comp.setBackground(bg);
+ }
+ currentValuePane.paintComponent(g, comp, comboBox, bounds.x, bounds.y,
+ bounds.width, bounds.height);
+ }
+ }
+
+ /**
+ * Paints the background of part of the combo box, where currently
+ * selected value is displayed. If the combo box has focus this method
+ * should also paint focus rectangle around the combo box.
+ *
+ * @param g graphics context
+ * @param bounds Rectangle representing the size of the largest item in the
+ * comboBox
+ * @param hasFocus true if combo box has fox and false otherwise
+ */
+ public void paintCurrentValueBackground(Graphics g, Rectangle bounds,
+ boolean hasFocus)
+ {
+ Color saved = g.getColor();
+ if (comboBox.isEnabled())
+ g.setColor(UIManager.getColor("UIManager.background"));
+ else
+ g.setColor(UIManager.getColor("UIManager.disabledBackground"));
+ g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
+ g.setColor(saved);
+ }
+
+ private static final ListCellRenderer DEFAULT_RENDERER
+ = new DefaultListCellRenderer();
+
+ /**
+ * Returns the default size for the display area of a combo box that does
+ * not contain any elements. This method returns the width and height of
+ * a single space in the current font, plus a margin of 1 pixel.
+ *
+ * @return The default display size.
+ *
+ * @see #getDisplaySize()
+ */
+ protected Dimension getDefaultSize()
+ {
+ Component comp = DEFAULT_RENDERER.getListCellRendererComponent(listBox,
+ " ", -1, false, false);
+ currentValuePane.add(comp);
+ comp.setFont(comboBox.getFont());
+ Dimension d = comp.getPreferredSize();
+ currentValuePane.remove(comp);
+ return d;
+ }
+
+ /**
+ * Returns the size of the display area for the combo box. This size will be
+ * the size of the combo box, not including the arrowButton.
+ *
+ * @return The size of the display area for the combo box.
+ */
+ protected Dimension getDisplaySize()
+ {
+ Dimension dim = new Dimension();
+ ListCellRenderer renderer = comboBox.getRenderer();
+ if (renderer == null)
+ {
+ renderer = DEFAULT_RENDERER;
+ }
+
+ Object prototype = comboBox.getPrototypeDisplayValue();
+ if (prototype != null)
+ {
+ Component comp = renderer.getListCellRendererComponent(listBox,
+ prototype, -1, false, false);
+ currentValuePane.add(comp);
+ comp.setFont(comboBox.getFont());
+ Dimension renderSize = comp.getPreferredSize();
+ currentValuePane.remove(comp);
+ dim.height = renderSize.height;
+ dim.width = renderSize.width;
+ }
+ else
+ {
+ ComboBoxModel model = comboBox.getModel();
+ int size = model.getSize();
+ if (size > 0)
+ {
+ for (int i = 0; i < size; ++i)
+ {
+ Component comp = renderer.getListCellRendererComponent(listBox,
+ model.getElementAt(i), -1, false, false);
+ currentValuePane.add(comp);
+ comp.setFont(comboBox.getFont());
+ Dimension renderSize = comp.getPreferredSize();
+ currentValuePane.remove(comp);
+ dim.width = Math.max(dim.width, renderSize.width);
+ dim.height = Math.max(dim.height, renderSize.height);
+ }
+ }
+ else
+ {
+ dim = getDefaultSize();
+ if (comboBox.isEditable())
+ dim.width = 100;
+ }
+ }
+ if (comboBox.isEditable())
+ {
+ Dimension editSize = editor.getPreferredSize();
+ dim.width = Math.max(dim.width, editSize.width);
+ dim.height = Math.max(dim.height, editSize.height);
+ }
+ displaySize.setSize(dim.width, dim.height);
+ return dim;
+ }
+
+ /**
+ * Installs the keyboard actions for the {@link JComboBox} as specified
+ * by the look and feel.
+ */
+ protected void installKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(comboBox,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ (InputMap) UIManager.get("ComboBox.ancestorInputMap"));
+ // Install any action maps here.
+ }
+
+ /**
+ * Uninstalls the keyboard actions for the {@link JComboBox} there were
+ * installed by in {@link #installListeners}.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(comboBox,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ // Uninstall any action maps here.
+ }
+
+ /**
+ * A {@link LayoutManager} used to position the sub-components of the
+ * {@link JComboBox}.
+ *
+ * @see BasicComboBoxUI#createLayoutManager()
+ */
+ public class ComboBoxLayoutManager implements LayoutManager
+ {
+ /**
+ * Creates a new ComboBoxLayoutManager object.
+ */
+ public ComboBoxLayoutManager()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds a component to the layout. This method does nothing, since the
+ * layout manager doesn't need to track the components.
+ *
+ * @param name the name to associate the component with (ignored).
+ * @param comp the component (ignored).
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Do nothing
+ }
+
+ /**
+ * Removes a component from the layout. This method does nothing, since
+ * the layout manager doesn't need to track the components.
+ *
+ * @param comp the component.
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Do nothing
+ }
+
+ /**
+ * Returns preferred layout size of the JComboBox.
+ *
+ * @param parent the Container for which the preferred size should be
+ * calculated.
+ *
+ * @return The preferred size for the given container
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ return parent.getPreferredSize();
+ }
+
+ /**
+ * Returns the minimum layout size.
+ *
+ * @param parent the container.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return parent.getMinimumSize();
+ }
+
+ /**
+ * Arranges the components in the container. It puts arrow
+ * button right end part of the comboBox. If the comboBox is editable
+ * then editor is placed to the left of arrow button, starting from the
+ * beginning.
+ *
+ * @param parent Container that should be layed out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ // Position editor component to the left of arrow button if combo box is
+ // editable
+ Insets i = getInsets();
+ int arrowSize = comboBox.getHeight() - (i.top + i.bottom);
+
+ if (arrowButton != null)
+ arrowButton.setBounds(comboBox.getWidth() - (i.right + arrowSize),
+ i.top, arrowSize, arrowSize);
+ if (editor != null)
+ editor.setBounds(rectangleForCurrentValue());
+ }
+ }
+
+ /**
+ * Handles focus changes occuring in the combo box. This class is
+ * responsible for repainting combo box whenever focus is gained or lost
+ * and also for hiding popup list of items whenever combo box loses its
+ * focus.
+ */
+ public class FocusHandler extends Object implements FocusListener
+ {
+ /**
+ * Creates a new FocusHandler object.
+ */
+ public FocusHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when combo box gains focus. It repaints main
+ * part of combo box accordingly.
+ *
+ * @param e the FocusEvent
+ */
+ public void focusGained(FocusEvent e)
+ {
+ hasFocus = true;
+ comboBox.repaint();
+ }
+
+ /**
+ * Invoked when the combo box loses focus. It repaints the main part
+ * of the combo box accordingly and hides the popup list of items.
+ *
+ * @param e the FocusEvent
+ */
+ public void focusLost(FocusEvent e)
+ {
+ hasFocus = false;
+ if (! e.isTemporary() && comboBox.isLightWeightPopupEnabled())
+ setPopupVisible(comboBox, false);
+ comboBox.repaint();
+ }
+ }
+
+ /**
+ * Handles {@link ItemEvent}s fired by the {@link JComboBox} when its
+ * selected item changes.
+ */
+ public class ItemHandler extends Object implements ItemListener
+ {
+ /**
+ * Creates a new ItemHandler object.
+ */
+ public ItemHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when selected item becomes deselected or when
+ * new item becomes selected.
+ *
+ * @param e the ItemEvent representing item's state change.
+ */
+ public void itemStateChanged(ItemEvent e)
+ {
+ ComboBoxModel model = comboBox.getModel();
+ Object v = model.getSelectedItem();
+ if (editor != null)
+ comboBox.configureEditor(comboBox.getEditor(), v);
+ comboBox.repaint();
+ }
+ }
+
+ /**
+ * KeyHandler handles key events occuring while JComboBox has focus.
+ */
+ public class KeyHandler extends KeyAdapter
+ {
+ public KeyHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked whenever key is pressed while JComboBox is in focus.
+ */
+ public void keyPressed(KeyEvent e)
+ {
+ if (comboBox.getModel().getSize() != 0 && comboBox.isEnabled())
+ {
+ if (! isNavigationKey(e.getKeyCode()))
+ {
+ if (! comboBox.isEditable())
+ if (comboBox.selectWithKeyChar(e.getKeyChar()))
+ e.consume();
+ }
+ else
+ {
+ if (e.getKeyCode() == KeyEvent.VK_UP && comboBox.isPopupVisible())
+ selectPreviousPossibleValue();
+ else if (e.getKeyCode() == KeyEvent.VK_DOWN)
+ {
+ if (comboBox.isPopupVisible())
+ selectNextPossibleValue();
+ else
+ comboBox.showPopup();
+ }
+ else if (e.getKeyCode() == KeyEvent.VK_ENTER
+ || e.getKeyCode() == KeyEvent.VK_ESCAPE)
+ popup.hide();
+ }
+ }
+ }
+ }
+
+ /**
+ * Handles the changes occurring in the JComboBox's data model.
+ */
+ public class ListDataHandler extends Object implements ListDataListener
+ {
+ /**
+ * Creates a new ListDataHandler object.
+ */
+ public ListDataHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked if the content's of JComboBox's data model are changed.
+ *
+ * @param e ListDataEvent describing the change.
+ */
+ public void contentsChanged(ListDataEvent e)
+ {
+ if (e.getIndex0() != -1 || e.getIndex1() != -1)
+ {
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ }
+ if (editor != null)
+ comboBox.configureEditor(comboBox.getEditor(),
+ comboBox.getSelectedItem());
+ comboBox.repaint();
+ }
+
+ /**
+ * Invoked when items are added to the JComboBox's data model.
+ *
+ * @param e ListDataEvent describing the change.
+ */
+ public void intervalAdded(ListDataEvent e)
+ {
+ int start = e.getIndex0();
+ int end = e.getIndex1();
+ if (start == 0 && comboBox.getItemCount() - (end - start + 1) == 0)
+ contentsChanged(e);
+ else if (start != -1 || end != -1)
+ {
+ ListCellRenderer renderer = comboBox.getRenderer();
+ ComboBoxModel model = comboBox.getModel();
+ int w = displaySize.width;
+ int h = displaySize.height;
+ // TODO: Optimize using prototype here.
+ for (int i = start; i <= end; ++i)
+ {
+ Component comp = renderer.getListCellRendererComponent(listBox,
+ model.getElementAt(i), -1, false, false);
+ currentValuePane.add(comp);
+ comp.setFont(comboBox.getFont());
+ Dimension dim = comp.getPreferredSize();
+ w = Math.max(w, dim.width);
+ h = Math.max(h, dim.height);
+ currentValuePane.remove(comp);
+ }
+ if (displaySize.width < w || displaySize.height < h)
+ {
+ if (displaySize.width < w)
+ displaySize.width = w;
+ if (displaySize.height < h)
+ displaySize.height = h;
+ comboBox.revalidate();
+ if (editor != null)
+ {
+ comboBox.configureEditor(comboBox.getEditor(),
+ comboBox.getSelectedItem());
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Invoked when items are removed from the JComboBox's
+ * data model.
+ *
+ * @param e ListDataEvent describing the change.
+ */
+ public void intervalRemoved(ListDataEvent e)
+ {
+ contentsChanged(e);
+ }
+ }
+
+ /**
+ * Handles {@link PropertyChangeEvent}s fired by the {@link JComboBox}.
+ */
+ public class PropertyChangeHandler extends Object
+ implements PropertyChangeListener
+ {
+ /**
+ * Creates a new instance.
+ */
+ public PropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked whenever bound property of JComboBox changes.
+ *
+ * @param e the event.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // Lets assume every change invalidates the minimumsize.
+ String propName = e.getPropertyName();
+ if (propName.equals("enabled"))
+ {
+ boolean enabled = comboBox.isEnabled();
+ if (editor != null)
+ editor.setEnabled(enabled);
+ if (arrowButton != null)
+ arrowButton.setEnabled(enabled);
+
+ comboBox.repaint();
+ }
+ else if (propName.equals("editor") && comboBox.isEditable())
+ {
+ addEditor();
+ comboBox.revalidate();
+ }
+ else if (e.getPropertyName().equals("editable"))
+ {
+ if (comboBox.isEditable())
+ {
+ addEditor();
+ }
+ else
+ {
+ removeEditor();
+ }
+
+ comboBox.revalidate();
+ }
+ else if (propName.equals("model"))
+ {
+ // remove ListDataListener from old model and add it to new model
+ ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
+ if (oldModel != null && listDataListener != null)
+ oldModel.removeListDataListener(listDataListener);
+
+ ComboBoxModel newModel = (ComboBoxModel) e.getNewValue();
+ if (newModel != null && listDataListener != null)
+ comboBox.getModel().addListDataListener(listDataListener);
+
+ if (editor != null)
+ {
+ comboBox.configureEditor(comboBox.getEditor(),
+ comboBox.getSelectedItem());
+ }
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ comboBox.repaint();
+ }
+ else if (propName.equals("font"))
+ {
+ Font font = (Font) e.getNewValue();
+ if (editor != null)
+ {
+ editor.setFont(font);
+ }
+ listBox.setFont(font);
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ }
+ else if (propName.equals("prototypeDisplayValue"))
+ {
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ }
+ else if (propName.equals("renderer"))
+ {
+ isMinimumSizeDirty = true;
+ comboBox.revalidate();
+ }
+ // FIXME: Need to handle changes in other bound properties.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java
new file mode 100644
index 000000000..3e54ca7c6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicComboPopup.java
@@ -0,0 +1,1104 @@
+/* BasicComboPopup.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionAdapter;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.BorderFactory;
+import javax.swing.ComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JList;
+import javax.swing.JPopupMenu;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.ListCellRenderer;
+import javax.swing.ListSelectionModel;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+
+/**
+ * UI Delegate for ComboPopup
+ *
+ * @author Olga Rodimina
+ */
+public class BasicComboPopup extends JPopupMenu implements ComboPopup
+{
+ /* Timer for autoscrolling */
+ protected Timer autoscrollTimer;
+
+ /** ComboBox associated with this popup */
+ protected JComboBox comboBox;
+
+ /** FIXME: Need to document */
+ protected boolean hasEntered;
+
+ /**
+ * Indicates whether the scroll bar located in popup menu with comboBox's
+ * list of items is currently autoscrolling. This happens when mouse event
+ * originated in the combo box and is dragged outside of its bounds
+ */
+ protected boolean isAutoScrolling;
+
+ /** ItemListener listening to the selection changes in the combo box */
+ protected ItemListener itemListener;
+
+ /** This listener is not used */
+ protected KeyListener keyListener;
+
+ /** JList which is used to display item is the combo box */
+ protected JList list;
+
+ /** This listener is not used */
+ protected ListDataListener listDataListener;
+
+ /**
+ * MouseListener listening to mouse events occuring in the combo box's
+ * list.
+ */
+ protected MouseListener listMouseListener;
+
+ /**
+ * MouseMotionListener listening to mouse motion events occuring in the
+ * combo box's list
+ */
+ protected MouseMotionListener listMouseMotionListener;
+
+ /** This listener is not used */
+ protected ListSelectionListener listSelectionListener;
+
+ /** MouseListener listening to mouse events occuring in the combo box */
+ protected MouseListener mouseListener;
+
+ /**
+ * MouseMotionListener listening to mouse motion events occuring in the
+ * combo box
+ */
+ protected MouseMotionListener mouseMotionListener;
+
+ /**
+ * PropertyChangeListener listening to changes occuring in the bound
+ * properties of the combo box
+ */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** direction for scrolling down list of combo box's items */
+ protected static final int SCROLL_DOWN = 1;
+
+ /** direction for scrolling up list of combo box's items */
+ protected static final int SCROLL_UP = 0;
+
+ /** Indicates auto scrolling direction */
+ protected int scrollDirection;
+
+ /** JScrollPane that contains list portion of the combo box */
+ protected JScrollPane scroller;
+
+ /** This field is not used */
+ protected boolean valueIsAdjusting;
+
+ /**
+ * Creates a new BasicComboPopup object.
+ *
+ * @param comboBox the combo box with which this popup should be associated
+ */
+ public BasicComboPopup(JComboBox comboBox)
+ {
+ this.comboBox = comboBox;
+ mouseListener = createMouseListener();
+ mouseMotionListener = createMouseMotionListener();
+ keyListener = createKeyListener();
+
+ list = createList();
+ configureList();
+ scroller = createScroller();
+ configureScroller();
+ configurePopup();
+ installComboBoxListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * This method displays drow down list of combo box items on the screen.
+ */
+ public void show()
+ {
+ Dimension size = comboBox.getSize();
+ size.height = getPopupHeightForRowCount(comboBox.getMaximumRowCount());
+ Insets i = getInsets();
+ size.width -= i.left + i.right;
+ Rectangle bounds = computePopupBounds(0, comboBox.getBounds().height,
+ size.width, size.height);
+
+ scroller.setMaximumSize(bounds.getSize());
+ scroller.setPreferredSize(bounds.getSize());
+ scroller.setMinimumSize(bounds.getSize());
+ list.invalidate();
+
+ syncListSelection();
+
+ list.ensureIndexIsVisible(list.getSelectedIndex());
+ setLightWeightPopupEnabled(comboBox.isLightWeightPopupEnabled());
+ show(comboBox, bounds.x, bounds.y);
+ }
+
+ /**
+ * This method hides drop down list of items
+ */
+ public void hide()
+ {
+ MenuSelectionManager menuSelectionManager =
+ MenuSelectionManager.defaultManager();
+ javax.swing.MenuElement[] menuElements =
+ menuSelectionManager.getSelectedPath();
+ for (int i = 0; i < menuElements.length; i++)
+ {
+ if (menuElements[i] == this)
+ {
+ menuSelectionManager.clearSelectedPath();
+ break;
+ }
+ }
+ comboBox.repaint();
+ }
+
+ /**
+ * Return list cointaining JComboBox's items
+ *
+ * @return list cointaining JComboBox's items
+ */
+ public JList getList()
+ {
+ return list;
+ }
+
+ /**
+ * Returns MouseListener that is listening to mouse events occuring in the
+ * combo box.
+ *
+ * @return MouseListener
+ */
+ public MouseListener getMouseListener()
+ {
+ return mouseListener;
+ }
+
+ /**
+ * Returns MouseMotionListener that is listening to mouse motion events
+ * occuring in the combo box.
+ *
+ * @return MouseMotionListener
+ */
+ public MouseMotionListener getMouseMotionListener()
+ {
+ return mouseMotionListener;
+ }
+
+ /**
+ * Returns KeyListener listening to key events occuring in the combo box.
+ * This method returns null because KeyHandler is not longer used.
+ *
+ * @return KeyListener
+ */
+ public KeyListener getKeyListener()
+ {
+ return keyListener;
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ */
+ public void uninstallingUI()
+ {
+ if (propertyChangeListener != null)
+ {
+ comboBox.removePropertyChangeListener(propertyChangeListener);
+ }
+ if (itemListener != null)
+ {
+ comboBox.removeItemListener(itemListener);
+ }
+ uninstallComboBoxModelListeners(comboBox.getModel());
+ uninstallKeyboardActions();
+ uninstallListListeners();
+ }
+
+ /**
+ * This method uninstalls listeners that were listening to changes occuring
+ * in the comb box's data model
+ *
+ * @param model data model for the combo box from which to uninstall
+ * listeners
+ */
+ protected void uninstallComboBoxModelListeners(ComboBoxModel model)
+ {
+ model.removeListDataListener(listDataListener);
+ }
+
+ /**
+ * This method uninstalls keyboard actions installed by the UI.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method fires PopupMenuEvent indicating that combo box's popup list
+ * of items will become visible
+ */
+ protected void firePopupMenuWillBecomeVisible()
+ {
+ PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].popupMenuWillBecomeVisible(new PopupMenuEvent(comboBox));
+ }
+
+ /**
+ * This method fires PopupMenuEvent indicating that combo box's popup list
+ * of items will become invisible.
+ */
+ protected void firePopupMenuWillBecomeInvisible()
+ {
+ PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].popupMenuWillBecomeInvisible(new PopupMenuEvent(comboBox));
+ }
+
+ /**
+ * This method fires PopupMenuEvent indicating that combo box's popup list
+ * of items was closed without selection.
+ */
+ protected void firePopupMenuCanceled()
+ {
+ PopupMenuListener[] ll = comboBox.getPopupMenuListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].popupMenuCanceled(new PopupMenuEvent(comboBox));
+ }
+
+ /**
+ * Creates MouseListener to listen to mouse events occuring in the combo
+ * box. Note that this listener doesn't listen to mouse events occuring in
+ * the popup portion of the combo box, it only listens to main combo box
+ * part.
+ *
+ * @return new MouseMotionListener that listens to mouse events occuring in
+ * the combo box
+ */
+ protected MouseListener createMouseListener()
+ {
+ return new InvocationMouseHandler();
+ }
+
+ /**
+ * Create Mouse listener that listens to mouse dragging events occuring in
+ * the combo box. This listener is responsible for changing the selection
+ * in the combo box list to the component over which mouse is being
+ * currently dragged
+ *
+ * @return new MouseMotionListener that listens to mouse dragging events
+ * occuring in the combo box
+ */
+ protected MouseMotionListener createMouseMotionListener()
+ {
+ return new InvocationMouseMotionHandler();
+ }
+
+ /**
+ * KeyListener created in this method is not used anymore.
+ *
+ * @return KeyListener that does nothing
+ */
+ protected KeyListener createKeyListener()
+ {
+ return new InvocationKeyHandler();
+ }
+
+ /**
+ * ListSelectionListener created in this method is not used anymore
+ *
+ * @return ListSelectionListener that does nothing
+ */
+ protected ListSelectionListener createListSelectionListener()
+ {
+ return new ListSelectionHandler();
+ }
+
+ /**
+ * Creates ListDataListener. This method returns null, because
+ * ListDataHandler class is obsolete and is no longer used.
+ *
+ * @return null
+ */
+ protected ListDataListener createListDataListener()
+ {
+ return null;
+ }
+
+ /**
+ * This method creates ListMouseListener to listen to mouse events occuring
+ * in the combo box's item list.
+ *
+ * @return MouseListener to listen to mouse events occuring in the combo
+ * box's items list.
+ */
+ protected MouseListener createListMouseListener()
+ {
+ return new ListMouseHandler();
+ }
+
+ /**
+ * Creates ListMouseMotionlistener to listen to mouse motion events occuring
+ * in the combo box's list. This listener is responsible for highlighting
+ * items in the list when mouse is moved over them.
+ *
+ * @return MouseMotionListener that handles mouse motion events occuring in
+ * the list of the combo box.
+ */
+ protected MouseMotionListener createListMouseMotionListener()
+ {
+ return new ListMouseMotionHandler();
+ }
+
+ /**
+ * Creates PropertyChangeListener to handle changes in the JComboBox's bound
+ * properties.
+ *
+ * @return PropertyChangeListener to handle changes in the JComboBox's bound
+ * properties.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates new ItemListener that will listen to ItemEvents occuring in the
+ * combo box.
+ *
+ * @return ItemListener to listen to ItemEvents occuring in the combo box.
+ */
+ protected ItemListener createItemListener()
+ {
+ return new ItemHandler();
+ }
+
+ /**
+ * Creates JList that will be used to display items in the combo box.
+ *
+ * @return JList that will be used to display items in the combo box.
+ */
+ protected JList createList()
+ {
+ JList l = new JList(comboBox.getModel());
+ return l;
+ }
+
+ /**
+ * This method configures the list of comboBox's items by setting default
+ * properties and installing listeners.
+ */
+ protected void configureList()
+ {
+ list.setFont(comboBox.getFont());
+ list.setForeground(comboBox.getForeground());
+ list.setBackground(comboBox.getBackground());
+ Color sfg = UIManager.getColor("ComboBox.selectionForeground");
+ list.setSelectionForeground(sfg);
+ Color sbg = UIManager.getColor("ComboBox.selectionBackground");
+ list.setSelectionBackground(sbg);
+ list.setBorder(null);
+ list.setCellRenderer(comboBox.getRenderer());
+ list.setFocusable(false);
+ list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+ installListListeners();
+ }
+
+ /**
+ * This method installs list listeners.
+ */
+ protected void installListListeners()
+ {
+ // mouse listener listening to mouse events occuring in the
+ // combo box's list of items.
+ listMouseListener = createListMouseListener();
+ list.addMouseListener(listMouseListener);
+
+ // mouse listener listening to mouse motion events occuring in the
+ // combo box's list of items
+ listMouseMotionListener = createListMouseMotionListener();
+ list.addMouseMotionListener(listMouseMotionListener);
+
+ listSelectionListener = createListSelectionListener();
+ list.addListSelectionListener(listSelectionListener);
+ }
+
+ /**
+ * This method creates scroll pane that will contain the list of comboBox's
+ * items inside of it.
+ *
+ * @return JScrollPane
+ */
+ protected JScrollPane createScroller()
+ {
+ return new JScrollPane(list, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
+ JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+ }
+
+ /**
+ * This method configures scroll pane to contain list of comboBox's items
+ */
+ protected void configureScroller()
+ {
+ scroller.setBorder(null);
+ scroller.setFocusable(false);
+ scroller.getVerticalScrollBar().setFocusable(false);
+ }
+
+ /**
+ * This method configures popup menu that will be used to display Scrollpane
+ * with list of items inside of it.
+ */
+ protected void configurePopup()
+ {
+ setBorderPainted(true);
+ setBorder(BorderFactory.createLineBorder(Color.BLACK));
+ setOpaque(false);
+ add(scroller);
+ setFocusable(false);
+ }
+
+ /*
+ * This method installs listeners that will listen to changes occuring
+ * in the combo box.
+ */
+ protected void installComboBoxListeners()
+ {
+ // item listener listenening to selection events in the combo box
+ itemListener = createItemListener();
+ comboBox.addItemListener(itemListener);
+
+ propertyChangeListener = createPropertyChangeListener();
+ comboBox.addPropertyChangeListener(propertyChangeListener);
+
+ installComboBoxModelListeners(comboBox.getModel());
+ }
+
+ /**
+ * This method installs listeners that will listen to changes occuring in
+ * the comb box's data model
+ *
+ * @param model data model for the combo box for which to install listeners
+ */
+ protected void installComboBoxModelListeners(ComboBoxModel model)
+ {
+ // list data listener to listen for ListDataEvents in combo box.
+ // This listener is now obsolete and nothing is done here
+ listDataListener = createListDataListener();
+ comboBox.getModel().addListDataListener(listDataListener);
+ }
+
+ /**
+ * Installs the keyboard actions.
+ */
+ protected void installKeyboardActions()
+ {
+ // Nothing to do here
+ }
+
+ /**
+ * This method always returns false to indicate that items in the combo box
+ * list are not focus traversable.
+ *
+ * @return false
+ */
+ public boolean isFocusTraversable()
+ {
+ return false;
+ }
+
+ /**
+ * This method start scrolling combo box's list of items either up or down
+ * depending on the specified 'direction'
+ *
+ * @param direction of the scrolling.
+ */
+ protected void startAutoScrolling(int direction)
+ {
+ // FIXME: add timer
+ isAutoScrolling = true;
+
+ if (direction == SCROLL_UP)
+ autoScrollUp();
+ else
+ autoScrollDown();
+ }
+
+ /**
+ * This method stops scrolling the combo box's list of items
+ */
+ protected void stopAutoScrolling()
+ {
+ // FIXME: add timer
+ isAutoScrolling = false;
+ }
+
+ /**
+ * This method scrolls up list of combo box's items up and highlights that
+ * just became visible.
+ */
+ protected void autoScrollUp()
+ {
+ // scroll up the scroll bar to make the item above visible
+ JScrollBar scrollbar = scroller.getVerticalScrollBar();
+ int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
+ SwingConstants.VERTICAL,
+ SCROLL_UP);
+
+ scrollbar.setValue(scrollbar.getValue() - scrollToNext);
+
+ // If we haven't reached the begging of the combo box's list of items,
+ // then highlight next element above currently highlighted element
+ if (list.getSelectedIndex() != 0)
+ list.setSelectedIndex(list.getSelectedIndex() - 1);
+ }
+
+ /**
+ * This method scrolls down list of combo box's and highlights item in the
+ * list that just became visible.
+ */
+ protected void autoScrollDown()
+ {
+ // scroll scrollbar down to make next item visible
+ JScrollBar scrollbar = scroller.getVerticalScrollBar();
+ int scrollToNext = list.getScrollableUnitIncrement(super.getBounds(),
+ SwingConstants.VERTICAL,
+ SCROLL_DOWN);
+ scrollbar.setValue(scrollbar.getValue() + scrollToNext);
+
+ // If we haven't reached the end of the combo box's list of items
+ // then highlight next element below currently highlighted element
+ if (list.getSelectedIndex() + 1 != comboBox.getItemCount())
+ list.setSelectedIndex(list.getSelectedIndex() + 1);
+ }
+
+ /**
+ * This method helps to delegate focus to the right component in the
+ * JComboBox. If the comboBox is editable then focus is sent to
+ * ComboBoxEditor, otherwise it is delegated to JComboBox.
+ *
+ * @param e MouseEvent
+ */
+ protected void delegateFocus(MouseEvent e)
+ {
+ if (comboBox.isEditable())
+ comboBox.getEditor().getEditorComponent().requestFocus();
+ else
+ comboBox.requestFocus();
+ }
+
+ /**
+ * This method displays combo box popup if the popup is not currently shown
+ * on the screen and hides it if it is currently visible
+ */
+ protected void togglePopup()
+ {
+ if (isVisible())
+ hide();
+ else
+ show();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param e DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected MouseEvent convertMouseEvent(MouseEvent e)
+ {
+ Point point = SwingUtilities.convertPoint((Component) e.getSource(),
+ e.getPoint(), list);
+ MouseEvent newEvent = new MouseEvent((Component) e.getSource(),
+ e.getID(), e.getWhen(),
+ e.getModifiers(), point.x, point.y,
+ e.getModifiers(),
+ e.isPopupTrigger());
+ return newEvent;
+ }
+
+ /**
+ * Returns required height of the popup such that number of items visible in
+ * it are equal to the maximum row count. By default
+ * comboBox.maximumRowCount=8
+ *
+ * @param maxRowCount number of maximum visible rows in the combo box's
+ * popup list of items
+ *
+ * @return height of the popup required to fit number of items equal to
+ * JComboBox.maximumRowCount.
+ */
+ protected int getPopupHeightForRowCount(int maxRowCount)
+ {
+ int totalHeight = 0;
+ ListCellRenderer rend = list.getCellRenderer();
+
+ if (comboBox.getItemCount() < maxRowCount)
+ maxRowCount = comboBox.getItemCount();
+
+ for (int i = 0; i < maxRowCount; i++)
+ {
+ Component comp = rend.getListCellRendererComponent(list,
+ comboBox.getModel()
+ .getElementAt(i),
+ -1, false, false);
+ Dimension dim = comp.getPreferredSize();
+ totalHeight += dim.height;
+ }
+
+ return totalHeight == 0 ? 100 : totalHeight;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param px DOCUMENT ME!
+ * @param py DOCUMENT ME!
+ * @param pw DOCUMENT ME!
+ * @param ph DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected Rectangle computePopupBounds(int px, int py, int pw, int ph)
+ {
+ return new Rectangle(px, py, pw, ph);
+ }
+
+ /**
+ * This method changes the selection in the list to the item over which the
+ * mouse is currently located.
+ *
+ * @param anEvent MouseEvent
+ * @param shouldScroll DOCUMENT ME!
+ */
+ protected void updateListBoxSelectionForEvent(MouseEvent anEvent,
+ boolean shouldScroll)
+ {
+ Point point = anEvent.getPoint();
+ if (list != null)
+ {
+ int index = list.locationToIndex(point);
+ if (index == -1)
+ {
+ if (point.y < 0)
+ index = 0;
+ else
+ index = comboBox.getModel().getSize() - 1;
+ }
+ if (list.getSelectedIndex() != index)
+ {
+ list.setSelectedIndex(index);
+ if (shouldScroll)
+ list.ensureIndexIsVisible(index);
+ }
+ }
+ }
+
+ /**
+ * InvocationMouseHandler is a listener that listens to mouse events
+ * occuring in the combo box. Note that this listener doesn't listen to
+ * mouse events occuring in the popup portion of the combo box, it only
+ * listens to main combo box part(area that displays selected item). This
+ * listener is responsible for showing and hiding popup portion of the
+ * combo box.
+ */
+ protected class InvocationMouseHandler extends MouseAdapter
+ {
+ /**
+ * Creates a new InvocationMouseHandler object.
+ */
+ protected InvocationMouseHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked whenever mouse is being pressed over the main
+ * part of the combo box. This method will show popup if the popup is
+ * not shown on the screen right now, and it will hide popup otherwise.
+ *
+ * @param e MouseEvent that should be handled
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (SwingUtilities.isLeftMouseButton(e) && comboBox.isEnabled())
+ {
+ delegateFocus(e);
+ togglePopup();
+ }
+ }
+
+ /**
+ * This method is invoked whenever mouse event was originated in the combo
+ * box and released either in the combBox list of items or in the combo
+ * box itself.
+ *
+ * @param e MouseEvent that should be handled
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ Component component = (Component) e.getSource();
+ Dimension size = component.getSize();
+ Rectangle bounds = new Rectangle(0, 0, size.width - 1, size.height - 1);
+ // If mouse was released inside the bounds of combo box then do nothing,
+ // Otherwise if mouse was released inside the list of combo box items
+ // then change selection and close popup
+ if (! bounds.contains(e.getPoint()))
+ {
+ MouseEvent convEvent = convertMouseEvent(e);
+ Point point = convEvent.getPoint();
+ Rectangle visRect = new Rectangle();
+ list.computeVisibleRect(visRect);
+ if (visRect.contains(point))
+ {
+ updateListBoxSelectionForEvent(convEvent, false);
+ comboBox.setSelectedIndex(list.getSelectedIndex());
+ }
+ hide();
+ }
+ hasEntered = false;
+ stopAutoScrolling();
+ }
+ }
+
+ /**
+ * InvocationMouseMotionListener is a mouse listener that listens to mouse
+ * dragging events occuring in the combo box.
+ */
+ protected class InvocationMouseMotionHandler extends MouseMotionAdapter
+ {
+ /**
+ * Creates a new InvocationMouseMotionHandler object.
+ */
+ protected InvocationMouseMotionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is responsible for highlighting item in the drop down list
+ * over which the mouse is currently being dragged.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ if (isVisible())
+ {
+ MouseEvent convEvent = convertMouseEvent(e);
+ Rectangle visRect = new Rectangle();
+ list.computeVisibleRect(visRect);
+ if (convEvent.getPoint().y >= visRect.y
+ && (convEvent.getPoint().y <= visRect.y + visRect.height - 1))
+ {
+ hasEntered = true;
+ if (isAutoScrolling)
+ stopAutoScrolling();
+ Point point = convEvent.getPoint();
+ if (visRect.contains(point))
+ {
+ valueIsAdjusting = true;
+ updateListBoxSelectionForEvent(convEvent, false);
+ valueIsAdjusting = false;
+ }
+ }
+ else if (hasEntered)
+ {
+ int dir = convEvent.getPoint().y < visRect.y ? SCROLL_UP
+ : SCROLL_DOWN;
+ if (isAutoScrolling && scrollDirection != dir)
+ {
+ stopAutoScrolling();
+ startAutoScrolling(dir);
+ }
+ else if (!isAutoScrolling)
+ startAutoScrolling(dir);
+ }
+ else if (e.getPoint().y < 0)
+ {
+ hasEntered = true;
+ startAutoScrolling(SCROLL_UP);
+ }
+ }
+ }
+ }
+
+ /**
+ * ItemHandler is an item listener that listens to selection events occuring
+ * in the combo box. FIXME: should specify here what it does when item is
+ * selected or deselected in the combo box list.
+ */
+ protected class ItemHandler extends Object implements ItemListener
+ {
+ /**
+ * Creates a new ItemHandler object.
+ */
+ protected ItemHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method responds to the selection events occuring in the combo box.
+ *
+ * @param e ItemEvent specifying the combo box's selection
+ */
+ public void itemStateChanged(ItemEvent e)
+ {
+ if (e.getStateChange() == ItemEvent.SELECTED && ! valueIsAdjusting)
+ {
+ valueIsAdjusting = true;
+ syncListSelection();
+ valueIsAdjusting = false;
+ list.ensureIndexIsVisible(comboBox.getSelectedIndex());
+ }
+ }
+ }
+
+ /**
+ * ListMouseHandler is a listener that listens to mouse events occuring in
+ * the combo box's list of items. This class is responsible for hiding
+ * popup portion of the combo box if the mouse is released inside the combo
+ * box's list.
+ */
+ protected class ListMouseHandler extends MouseAdapter
+ {
+ protected ListMouseHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void mousePressed(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseReleased(MouseEvent anEvent)
+ {
+ comboBox.setSelectedIndex(list.getSelectedIndex());
+ hide();
+ }
+ }
+
+ /**
+ * ListMouseMotionHandler listens to mouse motion events occuring in the
+ * combo box's list. This class is responsible for highlighting items in
+ * the list when mouse is moved over them
+ */
+ protected class ListMouseMotionHandler extends MouseMotionAdapter
+ {
+ protected ListMouseMotionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseMoved(MouseEvent anEvent)
+ {
+ Point point = anEvent.getPoint();
+ Rectangle visRect = new Rectangle();
+ list.computeVisibleRect(visRect);
+ if (visRect.contains(point))
+ {
+ valueIsAdjusting = true;
+ updateListBoxSelectionForEvent(anEvent, false);
+ valueIsAdjusting = false;
+ }
+ }
+ }
+
+ /**
+ * This class listens to changes occuring in the bound properties of the
+ * combo box
+ */
+ protected class PropertyChangeHandler extends Object
+ implements PropertyChangeListener
+ {
+ protected PropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("renderer"))
+ {
+ list.setCellRenderer(comboBox.getRenderer());
+ if (isVisible())
+ hide();
+ }
+ if (e.getPropertyName().equals("model"))
+ {
+ ComboBoxModel oldModel = (ComboBoxModel) e.getOldValue();
+ uninstallComboBoxModelListeners(oldModel);
+ ComboBoxModel newModel = (ComboBoxModel) e.getNewValue();
+ list.setModel(newModel);
+ installComboBoxModelListeners(newModel);
+ if (comboBox.getItemCount() > 0)
+ comboBox.setSelectedIndex(0);
+ if (isVisible())
+ hide();
+ }
+ }
+ }
+
+ // ------ private helper methods --------------------
+
+ /**
+ * This method uninstalls Listeners registered with combo boxes list of
+ * items
+ */
+ private void uninstallListListeners()
+ {
+ list.removeMouseListener(listMouseListener);
+ listMouseListener = null;
+
+ list.removeMouseMotionListener(listMouseMotionListener);
+ listMouseMotionListener = null;
+ }
+
+ void syncListSelection()
+ {
+ int index = comboBox.getSelectedIndex();
+ if (index == -1)
+ list.clearSelection();
+ else
+ list.setSelectedIndex(index);
+ }
+
+ // --------------------------------------------------------------------
+ // The following classes are here only for backwards API compatibility
+ // They aren't used.
+ // --------------------------------------------------------------------
+
+ /**
+ * This class is not used any more.
+ */
+ public class ListDataHandler extends Object implements ListDataListener
+ {
+ public ListDataHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void contentsChanged(ListDataEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void intervalAdded(ListDataEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void intervalRemoved(ListDataEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This class is not used anymore
+ */
+ protected class ListSelectionHandler extends Object
+ implements ListSelectionListener
+ {
+ protected ListSelectionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void valueChanged(ListSelectionEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This class is not used anymore
+ */
+ public class InvocationKeyHandler extends KeyAdapter
+ {
+ public InvocationKeyHandler()
+ {
+ // Nothing to do here.
+ }
+
+ public void keyReleased(KeyEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java
new file mode 100644
index 000000000..e52293812
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopIconUI.java
@@ -0,0 +1,592 @@
+/* BasicDesktopIconUI.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JInternalFrame;
+import javax.swing.JInternalFrame.JDesktopIcon;
+import javax.swing.SwingConstants;
+import javax.swing.border.Border;
+import javax.swing.event.MouseInputAdapter;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.DesktopIconUI;
+
+/**
+ * This class acts as the UI delegate for JDesktopIcons for the Basic look and feel.
+ */
+public class BasicDesktopIconUI extends DesktopIconUI
+{
+ /**
+ * This helper class handles mouse events that occur on the JDesktopIcon.
+ */
+ public class MouseInputHandler extends MouseInputAdapter
+ {
+ /** The x offset from the MouseEvent coordinates to the top left corner. */
+ private transient int xOffset;
+
+ /** The y offset fromt he MouseEvent coordinates to the top left corner. */
+ private transient int yOffset;
+
+ /** A cached value of the JDesktopPane that parents this JDesktopIcon. */
+ private transient JDesktopPane pane;
+
+ /**
+ * This method is called when the mouse is dragged in the JDesktopIcon.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ Rectangle b = desktopIcon.getBounds();
+
+ moveAndRepaint(desktopIcon, b.x + e.getX() - xOffset,
+ b.y + e.getY() - yOffset, b.width, b.height);
+ }
+
+ /**
+ * This method is called when the mouse is moved in the JDesktopIcon.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * This method is called when the mouse is pressed in the JDesktopIcon.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ xOffset = e.getX();
+ yOffset = e.getY();
+ pane = frame.getDesktopPane();
+ if (pane != null)
+ pane.getDesktopManager().beginDraggingFrame(desktopIcon);
+ }
+
+ /**
+ * This method is called when the mouse is released in the JDesktopIcon.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (pane != null)
+ pane.getDesktopManager().endDraggingFrame(desktopIcon);
+ xOffset = 0;
+ yOffset = 0;
+ }
+
+ /**
+ * This method moves and repaints the JDesktopIcon to the given bounds.
+ *
+ * @param f The JComponent to move and repaint.
+ * @param newX The new x coordinate.
+ * @param newY The new y coordinate.
+ * @param newWidth The new width.
+ * @param newHeight The new height.
+ */
+ public void moveAndRepaint(JComponent f, int newX, int newY, int newWidth,
+ int newHeight)
+ {
+ if (pane != null)
+ pane.getDesktopManager().dragFrame(f, newX, newY);
+ else
+ desktopIcon.setBounds(newX, newY, newWidth, newHeight);
+ }
+ }
+
+ /**
+ * This class acts as the border for the JDesktopIcon.
+ */
+ private class DesktopIconBorder implements Border
+ {
+ /** The left inset value. */
+ int left = 10;
+
+ /** The top inset value. */
+ int top = 4;
+
+ /** The right inset value. */
+ int right = top;
+
+ /** The bottom inset value. */
+ int bottom = top;
+
+ /**
+ * This method returns the insets of the border.
+ *
+ * @param c The Component to find border insets for.
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(top, left, bottom, right);
+ }
+
+ /**
+ * This method returns whether the border is opaque.
+ *
+ * @return Whether the border is opaque.
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * This method paints the border.
+ *
+ * @param c The Component the border is in.
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate of the Component.
+ * @param y The y coordinate of the Component.
+ * @param width The width of the Component.
+ * @param height The height of the Component.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+
+ g.setColor(Color.LIGHT_GRAY);
+
+ g.fillRect(0, 0, left, height);
+ g.fillRect(0, 0, width, top);
+ g.fillRect(0, height - bottom, width, bottom);
+ g.fillRect(width - right, 0, right, height);
+
+ g.setColor(Color.BLACK);
+ g.drawRect(0, 0, width - 1, height - 1);
+
+ int fHeight = height / 4;
+ int hLeft = left / 2;
+
+ g.setColor(Color.BLACK);
+ g.fillRect(hLeft, fHeight, 2, 2);
+ g.fillRect(hLeft, fHeight * 2, 2, 2);
+ g.fillRect(hLeft, fHeight * 3, 2, 2);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ }
+
+ /** The static width and height of the iconSize. */
+ private static final int iconSize = 16;
+
+ /**
+ * This class represents the default frame icon when none
+ * is supplied by the JInternalFrame.
+ */
+ static class InternalFrameDefaultMenuIcon implements Icon
+ {
+ /**
+ * This returns the icon height.
+ *
+ * @return The icon height.
+ */
+ public int getIconHeight()
+ {
+ return iconSize;
+ }
+
+ /**
+ * This returns the icon width.
+ *
+ * @return The icon width.
+ */
+ public int getIconWidth()
+ {
+ return iconSize;
+ }
+
+ /**
+ * This method paints the icon.
+ *
+ * @param c The Component this icon belongs to.
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate to paint at.
+ * @param y The y coordinate to paint at.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+
+ g.setColor(Color.BLUE);
+ g.fillRect(0, 0, iconSize, (int) ((double) iconSize / 3) + 1);
+
+ g.setColor(Color.WHITE);
+ g.fillRect(0, (int) ((double) iconSize / 3), iconSize, iconSize * 5 / 6);
+
+ g.setColor(Color.GRAY);
+ g.drawRect(0, 0, iconSize, iconSize);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ }
+
+ /** The default JDesktopIcon width. */
+ private static final int iconWidth = 160;
+
+ /** The default JDesktopIcon height */
+ private static final int iconHeight = 35;
+
+ /** The JDesktopIcon this UI delegate represents. */
+ protected JDesktopIcon desktopIcon;
+
+ /** The JInternalFrame associated with the JDesktopIcon. */
+ protected JInternalFrame frame;
+
+ /** The MouseListener responsible for reacting to MouseEvents on the JDesktopIcon. */
+ private transient MouseInputListener mouseHandler;
+
+ /** The Button in the JDesktopIcon responsible for deiconifying it.
+ * This is package-private to avoid an accessor method. */
+ transient BoundButton button;
+
+ /** The PropertyChangeListener listening to the JDesktopIcon. */
+ private transient PropertyChangeListener propertyHandler;
+
+ /** The default icon used when no frame icon is given by the JInternalFrame. */
+ static Icon defaultIcon = new InternalFrameDefaultMenuIcon();
+
+ /**
+ * This is a helper class that is used in JDesktopIcon and gives the Button a predetermined size.
+ */
+ private class BoundButton extends JButton
+ {
+ /**
+ * Creates a new BoundButton object.
+ *
+ * @param title The title of the button.
+ */
+ public BoundButton(String title)
+ {
+ super(title);
+ }
+
+ /**
+ * This method returns a standard size (based on the defaults of the JDesktopIcon) and the insets.
+ *
+ * @return The preferred size of the JDesktopIcon.
+ */
+ public Dimension getPreferredSize()
+ {
+ Insets insets = desktopIcon.getInsets();
+ return new Dimension(iconWidth - insets.left - insets.right,
+ iconHeight - insets.top - insets.bottom);
+ }
+
+ /**
+ * This method returns the minimum size of the button.
+ *
+ * @return The minimum size of the button.
+ */
+ public Dimension getMinimumSize()
+ {
+ return getPreferredSize();
+ }
+
+ /**
+ * This method returns the maximum size of the button.
+ *
+ * @return The maximum size of the button.
+ */
+ public Dimension getMaximumSize()
+ {
+ return getPreferredSize();
+ }
+ }
+
+ /**
+ * Creates a new BasicDesktopIconUI object.
+ */
+ public BasicDesktopIconUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method creates a new BasicDesktopIconUI for the given JComponent.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicDesktopIconUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicDesktopIconUI();
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install this UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JDesktopIcon)
+ {
+ desktopIcon = (JDesktopIcon) c;
+ desktopIcon.setLayout(new BorderLayout());
+ frame = desktopIcon.getInternalFrame();
+
+ installDefaults();
+ installComponents();
+ installListeners();
+
+ desktopIcon.setOpaque(true);
+ }
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall this UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ desktopIcon.setOpaque(false);
+
+ uninstallListeners();
+ uninstallComponents();
+ uninstallDefaults();
+
+ frame = null;
+ desktopIcon.setLayout(null);
+ desktopIcon = null;
+ }
+
+ /**
+ * This method installs the necessary sub components for the JDesktopIcon.
+ */
+ protected void installComponents()
+ {
+ // Try to create a button based on what the frame's
+ // state is currently
+ button = new BoundButton(frame.getTitle());
+ button.setHorizontalAlignment(SwingConstants.LEFT);
+ button.setHorizontalTextPosition(SwingConstants.TRAILING);
+
+ Icon use = frame.getFrameIcon();
+ if (use == null)
+ use = defaultIcon;
+ button.setIcon(use);
+
+ desktopIcon.add(button, SwingConstants.CENTER);
+ }
+
+ /**
+ * This method uninstalls the sub components for the JDesktopIcon.
+ */
+ protected void uninstallComponents()
+ {
+ desktopIcon.remove(button);
+
+ button = null;
+ }
+
+ /**
+ * This method installs the listeners needed by this UI.
+ */
+ protected void installListeners()
+ {
+ mouseHandler = createMouseInputListener();
+
+ desktopIcon.addMouseMotionListener(mouseHandler);
+ desktopIcon.addMouseListener(mouseHandler);
+
+ propertyHandler = new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(JInternalFrame.TITLE_PROPERTY))
+ button.setText(desktopIcon.getInternalFrame().getTitle());
+ else if (e.getPropertyName().equals(JInternalFrame.FRAME_ICON_PROPERTY))
+ {
+ Icon use = desktopIcon.getInternalFrame().getFrameIcon();
+ if (use == null)
+ use = defaultIcon;
+ button.setIcon(use);
+ }
+ desktopIcon.revalidate();
+ desktopIcon.repaint();
+ }
+ };
+ frame.addPropertyChangeListener(propertyHandler);
+
+ button.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ deiconize();
+ }
+ });
+ }
+
+ /**
+ * This method uninstalls the listeners needed by the UI.
+ */
+ protected void uninstallListeners()
+ {
+ // button is nulled so no need to remove it.
+
+ frame.removePropertyChangeListener(propertyHandler);
+ propertyHandler = null;
+
+ desktopIcon.removeMouseMotionListener(mouseHandler);
+ desktopIcon.removeMouseListener(mouseHandler);
+ }
+
+ /**
+ * This method installs the defaults for the JDesktopIcon.
+ */
+ protected void installDefaults()
+ {
+ // FIXME: Move border to defaults.
+ desktopIcon.setBorder(new DesktopIconBorder());
+ }
+
+ /**
+ * This method uninstalls the defaults for the JDesktopIcon.
+ */
+ protected void uninstallDefaults()
+ {
+ desktopIcon.setBorder(null);
+ }
+
+ /**
+ * This method creates a new MouseInputListener for the JDesktopIcon.
+ *
+ * @return A new MouseInputListener.
+ */
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * This method returns the preferred size for the given JComponent.
+ *
+ * @param c The JComponent to find a preferred size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return new Dimension(iconWidth, iconHeight);
+ }
+
+ /**
+ * This method returns the minimum size for the given JComponent.
+ *
+ * @param c The JComponent to find a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the maximum size for the given JComponent.
+ *
+ * @param c The JComponent to find a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the insets of the given JComponent.
+ *
+ * @param c The JComponent to find insets for.
+ *
+ * @return The insets of the given JComponent.
+ */
+ public Insets getInsets(JComponent c)
+ {
+ return c.getInsets();
+ }
+
+ /**
+ * This method deiconizes the JInternalFrame associated with the JDesktopIcon.
+ */
+ public void deiconize()
+ {
+ try
+ {
+ frame.setIcon(false);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java
new file mode 100644
index 000000000..cbc3f9f89
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicDesktopPaneUI.java
@@ -0,0 +1,470 @@
+/* BasicDesktopPaneUI.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.beans.PropertyVetoException;
+
+import javax.swing.AbstractAction;
+import javax.swing.DefaultDesktopManager;
+import javax.swing.DesktopManager;
+import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JInternalFrame;
+import javax.swing.KeyStroke;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.DesktopPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * This class is the UI delegate for JDesktopPane for the Basic look and feel.
+ */
+public class BasicDesktopPaneUI extends DesktopPaneUI
+{
+ /**
+ * This helper class is used to handle key events that cause JInternalFrames
+ * to be closed.
+ */
+ protected class CloseAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (desktop.getSelectedFrame() != null)
+ {
+ try
+ {
+ desktop.getSelectedFrame().setClosed(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempts has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ if (desktop.getSelectedFrame() != null)
+ return desktop.getSelectedFrame().isClosable();
+ return false;
+ }
+ }
+
+ /**
+ * This helper class is used to handle key events that cause JInternalFrames
+ * to be maximized.
+ */
+ protected class MaximizeAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (desktop.getSelectedFrame() != null)
+ {
+ try
+ {
+ desktop.getSelectedFrame().setMaximum(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempts has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ if (desktop.getSelectedFrame() != null)
+ return desktop.getSelectedFrame().isMaximizable();
+ return false;
+ }
+ }
+
+ /**
+ * This helper class is used to handle key events that cause JInternalFrames
+ * to be minimized.
+ */
+ protected class MinimizeAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (desktop.getSelectedFrame() != null)
+ {
+ try
+ {
+ desktop.getSelectedFrame().setIcon(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ if (desktop.getSelectedFrame() != null)
+ return desktop.getSelectedFrame().isIconifiable();
+ return false;
+ }
+ }
+
+ /**
+ * This helper class is used to handle key events that pass the SELECTED
+ * property to the next JInternalFrame in the JDesktopPane's list of
+ * children.
+ */
+ protected class NavigateAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // This is supposed to set the next selected frame.
+ JInternalFrame[] frames = desktop.getAllFrames();
+ if (frames.length == 0)
+ return;
+
+ JInternalFrame sFrame = frames[0];
+ if (desktop.getSelectedFrame() != null)
+ sFrame = desktop.getSelectedFrame();
+
+ int i = 0;
+ for (; i < frames.length; i++)
+ if (frames[i] == sFrame)
+ break;
+
+ // FIXME: Navigate actions go reverse too.
+ if (i == frames.length)
+ i = 0;
+
+ desktop.setSelectedFrame(frames[i]);
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether this action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ // Always true.
+ return true;
+ }
+ }
+
+ /**
+ * This helper class is used to restore the JInternalFrame to its original
+ * size before maximizing or iconifying.
+ */
+ protected class OpenAction extends AbstractAction
+ {
+ /**
+ * This method is called when the action is performed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JInternalFrame frame = desktop.getSelectedFrame();
+ if (frame != null)
+ {
+ try
+ {
+ if (frame.isIcon())
+ frame.setIcon(false);
+ else if (frame.isMaximum())
+ frame.setMaximum(false);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method returns whether the action is enabled.
+ *
+ * @return Whether this action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ // JInternalFrames are always restorable.
+ return true;
+ }
+ }
+
+ /**
+ * The KeyStroke associated with closing JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke closeKey;
+
+ /**
+ * The KeyStroke associated with maximizing JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke maximizeKey;
+
+ /**
+ * The KeyStroke associated with minimizing JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke minimizeKey;
+
+ /**
+ * The KeyStroke associated with navigating (forward?) through
+ * JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke navigateKey;
+
+ /**
+ * The KeyStroke associated with navigating (backward?) through
+ * JInternalFrames.
+ * @deprecated
+ */
+ protected KeyStroke navigateKey2;
+
+ /** The default desktop manager used with JDesktopPane. */
+ protected DesktopManager desktopManager;
+
+ /** The JDesktopPane this UI is used with. */
+ protected JDesktopPane desktop;
+
+ /**
+ * Creates a new BasicDesktopPaneUI object.
+ */
+ public BasicDesktopPaneUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method creates a BasicDesktopPaneUI for the given JComponent.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicDesktopPaneUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicDesktopPaneUI();
+ }
+
+ /**
+ * This method returns the maximum size for the given JComponent.
+ *
+ * @param c The JComponent to find a maximum size for.
+ *
+ * @return The maximum size for the given JComponent.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the minimum size for the given JComponent.
+ *
+ * @param c The JComponent to find a minimum size for.
+ *
+ * @return The minimum size for the given JComponent.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the preferred size for the given JComponent.
+ *
+ * @param c The JComponent to find a preferred size for.
+ *
+ * @return The preferred size for the given JComponent.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ // return null because JDesktopPanes don't have preferred sizes.
+ return null;
+ }
+
+ /**
+ * This method installs the defaults for the JDesktopPane provided by the
+ * current look and feel.
+ */
+ protected void installDefaults()
+ {
+ Color bg = desktop.getBackground();
+ if (bg == null || bg instanceof UIResource)
+ desktop.setBackground(UIManager.getColor("desktop"));
+ }
+
+ /**
+ * This method installs the desktop manager for the JDesktopPane.
+ */
+ protected void installDesktopManager()
+ {
+ desktopManager = new DefaultDesktopManager();
+ desktop.setDesktopManager(desktopManager);
+ }
+
+ /**
+ * This method installs the keyboard actions for the JDesktopPane.
+ */
+ protected void installKeyboardActions()
+ {
+ // FIXME: create actions and keystrokes.
+ registerKeyboardActions();
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install this UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JDesktopPane)
+ {
+ desktop = (JDesktopPane) c;
+
+ installDefaults();
+ installDesktopManager();
+ installKeyboardActions();
+ }
+ }
+
+ /**
+ * This method registers the actions to the appropriate Action and Input
+ * maps.
+ */
+ protected void registerKeyboardActions()
+ {
+ // FIXME: Do the binding.
+ // XXX: the gtk windows tend to intercept a lot of the
+ // key events for themselves. must figure a way past that
+ // before binding
+ }
+
+ /**
+ * This method reverses the work done by the installDefaults method.
+ */
+ protected void uninstallDefaults()
+ {
+ desktop.setBackground(null);
+ }
+
+ /**
+ * This method reverses the work done by the installDesktopManager method.
+ */
+ protected void uninstallDesktopManager()
+ {
+ desktopManager = null;
+ desktop.setDesktopManager(null);
+ }
+
+ /**
+ * This method reverses the work done by the installKeyboardActions method.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ unregisterKeyboardActions();
+ // FIXME: null the actions and keystrokes.
+ }
+
+ /**
+ * This method reverses the work done by the registerKeyboardActions method.
+ */
+ protected void unregisterKeyboardActions()
+ {
+ // FIXME: unmap the keystrokes
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent. It should reverse
+ * all the work done by the installUI method.
+ *
+ * @param c The JComponent to uninstall this UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallDesktopManager();
+ uninstallDefaults();
+
+ desktop = null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java
new file mode 100644
index 000000000..62657ad86
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicDirectoryModel.java
@@ -0,0 +1,586 @@
+/* BasicDirectoryModel.java --
+ Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing.plaf.basic;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+import javax.swing.AbstractListModel;
+import javax.swing.JFileChooser;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ListDataEvent;
+import javax.swing.filechooser.FileSystemView;
+
+
+/**
+ * Implements an AbstractListModel for directories where the source
+ * of the files is a JFileChooser object.
+ *
+ * This class is used for sorting and ordering the file list in
+ * a JFileChooser L&F object.
+ */
+public class BasicDirectoryModel extends AbstractListModel
+ implements PropertyChangeListener
+{
+ /** The list of files itself */
+ private Vector contents;
+
+ /**
+ * The directories in the list.
+ */
+ private Vector directories;
+
+ /**
+ * The files in the list.
+ */
+ private Vector files;
+
+ /** The listing mode of the associated JFileChooser,
+ either FILES_ONLY, DIRECTORIES_ONLY or FILES_AND_DIRECTORIES */
+ private int listingMode;
+
+ /** The JFileCooser associated with this model */
+ private JFileChooser filechooser;
+
+ /**
+ * The thread that loads the file view.
+ */
+ private DirectoryLoadThread loadThread;
+
+ /**
+ * This thread is responsible for loading file lists from the
+ * current directory and updating the model.
+ */
+ private class DirectoryLoadThread extends Thread
+ {
+
+ /**
+ * Updates the Swing list model.
+ */
+ private class UpdateSwingRequest
+ implements Runnable
+ {
+
+ private List added;
+ private int addIndex;
+ private List removed;
+ private int removeIndex;
+ private boolean cancel;
+
+ UpdateSwingRequest(List add, int ai, List rem, int ri)
+ {
+ added = add;
+ addIndex = ai;
+ removed = rem;
+ removeIndex = ri;
+ cancel = false;
+ }
+
+ public void run()
+ {
+ if (! cancel)
+ {
+ int numRemoved = removed == null ? 0 : removed.size();
+ int numAdded = added == null ? 0 : added.size();
+ synchronized (contents)
+ {
+ if (numRemoved > 0)
+ contents.removeAll(removed);
+ if (numAdded > 0)
+ contents.addAll(added);
+
+ files = null;
+ directories = null;
+ }
+ if (numRemoved > 0 && numAdded == 0)
+ fireIntervalRemoved(BasicDirectoryModel.this, removeIndex,
+ removeIndex + numRemoved - 1);
+ else if (numRemoved == 0 && numAdded > 0)
+ fireIntervalAdded(BasicDirectoryModel.this, addIndex,
+ addIndex + numAdded - 1);
+ else
+ fireContentsChanged();
+ }
+ }
+
+ void cancel()
+ {
+ cancel = true;
+ }
+ }
+
+ /**
+ * The directory beeing loaded.
+ */
+ File directory;
+
+ /**
+ * Stores all UpdateSwingRequests that are sent to the event queue.
+ */
+ private UpdateSwingRequest pending;
+
+ /**
+ * Creates a new DirectoryLoadThread that loads the specified
+ * directory.
+ *
+ * @param dir the directory to load
+ */
+ DirectoryLoadThread(File dir)
+ {
+ super("Basic L&F directory loader");
+ directory = dir;
+ }
+
+ public void run()
+ {
+ FileSystemView fsv = filechooser.getFileSystemView();
+ File[] files = fsv.getFiles(directory,
+ filechooser.isFileHidingEnabled());
+
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ // Check list for accepted files.
+ Vector accepted = new Vector();
+ for (int i = 0; i < files.length; i++)
+ {
+ if (filechooser.accept(files[i]))
+ accepted.add(files[i]);
+ }
+
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ // Sort list.
+ sort(accepted);
+
+ // Now split up directories from files so that we get the directories
+ // listed before the files.
+ Vector newFiles = new Vector();
+ Vector newDirectories = new Vector();
+ for (Iterator i = accepted.iterator(); i.hasNext();)
+ {
+ File f = (File) i.next();
+ boolean traversable = filechooser.isTraversable(f);
+ if (traversable)
+ newDirectories.add(f);
+ else if (! traversable && filechooser.isFileSelectionEnabled())
+ newFiles.add(f);
+
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ }
+
+ // Build up new file cache. Try to update only the changed elements.
+ // This will be important for actions like adding new files or
+ // directories inside a large file list.
+ Vector newCache = new Vector(newDirectories);
+ newCache.addAll(newFiles);
+
+ int newSize = newCache.size();
+ int oldSize = contents.size();
+ if (newSize < oldSize)
+ {
+ // Check for removed interval.
+ int start = -1;
+ int end = -1;
+ boolean found = false;
+ for (int i = 0; i < newSize && !found; i++)
+ {
+ if (! newCache.get(i).equals(contents.get(i)))
+ {
+ start = i;
+ end = i + oldSize - newSize;
+ found = true;
+ }
+ }
+ if (start >= 0 && end > start
+ && contents.subList(end, oldSize)
+ .equals(newCache.subList(start, newSize)))
+ {
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ Vector removed = new Vector(contents.subList(start, end));
+ UpdateSwingRequest r = new UpdateSwingRequest(null, 0,
+ removed, start);
+ invokeLater(r);
+ newCache = null;
+ }
+ }
+ else if (newSize > oldSize)
+ {
+ // Check for inserted interval.
+ int start = oldSize;
+ int end = newSize;
+ boolean found = false;
+ for (int i = 0; i < oldSize && ! found; i++)
+ {
+ if (! newCache.get(i).equals(contents.get(i)))
+ {
+ start = i;
+ boolean foundEnd = false;
+ for (int j = i; j < newSize && ! foundEnd; j++)
+ {
+ if (newCache.get(j).equals(contents.get(i)))
+ {
+ end = j;
+ foundEnd = true;
+ }
+ }
+ end = i + oldSize - newSize;
+ }
+ }
+ if (start >= 0 && end > start
+ && newCache.subList(end, newSize)
+ .equals(contents.subList(start, oldSize)))
+ {
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+
+ List added = newCache.subList(start, end);
+ UpdateSwingRequest r = new UpdateSwingRequest(added, start,
+ null, 0);
+ invokeLater(r);
+ newCache = null;
+ }
+ }
+
+ // Handle complete list changes (newCache != null).
+ if (newCache != null && ! contents.equals(newCache))
+ {
+ // Occasional check if we have been interrupted.
+ if (isInterrupted())
+ return;
+ UpdateSwingRequest r = new UpdateSwingRequest(newCache, 0,
+ contents, 0);
+ invokeLater(r);
+ }
+ }
+
+ /**
+ * Wraps SwingUtilities.invokeLater() and stores the request in
+ * a Vector so that we can still cancel it later.
+ *
+ * @param update the request to invoke
+ */
+ private void invokeLater(UpdateSwingRequest update)
+ {
+ pending = update;
+ SwingUtilities.invokeLater(update);
+ }
+
+ /**
+ * Cancels all pending update requests that might be in the AWT
+ * event queue.
+ */
+ void cancelPending()
+ {
+ if (pending != null)
+ pending.cancel();
+ }
+ }
+
+ /** A Comparator class/object for sorting the file list. */
+ private Comparator comparator = new Comparator()
+ {
+ public int compare(Object o1, Object o2)
+ {
+ if (lt((File) o1, (File) o2))
+ return -1;
+ else
+ return 1;
+ }
+ };
+
+ /**
+ * Creates a new BasicDirectoryModel object.
+ *
+ * @param filechooser DOCUMENT ME!
+ */
+ public BasicDirectoryModel(JFileChooser filechooser)
+ {
+ this.filechooser = filechooser;
+ filechooser.addPropertyChangeListener(this);
+ listingMode = filechooser.getFileSelectionMode();
+ contents = new Vector();
+ validateFileCache();
+ }
+
+ /**
+ * Returns whether a given (File) object is included in the list.
+ *
+ * @param o - The file object to test.
+ *
+ * @return <code>true</code> if the list contains the given object.
+ */
+ public boolean contains(Object o)
+ {
+ return contents.contains(o);
+ }
+
+ /**
+ * Fires a content change event.
+ */
+ public void fireContentsChanged()
+ {
+ fireContentsChanged(this, 0, getSize() - 1);
+ }
+
+ /**
+ * Returns a Vector of (java.io.File) objects containing
+ * the directories in this list.
+ *
+ * @return a Vector
+ */
+ public Vector<File> getDirectories()
+ {
+ // Synchronize this with the UpdateSwingRequest for the case when
+ // contents is modified.
+ synchronized (contents)
+ {
+ Vector dirs = directories;
+ if (dirs == null)
+ {
+ // Initializes this in getFiles().
+ getFiles();
+ dirs = directories;
+ }
+ return dirs;
+ }
+ }
+
+ /**
+ * Returns the (java.io.File) object at
+ * an index in the list.
+ *
+ * @param index The list index
+ * @return a File object
+ */
+ public Object getElementAt(int index)
+ {
+ if (index > getSize() - 1)
+ return null;
+ return contents.elementAt(index);
+ }
+
+ /**
+ * Returns a Vector of (java.io.File) objects containing
+ * the files in this list.
+ *
+ * @return a Vector
+ */
+ public Vector<File> getFiles()
+ {
+ synchronized (contents)
+ {
+ Vector f = files;
+ if (f == null)
+ {
+ f = new Vector();
+ Vector d = new Vector(); // Directories;
+ for (Iterator i = contents.iterator(); i.hasNext();)
+ {
+ File file = (File) i.next();
+ if (filechooser.isTraversable(file))
+ d.add(file);
+ else
+ f.add(file);
+ }
+ files = f;
+ directories = d;
+ }
+ return f;
+ }
+ }
+
+ /**
+ * Returns the size of the list, which only includes directories
+ * if the JFileChooser is set to DIRECTORIES_ONLY.
+ *
+ * Otherwise, both directories and files are included in the count.
+ *
+ * @return The size of the list.
+ */
+ public int getSize()
+ {
+ return contents.size();
+ }
+
+ /**
+ * Returns the index of an (java.io.File) object in the list.
+ *
+ * @param o The object - normally a File.
+ *
+ * @return the index of that object, or -1 if it is not in the list.
+ */
+ public int indexOf(Object o)
+ {
+ return contents.indexOf(o);
+ }
+
+ /**
+ * Obsoleted method which does nothing.
+ */
+ public void intervalAdded(ListDataEvent e)
+ {
+ // obsoleted
+ }
+
+ /**
+ * Obsoleted method which does nothing.
+ */
+ public void intervalRemoved(ListDataEvent e)
+ {
+ // obsoleted
+ }
+
+ /**
+ * Obsoleted method which does nothing.
+ */
+ public void invalidateFileCache()
+ {
+ // obsoleted
+ }
+
+ /**
+ * Less than, determine the relative order in the list of two files
+ * for sorting purposes.
+ *
+ * The order is: directories < files, and thereafter alphabetically,
+ * using the default locale collation.
+ *
+ * @param a the first file
+ * @param b the second file
+ *
+ * @return <code>true</code> if a > b, <code>false</code> if a < b.
+ */
+ protected boolean lt(File a, File b)
+ {
+ boolean aTrav = filechooser.isTraversable(a);
+ boolean bTrav = filechooser.isTraversable(b);
+
+ if (aTrav == bTrav)
+ {
+ String aname = a.getName().toLowerCase();
+ String bname = b.getName().toLowerCase();
+ return (aname.compareTo(bname) < 0) ? true : false;
+ }
+ else
+ {
+ if (aTrav)
+ return true;
+ else
+ return false;
+ }
+ }
+
+ /**
+ * Listens for a property change; the change in file selection mode of the
+ * associated JFileChooser. Reloads the file cache on that event.
+ *
+ * @param e - A PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String property = e.getPropertyName();
+ if (property.equals(JFileChooser.DIRECTORY_CHANGED_PROPERTY)
+ || property.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY)
+ || property.equals(JFileChooser.FILE_HIDING_CHANGED_PROPERTY)
+ || property.equals(JFileChooser.FILE_SELECTION_MODE_CHANGED_PROPERTY)
+ || property.equals(JFileChooser.FILE_VIEW_CHANGED_PROPERTY)
+ )
+ {
+ validateFileCache();
+ }
+ }
+
+ /**
+ * Renames a file - However, does <I>not</I> re-sort the list
+ * or replace the old file with the new one in the list.
+ *
+ * @param oldFile The old file
+ * @param newFile The new file name
+ *
+ * @return <code>true</code> if the rename succeeded
+ */
+ public boolean renameFile(File oldFile, File newFile)
+ {
+ return oldFile.renameTo( newFile );
+ }
+
+ /**
+ * Sorts a Vector of File objects.
+ *
+ * @param v The Vector to sort.
+ */
+ protected void sort(Vector<? extends File> v)
+ {
+ Collections.sort(v, comparator);
+ }
+
+ /**
+ * Re-loads the list of files
+ */
+ public void validateFileCache()
+ {
+ File dir = filechooser.getCurrentDirectory();
+ if (dir != null)
+ {
+ // Cancel all pending requests.
+ if (loadThread != null)
+ {
+ loadThread.interrupt();
+ loadThread.cancelPending();
+ }
+ loadThread = new DirectoryLoadThread(dir);
+ loadThread.start();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java
new file mode 100644
index 000000000..6ddd2513b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicEditorPaneUI.java
@@ -0,0 +1,96 @@
+/* BasicEditorPaneUI.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import javax.swing.JComponent;
+import javax.swing.JEditorPane;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.EditorKit;
+import javax.swing.text.JTextComponent;
+
+/**
+ * The UI class for {@link JEditorPane}s.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class BasicEditorPaneUI extends BasicTextUI
+{
+ /**
+ * Creates an instance of <code>BasicEditorPaneUI</code> for the text
+ * component <code>comp</code>.
+ *
+ * @param comp the component for which to create an UI
+ *
+ * @return the UI for <code>comp</code>
+ */
+ public static ComponentUI createUI(JComponent comp)
+ {
+ return new BasicEditorPaneUI();
+ }
+
+ /**
+ * Creates a new <code>BasicEditorPaneUI</code>
+ */
+ public BasicEditorPaneUI()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Returns the property prefix to be used by this UI class. This is
+ * <code>EditorPane</code> in this case.
+ *
+ * @return <code>EditorPane</code>
+ */
+ protected String getPropertyPrefix()
+ {
+ return "EditorPane";
+ }
+
+ /**
+ * Gets the EditorKit for the text component.
+ *
+ * @param textComponent the text component for which to fetch the editor kit
+ */
+ public EditorKit getEditorKit(JTextComponent textComponent)
+ {
+ return ((JEditorPane) textComponent).getEditorKit();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java
new file mode 100644
index 000000000..347686d6e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicFileChooserUI.java
@@ -0,0 +1,1437 @@
+/* BasicFileChooserUI.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing.plaf.basic;
+
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileSystemView;
+import javax.swing.filechooser.FileView;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.FileChooserUI;
+import javax.swing.plaf.metal.MetalIconFactory;
+
+
+/**
+ * A UI delegate for the {@link JFileChooser} component under the
+ * {@link BasicLookAndFeel}.
+ */
+public class BasicFileChooserUI extends FileChooserUI
+{
+ /**
+ * A file filter that accepts all files.
+ */
+ protected class AcceptAllFileFilter extends FileFilter
+ {
+ /**
+ * Creates a new instance.
+ */
+ public AcceptAllFileFilter()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns <code>true</code> always, as all files are accepted by this
+ * filter.
+ *
+ * @param f the file.
+ *
+ * @return Always <code>true</code>.
+ */
+ public boolean accept(File f)
+ {
+ return true;
+ }
+
+ /**
+ * Returns a description for this filter.
+ *
+ * @return A description for the file filter.
+ */
+ public String getDescription()
+ {
+ return acceptAllFileFilterText;
+ }
+ }
+
+ /**
+ * Handles a user action to approve the dialog selection.
+ *
+ * @see BasicFileChooserUI#getApproveSelectionAction()
+ */
+ protected class ApproveSelectionAction extends AbstractAction
+ {
+ /**
+ * Creates a new ApproveSelectionAction object.
+ */
+ protected ApproveSelectionAction()
+ {
+ super("approveSelection");
+ }
+
+ /**
+ * Sets the current selection and closes the dialog.
+ *
+ * @param e the action event.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ Object obj = null;
+ if (parentPath != null)
+ obj = new String(parentPath + getFileName());
+ else
+ obj = filechooser.getSelectedFile();
+ if (obj != null)
+ {
+ File f = filechooser.getFileSystemView().createFileObject(obj.toString());
+ File currSelected = filechooser.getSelectedFile();
+ if (filechooser.isTraversable(f))
+ {
+ filechooser.setCurrentDirectory(currSelected);
+ filechooser.rescanCurrentDirectory();
+ }
+ else
+ {
+ filechooser.approveSelection();
+ closeDialog();
+ }
+ }
+ else
+ {
+ File f = new File(filechooser.getCurrentDirectory(), getFileName());
+ if ( selectedDir != null )
+ f = selectedDir;
+ if (filechooser.isTraversable(f))
+ {
+ filechooser.setCurrentDirectory(f);
+ filechooser.rescanCurrentDirectory();
+ }
+ else
+ {
+ filechooser.setSelectedFile(f);
+ filechooser.approveSelection();
+ closeDialog();
+ }
+ }
+ }
+ }
+
+ /**
+ * Provides presentation information about files and directories.
+ */
+ protected class BasicFileView extends FileView
+ {
+ /** Storage for cached icons. */
+ protected Hashtable<File, Icon> iconCache = new Hashtable<File, Icon>();
+
+ /**
+ * Creates a new instance.
+ */
+ public BasicFileView()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds an icon to the cache, associating it with the given file/directory.
+ *
+ * @param f the file/directory.
+ * @param i the icon.
+ */
+ public void cacheIcon(File f, Icon i)
+ {
+ iconCache.put(f, i);
+ }
+
+ /**
+ * Clears the icon cache.
+ */
+ public void clearIconCache()
+ {
+ iconCache.clear();
+ }
+
+ /**
+ * Retrieves the icon associated with the specified file/directory, if
+ * there is one.
+ *
+ * @param f the file/directory.
+ *
+ * @return The cached icon (or <code>null</code>).
+ */
+ public Icon getCachedIcon(File f)
+ {
+ return (Icon) iconCache.get(f);
+ }
+
+ /**
+ * Returns a description of the given file/directory. In this
+ * implementation, the description is the same as the name returned by
+ * {@link #getName(File)}.
+ *
+ * @param f the file/directory.
+ *
+ * @return A description of the given file/directory.
+ */
+ public String getDescription(File f)
+ {
+ return getName(f);
+ }
+
+ /**
+ * Returns an icon appropriate for the given file or directory.
+ *
+ * @param f the file/directory.
+ *
+ * @return An icon.
+ */
+ public Icon getIcon(File f)
+ {
+ Icon val = getCachedIcon(f);
+ if (val != null)
+ return val;
+ if (filechooser.isTraversable(f))
+ val = directoryIcon;
+ else
+ val = fileIcon;
+ cacheIcon(f, val);
+ return val;
+ }
+
+ /**
+ * Returns the name for the given file/directory.
+ *
+ * @param f the file/directory.
+ *
+ * @return The name of the file/directory.
+ */
+ public String getName(File f)
+ {
+ String name = null;
+ if (f != null)
+ {
+ JFileChooser c = getFileChooser();
+ FileSystemView v = c.getFileSystemView();
+ name = v.getSystemDisplayName(f);
+ }
+ return name;
+ }
+
+ /**
+ * Returns a localised description for the type of file/directory.
+ *
+ * @param f the file/directory.
+ *
+ * @return A type description for the given file/directory.
+ */
+ public String getTypeDescription(File f)
+ {
+ if (filechooser.isTraversable(f))
+ return dirDescText;
+ else
+ return fileDescText;
+ }
+
+ /**
+ * Returns {@link Boolean#TRUE} if the given file/directory is hidden,
+ * and {@link Boolean#FALSE} otherwise.
+ *
+ * @param f the file/directory.
+ *
+ * @return {@link Boolean#TRUE} or {@link Boolean#FALSE}.
+ */
+ public Boolean isHidden(File f)
+ {
+ return Boolean.valueOf(filechooser.getFileSystemView().isHiddenFile(f));
+ }
+ }
+
+ /**
+ * Handles an action to cancel the file chooser.
+ *
+ * @see BasicFileChooserUI#getCancelSelectionAction()
+ */
+ protected class CancelSelectionAction extends AbstractAction
+ {
+ /**
+ * Creates a new <code>CancelSelectionAction</code> object.
+ */
+ protected CancelSelectionAction()
+ {
+ super(null);
+ }
+
+ /**
+ * Cancels the selection and closes the dialog.
+ *
+ * @param e the action event (ignored).
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ filechooser.setSelectedFile(null);
+ filechooser.setSelectedFiles(null);
+ filechooser.cancelSelection();
+ closeDialog();
+ }
+ }
+
+ /**
+ * An action to handle changes to the parent directory (for example, via
+ * a click on the "up folder" button).
+ *
+ * @see BasicFileChooserUI#getChangeToParentDirectoryAction()
+ */
+ protected class ChangeToParentDirectoryAction extends AbstractAction
+ {
+ /**
+ * Creates a new <code>ChangeToParentDirectoryAction</code> object.
+ */
+ protected ChangeToParentDirectoryAction()
+ {
+ super("Go Up");
+ }
+
+ /**
+ * Handles the action event.
+ *
+ * @param e the action event.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ filechooser.changeToParentDirectory();
+ filechooser.revalidate();
+ filechooser.repaint();
+ }
+ }
+
+ /**
+ * A mouse listener that handles double-click events.
+ *
+ * @see BasicFileChooserUI#createDoubleClickListener(JFileChooser, JList)
+ */
+ protected class DoubleClickListener extends MouseAdapter
+ {
+
+ /** DOCUMENT ME! */
+ private Object lastSelected;
+
+ /** DOCUMENT ME! */
+ private JList list;
+
+ /**
+ * Creates a new DoubleClickListener object.
+ *
+ * @param list DOCUMENT ME!
+ */
+ public DoubleClickListener(JList list)
+ {
+ this.list = list;
+ lastSelected = list.getSelectedValue();
+ setDirectorySelected(false);
+ }
+
+ /**
+ * Handles a mouse click event.
+ *
+ * @param e the event.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ Object p = list.getSelectedValue();
+ if (p == null)
+ return;
+ FileSystemView fsv = filechooser.getFileSystemView();
+ if (e.getClickCount() >= 2 && lastSelected != null &&
+ p.toString().equals(lastSelected.toString()))
+ {
+ File f = fsv.createFileObject(lastSelected.toString());
+ if (filechooser.isTraversable(f))
+ {
+ filechooser.setCurrentDirectory(f);
+ filechooser.rescanCurrentDirectory();
+ }
+ else
+ {
+ filechooser.setSelectedFile(f);
+ filechooser.approveSelection();
+ closeDialog();
+ }
+ }
+ else // single click
+ {
+ String path = p.toString();
+ File f = fsv.createFileObject(path);
+ filechooser.setSelectedFile(f);
+
+ if (filechooser.isMultiSelectionEnabled())
+ {
+ int[] inds = list.getSelectedIndices();
+ File[] allFiles = new File[inds.length];
+ for (int i = 0; i < inds.length; i++)
+ allFiles[i] = (File) list.getModel().getElementAt(inds[i]);
+ filechooser.setSelectedFiles(allFiles);
+ }
+
+ if (filechooser.isTraversable(f))
+ {
+ setDirectorySelected(true);
+ setDirectory(f);
+ }
+ else
+ {
+ setDirectorySelected(false);
+ setDirectory(null);
+ }
+ lastSelected = path;
+ parentPath = f.getParent();
+
+ if (f.isFile())
+ setFileName(f.getName());
+ else if (filechooser.getFileSelectionMode() !=
+ JFileChooser.FILES_ONLY)
+ setFileName(path);
+ }
+ }
+
+ /**
+ * Handles a mouse entered event (NOT IMPLEMENTED).
+ *
+ * @param e the mouse event.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // FIXME: Implement
+ }
+ }
+
+ /**
+ * An action that changes the file chooser to display the user's home
+ * directory.
+ *
+ * @see BasicFileChooserUI#getGoHomeAction()
+ */
+ protected class GoHomeAction extends AbstractAction
+ {
+ /**
+ * Creates a new <code>GoHomeAction</code> object.
+ */
+ protected GoHomeAction()
+ {
+ super("Go Home");
+ }
+
+ /**
+ * Sets the directory to the user's home directory, and repaints the
+ * file chooser component.
+ *
+ * @param e the action event (ignored).
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ filechooser.setCurrentDirectory(filechooser.getFileSystemView()
+ .getHomeDirectory());
+ filechooser.revalidate();
+ filechooser.repaint();
+ }
+ }
+
+ /**
+ * An action that handles the creation of a new folder/directory.
+ *
+ * @see BasicFileChooserUI#getNewFolderAction()
+ */
+ protected class NewFolderAction extends AbstractAction
+ {
+ /**
+ * Creates a new <code>NewFolderAction</code> object.
+ */
+ protected NewFolderAction()
+ {
+ super("New Folder");
+ }
+
+ /**
+ * Handles the event by creating a new folder.
+ *
+ * @param e the action event (ignored).
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ try
+ {
+ filechooser.getFileSystemView().createNewFolder(filechooser
+ .getCurrentDirectory());
+ }
+ catch (IOException ioe)
+ {
+ return;
+ }
+ filechooser.rescanCurrentDirectory();
+ filechooser.repaint();
+ }
+ }
+
+ /**
+ * A listener for selection events in the file list.
+ *
+ * @see BasicFileChooserUI#createListSelectionListener(JFileChooser)
+ */
+ protected class SelectionListener implements ListSelectionListener
+ {
+ /**
+ * Creates a new <code>SelectionListener</code> object.
+ */
+ protected SelectionListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the JFileChooser to the selected file on an update
+ *
+ * @param e DOCUMENT ME!
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ JList list = (JList) e.getSource();
+ Object f = list.getSelectedValue();
+ if (f == null)
+ return;
+ File file = filechooser.getFileSystemView().createFileObject(f.toString());
+ if (! filechooser.isTraversable(file))
+ {
+ selectedDir = null;
+ filechooser.setSelectedFile(file);
+ }
+ else
+ {
+ selectedDir = file;
+ filechooser.setSelectedFile(null);
+ }
+ }
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @see BasicFileChooserUI#getUpdateAction()
+ */
+ protected class UpdateAction extends AbstractAction
+ {
+ /**
+ * Creates a new UpdateAction object.
+ */
+ protected UpdateAction()
+ {
+ super(null);
+ }
+
+ /**
+ * NOT YET IMPLEMENTED.
+ *
+ * @param e the action event.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // FIXME: implement this
+ }
+ }
+
+ /** The localised mnemonic for the cancel button. */
+ protected int cancelButtonMnemonic;
+
+ /** The localised text for the cancel button. */
+ protected String cancelButtonText;
+
+ /** The localised tool tip text for the cancel button. */
+ protected String cancelButtonToolTipText;
+
+ /** An icon representing a computer. */
+ protected Icon computerIcon;
+
+ /** An icon for the "details view" button. */
+ protected Icon detailsViewIcon;
+
+ /** An icon representing a directory. */
+ protected Icon directoryIcon;
+
+ /** The localised Mnemonic for the open button. */
+ protected int directoryOpenButtonMnemonic;
+
+ /** The localised text for the open button. */
+ protected String directoryOpenButtonText;
+
+ /** The localised tool tip text for the open button. */
+ protected String directoryOpenButtonToolTipText;
+
+ /** An icon representing a file. */
+ protected Icon fileIcon;
+
+ /** An icon representing a floppy drive. */
+ protected Icon floppyDriveIcon;
+
+ /** An icon representing a hard drive. */
+ protected Icon hardDriveIcon;
+
+ /** The localised mnemonic for the "help" button. */
+ protected int helpButtonMnemonic;
+
+ /** The localised text for the "help" button. */
+ protected String helpButtonText;
+
+ /** The localised tool tip text for the help button. */
+ protected String helpButtonToolTipText;
+
+ /** An icon representing the user's home folder. */
+ protected Icon homeFolderIcon;
+
+ /** An icon for the "list view" button. */
+ protected Icon listViewIcon;
+
+ /** An icon for the "new folder" button. */
+ protected Icon newFolderIcon = directoryIcon;
+
+ /** The localised mnemonic for the "open" button. */
+ protected int openButtonMnemonic;
+
+ /** The localised text for the "open" button. */
+ protected String openButtonText;
+
+ /** The localised tool tip text for the "open" button. */
+ protected String openButtonToolTipText;
+
+ /** The localised mnemonic for the "save" button. */
+ protected int saveButtonMnemonic;
+
+ /** The localised text for the "save" button. */
+ protected String saveButtonText;
+
+ /** The localised tool tip text for the save button. */
+ protected String saveButtonToolTipText;
+
+ /** The localised mnemonic for the "update" button. */
+ protected int updateButtonMnemonic;
+
+ /** The localised text for the "update" button. */
+ protected String updateButtonText;
+
+ /** The localised tool tip text for the "update" button. */
+ protected String updateButtonToolTipText;
+
+ /** An icon for the "up folder" button. */
+ protected Icon upFolderIcon;
+
+ // -- begin private, but package local since used in inner classes --
+
+ /** The file chooser component represented by this UI delegate. */
+ JFileChooser filechooser;
+
+ /** The model for the directory list. */
+ BasicDirectoryModel model;
+
+ /** The file filter for all files. */
+ FileFilter acceptAll = new AcceptAllFileFilter();
+
+ /** The default file view. */
+ FileView fv = new BasicFileView();
+
+ /** The accept (open/save) button. */
+ JButton accept;
+
+ /** An optional accessory panel. */
+ JPanel accessoryPanel = new JPanel();
+
+ /** A property change listener. */
+ PropertyChangeListener propertyChangeListener;
+
+ /** The text describing the filter for "all files". */
+ String acceptAllFileFilterText;
+
+ /** The text describing a directory type. */
+ String dirDescText;
+
+ /** The text describing a file type. */
+ String fileDescText;
+
+ /** Is a directory selected? */
+ boolean dirSelected;
+
+ /** The current directory. */
+ File currDir;
+
+ // FIXME: describe what is contained in the bottom panel
+ /** The bottom panel. */
+ JPanel bottomPanel;
+
+ /** The close panel. */
+ JPanel closePanel;
+
+ /** Text box that displays file name */
+ JTextField entry;
+
+ /** Current parent path */
+ String parentPath;
+
+ /**
+ * The action for the 'approve' button.
+ * @see #getApproveSelectionAction()
+ */
+ private ApproveSelectionAction approveSelectionAction;
+
+ /**
+ * The action for the 'cancel' button.
+ * @see #getCancelSelectionAction()
+ */
+ private CancelSelectionAction cancelSelectionAction;
+
+ /**
+ * The action for the 'go home' control button.
+ * @see #getGoHomeAction()
+ */
+ private GoHomeAction goHomeAction;
+
+ /**
+ * The action for the 'up folder' control button.
+ * @see #getChangeToParentDirectoryAction()
+ */
+ private ChangeToParentDirectoryAction changeToParentDirectoryAction;
+
+ /**
+ * The action for the 'new folder' control button.
+ * @see #getNewFolderAction()
+ */
+ private NewFolderAction newFolderAction;
+
+ /**
+ * The action for ???. // FIXME: what is this?
+ * @see #getUpdateAction()
+ */
+ private UpdateAction updateAction;
+
+ /**
+ * When in FILES_ONLY, mode a directory cannot be selected, so
+ * we save a reference to any it here. This is used to enter
+ * the directory on "Open" when in that mode.
+ */
+ private File selectedDir;
+
+ // -- end private --
+
+ /**
+ * Closes the dialog.
+ */
+ void closeDialog()
+ {
+ Window owner = SwingUtilities.windowForComponent(filechooser);
+ if (owner instanceof JDialog)
+ ((JDialog) owner).dispose();
+ }
+
+ /**
+ * Creates a new <code>BasicFileChooserUI</code> object.
+ *
+ * @param b the file chooser component.
+ */
+ public BasicFileChooserUI(JFileChooser b)
+ {
+ }
+
+ /**
+ * Returns a UI delegate for the given component.
+ *
+ * @param c the component (should be a {@link JFileChooser}).
+ *
+ * @return A new UI delegate.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicFileChooserUI((JFileChooser) c);
+ }
+
+ /**
+ * Installs the UI for the specified component.
+ *
+ * @param c the component (should be a {@link JFileChooser}).
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JFileChooser)
+ {
+ JFileChooser fc = (JFileChooser) c;
+ this.filechooser = fc;
+ fc.resetChoosableFileFilters();
+ createModel();
+ clearIconCache();
+ installDefaults(fc);
+ installComponents(fc);
+ installListeners(fc);
+
+ File path = filechooser.getCurrentDirectory();
+ if (path != null)
+ parentPath = path.getParent();
+ }
+ }
+
+ /**
+ * Uninstalls this UI from the given component.
+ *
+ * @param c the component (should be a {@link JFileChooser}).
+ */
+ public void uninstallUI(JComponent c)
+ {
+ model = null;
+ uninstallListeners(filechooser);
+ uninstallComponents(filechooser);
+ uninstallDefaults(filechooser);
+ filechooser = null;
+ }
+
+ // FIXME: Indent the entries in the combobox
+ // Made this method package private to access it from within inner classes
+ // with better performance
+ void boxEntries()
+ {
+ ArrayList parentFiles = new ArrayList();
+ File parent = filechooser.getCurrentDirectory();
+ if (parent == null)
+ parent = filechooser.getFileSystemView().getDefaultDirectory();
+ while (parent != null)
+ {
+ String name = parent.getName();
+ if (name.equals(""))
+ name = parent.getAbsolutePath();
+
+ parentFiles.add(parentFiles.size(), name);
+ parent = parent.getParentFile();
+ }
+
+ if (parentFiles.size() == 0)
+ return;
+
+ }
+
+ /**
+ * Creates and install the subcomponents for the file chooser.
+ *
+ * @param fc the file chooser.
+ */
+ public void installComponents(JFileChooser fc)
+ {
+ }
+
+ /**
+ * Uninstalls the components from the file chooser.
+ *
+ * @param fc the file chooser.
+ */
+ public void uninstallComponents(JFileChooser fc)
+ {
+ }
+
+ /**
+ * Installs the listeners required by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void installListeners(JFileChooser fc)
+ {
+ propertyChangeListener = createPropertyChangeListener(filechooser);
+ if (propertyChangeListener != null)
+ filechooser.addPropertyChangeListener(propertyChangeListener);
+ fc.addPropertyChangeListener(getModel());
+ }
+
+ /**
+ * Uninstalls the listeners previously installed by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void uninstallListeners(JFileChooser fc)
+ {
+ if (propertyChangeListener != null)
+ {
+ filechooser.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ }
+ fc.removePropertyChangeListener(getModel());
+ }
+
+ /**
+ * Installs the defaults for this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void installDefaults(JFileChooser fc)
+ {
+ installIcons(fc);
+ installStrings(fc);
+ }
+
+ /**
+ * Uninstalls the defaults previously added by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void uninstallDefaults(JFileChooser fc)
+ {
+ uninstallStrings(fc);
+ uninstallIcons(fc);
+ }
+
+ /**
+ * Installs the icons for this UI delegate.
+ *
+ * @param fc the file chooser (ignored).
+ */
+ protected void installIcons(JFileChooser fc)
+ {
+ UIDefaults defaults = UIManager.getLookAndFeelDefaults();
+ computerIcon = MetalIconFactory.getTreeComputerIcon();
+ detailsViewIcon = defaults.getIcon("FileChooser.detailsViewIcon");
+ directoryIcon = new MetalIconFactory.TreeFolderIcon();
+ fileIcon = new MetalIconFactory.TreeLeafIcon();
+ floppyDriveIcon = MetalIconFactory.getTreeFloppyDriveIcon();
+ hardDriveIcon = MetalIconFactory.getTreeHardDriveIcon();
+ homeFolderIcon = defaults.getIcon("FileChooser.homeFolderIcon");
+ listViewIcon = defaults.getIcon("FileChooser.listViewIcon");
+ newFolderIcon = defaults.getIcon("FileChooser.newFolderIcon");
+ upFolderIcon = defaults.getIcon("FileChooser.upFolderIcon");
+ }
+
+ /**
+ * Uninstalls the icons previously added by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void uninstallIcons(JFileChooser fc)
+ {
+ computerIcon = null;
+ detailsViewIcon = null;
+ directoryIcon = null;
+ fileIcon = null;
+ floppyDriveIcon = null;
+ hardDriveIcon = null;
+ homeFolderIcon = null;
+ listViewIcon = null;
+ newFolderIcon = null;
+ upFolderIcon = null;
+ }
+
+ /**
+ * Installs the strings used by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void installStrings(JFileChooser fc)
+ {
+ UIDefaults defaults = UIManager.getLookAndFeelDefaults();
+
+ dirDescText = defaults.getString("FileChooser.directoryDescriptionText");
+ fileDescText = defaults.getString("FileChooser.fileDescriptionText");
+
+ acceptAllFileFilterText = defaults.getString("FileChooser.acceptAllFileFilterText");
+ cancelButtonText = "Cancel";
+ cancelButtonToolTipText = "Abort file chooser dialog";
+ cancelButtonMnemonic = new Integer((String) UIManager.get("FileChooser.cancelButtonMnemonic")).intValue();
+
+ directoryOpenButtonText = "Open";
+ directoryOpenButtonToolTipText = "Open selected directory";
+ directoryOpenButtonMnemonic
+ = new Integer((String) UIManager.get("FileChooser.directoryOpenButtonMnemonic")).intValue();
+
+ helpButtonText = "Help";
+ helpButtonToolTipText = "FileChooser help";
+ helpButtonMnemonic = new Integer((String) UIManager.get("FileChooser.helpButtonMnemonic")).intValue();
+
+ openButtonText = "Open";
+ openButtonToolTipText = "Open selected file";
+ openButtonMnemonic = new Integer((String) UIManager.get("FileChooser.openButtonMnemonic")).intValue();
+
+ saveButtonText = "Save";
+ saveButtonToolTipText = "Save selected file";
+ saveButtonMnemonic = new Integer((String) UIManager.get("FileChooser.saveButtonMnemonic")).intValue();
+
+ updateButtonText = "Update";
+ updateButtonToolTipText = "Update directory listing";
+ updateButtonMnemonic = new Integer((String) UIManager.get("FileChooser.updateButtonMnemonic")).intValue();
+ }
+
+ /**
+ * Uninstalls the strings previously added by this UI delegate.
+ *
+ * @param fc the file chooser.
+ */
+ protected void uninstallStrings(JFileChooser fc)
+ {
+ acceptAllFileFilterText = null;
+ dirDescText = null;
+ fileDescText = null;
+
+ cancelButtonText = null;
+ cancelButtonToolTipText = null;
+
+ directoryOpenButtonText = null;
+ directoryOpenButtonToolTipText = null;
+
+ helpButtonText = null;
+ helpButtonToolTipText = null;
+
+ openButtonText = null;
+ openButtonToolTipText = null;
+
+ saveButtonText = null;
+ saveButtonToolTipText = null;
+
+ updateButtonText = null;
+ updateButtonToolTipText = null;
+ }
+
+ /**
+ * Creates a new directory model.
+ */
+ protected void createModel()
+ {
+ model = new BasicDirectoryModel(filechooser);
+ }
+
+ /**
+ * Returns the directory model.
+ *
+ * @return The directory model.
+ */
+ public BasicDirectoryModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Creates a listener to handle changes to the properties of the given
+ * file chooser component.
+ *
+ * @param fc the file chooser component.
+ *
+ * @return A new listener.
+ */
+ public PropertyChangeListener createPropertyChangeListener(JFileChooser fc)
+ {
+ // The RI returns null here, so do we.
+ return null;
+ }
+
+ /**
+ * Returns the current file name.
+ *
+ * @return The current file name.
+ */
+ public String getFileName()
+ {
+ return entry.getText();
+ }
+
+ /**
+ * Returns the current directory name.
+ *
+ * @return The directory name.
+ *
+ * @see #setDirectoryName(String)
+ */
+ public String getDirectoryName()
+ {
+ // XXX: I don't see a case where the thing returns something non-null..
+ return null;
+ }
+
+ /**
+ * Sets the file name.
+ *
+ * @param filename the file name.
+ *
+ * @see #getFileName()
+ */
+ public void setFileName(String filename)
+ {
+ // FIXME: it might be the case that this method provides an access
+ // point for the JTextField (or whatever) a subclass is using...
+ //this.filename = filename;
+ }
+
+ /**
+ * Sets the directory name (NOT IMPLEMENTED).
+ *
+ * @param dirname the directory name.
+ *
+ * @see #getDirectoryName()
+ */
+ public void setDirectoryName(String dirname)
+ {
+ // FIXME: Implement
+ }
+
+ /**
+ * Rescans the current directory.
+ *
+ * @param fc the file chooser.
+ */
+ public void rescanCurrentDirectory(JFileChooser fc)
+ {
+ getModel().validateFileCache();
+ }
+
+ /**
+ * NOT YET IMPLEMENTED.
+ *
+ * @param fc the file chooser.
+ * @param f the file.
+ */
+ public void ensureFileIsVisible(JFileChooser fc, File f)
+ {
+ // XXX: Not sure what this does.
+ }
+
+ /**
+ * Returns the {@link JFileChooser} component that this UI delegate
+ * represents.
+ *
+ * @return The component represented by this UI delegate.
+ */
+ public JFileChooser getFileChooser()
+ {
+ return filechooser;
+ }
+
+ /**
+ * Returns the optional accessory panel.
+ *
+ * @return The optional accessory panel.
+ */
+ public JPanel getAccessoryPanel()
+ {
+ return accessoryPanel;
+ }
+
+ /**
+ * Returns the approve (open or save) button for the dialog.
+ *
+ * @param fc the file chooser.
+ *
+ * @return The button.
+ */
+ protected JButton getApproveButton(JFileChooser fc)
+ {
+ return accept;
+ }
+
+ /**
+ * Returns the tool tip text for the approve (open/save) button. This first
+ * checks the file chooser to see if a value has been explicitly set - if
+ * not, a default value appropriate for the type of file chooser is
+ * returned.
+ *
+ * @param fc the file chooser.
+ *
+ * @return The tool tip text.
+ */
+ public String getApproveButtonToolTipText(JFileChooser fc)
+ {
+ if (fc.getApproveButtonToolTipText() != null)
+ return fc.getApproveButtonToolTipText();
+ else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
+ return saveButtonToolTipText;
+ else
+ return openButtonToolTipText;
+ }
+
+ /**
+ * Clears the icon cache.
+ */
+ public void clearIconCache()
+ {
+ if (fv instanceof BasicFileView)
+ ((BasicFileView) fv).clearIconCache();
+ }
+
+ /**
+ * Creates a new listener to handle selections in the file list.
+ *
+ * @param fc the file chooser component.
+ *
+ * @return A new instance of {@link SelectionListener}.
+ */
+ public ListSelectionListener createListSelectionListener(JFileChooser fc)
+ {
+ return new SelectionListener();
+ }
+
+ /**
+ * Creates a new listener to handle double-click events.
+ *
+ * @param fc the file chooser component.
+ * @param list the list.
+ *
+ * @return A new instance of {@link DoubleClickListener}.
+ */
+ protected MouseListener createDoubleClickListener(JFileChooser fc, JList list)
+ {
+ return new DoubleClickListener(list);
+ }
+
+ /**
+ * Returns <code>true</code> if a directory is selected, and
+ * <code>false</code> otherwise.
+ *
+ * @return A boolean.
+ */
+ protected boolean isDirectorySelected()
+ {
+ return dirSelected;
+ }
+
+ /**
+ * Sets the flag that indicates whether the current directory is selected.
+ *
+ * @param selected the new flag value.
+ */
+ protected void setDirectorySelected(boolean selected)
+ {
+ dirSelected = selected;
+ }
+
+ /**
+ * Returns the current directory.
+ *
+ * @return The current directory.
+ */
+ protected File getDirectory()
+ {
+ return currDir;
+ }
+
+ /**
+ * Sets the current directory.
+ *
+ * @param f the directory.
+ */
+ protected void setDirectory(File f)
+ {
+ currDir = f;
+ }
+
+ /**
+ * Returns the "accept all" file filter.
+ *
+ * @param fc the file chooser component.
+ *
+ * @return The "accept all" file filter.
+ */
+ public FileFilter getAcceptAllFileFilter(JFileChooser fc)
+ {
+ return acceptAll;
+ }
+
+ /**
+ * Returns the default file view (NOT the file view from the file chooser,
+ * if there is one).
+ *
+ * @param fc the file chooser component.
+ *
+ * @return The file view.
+ *
+ * @see JFileChooser#getFileView()
+ */
+ public FileView getFileView(JFileChooser fc)
+ {
+ return fv;
+ }
+
+ /**
+ * Returns the dialog title.
+ *
+ * @param fc the file chooser (<code>null</code> not permitted).
+ *
+ * @return The dialog title.
+ *
+ * @see JFileChooser#getDialogTitle()
+ */
+ public String getDialogTitle(JFileChooser fc)
+ {
+ String result = fc.getDialogTitle();
+ if (result == null)
+ result = getApproveButtonText(fc);
+ return result;
+ }
+
+ /**
+ * Returns the approve button mnemonic.
+ *
+ * @param fc the file chooser (<code>null</code> not permitted).
+ *
+ * @return The approve button mnemonic.
+ *
+ * @see JFileChooser#getApproveButtonMnemonic()
+ */
+ public int getApproveButtonMnemonic(JFileChooser fc)
+ {
+ if (fc.getApproveButtonMnemonic() != 0)
+ return fc.getApproveButtonMnemonic();
+ else if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
+ return saveButtonMnemonic;
+ else
+ return openButtonMnemonic;
+ }
+
+ /**
+ * Returns the approve button text.
+ *
+ * @param fc the file chooser (<code>null</code> not permitted).
+ *
+ * @return The approve button text.
+ *
+ * @see JFileChooser#getApproveButtonText()
+ */
+ public String getApproveButtonText(JFileChooser fc)
+ {
+ String result = fc.getApproveButtonText();
+ if (result == null)
+ {
+ if (fc.getDialogType() == JFileChooser.SAVE_DIALOG)
+ result = saveButtonText;
+ else
+ result = openButtonText;
+ }
+ return result;
+ }
+
+ /**
+ * Creates and returns a new action that will be used with the "new folder"
+ * button.
+ *
+ * @return A new instance of {@link NewFolderAction}.
+ */
+ public Action getNewFolderAction()
+ {
+ if (newFolderAction == null)
+ newFolderAction = new NewFolderAction();
+ return newFolderAction;
+ }
+
+ /**
+ * Creates and returns a new action that will be used with the "home folder"
+ * button.
+ *
+ * @return A new instance of {@link GoHomeAction}.
+ */
+ public Action getGoHomeAction()
+ {
+ if (goHomeAction == null)
+ goHomeAction = new GoHomeAction();
+ return goHomeAction;
+ }
+
+ /**
+ * Returns the action that handles events for the "up folder" control button.
+ *
+ * @return An instance of {@link ChangeToParentDirectoryAction}.
+ */
+ public Action getChangeToParentDirectoryAction()
+ {
+ if (changeToParentDirectoryAction == null)
+ changeToParentDirectoryAction = new ChangeToParentDirectoryAction();
+ return changeToParentDirectoryAction;
+ }
+
+ /**
+ * Returns the action that handles events for the "approve" button.
+ *
+ * @return An instance of {@link ApproveSelectionAction}.
+ */
+ public Action getApproveSelectionAction()
+ {
+ if (approveSelectionAction == null)
+ approveSelectionAction = new ApproveSelectionAction();
+ return approveSelectionAction;
+ }
+
+ /**
+ * Returns the action that handles events for the "cancel" button.
+ *
+ * @return An instance of {@link CancelSelectionAction}.
+ */
+ public Action getCancelSelectionAction()
+ {
+ if (cancelSelectionAction == null)
+ cancelSelectionAction = new CancelSelectionAction();
+ return cancelSelectionAction;
+ }
+
+ /**
+ * Returns the update action (an instance of {@link UpdateAction}).
+ *
+ * @return An action.
+ */
+ public Action getUpdateAction()
+ {
+ if (updateAction == null)
+ updateAction = new UpdateAction();
+ return updateAction;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java
new file mode 100644
index 000000000..555921435
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicFormattedTextFieldUI.java
@@ -0,0 +1,69 @@
+/* BasicFormattedTextFieldUI.java
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import javax.swing.JComponent;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * @since 1.4
+ */
+public class BasicFormattedTextFieldUI extends BasicTextFieldUI
+{
+ public BasicFormattedTextFieldUI()
+ {
+ // Nothing to do here.
+ }
+
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicFormattedTextFieldUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "FormattedTextField"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "FormattedTextField";
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java
new file mode 100644
index 000000000..f270d3335
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicGraphicsUtils.java
@@ -0,0 +1,821 @@
+/* BasicGraphicsUtils.java
+ Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing.plaf.basic;
+
+import gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.font.FontRenderContext;
+import java.awt.font.LineMetrics;
+import java.awt.font.TextLayout;
+import java.awt.geom.Rectangle2D;
+
+import javax.swing.AbstractButton;
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+
+
+/**
+ * A utility class providing commonly used drawing and measurement
+ * routines.
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class BasicGraphicsUtils
+{
+ /**
+ * Used as a key for a client property to store cached TextLayouts in. This
+ * is used for speed-up drawing of text in
+ * {@link #drawString(Graphics, String, int, int, int)}.
+ */
+ static final String CACHED_TEXT_LAYOUT =
+ "BasicGraphicsUtils.cachedTextLayout";
+
+ /**
+ * Constructor. It is utterly unclear why this class should
+ * be constructable, but this is what the API specification
+ * says.
+ */
+ public BasicGraphicsUtils()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Draws a rectangle that appears etched into the surface, given
+ * four colors that are used for drawing.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-1.png" width="360"
+ * height="200" alt="[An illustration that shows which pixels
+ * get painted in what color]" />
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ *
+ * @param shadow the color that will be used for painting
+ * the outer side of the top and left edges.
+ *
+ * @param darkShadow the color that will be used for painting
+ * the inner side of the top and left edges.
+ *
+ * @param highlight the color that will be used for painting
+ * the inner side of the bottom and right edges.
+ *
+ * @param lightHighlight the color that will be used for painting
+ * the outer side of the bottom and right edges.
+ *
+ * @see #getEtchedInsets()
+ * @see javax.swing.border.EtchedBorder
+ */
+ public static void drawEtchedRect(Graphics g,
+ int x, int y, int width, int height,
+ Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ Color oldColor;
+ int x2, y2;
+
+ oldColor = g.getColor();
+ x2 = x + width - 1;
+ y2 = y + height - 1;
+
+ try
+ {
+ /* To understand this code, it might be helpful to look at the
+ * image "BasicGraphicsUtils-1.png" that is included with the
+ * JavaDoc. The file is located in the "doc-files" subdirectory.
+ *
+ * (x2, y2) is the coordinate of the most right and bottom pixel
+ * to be painted.
+ */
+ g.setColor(shadow);
+ g.drawLine(x, y, x2 - 1, y); // top, outer
+ g.drawLine(x, y + 1, x, y2 - 1); // left, outer
+
+ g.setColor(darkShadow);
+ g.drawLine(x + 1, y + 1, x2 - 2, y + 1); // top, inner
+ g.drawLine(x + 1, y + 2, x + 1, y2 - 2); // left, inner
+
+ g.setColor(highlight);
+ g.drawLine(x + 1, y2 - 1, x2 - 1, y2 - 1); // bottom, inner
+ g.drawLine(x2 - 1, y + 1, x2 - 1, y2 - 2); // right, inner
+
+ g.setColor(lightHighlight);
+ g.drawLine(x, y2, x2, y2); // bottom, outer
+ g.drawLine(x2, y, x2, y2 - 1); // right, outer
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Determines the width of the border that gets painted by
+ * {@link #drawEtchedRect}.
+ *
+ * @return an <code>Insets</code> object whose <code>top</code>,
+ * <code>left</code>, <code>bottom</code> and
+ * <code>right</code> field contain the border width at the
+ * respective edge in pixels.
+ */
+ public static Insets getEtchedInsets()
+ {
+ return new Insets(2, 2, 2, 2);
+ }
+
+
+ /**
+ * Draws a rectangle that appears etched into the surface, given
+ * two colors that are used for drawing.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-2.png" width="360"
+ * height="200" alt="[An illustration that shows which pixels
+ * get painted in what color]" />
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ *
+ * @param shadow the color that will be used for painting the outer
+ * side of the top and left edges, and for the inner side of
+ * the bottom and right ones.
+ *
+ * @param highlight the color that will be used for painting the
+ * inner side of the top and left edges, and for the outer
+ * side of the bottom and right ones.
+ *
+ * @see #getGrooveInsets()
+ * @see javax.swing.border.EtchedBorder
+ */
+ public static void drawGroove(Graphics g,
+ int x, int y, int width, int height,
+ Color shadow, Color highlight)
+ {
+ /* To understand this, it might be helpful to look at the image
+ * "BasicGraphicsUtils-2.png" that is included with the JavaDoc,
+ * and to compare it with "BasicGraphicsUtils-1.png" which shows
+ * the pixels painted by drawEtchedRect. These image files are
+ * located in the "doc-files" subdirectory.
+ */
+ drawEtchedRect(g, x, y, width, height,
+ /* outer topLeft */ shadow,
+ /* inner topLeft */ highlight,
+ /* inner bottomRight */ shadow,
+ /* outer bottomRight */ highlight);
+ }
+
+
+ /**
+ * Determines the width of the border that gets painted by
+ * {@link #drawGroove}.
+ *
+ * @return an <code>Insets</code> object whose <code>top</code>,
+ * <code>left</code>, <code>bottom</code> and
+ * <code>right</code> field contain the border width at the
+ * respective edge in pixels.
+ */
+ public static Insets getGrooveInsets()
+ {
+ return new Insets(2, 2, 2, 2);
+ }
+
+
+ /**
+ * Draws a border that is suitable for buttons of the Basic look and
+ * feel.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-3.png" width="500"
+ * height="300" alt="[An illustration that shows which pixels
+ * get painted in what color]" />
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ *
+ * @param isPressed <code>true</code> to draw the button border
+ * with a pressed-in appearance; <code>false</code> for
+ * normal (unpressed) appearance.
+ *
+ * @param isDefault <code>true</code> to draw the border with
+ * the appearance it has when hitting the enter key in a
+ * dialog will simulate a click to this button;
+ * <code>false</code> for normal appearance.
+ *
+ * @param shadow the shadow color.
+ * @param darkShadow a darker variant of the shadow color.
+ * @param highlight the highlight color.
+ * @param lightHighlight a brighter variant of the highlight color.
+ */
+ public static void drawBezel(Graphics g,
+ int x, int y, int width, int height,
+ boolean isPressed, boolean isDefault,
+ Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ Color oldColor = g.getColor();
+
+ /* To understand this, it might be helpful to look at the image
+ * "BasicGraphicsUtils-3.png" that is included with the JavaDoc,
+ * and to compare it with "BasicGraphicsUtils-1.png" which shows
+ * the pixels painted by drawEtchedRect. These image files are
+ * located in the "doc-files" subdirectory.
+ */
+ try
+ {
+ if ((isPressed == false) && (isDefault == false))
+ {
+ drawEtchedRect(g, x, y, width, height,
+ lightHighlight, highlight,
+ shadow, darkShadow);
+ }
+
+ if ((isPressed == true) && (isDefault == false))
+ {
+ g.setColor(shadow);
+ g.drawRect(x + 1, y + 1, width - 2, height - 2);
+ }
+
+ if ((isPressed == false) && (isDefault == true))
+ {
+ g.setColor(darkShadow);
+ g.drawRect(x, y, width - 1, height - 1);
+ drawEtchedRect(g, x + 1, y + 1, width - 2, height - 2,
+ lightHighlight, highlight,
+ shadow, darkShadow);
+ }
+
+ if ((isPressed == true) && (isDefault == true))
+ {
+ g.setColor(darkShadow);
+ g.drawRect(x, y, width - 1, height - 1);
+ g.setColor(shadow);
+ g.drawRect(x + 1, y + 1, width - 3, height - 3);
+ }
+ }
+ finally
+ {
+ g.setColor(oldColor);
+ }
+ }
+
+
+ /**
+ * Draws a rectangle that appears lowered into the surface, given
+ * four colors that are used for drawing.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-4.png" width="360"
+ * height="200" alt="[An illustration that shows which pixels
+ * get painted in what color]" />
+ *
+ * <p><strong>Compatibility with the Sun reference
+ * implementation:</strong> The Sun reference implementation seems
+ * to ignore the <code>x</code> and <code>y</code> arguments, at
+ * least in JDK 1.3.1 and 1.4.1_01. The method always draws the
+ * rectangular area at location (0, 0). A bug report has been filed
+ * with Sun; its &#x201c;bug ID&#x201d; is 4880003. The GNU Classpath
+ * implementation behaves correctly, thus not replicating this bug.
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ *
+ * @param shadow the color that will be used for painting
+ * the inner side of the top and left edges.
+ *
+ * @param darkShadow the color that will be used for painting
+ * the outer side of the top and left edges.
+ *
+ * @param highlight the color that will be used for painting
+ * the inner side of the bottom and right edges.
+ *
+ * @param lightHighlight the color that will be used for painting
+ * the outer side of the bottom and right edges.
+ */
+ public static void drawLoweredBezel(Graphics g,
+ int x, int y, int width, int height,
+ Color shadow, Color darkShadow,
+ Color highlight, Color lightHighlight)
+ {
+ /* Like drawEtchedRect, but swapping darkShadow and shadow.
+ *
+ * To understand this, it might be helpful to look at the image
+ * "BasicGraphicsUtils-4.png" that is included with the JavaDoc,
+ * and to compare it with "BasicGraphicsUtils-1.png" which shows
+ * the pixels painted by drawEtchedRect. These image files are
+ * located in the "doc-files" subdirectory.
+ */
+ drawEtchedRect(g, x, y, width, height,
+ darkShadow, shadow,
+ highlight, lightHighlight);
+ }
+
+
+ /**
+ * Draws a String at the given location, underlining the first
+ * occurence of a specified character. The algorithm for determining
+ * the underlined position is not sensitive to case. If the
+ * character is not part of <code>text</code>, the text will be
+ * drawn without underlining. Drawing is performed in the current
+ * color and font of <code>g</code>.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
+ * height="100" alt="[An illustration showing how to use the
+ * method]" />
+ *
+ * @param g the graphics into which the String is drawn.
+ *
+ * @param text the String to draw.
+ *
+ * @param underlinedChar the character whose first occurence in
+ * <code>text</code> will be underlined. It is not clear
+ * why the API specification declares this argument to be
+ * of type <code>int</code> instead of <code>char</code>.
+ * While this would allow to pass Unicode characters outside
+ * Basic Multilingual Plane 0 (U+0000 .. U+FFFE), at least
+ * the GNU Classpath implementation does not underline
+ * anything if <code>underlinedChar</code> is outside
+ * the range of <code>char</code>.
+ *
+ * @param x the x coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ *
+ * @param y the y coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ */
+ public static void drawString(Graphics g, String text,
+ int underlinedChar, int x, int y)
+ {
+ int index = -1;
+
+ /* It is intentional that lower case is used. In some languages,
+ * the set of lowercase characters is larger than the set of
+ * uppercase ones. Therefore, it is good practice to use lowercase
+ * for such comparisons (which really means that the author of this
+ * code can vaguely remember having read some Unicode techreport
+ * with this recommendation, but is too lazy to look for the URL).
+ */
+ if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
+ index = text.toLowerCase().indexOf(
+ Character.toLowerCase((char) underlinedChar));
+
+ drawStringUnderlineCharAt(g, text, index, x, y);
+ }
+
+
+ /**
+ * Draws a String at the given location, underlining the character
+ * at the specified index. Drawing is performed in the current color
+ * and font of <code>g</code>.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
+ * height="100" alt="[An illustration showing how to use the
+ * method]" />
+ *
+ * @param g the graphics into which the String is drawn.
+ *
+ * @param text the String to draw.
+ *
+ * @param underlinedIndex the index of the underlined character in
+ * <code>text</code>. If <code>underlinedIndex</code> falls
+ * outside the range <code>[0, text.length() - 1]</code>, the
+ * text will be drawn without underlining anything.
+ *
+ * @param x the x coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ *
+ * @param y the y coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ *
+ * @since 1.4
+ */
+ public static void drawStringUnderlineCharAt(Graphics g, String text,
+ int underlinedIndex,
+ int x, int y)
+ {
+ Graphics2D g2;
+ Rectangle2D.Double underline;
+ FontRenderContext frc;
+ FontMetrics fmet;
+ LineMetrics lineMetrics;
+ Font font;
+ TextLayout layout;
+ double underlineX1, underlineX2;
+ boolean drawUnderline;
+ int textLength;
+
+ textLength = text.length();
+ if (textLength == 0)
+ return;
+
+ drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
+
+ // FIXME: unfortunately pango and cairo can't agree on metrics
+ // so for the time being we continue to *not* use TextLayouts.
+ if (true || !(g instanceof Graphics2D))
+ {
+ /* Fall-back. This is likely to produce garbage for any text
+ * containing right-to-left (Hebrew or Arabic) characters, even
+ * if the underlined character is left-to-right.
+ */
+ g.drawString(text, x, y);
+ if (drawUnderline)
+ {
+ fmet = g.getFontMetrics();
+ g.fillRect(
+ /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
+ /* y */ y + fmet.getDescent() - 1,
+ /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
+ /* height */ 1);
+ }
+
+ return;
+ }
+
+ g2 = (Graphics2D) g;
+ font = g2.getFont();
+ frc = g2.getFontRenderContext();
+ lineMetrics = font.getLineMetrics(text, frc);
+ layout = new TextLayout(text, font, frc);
+
+ /* Draw the text. */
+ layout.draw(g2, x, y);
+ if (!drawUnderline)
+ return;
+
+ underlineX1 = x + layout.getLogicalHighlightShape(
+ underlinedIndex, underlinedIndex).getBounds2D().getX();
+ underlineX2 = x + layout.getLogicalHighlightShape(
+ underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
+
+ underline = new Rectangle2D.Double();
+ if (underlineX1 < underlineX2)
+ {
+ underline.x = underlineX1;
+ underline.width = underlineX2 - underlineX1;
+ }
+ else
+ {
+ underline.x = underlineX2;
+ underline.width = underlineX1 - underlineX2;
+ }
+
+
+ underline.height = lineMetrics.getUnderlineThickness();
+ underline.y = lineMetrics.getUnderlineOffset();
+ if (underline.y == 0)
+ {
+ /* Some fonts do not specify an underline offset, although they
+ * actually should do so. In that case, the result of calling
+ * lineMetrics.getUnderlineOffset() will be zero. Since it would
+ * look very ugly if the underline was be positioned immediately
+ * below the baseline, we check for this and move the underline
+ * below the descent, as shown in the following ASCII picture:
+ *
+ * ##### ##### #
+ * # # # #
+ * # # # #
+ * # # # #
+ * ##### ###### ---- baseline (0)
+ * #
+ * #
+ * ------------------###----------- lineMetrics.getDescent()
+ */
+ underline.y = lineMetrics.getDescent();
+ }
+
+ underline.y += y;
+ g2.fill(underline);
+ }
+
+ /**
+ * Draws a string on the specified component.
+ *
+ * @param c the component
+ * @param g the Graphics context
+ * @param text the string
+ * @param underlinedChar the character to be underlined
+ * @param x the X location
+ * @param y the Y location
+ */
+ static void drawString(JComponent c, Graphics g, String text,
+ int underlinedChar, int x, int y)
+ {
+ int index = -1;
+
+ /* It is intentional that lower case is used. In some languages,
+ * the set of lowercase characters is larger than the set of
+ * uppercase ones. Therefore, it is good practice to use lowercase
+ * for such comparisons (which really means that the author of this
+ * code can vaguely remember having read some Unicode techreport
+ * with this recommendation, but is too lazy to look for the URL).
+ */
+ if ((underlinedChar >= 0) || (underlinedChar <= 0xffff))
+ index = text.toLowerCase().indexOf(
+ Character.toLowerCase((char) underlinedChar));
+
+ drawStringUnderlineCharAt(c, g, text, index, x, y);
+ }
+
+
+ /**
+ * Draws a String at the given location, underlining the character
+ * at the specified index. Drawing is performed in the current color
+ * and font of <code>g</code>.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-5.png" width="500"
+ * height="100" alt="[An illustration showing how to use the
+ * method]" />
+ *
+ * This is an accelerated version of the method with the same name. It
+ * uses a pre-laid out TextLayout stored in a client property.
+ *
+ * @param c the component that is drawn
+ * @param g the graphics into which the String is drawn.
+ *
+ * @param text the String to draw.
+ *
+ * @param underlinedIndex the index of the underlined character in
+ * <code>text</code>. If <code>underlinedIndex</code> falls
+ * outside the range <code>[0, text.length() - 1]</code>, the
+ * text will be drawn without underlining anything.
+ *
+ * @param x the x coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ *
+ * @param y the y coordinate of the text, as it would be passed to
+ * {@link java.awt.Graphics#drawString(java.lang.String,
+ * int, int)}.
+ */
+ static void drawStringUnderlineCharAt(JComponent c, Graphics g, String text,
+ int underlinedIndex,
+ int x, int y)
+ {
+ Graphics2D g2;
+ Rectangle2D.Double underline;
+ FontRenderContext frc;
+ FontMetrics fmet;
+ LineMetrics lineMetrics;
+ Font font;
+ TextLayout layout;
+ double underlineX1, underlineX2;
+ boolean drawUnderline;
+ int textLength;
+
+ textLength = text.length();
+ if (textLength == 0)
+ return;
+
+ drawUnderline = (underlinedIndex >= 0) && (underlinedIndex < textLength);
+
+ // FIXME: unfortunately pango and cairo can't agree on metrics
+ // so for the time being we continue to *not* use TextLayouts.
+ if (!(g instanceof Graphics2D)
+ || SystemProperties.getProperty("gnu.javax.swing.noGraphics2D") != null)
+ {
+ /* Fall-back. This is likely to produce garbage for any text
+ * containing right-to-left (Hebrew or Arabic) characters, even
+ * if the underlined character is left-to-right.
+ */
+ g.drawString(text, x, y);
+ if (drawUnderline)
+ {
+ fmet = g.getFontMetrics();
+ g.fillRect(
+ /* x */ x + fmet.stringWidth(text.substring(0, underlinedIndex)),
+ /* y */ y + 1,
+ /* width */ fmet.charWidth(text.charAt(underlinedIndex)),
+ /* height */ 1);
+ }
+
+ return;
+ }
+
+ g2 = (Graphics2D) g;
+ font = g2.getFont();
+ frc = g2.getFontRenderContext();
+ lineMetrics = font.getLineMetrics(text, frc);
+ layout = (TextLayout) c.getClientProperty(CACHED_TEXT_LAYOUT);
+ if (layout == null)
+ {
+ layout = new TextLayout(text, font, frc);
+ System.err.println("Unable to use cached TextLayout for: " + text);
+ }
+
+ /* Draw the text. */
+ layout.draw(g2, x, y);
+ if (!drawUnderline)
+ return;
+
+ underlineX1 = x + layout.getLogicalHighlightShape(
+ underlinedIndex, underlinedIndex).getBounds2D().getX();
+ underlineX2 = x + layout.getLogicalHighlightShape(
+ underlinedIndex + 1, underlinedIndex + 1).getBounds2D().getX();
+
+ underline = new Rectangle2D.Double();
+ if (underlineX1 < underlineX2)
+ {
+ underline.x = underlineX1;
+ underline.width = underlineX2 - underlineX1;
+ }
+ else
+ {
+ underline.x = underlineX2;
+ underline.width = underlineX1 - underlineX2;
+ }
+
+
+ underline.height = lineMetrics.getUnderlineThickness();
+ underline.y = lineMetrics.getUnderlineOffset();
+ if (underline.y == 0)
+ {
+ /* Some fonts do not specify an underline offset, although they
+ * actually should do so. In that case, the result of calling
+ * lineMetrics.getUnderlineOffset() will be zero. Since it would
+ * look very ugly if the underline was be positioned immediately
+ * below the baseline, we check for this and move the underline
+ * below the descent, as shown in the following ASCII picture:
+ *
+ * ##### ##### #
+ * # # # #
+ * # # # #
+ * # # # #
+ * ##### ###### ---- baseline (0)
+ * #
+ * #
+ * ------------------###----------- lineMetrics.getDescent()
+ */
+ underline.y = lineMetrics.getDescent();
+ }
+
+ underline.y += y;
+ g2.fill(underline);
+ }
+
+ /**
+ * Draws a rectangle, simulating a dotted stroke by painting only
+ * every second pixel along the one-pixel thick edge. The color of
+ * those pixels is the current color of the Graphics <code>g</code>.
+ * Any other pixels are left unchanged.
+ *
+ * <p><img src="doc-files/BasicGraphicsUtils-7.png" width="360"
+ * height="200" alt="[An illustration that shows which pixels
+ * get painted]" />
+ *
+ * @param g the graphics into which the rectangle is drawn.
+ * @param x the x coordinate of the rectangle.
+ * @param y the y coordinate of the rectangle.
+ * @param width the width of the rectangle in pixels.
+ * @param height the height of the rectangle in pixels.
+ */
+ public static void drawDashedRect(Graphics g,
+ int x, int y, int width, int height)
+ {
+ int right = x + width - 1;
+ int bottom = y + height - 1;
+
+ /* Draw the top and bottom edge of the dotted rectangle. */
+ for (int i = x; i <= right; i += 2)
+ {
+ g.drawLine(i, y, i, y);
+ g.drawLine(i, bottom, i, bottom);
+ }
+
+ /* Draw the left and right edge of the dotted rectangle. */
+ for (int i = y; i <= bottom; i += 2)
+ {
+ g.drawLine(x, i, x, i);
+ g.drawLine(right, i, right, i);
+ }
+ }
+
+ /**
+ * Determines the preferred width and height of an AbstractButton,
+ * given the gap between the button&#x2019;s text and icon.
+ *
+ * @param b the button whose preferred size is determined.
+ *
+ * @param textIconGap the gap between the button&#x2019;s text and
+ * icon.
+ *
+ * @return a <code>Dimension</code> object whose <code>width</code>
+ * and <code>height</code> fields indicate the preferred
+ * extent in pixels.
+ *
+ * @see javax.swing.SwingUtilities#layoutCompoundLabel(JComponent,
+ * FontMetrics, String, Icon, int, int, int, int, Rectangle, Rectangle,
+ * Rectangle, int)
+ */
+ public static Dimension getPreferredButtonSize(AbstractButton b,
+ int textIconGap)
+ {
+ // 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.getFontMetrics(b.getFont()), // see comment above
+ b.getText(),
+ b.getIcon(),
+ b.getVerticalAlignment(),
+ b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(),
+ viewRect, iconRect, textRect,
+ textIconGap);
+
+ /* +------------------------+ +------------------------+
+ * | | | |
+ * | ICON | | CONTENTCONTENTCONTENT |
+ * | TEXTTEXTTEXT | --> | CONTENTCONTENTCONTENT |
+ * | TEXTTEXTTEXT | | CONTENTCONTENTCONTENT |
+ * +------------------------+ +------------------------+
+ */
+
+ 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/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java
new file mode 100644
index 000000000..a7ce8b15b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicHTML.java
@@ -0,0 +1,471 @@
+/* BasicHTML.java -- Provides HTML support to ComponentUI implementations
+ 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.plaf.basic;
+
+import java.awt.Container;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.io.IOException;
+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;
+import javax.swing.text.Element;
+import javax.swing.text.Position;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
+
+/**
+ * Provides support for HTML rendering to {@link javax.swing.plaf.ComponentUI}
+ * implementations.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class BasicHTML
+{
+
+ /**
+ * This class serves as the root view for HTML rendering components.
+ * Its purpose and implementation is similar to the BasicTextUI.RootView
+ * class, only that is implements some stuff differently due to the nature
+ * of not beeing inside a JTextComponent.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class HTMLRootView extends View
+ {
+ /**
+ * The real root view.
+ */
+ private View view;
+
+ /**
+ * The component on which to render the view.
+ */
+ private JComponent component;
+
+ /**
+ * The EditorKit.
+ */
+ private EditorKit editorKit;
+
+ /**
+ * The document to use.
+ */
+ private Document document;
+
+ /**
+ * Creates a new RootView.
+ */
+ public HTMLRootView(JComponent c, View view, EditorKit kit, Document doc)
+ {
+ super(null);
+ component = c;
+ editorKit = kit;
+ document = doc;
+ setView(view);
+ setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS));
+ }
+
+ /**
+ * Returns the ViewFactory for this RootView. If the current EditorKit
+ * provides a ViewFactory, this is used. Otherwise the TextUI itself
+ * is returned as a ViewFactory.
+ *
+ * @return the ViewFactory for this RootView
+ */
+ public ViewFactory getViewFactory()
+ {
+ return editorKit.getViewFactory();
+ }
+
+ /**
+ * Indicates that the preferences of one of the child view has changed.
+ * This calls revalidate on the text component.
+ *
+ * @param v the child view which's preference has changed
+ * @param width <code>true</code> if the width preference has changed
+ * @param height <code>true</code> if the height preference has changed
+ */
+ public void preferenceChanged(View v, boolean width, boolean height)
+ {
+ component.revalidate();
+ }
+
+ /**
+ * Sets the real root view.
+ *
+ * @param v the root view to set
+ */
+ public void setView(View v)
+ {
+ if (view != null)
+ view.setParent(null);
+
+ if (v != null)
+ v.setParent(this);
+
+ view = v;
+ }
+
+ /**
+ * 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
+ *
+ * @return the real root view, regardless of the index.
+ */
+ public View getView(int index)
+ {
+ return view;
+ }
+
+ /**
+ * Returns <code>1</code> since the RootView always contains one
+ * child, that is the real root of the View hierarchy.
+ *
+ * @return <code>1</code> since the RootView always contains one
+ * child, that is the real root of the View hierarchy
+ */
+ public int getViewCount()
+ {
+ int count = 0;
+ if (view != null)
+ count = 1;
+ return count;
+ }
+
+ /**
+ * Returns the <code>Container</code> that contains this view. This
+ * normally will be the text component that is managed by this TextUI.
+ *
+ * @return the <code>Container</code> that contains this view
+ */
+ public Container getContainer()
+ {
+ return component;
+ }
+
+ /**
+ * 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)
+ {
+ if (view != null)
+ return view.getPreferredSpan(axis);
+
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Paints the view. This is delegated to the real root view.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ * @param s the allocation for the View
+ */
+ public void paint(Graphics g, Shape s)
+ {
+ if (view != null)
+ {
+ Rectangle b = s.getBounds();
+ view.setSize(b.width, b.height);
+ view.paint(g, s);
+ }
+ }
+
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * This is delegated to the real root view.
+ *
+ * @param position the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param bias either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Shape modelToView(int position, Shape a, Position.Bias bias)
+ throws BadLocationException
+ {
+ return view.modelToView(position, a, bias);
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ return view.viewToModel(x, y, a, b);
+ }
+
+ /**
+ * Notification about text insertions. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ view.insertUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Notification about text removals. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ view.removeUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Notification about text changes. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ view.changedUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param a the allocation for the view
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
+ int d, Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ return view.getNextVisualPositionFrom(pos, b, a, d, biasRet);
+ }
+
+ public int getStartOffset()
+ {
+ return 0;
+ }
+
+ public int getEndOffset()
+ {
+ return getDocument().getLength();
+ }
+
+ public Document getDocument()
+ {
+ 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();
+ }
+ }
+
+ /**
+ * The key that is used to store a HTML view in a JComponent's client
+ * properties.
+ */
+ public static final String propertyKey = "html";
+
+ /**
+ * The key that is used to store the document base in a JComponent's client
+ * properties. The document base is used to resolve relative references
+ * in HTML.
+ */
+ public static final String documentBaseKey = "html.base";
+
+ /**
+ * Creates a new instance of BasicHTML. This should not be necessary since
+ * all methods in this class are static.
+ */
+ public BasicHTML()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a {@link View} instance that can be used by the component
+ * <code>c</code> to render the HTML string <code>html</code>.
+ *
+ * @param c the component that needs to render the HTML string
+ * @param html the HTML string to be rendered
+ *
+ * @return a view that can render the HTML string
+ */
+ public static View createHTMLView(JComponent c, String html)
+ {
+ // TODO: This might be wrong. Lets see if it turns out good when
+ // the javax.swing.text.html package is in a good shape.
+ HTMLDocument doc = new HTMLDocument();
+ HTMLEditorKit kit = new HTMLEditorKit();
+ StringReader reader = new StringReader(html);
+ try
+ {
+ kit.read(reader, doc, 0);
+ }
+ catch (IOException ex)
+ {
+ AssertionError err = new AssertionError("unexpected IOException");
+ err.initCause(ex);
+ throw err;
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err =
+ new AssertionError("unexpected BadLocationException");
+ err.initCause(ex);
+ throw err;
+ }
+ ViewFactory vf = kit.getViewFactory();
+ Element root = doc.getDefaultRootElement();
+ View view = vf.create(root);
+ HTMLRootView rootView = new HTMLRootView(c, view, kit, doc);
+ return rootView;
+ }
+
+ /**
+ * Returns <code>true</code> if <code>s</code> is HTML, <code>false</code>
+ * otherwise.
+ *
+ * @param s the string to test
+ *
+ * @return <code>true</code> if <code>s</code> is HTML, <code>false</code>
+ * otherwise
+ */
+ public static boolean isHTMLString(String s)
+ {
+ // We consider a string to be HTML if it contains both the '<' and '>'
+ // character at least once.
+ return (s != null) && s.contains("<") && s.contains(">");
+ }
+
+ /**
+ * Stores a HTML renderer in <code>c</code>'s client property if
+ * <code>text</code> is HTML, otherwise it clears the corresponding client
+ * property. This is useful for {@link javax.swing.plaf.ComponentUI}
+ * implementations that are shared between it's components.
+ *
+ * @param c the component to update the renderer for
+ * @param text the string to be rendered
+ */
+ public static void updateRenderer(JComponent c, String text)
+ {
+ if (isHTMLString(text))
+ c.putClientProperty(propertyKey, createHTMLView(c, text));
+ else
+ c.putClientProperty(propertyKey, null);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java b/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java
new file mode 100644
index 000000000..1b5afa7f1
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicIconFactory.java
@@ -0,0 +1,328 @@
+/* BasicIconFactory.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Graphics;
+import java.io.Serializable;
+
+import javax.swing.Icon;
+import javax.swing.JCheckBoxMenuItem;
+
+/**
+ * Creates icons for the {@link BasicLookAndFeel}.
+ */
+public class BasicIconFactory implements Serializable
+{
+ static final long serialVersionUID = 5605588811185324383L;
+
+ private static class DummyIcon
+ implements Icon
+ {
+ public int getIconHeight()
+ {
+ return 10;
+ }
+ public int getIconWidth()
+ {
+ return 10;
+ }
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color save = g.getColor();
+ g.setColor(c.getForeground());
+ g.drawRect(x, y, 10, 10);
+ g.setColor(save);
+ }
+ }
+
+ /**
+ * The icon used for CheckBoxes in the BasicLookAndFeel. This is an empty
+ * icon with a size of 13x13 pixels.
+ */
+ static class CheckBoxIcon
+ implements Icon
+ {
+ /**
+ * Returns the height of the icon. The BasicLookAndFeel CheckBox icon
+ * has a height of 13 pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconHeight()
+ {
+ return 13;
+ }
+
+ /**
+ * Returns the width of the icon. The BasicLookAndFeel CheckBox icon
+ * has a width of 13 pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconWidth()
+ {
+ return 13;
+ }
+
+ /**
+ * Paints the icon. The BasicLookAndFeel CheckBox icon is empty and does
+ * not need to be painted.
+ *
+ * @param c the component to be painted
+ * @param g the Graphics context to be painted with
+ * @param x the x position of the icon
+ * @param y the y position of the icon
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // The icon is empty and needs no painting.
+ }
+ }
+
+ /**
+ * The icon used for {@link JCheckBoxMenuItem}s in the
+ * {@link BasicLookAndFeel}. This icon has a size of 9x9 pixels.
+ */
+ static class CheckBoxMenuItemIcon
+ implements Icon
+ {
+ /**
+ * Returns the height of the icon in pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconHeight()
+ {
+ return 9;
+ }
+
+ /**
+ * Returns the width of the icon in pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconWidth()
+ {
+ return 9;
+ }
+
+ /**
+ * Paints the icon.
+ *
+ * @param c the component to be painted
+ * @param g the Graphics context to be painted with
+ * @param x the x position of the icon
+ * @param y the y position of the icon
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ JCheckBoxMenuItem item = (JCheckBoxMenuItem) c;
+ if (item.isSelected())
+ {
+ // paint the check...
+ g.setColor(Color.black);
+ g.drawLine(x + 1, y + 3, x + 1, y + 4);
+ g.drawLine(x + 2, y + 4, x + 2, y + 5);
+ for (int i = 0; i < 5; i++)
+ g.drawLine(x + 3 + i, y + 5 - i, x + 3 + i, y + 6 - i);
+ }
+ }
+ }
+
+ /**
+ * The icon used for RadioButtons in the BasicLookAndFeel. This is an empty
+ * icon with a size of 13x13 pixels.
+ */
+ static class RadioButtonIcon
+ implements Icon
+ {
+ /**
+ * Returns the height of the icon. The BasicLookAndFeel RadioButton icon
+ * has a height of 13 pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconHeight()
+ {
+ return 13;
+ }
+
+ /**
+ * Returns the width of the icon. The BasicLookAndFeel RadioButton icon
+ * has a width of 13 pixels.
+ *
+ * @return the height of the icon
+ */
+ public int getIconWidth()
+ {
+ return 13;
+ }
+
+ /**
+ * Paints the icon. The BasicLookAndFeel RadioButton icon is empty and does
+ * not need to be painted.
+ *
+ * @param c the component to be painted
+ * @param g the Graphics context to be painted with
+ * @param x the x position of the icon
+ * @param y the y position of the icon
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // The icon is empty and needs no painting.
+ }
+ }
+ /** The cached CheckBoxIcon instance. */
+ private static CheckBoxIcon checkBoxIcon;
+
+ /** The cached RadioButtonIcon instance. */
+ private static RadioButtonIcon radioButtonIcon;
+
+ public static Icon getMenuItemCheckIcon()
+ {
+ return new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 13;
+ }
+
+ public int getIconWidth()
+ {
+ return 13;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+ g.drawLine(3 + x, 5 + y, 3 + x, 9 + y);
+ g.drawLine(4 + x, 5 + y, 4 + x, 9 + y);
+ g.drawLine(5 + x, 7 + y, 9 + x, 3 + y);
+ g.drawLine(5 + x, 8 + y, 9 + x, 4 + y);
+ g.setColor(saved);
+ }
+ };
+ }
+ public static Icon getMenuItemArrowIcon()
+ {
+ return new DummyIcon();
+ }
+
+ /**
+ * Returns a new instance of a 4 x 8 icon showing a small black triangle that
+ * points to the right. This is displayed in menu items that have a
+ * sub menu.
+ *
+ * @return The icon.
+ */
+ public static Icon getMenuArrowIcon()
+ {
+ return new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 8;
+ }
+ public int getIconWidth()
+ {
+ return 4;
+ }
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+ for (int i = 0; i < 4; i++)
+ g.drawLine(x + i, y + i, x + i, y + 7 - i);
+ g.setColor(saved);
+ }
+ };
+ }
+
+ /**
+ * Returns an icon for CheckBoxes in the BasicLookAndFeel. CheckBox icons
+ * in the Basic L&amp;F are empty and have a size of 13x13 pixels.
+ * This method returns a shared single instance of this icon.
+ *
+ * @return an icon for CheckBoxes in the BasicLookAndFeel
+ */
+ public static Icon getCheckBoxIcon()
+ {
+ if (checkBoxIcon == null)
+ checkBoxIcon = new CheckBoxIcon();
+ return checkBoxIcon;
+ }
+
+ /**
+ * Returns an icon for RadioButtons in the BasicLookAndFeel. RadioButton
+ * icons in the Basic L&amp;F are empty and have a size of 13x13 pixels.
+ * This method returns a shared single instance of this icon.
+ *
+ * @return an icon for RadioButtons in the BasicLookAndFeel
+ */
+ public static Icon getRadioButtonIcon()
+ {
+ if (radioButtonIcon == null)
+ radioButtonIcon = new RadioButtonIcon();
+ return radioButtonIcon;
+ }
+
+ /**
+ * Creates and returns an icon used when rendering {@link JCheckBoxMenuItem}
+ * components.
+ *
+ * @return An icon.
+ */
+ public static Icon getCheckBoxMenuItemIcon()
+ {
+ return new CheckBoxMenuItemIcon();
+ }
+
+ public static Icon getRadioButtonMenuItemIcon()
+ {
+ return getRadioButtonIcon();
+ }
+
+ public static Icon createEmptyFrameIcon()
+ {
+ return new DummyIcon();
+ }
+} // class BasicIconFactory
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java
new file mode 100644
index 000000000..484660501
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameTitlePane.java
@@ -0,0 +1,1015 @@
+/* BasicInternalFrameTitlePane.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+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.LayoutManager;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.Icon;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+
+/**
+ * This class acts as a titlebar for JInternalFrames.
+ */
+public class BasicInternalFrameTitlePane extends JComponent
+{
+ /**
+ * The Action responsible for closing the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class CloseAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public CloseAction()
+ {
+ super("Close");
+ }
+
+ /**
+ * This method is called when something closes the JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (frame.isClosable())
+ {
+ try
+ {
+ frame.setClosed(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+ }
+
+ /**
+ * This Action is responsible for iconifying the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class IconifyAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public IconifyAction()
+ {
+ super("Minimize");
+ }
+
+ /**
+ * This method is called when the user wants to iconify the
+ * JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (frame.isIconifiable() && ! frame.isIcon())
+ {
+ try
+ {
+ frame.setIcon(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+ }
+
+ /**
+ * This Action is responsible for maximizing the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class MaximizeAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public MaximizeAction()
+ {
+ super("Maximize");
+ }
+ /**
+ * This method is called when the user wants to maximize the
+ * JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ try
+ {
+ if (frame.isMaximizable() && ! frame.isMaximum())
+ {
+ frame.setMaximum(true);
+ maxButton.setIcon(minIcon);
+ }
+ else if (frame.isMaximum())
+ {
+ frame.setMaximum(false);
+ maxButton.setIcon(maxIcon);
+ }
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+
+ /**
+ * This Action is responsible for dragging the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class MoveAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public MoveAction()
+ {
+ super("Move");
+ }
+ /**
+ * This method is called when the user wants to drag the JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // FIXME: Implement keyboard driven? move actions.
+ }
+ }
+
+ /**
+ * This Action is responsible for restoring the JInternalFrame. Restoring
+ * the JInternalFrame is the same as setting the maximum property to false.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class RestoreAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public RestoreAction()
+ {
+ super("Restore");
+ }
+ /**
+ * This method is called when the user wants to restore the
+ * JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (frame.isMaximum())
+ {
+ try
+ {
+ frame.setMaximum(false);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+ }
+ }
+ }
+
+ /**
+ * This action is responsible for sizing the JInternalFrame.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class SizeAction extends AbstractAction
+ {
+ /**
+ * Creates a new action.
+ */
+ public SizeAction()
+ {
+ super("Size");
+ }
+ /**
+ * This method is called when the user wants to resize the JInternalFrame.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // FIXME: Not sure how size actions should be handled.
+ }
+ }
+
+ /**
+ * This class is responsible for handling property change events from the
+ * JInternalFrame and adjusting the Title Pane as necessary.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when a PropertyChangeEvent is received by the
+ * Title Pane.
+ *
+ * @param evt The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ String propName = evt.getPropertyName();
+ if (propName.equals("closable"))
+ {
+ if (evt.getNewValue().equals(Boolean.TRUE))
+ closeButton.setVisible(true);
+ else
+ closeButton.setVisible(false);
+ }
+ else if (propName.equals("iconable"))
+ {
+ if (evt.getNewValue().equals(Boolean.TRUE))
+ iconButton.setVisible(true);
+ else
+ iconButton.setVisible(false);
+ }
+ else if (propName.equals("maximizable"))
+ {
+ if (evt.getNewValue().equals(Boolean.TRUE))
+ maxButton.setVisible(true);
+ else
+ maxButton.setVisible(false);
+ }
+ enableActions();
+ }
+ }
+
+ /**
+ * This class acts as the MenuBar for the TitlePane. Clicking on the Frame
+ * Icon in the top left corner will activate it.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class SystemMenuBar extends JMenuBar
+ {
+ /**
+ * This method returns true if it can receive focus.
+ *
+ * @return True if this Component can receive focus.
+ */
+ public boolean isFocusTraversable()
+ {
+ return true;
+ }
+
+ /**
+ * This method returns true if this Component is expected to paint all of
+ * itself.
+ *
+ * @return True if this Component is expect to paint all of itself.
+ */
+ public boolean isOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * This method paints this Component.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ Icon frameIcon = frame.getFrameIcon();
+ if (frameIcon == null)
+ frameIcon = BasicDesktopIconUI.defaultIcon;
+ frameIcon.paintIcon(this, g, 0, 0);
+ }
+
+ /**
+ * This method requests that focus be given to this Component.
+ */
+ public void requestFocus()
+ {
+ super.requestFocus();
+ }
+ }
+
+ /**
+ * This class acts as the Layout Manager for the TitlePane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TitlePaneLayout implements LayoutManager
+ {
+ /**
+ * Creates a new <code>TitlePaneLayout</code> object.
+ */
+ public TitlePaneLayout()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called when adding a Component to the Container.
+ *
+ * @param name The name to reference the added Component by.
+ * @param c The Component to add.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the children of the Title Pane.
+ *
+ * @param c The Container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+ Dimension size = c.getSize();
+ Insets insets = c.getInsets();
+ int width = size.width - insets.left - insets.right;
+ int height = size.height - insets.top - insets.bottom;
+
+ // MenuBar is always present and located at the top left corner.
+ Dimension menupref = menuBar.getPreferredSize();
+ menuBar.setBounds(insets.left, insets.top, menupref.width, height);
+
+ int loc = width + insets.left - 1;
+ int top = insets.top + 1;
+ int buttonHeight = height - 4;
+ if (closeButton.isVisible())
+ {
+ int buttonWidth = closeIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ closeButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (maxButton.isVisible())
+ {
+ int buttonWidth = maxIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ maxButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (iconButton.isVisible())
+ {
+ int buttonWidth = iconIcon.getIconWidth();
+ loc -= buttonWidth + 2;
+ iconButton.setBounds(loc, top, buttonWidth, buttonHeight);
+ }
+
+ if (title != null)
+ title.setBounds(insets.left + menupref.width, insets.top,
+ loc - menupref.width - insets.left, height);
+ }
+
+ /**
+ * This method returns the minimum size of the given Container given the
+ * children that it has.
+ *
+ * @param c The Container to get a minimum size for.
+ *
+ * @return The minimum size of the Container.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return preferredLayoutSize(c);
+ }
+
+ /**
+ * This method returns the preferred size of the given Container taking
+ * into account the children that it has.
+ *
+ * @param c The Container to lay out.
+ *
+ * @return The preferred size of the Container.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ return new Dimension(22, 18);
+ }
+
+ /**
+ * This method is called when removing a Component from the Container.
+ *
+ * @param c The Component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This helper class is used to create the minimize, maximize and close
+ * buttons in the top right corner of the Title Pane. These buttons are
+ * special since they cannot be given focus and have no border.
+ */
+ private class PaneButton extends JButton
+ {
+ /**
+ * Creates a new PaneButton object with the given Action.
+ *
+ * @param a The Action that the button uses.
+ */
+ public PaneButton(Action a)
+ {
+ super(a);
+ setMargin(new Insets(0, 0, 0, 0));
+ }
+
+ /**
+ * This method returns true if the Component can be focused.
+ *
+ * @return false.
+ */
+ public boolean isFocusable()
+ {
+ // These buttons cannot be given focus.
+ return false;
+ }
+
+ }
+
+ /** The action command for the Close action. */
+ protected static final String CLOSE_CMD;
+
+ /** The action command for the Minimize action. */
+ protected static final String ICONIFY_CMD;
+
+ /** The action command for the Maximize action. */
+ protected static final String MAXIMIZE_CMD;
+
+ /** The action command for the Move action. */
+ protected static final String MOVE_CMD;
+
+ /** The action command for the Restore action. */
+ protected static final String RESTORE_CMD;
+
+ /** The action command for the Size action. */
+ protected static final String SIZE_CMD;
+
+ /** The action associated with closing the JInternalFrame. */
+ protected Action closeAction;
+
+ /** The action associated with iconifying the JInternalFrame. */
+ protected Action iconifyAction;
+
+ /** The action associated with maximizing the JInternalFrame. */
+ protected Action maximizeAction;
+
+ /** The action associated with moving the JInternalFrame. */
+ protected Action moveAction;
+
+ /** The action associated with restoring the JInternalFrame. */
+ protected Action restoreAction;
+
+ /** The action associated with resizing the JInternalFrame. */
+ protected Action sizeAction;
+
+ /** The button that closes the JInternalFrame. */
+ protected JButton closeButton;
+
+ /** The button that iconifies the JInternalFrame. */
+ protected JButton iconButton;
+
+ /** The button that maximizes the JInternalFrame. */
+ protected JButton maxButton;
+
+ /** The icon displayed in the restore button. */
+ protected Icon minIcon = BasicIconFactory.createEmptyFrameIcon();
+
+ /** The icon displayed in the maximize button. */
+ protected Icon maxIcon = BasicIconFactory.createEmptyFrameIcon();
+
+ /** The icon displayed in the iconify button. */
+ protected Icon iconIcon = BasicIconFactory.createEmptyFrameIcon();
+
+ /** The icon displayed in the close button. */
+ protected Icon closeIcon;
+
+ /** The JInternalFrame that this TitlePane is used in. */
+ protected JInternalFrame frame;
+
+ /** The JMenuBar that is located at the top left of the Title Pane. */
+ protected JMenuBar menuBar;
+
+ /** The JMenu inside the menuBar. */
+ protected JMenu windowMenu;
+
+ /**
+ * The text color of the TitlePane when the JInternalFrame is not selected.
+ */
+ protected Color notSelectedTextColor;
+
+ /**
+ * The background color of the TitlePane when the JInternalFrame is not
+ * selected.
+ */
+ protected Color notSelectedTitleColor;
+
+ /** The text color of the titlePane when the JInternalFrame is selected. */
+ protected Color selectedTextColor;
+
+ /**
+ * The background color of the TitlePane when the JInternalFrame is
+ * selected.
+ */
+ protected Color selectedTitleColor;
+
+ /** The Property Change listener that listens to the JInternalFrame. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /**
+ * The label used to display the title. This label is not added to the
+ * TitlePane.
+ * This is package-private to avoid an accessor method.
+ */
+ transient JLabel title;
+
+ static
+ {
+ // not constants in JDK
+ CLOSE_CMD = "Close";
+ ICONIFY_CMD = "Minimize";
+ MAXIMIZE_CMD = "Maximize";
+ MOVE_CMD = "Move";
+ RESTORE_CMD = "Restore";
+ SIZE_CMD = "Size";
+ }
+
+ /**
+ * Creates a new BasicInternalFrameTitlePane object that is used in the
+ * given JInternalFrame.
+ *
+ * @param f The JInternalFrame this BasicInternalFrameTitlePane will be used
+ * in.
+ */
+ public BasicInternalFrameTitlePane(JInternalFrame f)
+ {
+ frame = f;
+ setLayout(createLayout());
+ title = new JLabel();
+ title.setHorizontalAlignment(SwingConstants.LEFT);
+ title.setHorizontalTextPosition(SwingConstants.LEFT);
+ title.setOpaque(false);
+ setOpaque(true);
+
+ setBackground(Color.LIGHT_GRAY);
+ setOpaque(true);
+
+ installTitlePane();
+ }
+
+ /**
+ * This method installs the TitlePane onto the JInternalFrameTitlePane. It
+ * also creates any children components that need to be created and adds
+ * listeners to the appropriate components.
+ */
+ protected void installTitlePane()
+ {
+ installDefaults();
+ installListeners();
+ createActions();
+
+ assembleSystemMenu();
+
+ createButtons();
+ setButtonIcons();
+ addSubComponents();
+ enableActions();
+ }
+
+ /**
+ * This method adds the sub components to the TitlePane.
+ */
+ protected void addSubComponents()
+ {
+ add(menuBar);
+
+ add(closeButton);
+ add(iconButton);
+ add(maxButton);
+ }
+
+ /**
+ * This method creates the actions that are used to manipulate the
+ * JInternalFrame.
+ */
+ protected void createActions()
+ {
+ closeAction = new CloseAction();
+ closeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, CLOSE_CMD);
+
+ iconifyAction = new IconifyAction();
+ iconifyAction.putValue(AbstractAction.ACTION_COMMAND_KEY, ICONIFY_CMD);
+
+ maximizeAction = new MaximizeAction();
+ maximizeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, MAXIMIZE_CMD);
+
+ sizeAction = new SizeAction();
+ sizeAction.putValue(AbstractAction.ACTION_COMMAND_KEY, SIZE_CMD);
+
+ restoreAction = new RestoreAction();
+ restoreAction.putValue(AbstractAction.ACTION_COMMAND_KEY, RESTORE_CMD);
+
+ moveAction = new MoveAction();
+ moveAction.putValue(AbstractAction.ACTION_COMMAND_KEY, MOVE_CMD);
+ }
+
+ /**
+ * This method is used to install the listeners.
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+ frame.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * This method is used to uninstall the listeners.
+ */
+ protected void uninstallListeners()
+ {
+ frame.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ }
+
+ /**
+ * This method installs the defaults determined by the look and feel.
+ */
+ protected void installDefaults()
+ {
+ title.setFont(UIManager.getFont("InternalFrame.titleFont"));
+ selectedTextColor = UIManager.getColor("InternalFrame.activeTitleForeground");
+ selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground");
+ notSelectedTextColor = UIManager.getColor("InternalFrame.inactiveTitleForeground");
+ notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground");
+
+ closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
+ iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
+ maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
+ }
+
+ /**
+ * This method uninstalls the defaults.
+ */
+ protected void uninstallDefaults()
+ {
+ setFont(null);
+ selectedTextColor = null;
+ selectedTitleColor = null;
+ notSelectedTextColor = null;
+ notSelectedTitleColor = null;
+
+ closeIcon = null;
+ iconIcon = null;
+ maxIcon = null;
+ }
+
+ /**
+ * This method creates the buttons used in the TitlePane.
+ */
+ protected void createButtons()
+ {
+ closeButton = new PaneButton(closeAction);
+ closeButton.setText(null);
+ if (!frame.isClosable())
+ closeButton.setVisible(false);
+ iconButton = new PaneButton(iconifyAction);
+ iconButton.setText(null);
+ if (!frame.isIconifiable())
+ iconButton.setVisible(false);
+ maxButton = new PaneButton(maximizeAction);
+ maxButton.setText(null);
+ if (!frame.isMaximizable())
+ maxButton.setVisible(false);
+ }
+
+ /**
+ * Set icons for the minimize-, maximize- and close-buttons.
+ */
+ protected void setButtonIcons()
+ {
+ if (closeIcon != null && closeButton != null)
+ closeButton.setIcon(closeIcon);
+ if (iconIcon != null && iconButton != null)
+ iconButton.setIcon(iconIcon);
+ if (maxIcon != null && maxButton != null)
+ maxButton.setIcon(maxIcon);
+ }
+
+ /**
+ * This method creates the MenuBar used in the TitlePane.
+ */
+ protected void assembleSystemMenu()
+ {
+ menuBar = createSystemMenuBar();
+ windowMenu = createSystemMenu();
+
+ menuBar.add(windowMenu);
+
+ addSystemMenuItems(windowMenu);
+ enableActions();
+ }
+
+ /**
+ * This method adds the MenuItems to the given JMenu.
+ *
+ * @param systemMenu The JMenu to add MenuItems to.
+ */
+ protected void addSystemMenuItems(JMenu systemMenu)
+ {
+ JMenuItem tmp;
+
+ tmp = new JMenuItem(RESTORE_CMD);
+ tmp.addActionListener(restoreAction);
+ tmp.setMnemonic(KeyEvent.VK_R);
+ systemMenu.add(tmp);
+
+ tmp = new JMenuItem(MOVE_CMD);
+ tmp.addActionListener(moveAction);
+ tmp.setMnemonic(KeyEvent.VK_M);
+ systemMenu.add(tmp);
+
+ tmp = new JMenuItem(SIZE_CMD);
+ tmp.addActionListener(sizeAction);
+ tmp.setMnemonic(KeyEvent.VK_S);
+ systemMenu.add(tmp);
+
+ tmp = new JMenuItem(ICONIFY_CMD);
+ tmp.addActionListener(iconifyAction);
+ tmp.setMnemonic(KeyEvent.VK_N);
+ systemMenu.add(tmp);
+
+ tmp = new JMenuItem(MAXIMIZE_CMD);
+ tmp.addActionListener(maximizeAction);
+ tmp.setMnemonic(KeyEvent.VK_X);
+ systemMenu.add(tmp);
+
+ systemMenu.addSeparator();
+
+ tmp = new JMenuItem(CLOSE_CMD);
+ tmp.addActionListener(closeAction);
+ tmp.setMnemonic(KeyEvent.VK_C);
+ systemMenu.add(tmp);
+ }
+
+ /**
+ * This method creates a new JMenubar.
+ *
+ * @return A new JMenuBar.
+ */
+ protected JMenuBar createSystemMenuBar()
+ {
+ if (menuBar == null)
+ menuBar = new SystemMenuBar();
+ menuBar.removeAll();
+ return menuBar;
+ }
+
+ /**
+ * This method creates a new JMenu.
+ *
+ * @return A new JMenu.
+ */
+ protected JMenu createSystemMenu()
+ {
+ if (windowMenu == null)
+ windowMenu = new JMenu();
+ windowMenu.removeAll();
+ return windowMenu;
+ }
+
+ /**
+ * This method programmatically shows the JMenu.
+ */
+ protected void showSystemMenu()
+ {
+ // FIXME: Untested as KeyEvents are not hooked up.
+ menuBar.getMenu(1).getPopupMenu().show();
+ }
+
+ /**
+ * This method paints the TitlePane.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paintComponent(Graphics g)
+ {
+ paintTitleBackground(g);
+ if (frame.getTitle() != null && title != null)
+ {
+ Color saved = g.getColor();
+ Font f = title.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+ if (frame.isSelected())
+ g.setColor(selectedTextColor);
+ else
+ g.setColor(notSelectedTextColor);
+ title.setText(getTitle(frame.getTitle(), fm, title.getBounds().width));
+ SwingUtilities.paintComponent(g, title, null, title.getBounds());
+ g.setColor(saved);
+ }
+ }
+
+ /**
+ * This method paints the TitlePane's background.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintTitleBackground(Graphics g)
+ {
+ if (!isOpaque())
+ return;
+
+ Color saved = g.getColor();
+ Dimension dims = getSize();
+
+ Color bg = getBackground();
+ if (frame.isSelected())
+ bg = selectedTitleColor;
+ else
+ bg = notSelectedTitleColor;
+ g.setColor(bg);
+ g.fillRect(0, 0, dims.width, dims.height);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method returns the title string based on the available width and the
+ * font metrics.
+ *
+ * @param text The desired title.
+ * @param fm The FontMetrics of the font used.
+ * @param availableWidth The available width.
+ *
+ * @return The allowable string.
+ */
+ protected String getTitle(String text, FontMetrics fm, int availableWidth)
+ {
+ Rectangle vr = new Rectangle(0, 0, availableWidth, fm.getHeight());
+ Rectangle ir = new Rectangle();
+ Rectangle tr = new Rectangle();
+ String value = SwingUtilities.layoutCompoundLabel(this, fm, text, null,
+ SwingConstants.CENTER,
+ SwingConstants.LEFT,
+ SwingConstants.CENTER,
+ SwingConstants.LEFT, vr,
+ ir, tr, 0);
+ return value;
+ }
+
+ /**
+ * This method fires something similar to a WINDOW_CLOSING event.
+ *
+ * @param frame The JInternalFrame that is being closed.
+ */
+ protected void postClosingEvent(JInternalFrame frame)
+ {
+ // FIXME: Implement postClosingEvent when I figure out what
+ // it's supposed to do.
+ // It says that this fires an WINDOW_CLOSING like event.
+ // So the closest thing is some kind of InternalFrameEvent.
+ // But none is fired.
+ // Can't see it called or anything.
+ }
+
+ /**
+ * This method enables the actions for the TitlePane given the frame's
+ * properties.
+ */
+ protected void enableActions()
+ {
+ closeAction.setEnabled(frame.isClosable());
+
+ iconifyAction.setEnabled(frame.isIconifiable());
+ // The maximize action is responsible for restoring it
+ // as well, if clicked from the button
+ maximizeAction.setEnabled(frame.isMaximizable());
+
+ // The restoring action is only active when selected
+ // from the menu.
+ restoreAction.setEnabled(frame.isMaximum());
+
+ sizeAction.setEnabled(frame.isResizable());
+
+ // FIXME: Tie MoveAction enabled status to a variable.
+ moveAction.setEnabled(false);
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method creates a new LayoutManager for the TitlePane.
+ *
+ * @return A new LayoutManager.
+ */
+ protected LayoutManager createLayout()
+ {
+ return new TitlePaneLayout();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java
new file mode 100644
index 000000000..da37e2bd8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicInternalFrameUI.java
@@ -0,0 +1,1786 @@
+/* BasicInternalFrameUI.java --
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.LayoutManager2;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.DefaultDesktopManager;
+import javax.swing.DesktopManager;
+import javax.swing.JComponent;
+import javax.swing.JDesktopPane;
+import javax.swing.JInternalFrame;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.AbstractBorder;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
+import javax.swing.event.MouseInputAdapter;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.InternalFrameUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * This is the UI delegate for the Basic look and feel for JInternalFrames.
+ */
+public class BasicInternalFrameUI extends InternalFrameUI
+{
+ /**
+ * This is a helper class that listens to the JInternalFrame for
+ * InternalFrameEvents.
+ */
+ protected class BasicInternalFrameListener implements InternalFrameListener
+ {
+ /**
+ * This method is called when the JInternalFrame is activated.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameActivated(InternalFrameEvent e)
+ {
+ frame.getGlassPane().setVisible(false);
+ }
+
+ /**
+ * This method is called when the JInternalFrame is closed.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameClosed(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method is called when the JInternalFrame is closing.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameClosing(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method is called when the JInternalFrame is deactivated.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameDeactivated(InternalFrameEvent e)
+ {
+ frame.getGlassPane().setVisible(true);
+ }
+
+ /**
+ * This method is called when the JInternalFrame is deiconified.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameDeiconified(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method is called when the JInternalFrame is iconified.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameIconified(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method is called when the JInternalFrame is opened.
+ *
+ * @param e The InternalFrameEvent.
+ */
+ public void internalFrameOpened(InternalFrameEvent e)
+ {
+ // FIXME: Implement.
+ }
+ }
+
+ /**
+ * This helper class listens to the edges of the JInternalFrame and the
+ * TitlePane for mouse events. It is responsible for dragging and resizing
+ * the JInternalFrame in response to the MouseEvents.
+ */
+ protected class BorderListener extends MouseInputAdapter
+ implements SwingConstants
+ {
+ /**
+ * The current shape of the cursor.
+ */
+ transient int showingCursor;
+
+ /** FIXME: Use for something. */
+ protected final int RESIZE_NONE = 0;
+
+ /** The x offset from the top left corner of the JInternalFrame. */
+ private transient int xOffset;
+
+ /** The y offset from the top left corner of the JInternalFrame. */
+ private transient int yOffset;
+
+ /** The direction that the resize is occuring in. */
+ private transient int direction = -1;
+
+ /** Cache rectangle that can be reused. */
+ private transient Rectangle cacheRect = new Rectangle();
+
+ /**
+ * This method is called when the mouse is clicked.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ // Do minimization/maximization when double-clicking in the title pane.
+ if (e.getSource() == titlePane && e.getClickCount() == 2)
+ try
+ {
+ if (frame.isMaximizable() && ! frame.isMaximum())
+ frame.setMaximum(true);
+ else if (frame.isMaximum())
+ frame.setMaximum(false);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if the attempt has been vetoed.
+ }
+
+ // There is nothing to do when the mouse is clicked
+ // on the border.
+ }
+
+ /**
+ * This method is called when the mouse is dragged. This method is
+ * responsible for resizing or dragging the JInternalFrame.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ // If the frame is maximized, there is nothing that
+ // can be dragged around.
+ if (frame.isMaximum())
+ return;
+ DesktopManager dm = getDesktopManager();
+ Rectangle b = frame.getBounds();
+ Dimension min = frame.getMinimumSize();
+ if (min == null)
+ min = new Dimension(0, 0);
+ Insets insets = frame.getInsets();
+ int x = e.getX();
+ int y = e.getY();
+ if (e.getSource() == frame && frame.isResizable())
+ {
+ switch (direction)
+ {
+ case Cursor.N_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, Math.min(b.y + y, b.y + b.height
+ - min.height),
+ b.width, b.height - y);
+ break;
+ case Cursor.NE_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, Math.min(b.y + y, b.y + b.height
+ - min.height), x + 1,
+ b.height - y);
+ break;
+ case Cursor.E_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, b.y, x + 1, b.height);
+ break;
+ case Cursor.SE_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, b.y, x + 1, y + 1);
+ break;
+ case Cursor.S_RESIZE_CURSOR:
+ cacheRect.setBounds(b.x, b.y, b.width, y + 1);
+ break;
+ case Cursor.SW_RESIZE_CURSOR:
+ cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width),
+ b.y, b.width - x, y + 1);
+ break;
+ case Cursor.W_RESIZE_CURSOR:
+ cacheRect.setBounds(Math.min(b.x + x, b.x + b.width - min.width),
+ b.y, b.width - x, b.height);
+ break;
+ case Cursor.NW_RESIZE_CURSOR:
+ cacheRect.setBounds(
+ Math.min(b.x + x, b.x + b.width - min.width),
+ Math.min(b.y + y, b.y + b.height - min.height),
+ b.width - x, b.height - y);
+ break;
+ }
+ dm.resizeFrame(frame, cacheRect.x, cacheRect.y,
+ Math.max(min.width, cacheRect.width),
+ Math.max(min.height, cacheRect.height));
+ setCursor(e);
+ }
+ else if (e.getSource() == titlePane)
+ {
+ Rectangle fBounds = frame.getBounds();
+ frame.putClientProperty("bufferedDragging", Boolean.TRUE);
+ dm.dragFrame(frame, e.getX() - xOffset + b.x, e.getY() - yOffset
+ + b.y);
+ }
+ }
+
+ /**
+ * This method is called when the mouse exits the JInternalFrame.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ if (showingCursor != Cursor.DEFAULT_CURSOR)
+ {
+ frame.setCursor(Cursor.getDefaultCursor());
+ showingCursor = Cursor.DEFAULT_CURSOR;
+ }
+ }
+
+ /**
+ * This method is called when the mouse is moved inside the JInternalFrame.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Turn off the resize cursor if we are in the frame header.
+ if (showingCursor != Cursor.DEFAULT_CURSOR && e.getSource() != frame)
+ {
+ frame.setCursor(Cursor.getDefaultCursor());
+ showingCursor = Cursor.DEFAULT_CURSOR;
+ }
+ else if (e.getSource() == frame && frame.isResizable())
+ {
+ setCursor(e);
+ }
+ }
+
+ /**
+ * Set the mouse cursor, how applicable.
+ *
+ * @param e the current mouse event.
+ */
+ void setCursor(MouseEvent e)
+ {
+ int cursor = sectionOfClick(e.getX(), e.getY());
+ if (cursor != showingCursor)
+ {
+ Cursor resize = Cursor.getPredefinedCursor(cursor);
+ frame.setCursor(resize);
+ showingCursor = cursor;
+ }
+ }
+
+ /**
+ * This method is called when the mouse is pressed.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ activateFrame(frame);
+ DesktopManager dm = getDesktopManager();
+ int x = e.getX();
+ int y = e.getY();
+ Insets insets = frame.getInsets();
+
+ if (e.getSource() == frame && frame.isResizable())
+ {
+ direction = sectionOfClick(x, y);
+ dm.beginResizingFrame(frame, direction);
+ }
+ else if (e.getSource() == titlePane)
+ {
+ Rectangle tBounds = titlePane.getBounds();
+
+ xOffset = e.getX() - tBounds.x + insets.left;
+ yOffset = e.getY() - tBounds.y + insets.top;
+
+ dm.beginDraggingFrame(frame);
+ }
+ }
+
+ /**
+ * This method is called when the mouse is released.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ DesktopManager dm = getDesktopManager();
+ xOffset = 0;
+ yOffset = 0;
+ if (e.getSource() == frame && frame.isResizable())
+ dm.endResizingFrame(frame);
+ else if (e.getSource() == titlePane)
+ {
+ dm.endDraggingFrame(frame);
+ frame.putClientProperty("bufferedDragging", null);
+ }
+
+ setCursor(e);
+ }
+
+ /**
+ * This method determines the direction of the resize based on the
+ * coordinates and the size of the JInternalFrame.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ *
+ * @return The cursor constant, determining the resizing direction.
+ */
+ private int sectionOfClick(int x, int y)
+ {
+ Rectangle b = frame.getBounds();
+ int corner = InternalFrameBorder.cornerSize;
+
+ if (x < corner && y < corner)
+ return Cursor.NW_RESIZE_CURSOR;
+ else if (x > b.width - corner && y < corner)
+ return Cursor.NE_RESIZE_CURSOR;
+ else if (x > b.width - corner && y > b.height - corner)
+ return Cursor.SE_RESIZE_CURSOR;
+ else if (x < corner && y > b.height - corner)
+ return Cursor.SW_RESIZE_CURSOR;
+ else if (y < corner)
+ return Cursor.N_RESIZE_CURSOR;
+ else if (x < corner)
+ return Cursor.W_RESIZE_CURSOR;
+ else if (y > b.height - corner)
+ return Cursor.S_RESIZE_CURSOR;
+ else if (x > b.width - corner)
+ return Cursor.E_RESIZE_CURSOR;
+
+ return Cursor.DEFAULT_CURSOR;
+ }
+ }
+
+ /**
+ * This helper class listens to the JDesktopPane that parents this
+ * JInternalFrame and listens for resize events and resizes the
+ * JInternalFrame appropriately.
+ */
+ protected class ComponentHandler implements ComponentListener
+ {
+ /**
+ * This method is called when the JDesktopPane is hidden.
+ *
+ * @param e
+ * The ComponentEvent fired.
+ */
+ public void componentHidden(ComponentEvent e)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called when the JDesktopPane is moved.
+ *
+ * @param e
+ * The ComponentEvent fired.
+ */
+ public void componentMoved(ComponentEvent e)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called when the JDesktopPane is resized.
+ *
+ * @param e
+ * The ComponentEvent fired.
+ */
+ public void componentResized(ComponentEvent e)
+ {
+ if (frame.isMaximum())
+ {
+ Container parent = frame.getParent();
+ Insets i = parent.getInsets();
+ int width = parent.getWidth() - i.left - i.right;
+ int height = parent.getHeight() - i.top - i.bottom;
+ frame.setBounds(0, 0, width, height);
+ }
+ }
+
+ /**
+ * This method is called when the JDesktopPane is shown.
+ *
+ * @param e
+ * The ComponentEvent fired.
+ */
+ public void componentShown(ComponentEvent e)
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * This helper class acts as the LayoutManager for JInternalFrames.
+ */
+ public class InternalFrameLayout implements LayoutManager
+ {
+ /**
+ * This method is called when the given Component is added to the
+ * JInternalFrame.
+ *
+ * @param name
+ * The name of the Component.
+ * @param c
+ * The Component added.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is used to set the bounds of the children of the
+ * JInternalFrame.
+ *
+ * @param c
+ * The Container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+ Dimension dims = frame.getSize();
+ Insets insets = frame.getInsets();
+
+ dims.width -= insets.left + insets.right;
+ dims.height -= insets.top + insets.bottom;
+
+ int nh = 0;
+ int sh = 0;
+ int ew = 0;
+ int ww = 0;
+
+ if (northPane != null)
+ {
+ Dimension nDims = northPane.getPreferredSize();
+ nh = Math.min(nDims.height, dims.height);
+
+ northPane.setBounds(insets.left, insets.top, dims.width, nh);
+ }
+
+ if (southPane != null)
+ {
+ Dimension sDims = southPane.getPreferredSize();
+ sh = Math.min(sDims.height, dims.height - nh);
+
+ southPane.setBounds(insets.left, insets.top + dims.height - sh,
+ dims.width, sh);
+ }
+
+ int remHeight = dims.height - sh - nh;
+
+ if (westPane != null)
+ {
+ Dimension wDims = westPane.getPreferredSize();
+ ww = Math.min(dims.width, wDims.width);
+
+ westPane.setBounds(insets.left, insets.top + nh, ww, remHeight);
+ }
+
+ if (eastPane != null)
+ {
+ Dimension eDims = eastPane.getPreferredSize();
+ ew = Math.min(eDims.width, dims.width - ww);
+
+ eastPane.setBounds(insets.left + dims.width - ew, insets.top + nh,
+ ew, remHeight);
+ }
+
+ int remWidth = dims.width - ww - ew;
+
+ frame.getRootPane().setBounds(insets.left + ww, insets.top + nh,
+ remWidth, remHeight);
+ }
+
+ /**
+ * This method returns the minimum layout size.
+ *
+ * @param c
+ * The Container to find a minimum layout size for.
+ * @return The minimum dimensions for the JInternalFrame.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return getSize(c, true);
+ }
+
+ /**
+ * Th8is method returns the preferred layout size.
+ *
+ * @param c
+ * The Container to find a preferred layout size for.
+ * @return The preferred dimensions for the JInternalFrame.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ return getSize(c, false);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param c
+ * DOCUMENT ME!
+ * @param min
+ * DOCUMENT ME!
+ * @return DOCUMENT ME!
+ */
+ private Dimension getSize(Container c, boolean min)
+ {
+ Insets insets = frame.getInsets();
+
+ Dimension contentDims = frame.getContentPane().getPreferredSize();
+ if (min)
+ contentDims.width = contentDims.height = 0;
+ int nWidth = 0;
+ int nHeight = 0;
+ int sWidth = 0;
+ int sHeight = 0;
+ int eWidth = 0;
+ int eHeight = 0;
+ int wWidth = 0;
+ int wHeight = 0;
+ Dimension dims;
+
+ if (northPane != null)
+ {
+ dims = northPane.getPreferredSize();
+ if (dims != null)
+ {
+ nWidth = dims.width;
+ nHeight = dims.height;
+ }
+ }
+
+ if (southPane != null)
+ {
+ dims = southPane.getPreferredSize();
+ if (dims != null)
+ {
+ sWidth = dims.width;
+ sHeight = dims.height;
+ }
+ }
+
+ if (eastPane != null)
+ {
+ dims = eastPane.getPreferredSize();
+ if (dims != null)
+ {
+ sWidth = dims.width;
+ sHeight = dims.height;
+ }
+ }
+
+ if (westPane != null)
+ {
+ dims = westPane.getPreferredSize();
+ if (dims != null)
+ {
+ wWidth = dims.width;
+ wHeight = dims.height;
+ }
+ }
+
+ int width = Math.max(sWidth, nWidth);
+ width = Math.max(width, contentDims.width + eWidth + wWidth);
+
+ int height = Math.max(eHeight, wHeight);
+ height = Math.max(height, contentDims.height);
+ height += nHeight + sHeight;
+
+ width += insets.left + insets.right;
+ height += insets.top + insets.bottom;
+
+ return new Dimension(width, height);
+ }
+
+ /**
+ * This method is called when a Component is removed from the
+ * JInternalFrame.
+ *
+ * @param c The Component that was removed.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This helper class is used to listen to the JDesktopPane's glassPane for
+ * MouseEvents. The JInternalFrame can then be selected if a click is
+ * detected on its children.
+ */
+ protected class GlassPaneDispatcher implements MouseInputListener
+ {
+ /** The MouseEvent target. */
+ private transient Component mouseEventTarget;
+
+ private Component dragTarget;
+
+ /**
+ * Indicates if we are currently in a dragging operation or not.
+ */
+ private boolean isDragging;
+
+ /**
+ * This method is called when the mouse enters the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is clicked on the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is dragged in the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse exits the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is moved in the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is pressed in the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ // Experiments show that this seems to call the
+ // borderListener.mousePressed() method to activate the frame.
+ if (borderListener != null)
+ borderListener.mousePressed(e);
+ handleEvent(e);
+ }
+
+ /**
+ * This method is called when the mouse is released in the glass pane.
+ *
+ * @param e
+ * The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ handleEvent(e);
+ }
+
+ /**
+ * This is a helper method that dispatches the GlassPane MouseEvents to the
+ * proper component.
+ *
+ * @param e the mouse event to be dispatched
+ */
+ private void handleEvent(MouseEvent e)
+ {
+ // Find candidate component inside the JInternalFrame.
+ Component target = frame.getLayeredPane().findComponentAt(e.getX(),
+ e.getY());
+
+ // Now search upwards to find a component that actually has
+ // a MouseListener attached.
+ while (target != null
+ && target.getMouseListeners().length == 0
+ && target.getMouseMotionListeners().length == 0
+ && target.getMouseWheelListeners().length == 0)
+ {
+ target = target.getParent();
+ }
+
+ if (target != null)
+ {
+ int id = e.getID();
+ switch (id)
+ {
+ case MouseEvent.MOUSE_ENTERED:
+ // Now redispatch the thing.
+ if (! isDragging || frame.isSelected())
+ {
+ mouseEventTarget = target;
+ redispatch(id, e, mouseEventTarget);
+ }
+ break;
+ case MouseEvent.MOUSE_EXITED:
+ if (! isDragging || frame.isSelected())
+ {
+ redispatch(id, e, mouseEventTarget);
+ }
+ break;
+ case MouseEvent.MOUSE_PRESSED:
+ mouseEventTarget = target;
+ redispatch(id, e, mouseEventTarget);
+ // Start dragging.
+ dragTarget = target;
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ if (isDragging)
+ {
+ redispatch(id, e, dragTarget);
+ isDragging = false;
+ }
+ else
+ redispatch(id, e, mouseEventTarget);
+ break;
+ case MouseEvent.MOUSE_CLICKED:
+ redispatch(id, e, mouseEventTarget);
+ break;
+ case MouseEvent.MOUSE_MOVED:
+ if (target != mouseEventTarget)
+ {
+ // Create additional MOUSE_EXITED/MOUSE_ENTERED pairs.
+ redispatch(MouseEvent.MOUSE_EXITED, e, mouseEventTarget);
+ mouseEventTarget = target;
+ redispatch(MouseEvent.MOUSE_ENTERED, e, mouseEventTarget);
+ }
+ redispatch(id, e, mouseEventTarget);
+ break;
+ case MouseEvent.MOUSE_DRAGGED:
+ if (! isDragging)
+ isDragging = true;
+ redispatch(id, e, mouseEventTarget);
+ break;
+ case MouseEvent.MOUSE_WHEEL:
+ redispatch(id, e, mouseEventTarget);
+ break;
+ default:
+ assert false : "Must not reach here";
+ }
+ }
+ }
+
+ /**
+ * Redispatches the event to the real target with the specified id.
+ *
+ * @param id the new event ID
+ * @param e the original event
+ * @param target the real event target
+ */
+ private void redispatch(int id, MouseEvent e, Component target)
+ {
+ Point p = SwingUtilities.convertPoint(frame.getLayeredPane(), e.getX(),
+ e.getY(), target);
+ MouseEvent ev = new MouseEvent(target, id, e.getWhen(),
+ e.getModifiers() | e.getModifiersEx(),
+ p.x, p.y, e.getClickCount(),
+ e.isPopupTrigger());
+ target.dispatchEvent(ev);
+ }
+ }
+
+ /**
+ * This helper class listens for PropertyChangeEvents from the
+ * JInternalFrame.
+ */
+ public class InternalFramePropertyChangeListener
+ implements PropertyChangeListener
+ {
+
+ /**
+ * This method is called when one of the JInternalFrame's properties change.
+ *
+ * @param evt
+ * The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ String property = evt.getPropertyName();
+ if (property.equals(JInternalFrame.IS_MAXIMUM_PROPERTY))
+ {
+ if (frame.isMaximum())
+ maximizeFrame(frame);
+ else
+ minimizeFrame(frame);
+ }
+ else if (property.equals(JInternalFrame.IS_ICON_PROPERTY))
+ {
+ if (frame.isIcon())
+ iconifyFrame(frame);
+ else
+ deiconifyFrame(frame);
+ }
+ else if (property.equals(JInternalFrame.IS_SELECTED_PROPERTY))
+ {
+ Component glassPane = frame.getGlassPane();
+ if (frame.isSelected())
+ {
+ activateFrame(frame);
+ glassPane.setVisible(false);
+ }
+ else
+ {
+ deactivateFrame(frame);
+ glassPane.setVisible(true);
+ }
+ }
+ else if (property.equals(JInternalFrame.ROOT_PANE_PROPERTY)
+ || property.equals(JInternalFrame.GLASS_PANE_PROPERTY))
+ {
+ Component old = (Component) evt.getOldValue();
+ if (old != null)
+ {
+ old.removeMouseListener(glassPaneDispatcher);
+ old.removeMouseMotionListener(glassPaneDispatcher);
+ }
+
+ Component newPane = (Component) evt.getNewValue();
+ if (newPane != null)
+ {
+ newPane.addMouseListener(glassPaneDispatcher);
+ newPane.addMouseMotionListener(glassPaneDispatcher);
+ }
+
+ frame.revalidate();
+ }
+ else if (property.equals(JInternalFrame.IS_CLOSED_PROPERTY))
+ {
+ if (evt.getNewValue() == Boolean.TRUE)
+ {
+ Container parent = frame.getParent();
+ if (parent != null)
+ parent.removeComponentListener(componentListener);
+ closeFrame(frame);
+ }
+ }
+ else if (property.equals("ancestor"))
+ {
+ Container newParent = (Container) evt.getNewValue();
+ Container oldParent = (Container) evt.getOldValue();
+ if (newParent != null)
+ {
+ newParent.addComponentListener(componentListener);
+ }
+ else if (oldParent != null)
+ {
+ oldParent.removeComponentListener(componentListener);
+ }
+ }
+ }
+ }
+
+ /**
+ * This helper class is the border for the JInternalFrame.
+ */
+ class InternalFrameBorder extends AbstractBorder implements
+ UIResource
+ {
+ /**
+ * The width of the border.
+ */
+ static final int bSize = 5;
+
+ /**
+ * The size of the corners (also used by the mouse listener).
+ */
+ static final int cornerSize = 10;
+
+ /**
+ * This method returns whether the border is opaque.
+ *
+ * @return Whether the border is opaque.
+ */
+ public boolean isBorderOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * This method returns the insets of the border.
+ *
+ * @param c
+ * The Component to find border insets for.
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ return new Insets(bSize, bSize, bSize, bSize);
+ }
+
+ /**
+ * This method paints the border.
+ *
+ * @param c
+ * The Component that owns the border.
+ * @param g
+ * The Graphics object to paint with.
+ * @param x
+ * The x coordinate to paint at.
+ * @param y
+ * The y coordinate to paint at.
+ * @param width
+ * The width of the Component.
+ * @param height
+ * The height of the Component.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+ Rectangle b = frame.getBounds();
+
+ Color d = c.getBackground();
+ g.setColor(d);
+ g.fillRect(0, 0, bSize, b.height);
+ g.fillRect(0, 0, b.width, bSize);
+ g.fillRect(0, b.height - bSize, b.width, bSize);
+ g.fillRect(b.width - bSize, 0, bSize, b.height);
+
+ int x1 = 0;
+ int x2 = bSize;
+ int x3 = b.width - bSize;
+ int x4 = b.width;
+
+ int y1 = 0;
+ int y2 = bSize;
+ int y3 = b.height - bSize;
+ int y4 = b.height;
+
+ g.setColor(Color.GRAY);
+ g.fillRect(0, 0, bSize, y4);
+ g.fillRect(0, 0, x4, bSize);
+ g.fillRect(0, y3, b.width, bSize);
+ g.fillRect(x3, 0, bSize, b.height);
+
+ g.fill3DRect(0, cornerSize, bSize, b.height - 2 * cornerSize, false);
+ g.fill3DRect(cornerSize, 0, b.width - 2 * cornerSize, bSize, false);
+ g.fill3DRect(cornerSize, b.height - bSize, b.width - 2 * cornerSize,
+ bSize, false);
+ g.fill3DRect(b.width - bSize, cornerSize, bSize,
+ b.height - 2 * cornerSize, false);
+
+ g.translate(-x, -y);
+ g.setColor(saved);
+ }
+ }
+
+ /**
+ * This action triggers the system menu.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class ShowSystemMenuAction
+ extends AbstractAction
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ if (titlePane != null)
+ {
+ titlePane.showSystemMenu();
+ }
+ }
+ }
+
+ /**
+ * The MouseListener that is responsible for dragging and resizing the
+ * JInternalFrame in response to MouseEvents.
+ */
+ protected MouseInputAdapter borderListener;
+
+ /**
+ * The ComponentListener that is responsible for resizing the JInternalFrame
+ * in response to ComponentEvents from the JDesktopPane.
+ */
+ protected ComponentListener componentListener;
+
+ /**
+ * The MouseListener that is responsible for activating the JInternalFrame
+ * when the mouse press activates one of its descendents.
+ */
+ protected MouseInputListener glassPaneDispatcher;
+
+ /**
+ * The PropertyChangeListener that is responsible for listening to
+ * PropertyChangeEvents from the JInternalFrame.
+ */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The InternalFrameListener that listens to the JInternalFrame. */
+ private transient BasicInternalFrameListener internalFrameListener;
+
+ /** The JComponent placed at the east region of the JInternalFrame. */
+ protected JComponent eastPane;
+
+ /** The JComponent placed at the north region of the JInternalFrame. */
+ protected JComponent northPane;
+
+ /** The JComponent placed at the south region of the JInternalFrame. */
+ protected JComponent southPane;
+
+ /** The JComponent placed at the west region of the JInternalFrame. */
+ protected JComponent westPane;
+
+ /**
+ * The Keystroke bound to open the menu.
+ * @deprecated
+ */
+ protected KeyStroke openMenuKey;
+
+ /** The TitlePane displayed at the top of the JInternalFrame. */
+ protected BasicInternalFrameTitlePane titlePane;
+
+ /** The JInternalFrame this UI is responsible for. */
+ protected JInternalFrame frame;
+
+ /** The LayoutManager used in the JInternalFrame. */
+ protected LayoutManager internalFrameLayout;
+
+ /** The JDesktopPane that is the parent of the JInternalFrame. */
+ private transient JDesktopPane desktopPane;
+
+ /**
+ * Creates a new BasicInternalFrameUI object.
+ *
+ * @param b The JInternalFrame this UI will represent.
+ */
+ public BasicInternalFrameUI(JInternalFrame b)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method will create a new BasicInternalFrameUI for the given
+ * JComponent.
+ *
+ * @param b The JComponent to create a BasicInternalFrameUI for.
+ *
+ * @return A new BasicInternalFrameUI.
+ */
+ public static ComponentUI createUI(JComponent b)
+ {
+ return new BasicInternalFrameUI((JInternalFrame) b);
+ }
+
+ /**
+ * This method installs a UI for the JInternalFrame.
+ *
+ * @param c The JComponent to install this UI on.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JInternalFrame)
+ {
+ frame = (JInternalFrame) c;
+
+ installDefaults();
+ installListeners();
+ installComponents();
+ installKeyboardActions();
+
+ if (! frame.isSelected())
+ frame.getGlassPane().setVisible(true);
+ }
+ }
+
+ /**
+ * This method reverses the work done by installUI.
+ *
+ * @param c The JComponent to uninstall this UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallComponents();
+ uninstallListeners();
+ uninstallDefaults();
+
+ frame.getRootPane().getGlassPane().setVisible(false);
+ frame = null;
+ }
+
+ /**
+ * This method installs the defaults specified by the look and feel.
+ */
+ protected void installDefaults()
+ {
+ internalFrameLayout = createLayoutManager();
+ frame.setLayout(internalFrameLayout);
+ LookAndFeel.installBorder(frame, "InternalFrame.border");
+ frame.setFrameIcon(UIManager.getIcon("InternalFrame.icon"));
+
+ // Let the content pane inherit the background color from its
+ // frame by setting the background to null.
+ Component contentPane = frame.getContentPane();
+ if (contentPane != null
+ && contentPane.getBackground() instanceof UIResource)
+ {
+ contentPane.setBackground(null);
+ }
+ }
+
+ /**
+ * This method installs the keyboard actions for the JInternalFrame.
+ */
+ protected void installKeyboardActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ am.put("showSystemMenu", new ShowSystemMenuAction());
+
+ // The RI impl installs the audio actions as parent of the UI action map,
+ // so do we.
+ BasicLookAndFeel blaf = (BasicLookAndFeel) UIManager.getLookAndFeel();
+ ActionMap audioActionMap = blaf.getAudioActionMap();
+ am.setParent(audioActionMap);
+
+ SwingUtilities.replaceUIActionMap(frame, am);
+ }
+
+ /**
+ * This method installs the Components for the JInternalFrame.
+ */
+ protected void installComponents()
+ {
+ setNorthPane(createNorthPane(frame));
+ setSouthPane(createSouthPane(frame));
+ setEastPane(createEastPane(frame));
+ setWestPane(createWestPane(frame));
+ }
+
+ /**
+ * This method installs the listeners for the JInternalFrame.
+ */
+ protected void installListeners()
+ {
+ glassPaneDispatcher = createGlassPaneDispatcher();
+ createInternalFrameListener();
+ borderListener = createBorderListener(frame);
+ componentListener = createComponentListener();
+ propertyChangeListener = createPropertyChangeListener();
+
+ frame.addMouseListener(borderListener);
+ frame.addMouseMotionListener(borderListener);
+ frame.addInternalFrameListener(internalFrameListener);
+ frame.addPropertyChangeListener(propertyChangeListener);
+ frame.getRootPane().getGlassPane().addMouseListener(glassPaneDispatcher);
+ frame.getRootPane().getGlassPane().addMouseMotionListener(glassPaneDispatcher);
+
+ Container parent = frame.getParent();
+ if (parent != null)
+ {
+ parent.addComponentListener(componentListener);
+ }
+ }
+
+ /**
+ * This method uninstalls the defaults for the JInternalFrame.
+ */
+ protected void uninstallDefaults()
+ {
+ frame.setBorder(null);
+ frame.setLayout(null);
+ internalFrameLayout = null;
+ }
+
+ /**
+ * This method uninstalls the Components for the JInternalFrame.
+ */
+ protected void uninstallComponents()
+ {
+ setNorthPane(null);
+ setSouthPane(null);
+ setEastPane(null);
+ setWestPane(null);
+ }
+
+ /**
+ * This method uninstalls the listeners for the JInternalFrame.
+ */
+ protected void uninstallListeners()
+ {
+
+ Container parent = frame.getParent();
+ if (parent != null)
+ {
+ parent.removeComponentListener(componentListener);
+ }
+ componentListener = null;
+
+ frame.getRootPane().getGlassPane().removeMouseMotionListener(glassPaneDispatcher);
+ frame.getRootPane().getGlassPane().removeMouseListener(glassPaneDispatcher);
+
+ frame.removePropertyChangeListener(propertyChangeListener);
+ frame.removeInternalFrameListener(internalFrameListener);
+ frame.removeMouseMotionListener(borderListener);
+ frame.removeMouseListener(borderListener);
+
+ propertyChangeListener = null;
+
+ borderListener = null;
+ internalFrameListener = null;
+ glassPaneDispatcher = null;
+ }
+
+ /**
+ * This method uninstalls the keyboard actions for the JInternalFrame.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIActionMap(frame, null);
+ SwingUtilities.replaceUIInputMap(frame, JComponent.WHEN_IN_FOCUSED_WINDOW,
+ null);
+ }
+
+ /**
+ * This method creates a new LayoutManager for the JInternalFrame.
+ *
+ * @return A new LayoutManager for the JInternalFrame.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new InternalFrameLayout();
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener for the JInternalFrame.
+ *
+ * @return A new PropertyChangeListener for the JInternalFrame.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new InternalFramePropertyChangeListener();
+ }
+
+ /**
+ * This method returns the preferred size of the given JComponent.
+ *
+ * @param x The JComponent to find a preferred size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent x)
+ {
+ Dimension pref = null;
+ LayoutManager layout = frame.getLayout();
+ if (frame == x && layout != null)
+ pref = layout.preferredLayoutSize(frame);
+ else
+ pref = new Dimension(100, 100);
+ return pref;
+ }
+
+ /**
+ * This method returns the minimum size of the given JComponent.
+ *
+ * @param x The JComponent to find a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent x)
+ {
+ Dimension min = null;
+ LayoutManager layout = frame.getLayout();
+ if (frame == x && layout != null)
+ min = layout.minimumLayoutSize(frame);
+ else
+ min = new Dimension(0, 0);
+ return min;
+ }
+
+ /**
+ * This method returns the maximum size of the given JComponent.
+ *
+ * @param x The JComponent to find a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent x)
+ {
+ Dimension max = null;
+ LayoutManager layout = frame.getLayout();
+ if (frame == x && layout != null && layout instanceof LayoutManager2)
+ max = ((LayoutManager2) layout).maximumLayoutSize(frame);
+ else
+ max = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ return max;
+ }
+
+ /**
+ * This method replaces the currentPane with the newPane. When replacing it
+ * also removes the MouseHandlers for the old pane and installs them on
+ * the new pane.
+ *
+ * @param currentPane The old pane to remove.
+ * @param newPane The new pane to install.
+ */
+ protected void replacePane(JComponent currentPane, JComponent newPane)
+ {
+ if (currentPane != null)
+ {
+ deinstallMouseHandlers(currentPane);
+ frame.remove(currentPane);
+ }
+
+ if (newPane != null)
+ {
+ installMouseHandlers(newPane);
+ frame.add(newPane);
+ }
+ }
+
+ /**
+ * This method removes the necessary MouseListeners from the given
+ * JComponent.
+ *
+ * @param c The JComponent to remove MouseListeners from.
+ */
+ protected void deinstallMouseHandlers(JComponent c)
+ {
+ c.removeMouseListener(borderListener);
+ c.removeMouseMotionListener(borderListener);
+ }
+
+ /**
+ * This method installs the necessary MouseListeners from the given
+ * JComponent.
+ *
+ * @param c The JComponent to install MouseListeners on.
+ */
+ protected void installMouseHandlers(JComponent c)
+ {
+ c.addMouseListener(borderListener);
+ c.addMouseMotionListener(borderListener);
+ }
+
+ /**
+ * This method creates the north pane used in the JInternalFrame.
+ *
+ * @param w The JInternalFrame to create a north pane for.
+ *
+ * @return The north pane.
+ */
+ protected JComponent createNorthPane(JInternalFrame w)
+ {
+ titlePane = new BasicInternalFrameTitlePane(w);
+ return titlePane;
+ }
+
+ /**
+ * This method creates the west pane used in the JInternalFrame.
+ *
+ * @param w The JInternalFrame to create a west pane for.
+ *
+ * @return The west pane.
+ */
+ protected JComponent createWestPane(JInternalFrame w)
+ {
+ return null;
+ }
+
+ /**
+ * This method creates the south pane used in the JInternalFrame.
+ *
+ * @param w The JInternalFrame to create a south pane for.
+ *
+ * @return The south pane.
+ */
+ protected JComponent createSouthPane(JInternalFrame w)
+ {
+ return null;
+ }
+
+ /**
+ * This method creates the east pane used in the JInternalFrame.
+ *
+ * @param w The JInternalFrame to create an east pane for.
+ *
+ * @return The east pane.
+ */
+ protected JComponent createEastPane(JInternalFrame w)
+ {
+ return null;
+ }
+
+ /**
+ * This method returns a new BorderListener for the given JInternalFrame.
+ *
+ * @param w The JIntenalFrame to create a BorderListener for.
+ *
+ * @return A new BorderListener.
+ */
+ protected MouseInputAdapter createBorderListener(JInternalFrame w)
+ {
+ return new BorderListener();
+ }
+
+ /**
+ * This method creates a new InternalFrameListener for the JInternalFrame.
+ */
+ protected void createInternalFrameListener()
+ {
+ internalFrameListener = new BasicInternalFrameListener();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected final boolean isKeyBindingRegistered()
+ {
+ // FIXME: Implement.
+ return false;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param b DOCUMENT ME!
+ */
+ protected final void setKeyBindingRegistered(boolean b)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public final boolean isKeyBindingActive()
+ {
+ // FIXME: Implement.
+ return false;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param b DOCUMENT ME!
+ */
+ protected final void setKeyBindingActive(boolean b)
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ protected void setupMenuOpenKey()
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ protected void setupMenuCloseKey()
+ {
+ // FIXME: Implement.
+ }
+
+ /**
+ * This method returns the north pane.
+ *
+ * @return The north pane.
+ */
+ public JComponent getNorthPane()
+ {
+ return northPane;
+ }
+
+ /**
+ * This method sets the north pane to be the given JComponent.
+ *
+ * @param c The new north pane.
+ */
+ public void setNorthPane(JComponent c)
+ {
+ replacePane(northPane, c);
+ northPane = c;
+ // the following is needed to make internal frames draggable when using
+ // the JGoodies PlasticLookAndFeel, because it overrides the
+ // createNorthPane() method and doesn't assign anything to the titlePane
+ // field. It is possible there is another way to make this work, but
+ // I didn't find it...
+ if (c instanceof BasicInternalFrameTitlePane)
+ titlePane = (BasicInternalFrameTitlePane) c;
+ }
+
+ /**
+ * This method returns the south pane.
+ *
+ * @return The south pane.
+ */
+ public JComponent getSouthPane()
+ {
+ return southPane;
+ }
+
+ /**
+ * This method sets the south pane to be the given JComponent.
+ *
+ * @param c The new south pane.
+ */
+ public void setSouthPane(JComponent c)
+ {
+ replacePane(southPane, c);
+ southPane = c;
+ }
+
+ /**
+ * This method sets the east pane to be the given JComponent.
+ *
+ * @param c The new east pane.
+ */
+ public void setEastPane(JComponent c)
+ {
+ replacePane(eastPane, c);
+ eastPane = c;
+ }
+
+ /**
+ * This method returns the east pane.
+ *
+ * @return The east pane.
+ */
+ public JComponent getEastPane()
+ {
+ return eastPane;
+ }
+
+ /**
+ * This method sets the west pane to be the given JComponent.
+ *
+ * @param c The new west pane.
+ */
+ public void setWestPane(JComponent c)
+ {
+ replacePane(westPane, c);
+ westPane = c;
+ }
+
+ /**
+ * This method returns the west pane.
+ *
+ * @return The west pane.
+ */
+ public JComponent getWestPane()
+ {
+ return westPane;
+ }
+
+ /**
+ * This method returns the DesktopManager to use with the JInternalFrame.
+ *
+ * @return The DesktopManager to use with the JInternalFrame.
+ */
+ protected DesktopManager getDesktopManager()
+ {
+ DesktopManager value = null;
+ JDesktopPane pane = frame.getDesktopPane();
+ if (pane != null)
+ value = frame.getDesktopPane().getDesktopManager();
+ if (value == null)
+ value = createDesktopManager();
+ return value;
+ }
+
+ /**
+ * This method returns a default DesktopManager that can be used with this
+ * JInternalFrame.
+ *
+ * @return A default DesktopManager that can be used with this
+ * JInternalFrame.
+ */
+ protected DesktopManager createDesktopManager()
+ {
+ return new DefaultDesktopManager();
+ }
+
+ /**
+ * This is a convenience method that closes the JInternalFrame.
+ *
+ * @param f The JInternalFrame to close.
+ */
+ protected void closeFrame(JInternalFrame f)
+ {
+ getDesktopManager().closeFrame(f);
+ }
+
+ /**
+ * This is a convenience method that maximizes the JInternalFrame.
+ *
+ * @param f The JInternalFrame to maximize.
+ */
+ protected void maximizeFrame(JInternalFrame f)
+ {
+ getDesktopManager().maximizeFrame(f);
+ }
+
+ /**
+ * This is a convenience method that minimizes the JInternalFrame.
+ *
+ * @param f The JInternalFrame to minimize.
+ */
+ protected void minimizeFrame(JInternalFrame f)
+ {
+ getDesktopManager().minimizeFrame(f);
+ }
+
+ /**
+ * This is a convenience method that iconifies the JInternalFrame.
+ *
+ * @param f The JInternalFrame to iconify.
+ */
+ protected void iconifyFrame(JInternalFrame f)
+ {
+ getDesktopManager().iconifyFrame(f);
+ }
+
+ /**
+ * This is a convenience method that deiconifies the JInternalFrame.
+ *
+ * @param f The JInternalFrame to deiconify.
+ */
+ protected void deiconifyFrame(JInternalFrame f)
+ {
+ getDesktopManager().deiconifyFrame(f);
+ }
+
+ /**
+ * This is a convenience method that activates the JInternalFrame.
+ *
+ * @param f The JInternalFrame to activate.
+ */
+ protected void activateFrame(JInternalFrame f)
+ {
+ getDesktopManager().activateFrame(f);
+ }
+
+ /**
+ * This is a convenience method that deactivates the JInternalFrame.
+ *
+ * @param f the JInternalFrame to deactivate
+ */
+ protected void deactivateFrame(JInternalFrame f)
+ {
+ getDesktopManager().deactivateFrame(f);
+ }
+
+ /**
+ * This method returns a new ComponentListener for the JDesktopPane.
+ *
+ * @return A new ComponentListener.
+ */
+ protected ComponentListener createComponentListener()
+ {
+ return new ComponentHandler();
+ }
+
+ /**
+ * This method returns a new GlassPaneDispatcher.
+ *
+ * @return A new GlassPaneDispatcher.
+ */
+ protected MouseInputListener createGlassPaneDispatcher()
+ {
+ return new GlassPaneDispatcher();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java
new file mode 100644
index 000000000..9469b5695
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicLabelUI.java
@@ -0,0 +1,542 @@
+/* BasicLabelUI.java
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+ This file is part of GNU Classpath.
+
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Classpath is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library. Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module. An independent module is a module which is not derived from
+ or based on this library. If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version. */
+
+package javax.swing.plaf.basic;
+
+import java.awt.Component;
+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.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.LabelUI;
+import javax.swing.text.View;
+
+/**
+ * This is the Basic Look and Feel class for the JLabel. One BasicLabelUI
+ * object is used to paint all JLabels that utilize the Basic Look and Feel.
+ */
+public class BasicLabelUI extends LabelUI implements PropertyChangeListener
+{
+ /** The labelUI that is shared by all labels. */
+ protected static BasicLabelUI labelUI;
+
+ /**
+ * These fields hold the rectangles for the whole label,
+ * the icon and the text.
+ */
+ private Rectangle vr;
+ private Rectangle ir;
+ private Rectangle tr;
+
+ /**
+ * A cached Insets object for reuse in the label layout methods.
+ */
+ private Insets cachedInsets;
+
+ /**
+ * Creates a new BasicLabelUI object.
+ */
+ public BasicLabelUI()
+ {
+ super();
+ vr = new Rectangle();
+ ir = new Rectangle();
+ tr = new Rectangle();
+ }
+
+ /**
+ * Creates and returns a UI for the label. Since one UI is shared by all
+ * labels, this means creating only if necessary and returning the shared
+ * UI.
+ *
+ * @param c The {@link JComponent} that a UI is being created for.
+ *
+ * @return A label UI for the Basic Look and Feel.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ if (labelUI == null)
+ labelUI = new BasicLabelUI();
+ return labelUI;
+ }
+
+ /**
+ * Returns the preferred size of this component as calculated by the
+ * {@link #layoutCL(JLabel, FontMetrics, String, Icon, Rectangle, Rectangle,
+ * Rectangle)} method.
+ *
+ * @param c This {@link JComponent} to get a preferred size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ JLabel lab = (JLabel) c;
+ Insets insets = lab.getInsets();
+ 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 = getFontMetrics(lab);
+ 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;
+ }
+
+ /**
+ * This method returns the minimum size of the {@link JComponent} given. If
+ * this method returns null, then it is up to the Layout Manager to give
+ * this component a minimum size.
+ *
+ * @param c The {@link JComponent} to get a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the maximum size of the {@link JComponent} given. If
+ * this method returns null, then it is up to the Layout Manager to give
+ * this component a maximum size.
+ *
+ * @param c The {@link JComponent} to get a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * The method that paints the label according to its current state.
+ *
+ * @param g The {@link Graphics} object to paint with.
+ * @param c The {@link JComponent} to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ JLabel b = (JLabel) c;
+ Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon();
+ String text = b.getText();
+ if (icon != null || (text != null && ! text.equals("")))
+ {
+ FontMetrics fm = getFontMetrics(b);
+ Insets i = c.getInsets(cachedInsets);
+ 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;
+
+ text = layoutCL(b, fm, text, icon, vr, ir, tr);
+
+ if (icon != null)
+ icon.paintIcon(b, g, ir.x, ir.y);
+
+ if (text != null && ! text.equals(""))
+ {
+ Object htmlRenderer = b.getClientProperty(BasicHTML.propertyKey);
+ if (htmlRenderer == null)
+ {
+ if (b.isEnabled())
+ paintEnabledText(b, g, text, tr.x, tr.y + fm.getAscent());
+ else
+ paintDisabledText(b, g, text, tr.x, tr.y + fm.getAscent());
+ }
+ else
+ {
+ ((View) htmlRenderer).paint(g, tr);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method is simply calls SwingUtilities's layoutCompoundLabel.
+ *
+ * @param label The label to lay out.
+ * @param fontMetrics The FontMetrics for the font used.
+ * @param text The text to paint.
+ * @param icon The icon to draw.
+ * @param viewR The entire viewable rectangle.
+ * @param iconR The icon bounds rectangle.
+ * @param textR The text bounds rectangle.
+ *
+ * @return A possibly clipped version of the text.
+ */
+ protected String layoutCL(JLabel label, FontMetrics fontMetrics, String text,
+ Icon icon, Rectangle viewR, Rectangle iconR, Rectangle textR)
+ {
+ return SwingUtilities.layoutCompoundLabel(label, fontMetrics, text, icon,
+ label.getVerticalAlignment(), label.getHorizontalAlignment(), label
+ .getVerticalTextPosition(), label.getHorizontalTextPosition(),
+ viewR, iconR, textR, label.getIconTextGap());
+ }
+
+ /**
+ * Paints the text if the label is disabled. By default, this paints the
+ * clipped text returned by layoutCompoundLabel using the
+ * background.brighter() color. It also paints the same text using the
+ * background.darker() color one pixel to the right and one pixel down.
+ *
+ * @param l The {@link JLabel} being painted.
+ * @param g The {@link Graphics} object to paint with.
+ * @param s The String to paint.
+ * @param textX The x coordinate of the start of the baseline.
+ * @param textY The y coordinate of the start of the baseline.
+ */
+ protected void paintDisabledText(JLabel l, Graphics g, String s, int textX,
+ int textY)
+ {
+ g.setColor(l.getBackground().brighter());
+
+ int mnemIndex = l.getDisplayedMnemonicIndex();
+
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX,
+ textY);
+ else
+ g.drawString(s, textX, textY);
+
+ g.setColor(l.getBackground().darker());
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX + 1,
+ textY + 1);
+ else
+ g.drawString(s, textX + 1, textY + 1);
+ }
+
+ /**
+ * Paints the text if the label is enabled. The text is painted using the
+ * foreground color.
+ *
+ * @param l The {@link JLabel} being painted.
+ * @param g The {@link Graphics} object to paint with.
+ * @param s The String to paint.
+ * @param textX The x coordinate of the start of the baseline.
+ * @param textY The y coordinate of the start of the baseline.
+ */
+ protected void paintEnabledText(JLabel l, Graphics g, String s, int textX,
+ int textY)
+ {
+ g.setColor(l.getForeground());
+
+ int mnemIndex = l.getDisplayedMnemonicIndex();
+
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, s, mnemIndex, textX,
+ textY);
+ else
+ g.drawString(s, textX, textY);
+ }
+
+ /**
+ * This method installs the UI for the given {@link JComponent}. This
+ * method will install the component, defaults, listeners, and keyboard
+ * actions.
+ *
+ * @param c The {@link JComponent} that this UI is being installed on.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JLabel)
+ {
+ JLabel l = (JLabel) c;
+
+ installComponents(l);
+ installDefaults(l);
+ installListeners(l);
+ installKeyboardActions(l);
+ }
+ }
+
+ /**
+ * This method uninstalls the UI for the given {@link JComponent}. This
+ * method will uninstall the component, defaults, listeners, and keyboard
+ * actions.
+ *
+ * @param c The {@link JComponent} that this UI is being installed on.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ if (c instanceof JLabel)
+ {
+ JLabel l = (JLabel) c;
+
+ uninstallKeyboardActions(l);
+ uninstallListeners(l);
+ uninstallDefaults(l);
+ uninstallComponents(l);
+ }
+ }
+
+ /**
+ * This method installs the components for this {@link JLabel}.
+ *
+ * @param c The {@link JLabel} to install components for.
+ */
+ protected void installComponents(JLabel c)
+ {
+ BasicHTML.updateRenderer(c, c.getText());
+ }
+
+ /**
+ * This method uninstalls the components for this {@link JLabel}.
+ *
+ * @param c The {@link JLabel} to uninstall components for.
+ */
+ protected void uninstallComponents(JLabel c)
+ {
+ c.putClientProperty(BasicHTML.propertyKey, null);
+ c.putClientProperty(BasicHTML.documentBaseKey, null);
+ }
+
+ /**
+ * This method installs the defaults that are defined in the Basic look and
+ * feel for this {@link JLabel}.
+ *
+ * @param c The {@link JLabel} to install defaults for.
+ */
+ protected void installDefaults(JLabel c)
+ {
+ LookAndFeel.installColorsAndFont(c, "Label.background", "Label.foreground",
+ "Label.font");
+ //XXX: There are properties we don't use called disabledForeground
+ //and disabledShadow.
+ }
+
+ /**
+ * This method uninstalls the defaults that are defined in the Basic look
+ * and feel for this {@link JLabel}.
+ *
+ * @param c The {@link JLabel} to uninstall defaults for.
+ */
+ protected void uninstallDefaults(JLabel c)
+ {
+ c.setForeground(null);
+ c.setBackground(null);
+ c.setFont(null);
+ }
+
+ /**
+ * Installs the keyboard actions for the given {@link JLabel}.
+ *
+ * @param l The {@link JLabel} to install keyboard actions for.
+ */
+ protected void installKeyboardActions(JLabel l)
+ {
+ Component c = l.getLabelFor();
+ if (c != null)
+ {
+ int mnemonic = l.getDisplayedMnemonic();
+ if (mnemonic > 0)
+ {
+ // add a keystroke for the given mnemonic mapping to 'press';
+ InputMap keyMap = new InputMap();
+ keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.VK_ALT),
+ "press");
+ SwingUtilities.replaceUIInputMap(l,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, keyMap);
+
+ // add an action to focus the component when 'press' happens
+ ActionMap map = new ActionMap();
+ map.put("press", new AbstractAction() {
+ public void actionPerformed(ActionEvent event)
+ {
+ JLabel label = (JLabel) event.getSource();
+ Component c = label.getLabelFor();
+ if (c != null)
+ c.requestFocus();
+ }
+ });
+ SwingUtilities.replaceUIActionMap(l, map);
+ }
+ }
+ }
+
+ /**
+ * This method uninstalls the keyboard actions for the given {@link JLabel}.
+ *
+ * @param l The {@link JLabel} to uninstall keyboard actions for.
+ */
+ protected void uninstallKeyboardActions(JLabel l)
+ {
+ SwingUtilities.replaceUIActionMap(l, null);
+ SwingUtilities.replaceUIInputMap(l, JComponent.WHEN_IN_FOCUSED_WINDOW,
+ null);
+ }
+
+ /**
+ * This method installs the listeners for the given {@link JLabel}. The UI
+ * delegate only listens to the label.
+ *
+ * @param c The {@link JLabel} to install listeners for.
+ */
+ protected void installListeners(JLabel c)
+ {
+ c.addPropertyChangeListener(this);
+ }
+
+ /**
+ * This method uninstalls the listeners for the given {@link JLabel}. The UI
+ * delegate only listens to the label.
+ *
+ * @param c The {@link JLabel} to uninstall listeners for.
+ */
+ protected void uninstallListeners(JLabel c)
+ {
+ c.removePropertyChangeListener(this);
+ }
+
+ /**
+ * This method is called whenever any JLabel's that use this UI has one of
+ * their properties change.
+ *
+ * @param e The {@link PropertyChangeEvent} that describes the change.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("text"))
+ {
+ String text = (String) e.getNewValue();
+ JLabel l = (JLabel) e.getSource();
+ BasicHTML.updateRenderer(l, text);
+ }
+ else if (e.getPropertyName().equals("displayedMnemonic"))
+ {
+ // update the key to action mapping
+ JLabel label = (JLabel) e.getSource();
+ if (label.getLabelFor() != null)
+ {
+ int oldMnemonic = ((Integer) e.getOldValue()).intValue();
+ int newMnemonic = ((Integer) e.getNewValue()).intValue();
+ InputMap keyMap = label.getInputMap(
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ keyMap.put(KeyStroke.getKeyStroke(oldMnemonic,
+ KeyEvent.ALT_DOWN_MASK), null);
+ keyMap.put(KeyStroke.getKeyStroke(newMnemonic,
+ KeyEvent.ALT_DOWN_MASK), "press");
+ }
+ }
+ else if (e.getPropertyName().equals("labelFor"))
+ {
+ JLabel label = (JLabel) e.getSource();
+ InputMap keyMap = label.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+ int mnemonic = label.getDisplayedMnemonic();
+ if (mnemonic > 0)
+ keyMap.put(KeyStroke.getKeyStroke(mnemonic, KeyEvent.ALT_DOWN_MASK),
+ "press");
+ }
+ }
+
+ /**
+ * Fetches a font metrics object for the specified label. This first
+ * tries to get it from the label object itself by calling
+ * {@link Component#getFontMetrics(Font)}, and if that does not work
+ * (for instance, when we are in the initialization and have no parent yet),
+ * it asks the Toolkit for a font metrics object.
+ *
+ * @param l the label
+ *
+ * @return a suitable font metrics object
+ */
+ private FontMetrics getFontMetrics(JLabel l)
+ {
+ Font font = l.getFont();
+ FontMetrics fm = l.getFontMetrics(font);
+ if (fm == null)
+ {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ fm = tk.getFontMetrics(font);
+ }
+ return fm;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java
new file mode 100644
index 000000000..0e33957f4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicListUI.java
@@ -0,0 +1,1421 @@
+/* BasicListUI.java --
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.CellRendererPane;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JList;
+import javax.swing.ListCellRenderer;
+import javax.swing.ListModel;
+import javax.swing.ListSelectionModel;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.TransferHandler;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ListUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * The Basic Look and Feel UI delegate for the
+ * JList.
+ */
+public class BasicListUI extends ListUI
+{
+
+ /**
+ * A helper class which listens for {@link FocusEvent}s
+ * from the JList.
+ */
+ public class FocusHandler implements FocusListener
+ {
+ /**
+ * Called when the JList acquires focus.
+ *
+ * @param e The FocusEvent representing focus acquisition
+ */
+ public void focusGained(FocusEvent e)
+ {
+ repaintCellFocus();
+ }
+
+ /**
+ * Called when the JList loses focus.
+ *
+ * @param e The FocusEvent representing focus loss
+ */
+ public void focusLost(FocusEvent e)
+ {
+ repaintCellFocus();
+ }
+
+ /**
+ * Helper method to repaint the focused cell's
+ * lost or acquired focus state.
+ */
+ protected void repaintCellFocus()
+ {
+ // TODO: Implement this properly.
+ }
+ }
+
+ /**
+ * A helper class which listens for {@link ListDataEvent}s generated by
+ * the {@link JList}'s {@link ListModel}.
+ *
+ * @see javax.swing.JList#getModel()
+ */
+ public class ListDataHandler implements ListDataListener
+ {
+ /**
+ * Called when a general change has happened in the model which cannot
+ * be represented in terms of a simple addition or deletion.
+ *
+ * @param e The event representing the change
+ */
+ public void contentsChanged(ListDataEvent e)
+ {
+ updateLayoutStateNeeded |= modelChanged;
+ list.revalidate();
+ }
+
+ /**
+ * Called when an interval of objects has been added to the model.
+ *
+ * @param e The event representing the addition
+ */
+ public void intervalAdded(ListDataEvent e)
+ {
+ updateLayoutStateNeeded |= modelChanged;
+ list.revalidate();
+ }
+
+ /**
+ * Called when an inteval of objects has been removed from the model.
+ *
+ * @param e The event representing the removal
+ */
+ public void intervalRemoved(ListDataEvent e)
+ {
+ updateLayoutStateNeeded |= modelChanged;
+ list.revalidate();
+ }
+ }
+
+ /**
+ * A helper class which listens for {@link ListSelectionEvent}s
+ * from the {@link JList}'s {@link ListSelectionModel}.
+ */
+ public class ListSelectionHandler implements ListSelectionListener
+ {
+ /**
+ * Called when the list selection changes.
+ *
+ * @param e The event representing the change
+ */
+ public void valueChanged(ListSelectionEvent e)
+ {
+ int index1 = e.getFirstIndex();
+ int index2 = e.getLastIndex();
+ Rectangle damaged = getCellBounds(list, index1, index2);
+ if (damaged != null)
+ list.repaint(damaged);
+ }
+ }
+
+ /**
+ * This class is used to mimmic the behaviour of the JDK when registering
+ * keyboard actions. It is the same as the private class used in JComponent
+ * for the same reason. This class receives an action event and dispatches
+ * it to the true receiver after altering the actionCommand property of the
+ * event.
+ */
+ private static class ActionListenerProxy
+ extends AbstractAction
+ {
+ ActionListener target;
+ String bindingCommandName;
+
+ public ActionListenerProxy(ActionListener li,
+ String cmd)
+ {
+ target = li;
+ bindingCommandName = cmd;
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ ActionEvent derivedEvent = new ActionEvent(e.getSource(),
+ e.getID(),
+ bindingCommandName,
+ e.getModifiers());
+ target.actionPerformed(derivedEvent);
+ }
+ }
+
+ /**
+ * Implements the action for the JList's keyboard commands.
+ */
+ private class ListAction
+ extends AbstractAction
+ {
+ // TODO: Maybe make a couple of classes out of this bulk Action.
+ // Form logical groups of Actions when doing this.
+
+ /**
+ * Creates a new ListAction for the specified command.
+ *
+ * @param cmd the actionCommand to set
+ */
+ ListAction(String cmd)
+ {
+ putValue(ACTION_COMMAND_KEY, cmd);
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ int lead = list.getLeadSelectionIndex();
+ int max = list.getModel().getSize() - 1;
+ DefaultListSelectionModel selModel
+ = (DefaultListSelectionModel) list.getSelectionModel();
+ String command = e.getActionCommand();
+ // Do nothing if list is empty
+ if (max == -1)
+ return;
+
+ if (command.equals("selectNextRow"))
+ {
+ selectNextIndex();
+ }
+ else if (command.equals("selectPreviousRow"))
+ {
+ selectPreviousIndex();
+ }
+ else if (command.equals("clearSelection"))
+ {
+ list.clearSelection();
+ }
+ else if (command.equals("selectAll"))
+ {
+ list.setSelectionInterval(0, max);
+ // this next line is to restore the lead selection index to the old
+ // position, because select-all should not change the lead index
+ list.addSelectionInterval(lead, lead);
+ }
+ else if (command.equals("selectLastRow"))
+ {
+ list.setSelectedIndex(list.getModel().getSize() - 1);
+ }
+ else if (command.equals("selectLastRowChangeLead"))
+ {
+ selModel.moveLeadSelectionIndex(list.getModel().getSize() - 1);
+ }
+ else if (command.equals("scrollDownExtendSelection"))
+ {
+ int target;
+ if (lead == list.getLastVisibleIndex())
+ {
+ target = Math.min(max, lead + (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getLastVisibleIndex();
+ selModel.setLeadSelectionIndex(target);
+ }
+ else if (command.equals("scrollDownChangeLead"))
+ {
+ int target;
+ if (lead == list.getLastVisibleIndex())
+ {
+ target = Math.min(max, lead + (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getLastVisibleIndex();
+ selModel.moveLeadSelectionIndex(target);
+ }
+ else if (command.equals("scrollUpExtendSelection"))
+ {
+ int target;
+ if (lead == list.getFirstVisibleIndex())
+ {
+ target = Math.max(0, lead - (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getFirstVisibleIndex();
+ selModel.setLeadSelectionIndex(target);
+ }
+ else if (command.equals("scrollUpChangeLead"))
+ {
+ int target;
+ if (lead == list.getFirstVisibleIndex())
+ {
+ target = Math.max(0, lead - (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getFirstVisibleIndex();
+ selModel.moveLeadSelectionIndex(target);
+ }
+ else if (command.equals("selectNextRowExtendSelection"))
+ {
+ selModel.setLeadSelectionIndex(Math.min(lead + 1, max));
+ }
+ else if (command.equals("selectFirstRow"))
+ {
+ list.setSelectedIndex(0);
+ }
+ else if (command.equals("selectFirstRowChangeLead"))
+ {
+ selModel.moveLeadSelectionIndex(0);
+ }
+ else if (command.equals("selectFirstRowExtendSelection"))
+ {
+ selModel.setLeadSelectionIndex(0);
+ }
+ else if (command.equals("selectPreviousRowExtendSelection"))
+ {
+ selModel.setLeadSelectionIndex(Math.max(0, lead - 1));
+ }
+ else if (command.equals("scrollUp"))
+ {
+ int target;
+ if (lead == list.getFirstVisibleIndex())
+ {
+ target = Math.max(0, lead - (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getFirstVisibleIndex();
+ list.setSelectedIndex(target);
+ }
+ else if (command.equals("selectLastRowExtendSelection"))
+ {
+ selModel.setLeadSelectionIndex(list.getModel().getSize() - 1);
+ }
+ else if (command.equals("scrollDown"))
+ {
+ int target;
+ if (lead == list.getLastVisibleIndex())
+ {
+ target = Math.min(max, lead + (list.getLastVisibleIndex()
+ - list.getFirstVisibleIndex() + 1));
+ }
+ else
+ target = list.getLastVisibleIndex();
+ list.setSelectedIndex(target);
+ }
+ else if (command.equals("selectNextRowChangeLead"))
+ {
+ if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ selectNextIndex();
+ else
+ {
+ selModel.moveLeadSelectionIndex(Math.min(max, lead + 1));
+ }
+ }
+ else if (command.equals("selectPreviousRowChangeLead"))
+ {
+ if (selModel.getSelectionMode() != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ selectPreviousIndex();
+ else
+ {
+ selModel.moveLeadSelectionIndex(Math.max(0, lead - 1));
+ }
+ }
+ else if (command.equals("addToSelection"))
+ {
+ list.addSelectionInterval(lead, lead);
+ }
+ else if (command.equals("extendTo"))
+ {
+ selModel.setSelectionInterval(selModel.getAnchorSelectionIndex(),
+ lead);
+ }
+ else if (command.equals("toggleAndAnchor"))
+ {
+ if (!list.isSelectedIndex(lead))
+ list.addSelectionInterval(lead, lead);
+ else
+ list.removeSelectionInterval(lead, lead);
+ selModel.setAnchorSelectionIndex(lead);
+ }
+ else
+ {
+ // DEBUG: uncomment the following line to print out
+ // key bindings that aren't implemented yet
+
+ // System.out.println ("not implemented: "+e.getActionCommand());
+ }
+
+ list.ensureIndexIsVisible(list.getLeadSelectionIndex());
+ }
+ }
+
+ /**
+ * A helper class which listens for {@link MouseEvent}s
+ * from the {@link JList}.
+ */
+ public class MouseInputHandler implements MouseInputListener
+ {
+ /**
+ * Called when a mouse button press/release cycle completes
+ * on the {@link JList}
+ *
+ * @param event The event representing the mouse click
+ */
+ public void mouseClicked(MouseEvent event)
+ {
+ Point click = event.getPoint();
+ int index = locationToIndex(list, click);
+ if (index == -1)
+ return;
+ if (event.isShiftDown())
+ {
+ if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
+ list.setSelectedIndex(index);
+ else if (list.getSelectionMode() ==
+ ListSelectionModel.SINGLE_INTERVAL_SELECTION)
+ // COMPAT: the IBM VM is compatible with the following line of code.
+ // However, compliance with Sun's VM would correspond to replacing
+ // getAnchorSelectionIndex() with getLeadSelectionIndex().This is
+ // both unnatural and contradictory to the way they handle other
+ // similar UI interactions.
+ list.setSelectionInterval(list.getAnchorSelectionIndex(), index);
+ else
+ // COMPAT: both Sun and IBM are compatible instead with:
+ // list.setSelectionInterval
+ // (list.getLeadSelectionIndex(),index);
+ // Note that for IBM this is contradictory to what they did in
+ // the above situation for SINGLE_INTERVAL_SELECTION.
+ // The most natural thing to do is the following:
+ if (list.isSelectedIndex(list.getAnchorSelectionIndex()))
+ list.getSelectionModel().setLeadSelectionIndex(index);
+ else
+ list.addSelectionInterval(list.getAnchorSelectionIndex(), index);
+ }
+ else if (event.isControlDown())
+ {
+ if (list.getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
+ list.setSelectedIndex(index);
+ else if (list.isSelectedIndex(index))
+ list.removeSelectionInterval(index, index);
+ else
+ list.addSelectionInterval(index, index);
+ }
+ else
+ list.setSelectedIndex(index);
+
+ list.ensureIndexIsVisible(list.getLeadSelectionIndex());
+ }
+
+ /**
+ * Called when a mouse button is pressed down on the
+ * {@link JList}.
+ *
+ * @param event The event representing the mouse press
+ */
+ public void mousePressed(MouseEvent event)
+ {
+ // We need to explicitly request focus.
+ list.requestFocusInWindow();
+ }
+
+ /**
+ * Called when a mouse button is released on
+ * the {@link JList}
+ *
+ * @param event The event representing the mouse press
+ */
+ public void mouseReleased(MouseEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Called when the mouse pointer enters the area bounded
+ * by the {@link JList}
+ *
+ * @param event The event representing the mouse entry
+ */
+ public void mouseEntered(MouseEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Called when the mouse pointer leaves the area bounded
+ * by the {@link JList}
+ *
+ * @param event The event representing the mouse exit
+ */
+ public void mouseExited(MouseEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Called when the mouse pointer moves over the area bounded
+ * by the {@link JList} while a button is held down.
+ *
+ * @param event The event representing the mouse drag
+ */
+ public void mouseDragged(MouseEvent event)
+ {
+ Point click = event.getPoint();
+ int index = locationToIndex(list, click);
+ if (index == -1)
+ return;
+ if (!event.isShiftDown() && !event.isControlDown())
+ list.setSelectedIndex(index);
+
+ list.ensureIndexIsVisible(list.getLeadSelectionIndex());
+ }
+
+ /**
+ * Called when the mouse pointer moves over the area bounded
+ * by the {@link JList}.
+ *
+ * @param event The event representing the mouse move
+ */
+ public void mouseMoved(MouseEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * Helper class which listens to {@link PropertyChangeEvent}s
+ * from the {@link JList}.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Called when the {@link JList} changes one of its bound properties.
+ *
+ * @param e The event representing the property change
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("model"))
+ {
+ if (e.getOldValue() != null && e.getOldValue() instanceof ListModel)
+ {
+ ListModel oldModel = (ListModel) e.getOldValue();
+ oldModel.removeListDataListener(listDataListener);
+ }
+ if (e.getNewValue() != null && e.getNewValue() instanceof ListModel)
+ {
+ ListModel newModel = (ListModel) e.getNewValue();
+ newModel.addListDataListener(BasicListUI.this.listDataListener);
+ }
+
+ updateLayoutStateNeeded |= modelChanged;
+ }
+ else if (e.getPropertyName().equals("selectionModel"))
+ updateLayoutStateNeeded |= selectionModelChanged;
+ else if (e.getPropertyName().equals("font"))
+ updateLayoutStateNeeded |= fontChanged;
+ else if (e.getPropertyName().equals("fixedCellWidth"))
+ updateLayoutStateNeeded |= fixedCellWidthChanged;
+ else if (e.getPropertyName().equals("fixedCellHeight"))
+ updateLayoutStateNeeded |= fixedCellHeightChanged;
+ else if (e.getPropertyName().equals("prototypeCellValue"))
+ updateLayoutStateNeeded |= prototypeCellValueChanged;
+ else if (e.getPropertyName().equals("cellRenderer"))
+ updateLayoutStateNeeded |= cellRendererChanged;
+ }
+ }
+
+ /**
+ * A constant to indicate that the model has changed.
+ */
+ protected static final int modelChanged = 1;
+
+ /**
+ * A constant to indicate that the selection model has changed.
+ */
+ protected static final int selectionModelChanged = 2;
+
+ /**
+ * A constant to indicate that the font has changed.
+ */
+ protected static final int fontChanged = 4;
+
+ /**
+ * A constant to indicate that the fixedCellWidth has changed.
+ */
+ protected static final int fixedCellWidthChanged = 8;
+
+ /**
+ * A constant to indicate that the fixedCellHeight has changed.
+ */
+ protected static final int fixedCellHeightChanged = 16;
+
+ /**
+ * A constant to indicate that the prototypeCellValue has changed.
+ */
+ protected static final int prototypeCellValueChanged = 32;
+
+ /**
+ * A constant to indicate that the cellRenderer has changed.
+ */
+ protected static final int cellRendererChanged = 64;
+
+ /**
+ * Creates a new BasicListUI for the component.
+ *
+ * @param c The component to create a UI for
+ *
+ * @return A new UI
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ return new BasicListUI();
+ }
+
+ /** The current focus listener. */
+ protected FocusListener focusListener;
+
+ /** The data listener listening to the model. */
+ protected ListDataListener listDataListener;
+
+ /** The selection listener listening to the selection model. */
+ protected ListSelectionListener listSelectionListener;
+
+ /** The mouse listener listening to the list. */
+ protected MouseInputListener mouseInputListener;
+
+ /** The property change listener listening to the list. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** Saved reference to the list this UI was created for. */
+ protected JList list;
+
+ /**
+ * The height of a single cell in the list. This field is used when the
+ * fixedCellHeight property of the list is set. Otherwise this field is
+ * set to <code>-1</code> and {@link #cellHeights} is used instead.
+ */
+ protected int cellHeight;
+
+ /** The width of a single cell in the list. */
+ protected int cellWidth;
+
+ /**
+ * An array of varying heights of cells in the list, in cases where each
+ * cell might have a different height. This field is used when the
+ * <code>fixedCellHeight</code> property of the list is not set. Otherwise
+ * this field is <code>null</code> and {@link #cellHeight} is used.
+ */
+ protected int[] cellHeights;
+
+ /**
+ * A bitmask that indicates which properties of the JList have changed.
+ * When nonzero, indicates that the UI class is out of
+ * date with respect to the underlying list, and must recalculate the
+ * list layout before painting or performing size calculations.
+ *
+ * @see #modelChanged
+ * @see #selectionModelChanged
+ * @see #fontChanged
+ * @see #fixedCellWidthChanged
+ * @see #fixedCellHeightChanged
+ * @see #prototypeCellValueChanged
+ * @see #cellRendererChanged
+ */
+ protected int updateLayoutStateNeeded;
+
+ /**
+ * The {@link CellRendererPane} that is used for painting.
+ */
+ protected CellRendererPane rendererPane;
+
+ /** The action bound to KeyStrokes. */
+ ListAction action;
+
+ /**
+ * Calculate the height of a particular row. If there is a fixed {@link
+ * #cellHeight}, return it; otherwise return the specific row height
+ * requested from the {@link #cellHeights} array. If the requested row
+ * is invalid, return <code>-1</code>.
+ *
+ * @param row The row to get the height of
+ *
+ * @return The height, in pixels, of the specified row
+ */
+ protected int getRowHeight(int row)
+ {
+ int height;
+ if (cellHeights == null)
+ height = cellHeight;
+ else
+ {
+ if (row < 0 || row >= cellHeights.length)
+ height = -1;
+ else
+ height = cellHeights[row];
+ }
+ return height;
+ }
+
+ /**
+ * Calculate the bounds of a particular cell, considering the upper left
+ * corner of the list as the origin position <code>(0,0)</code>.
+ *
+ * @param l Ignored; calculates over <code>this.list</code>
+ * @param index1 The first row to include in the bounds
+ * @param index2 The last row to incude in the bounds
+ *
+ * @return A rectangle encompassing the range of rows between
+ * <code>index1</code> and <code>index2</code> inclusive, or null
+ * such a rectangle couldn't be calculated for the given indexes.
+ */
+ public Rectangle getCellBounds(JList l, int index1, int index2)
+ {
+ maybeUpdateLayoutState();
+
+ if (l != list || cellWidth == -1)
+ return null;
+
+ int minIndex = Math.min(index1, index2);
+ int maxIndex = Math.max(index1, index2);
+ Point loc = indexToLocation(list, minIndex);
+
+ // When the layoutOrientation is VERTICAL, then the width == the list
+ // width. Otherwise the cellWidth field is used.
+ int width = cellWidth;
+ if (l.getLayoutOrientation() == JList.VERTICAL)
+ width = l.getWidth();
+
+ Rectangle bounds = new Rectangle(loc.x, loc.y, width,
+ getCellHeight(minIndex));
+ for (int i = minIndex + 1; i <= maxIndex; i++)
+ {
+ Point hiLoc = indexToLocation(list, i);
+ bounds = SwingUtilities.computeUnion(hiLoc.x, hiLoc.y, width,
+ getCellHeight(i), bounds);
+ }
+
+ return bounds;
+ }
+
+ /**
+ * Calculates the maximum cell height.
+ *
+ * @param index the index of the cell
+ *
+ * @return the maximum cell height
+ */
+ private int getCellHeight(int index)
+ {
+ int height = cellHeight;
+ if (height <= 0)
+ {
+ if (list.getLayoutOrientation() == JList.VERTICAL)
+ height = getRowHeight(index);
+ else
+ {
+ for (int j = 0; j < cellHeights.length; j++)
+ height = Math.max(height, cellHeights[j]);
+ }
+ }
+ return height;
+ }
+
+ /**
+ * Calculate the Y coordinate of the upper edge of a particular row,
+ * considering the Y coordinate <code>0</code> to occur at the top of the
+ * list.
+ *
+ * @param row The row to calculate the Y coordinate of
+ *
+ * @return The Y coordinate of the specified row, or <code>-1</code> if
+ * the specified row number is invalid
+ */
+ protected int convertRowToY(int row)
+ {
+ int y = 0;
+ for (int i = 0; i < row; ++i)
+ {
+ int h = getRowHeight(i);
+ if (h == -1)
+ return -1;
+ y += h;
+ }
+ return y;
+ }
+
+ /**
+ * Calculate the row number containing a particular Y coordinate,
+ * considering the Y coodrinate <code>0</code> to occur at the top of the
+ * list.
+ *
+ * @param y0 The Y coordinate to calculate the row number for
+ *
+ * @return The row number containing the specified Y value, or <code>-1</code>
+ * if the list model is empty
+ *
+ * @specnote This method is specified to return -1 for an invalid Y
+ * coordinate. However, some simple tests show that the behaviour
+ * is to return the index of the last list element for an Y
+ * coordinate that lies outside of the list bounds (even for
+ * negative indices). <code>-1</code>
+ * is only returned if the list model is empty.
+ */
+ protected int convertYToRow(int y0)
+ {
+ if (list.getModel().getSize() == 0)
+ return -1;
+
+ // When y0 < 0, then the JDK returns the maximum row index of the list. So
+ // do we.
+ if (y0 < 0)
+ return list.getModel().getSize() - 1;
+
+ // Update the layout if necessary.
+ maybeUpdateLayoutState();
+
+ int index = list.getModel().getSize() - 1;
+
+ // If a fixed cell height is set, then we can work more efficient.
+ if (cellHeight > 0)
+ index = Math.min(y0 / cellHeight, index);
+ // If we have no fixed cell height, we must add up each cell height up
+ // to y0.
+ else
+ {
+ int h = 0;
+ for (int row = 0; row < cellHeights.length; ++row)
+ {
+ h += cellHeights[row];
+ if (y0 < h)
+ {
+ index = row;
+ break;
+ }
+ }
+ }
+ return index;
+ }
+
+ /**
+ * Recomputes the {@link #cellHeights}, {@link #cellHeight}, and {@link
+ * #cellWidth} properties by examining the variouis properties of the
+ * {@link JList}.
+ */
+ protected void updateLayoutState()
+ {
+ int nrows = list.getModel().getSize();
+ cellHeight = -1;
+ cellWidth = -1;
+ if (cellHeights == null || cellHeights.length != nrows)
+ cellHeights = new int[nrows];
+ ListCellRenderer rend = list.getCellRenderer();
+ // Update the cellHeight(s) fields.
+ int fixedCellHeight = list.getFixedCellHeight();
+ if (fixedCellHeight > 0)
+ {
+ cellHeight = fixedCellHeight;
+ cellHeights = null;
+ }
+ else
+ {
+ cellHeight = -1;
+ for (int i = 0; i < nrows; ++i)
+ {
+ Component flyweight =
+ rend.getListCellRendererComponent(list,
+ list.getModel().getElementAt(i),
+ i, list.isSelectedIndex(i),
+ list.getSelectionModel().getAnchorSelectionIndex() == i);
+ Dimension dim = flyweight.getPreferredSize();
+ cellHeights[i] = dim.height;
+ }
+ }
+
+ // Update the cellWidth field.
+ int fixedCellWidth = list.getFixedCellWidth();
+ if (fixedCellWidth > 0)
+ cellWidth = fixedCellWidth;
+ else
+ {
+ for (int i = 0; i < nrows; ++i)
+ {
+ Component flyweight =
+ rend.getListCellRendererComponent(list,
+ list.getModel().getElementAt(i),
+ i, list.isSelectedIndex(i),
+ list.getSelectionModel().getAnchorSelectionIndex() == i);
+ Dimension dim = flyweight.getPreferredSize();
+ cellWidth = Math.max(cellWidth, dim.width);
+ }
+ }
+ }
+
+ /**
+ * Calls {@link #updateLayoutState} if {@link #updateLayoutStateNeeded}
+ * is nonzero, then resets {@link #updateLayoutStateNeeded} to zero.
+ */
+ protected void maybeUpdateLayoutState()
+ {
+ if (updateLayoutStateNeeded != 0 || !list.isValid())
+ {
+ updateLayoutState();
+ updateLayoutStateNeeded = 0;
+ }
+ }
+
+ /**
+ * Creates a new BasicListUI object.
+ */
+ public BasicListUI()
+ {
+ updateLayoutStateNeeded = 1;
+ rendererPane = new CellRendererPane();
+ }
+
+ /**
+ * Installs various default settings (mostly colors) from the {@link
+ * UIDefaults} into the {@link JList}
+ *
+ * @see #uninstallDefaults
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(list, "List.background",
+ "List.foreground", "List.font");
+ list.setSelectionForeground(UIManager.getColor("List.selectionForeground"));
+ list.setSelectionBackground(UIManager.getColor("List.selectionBackground"));
+ list.setOpaque(true);
+ }
+
+ /**
+ * Resets to <code>null</code> those defaults which were installed in
+ * {@link #installDefaults}
+ */
+ protected void uninstallDefaults()
+ {
+ list.setForeground(null);
+ list.setBackground(null);
+ list.setSelectionForeground(null);
+ list.setSelectionBackground(null);
+ }
+
+ /**
+ * Attaches all the listeners we have in the UI class to the {@link
+ * JList}, its model and its selection model.
+ *
+ * @see #uninstallListeners
+ */
+ protected void installListeners()
+ {
+ if (focusListener == null)
+ focusListener = createFocusListener();
+ list.addFocusListener(focusListener);
+ if (listDataListener == null)
+ listDataListener = createListDataListener();
+ list.getModel().addListDataListener(listDataListener);
+ if (listSelectionListener == null)
+ listSelectionListener = createListSelectionListener();
+ list.addListSelectionListener(listSelectionListener);
+ if (mouseInputListener == null)
+ mouseInputListener = createMouseInputListener();
+ list.addMouseListener(mouseInputListener);
+ list.addMouseMotionListener(mouseInputListener);
+ if (propertyChangeListener == null)
+ propertyChangeListener = createPropertyChangeListener();
+ list.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Detaches all the listeners we attached in {@link #installListeners}.
+ */
+ protected void uninstallListeners()
+ {
+ list.removeFocusListener(focusListener);
+ list.getModel().removeListDataListener(listDataListener);
+ list.removeListSelectionListener(listSelectionListener);
+ list.removeMouseListener(mouseInputListener);
+ list.removeMouseMotionListener(mouseInputListener);
+ list.removePropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Installs keyboard actions for this UI in the {@link JList}.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install UI InputMap.
+ InputMap focusInputMap = (InputMap) UIManager.get("List.focusInputMap");
+ SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED,
+ focusInputMap);
+
+ // Install UI ActionMap.
+ ActionMap am = (ActionMap) UIManager.get("List.actionMap");
+ if (am == null)
+ {
+ // Create the actionMap once and store it in the current UIDefaults
+ // for use in other components.
+ am = new ActionMapUIResource();
+ ListAction action;
+ action = new ListAction("selectPreviousRow");
+ am.put("selectPreviousRow", action);
+ action = new ListAction("selectNextRow");
+ am.put("selectNextRow", action);
+ action = new ListAction("selectPreviousRowExtendSelection");
+ am.put("selectPreviousRowExtendSelection", action);
+ action = new ListAction("selectNextRowExtendSelection");
+ am.put("selectNextRowExtendSelection", action);
+
+ action = new ListAction("selectPreviousColumn");
+ am.put("selectPreviousColumn", action);
+ action = new ListAction("selectNextColumn");
+ am.put("selectNextColumn", action);
+ action = new ListAction("selectPreviousColumnExtendSelection");
+ am.put("selectPreviousColumnExtendSelection", action);
+ action = new ListAction("selectNextColumnExtendSelection");
+ am.put("selectNextColumnExtendSelection", action);
+
+ action = new ListAction("selectFirstRow");
+ am.put("selectFirstRow", action);
+ action = new ListAction("selectLastRow");
+ am.put("selectLastRow", action);
+ action = new ListAction("selectFirstRowExtendSelection");
+ am.put("selectFirstRowExtendSelection", action);
+ action = new ListAction("selectLastRowExtendSelection");
+ am.put("selectLastRowExtendSelection", action);
+
+ action = new ListAction("scrollUp");
+ am.put("scrollUp", action);
+ action = new ListAction("scrollUpExtendSelection");
+ am.put("scrollUpExtendSelection", action);
+ action = new ListAction("scrollDown");
+ am.put("scrollDown", action);
+ action = new ListAction("scrollDownExtendSelection");
+ am.put("scrollDownExtendSelection", action);
+
+ action = new ListAction("selectAll");
+ am.put("selectAll", action);
+ action = new ListAction("clearSelection");
+ am.put("clearSelection", action);
+
+ am.put("copy", TransferHandler.getCopyAction());
+ am.put("cut", TransferHandler.getCutAction());
+ am.put("paste", TransferHandler.getPasteAction());
+
+ UIManager.put("List.actionMap", am);
+ }
+
+ SwingUtilities.replaceUIActionMap(list, am);
+ }
+
+ /**
+ * Uninstalls keyboard actions for this UI in the {@link JList}.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ // Uninstall the InputMap.
+ InputMap im = SwingUtilities.getUIInputMap(list, JComponent.WHEN_FOCUSED);
+ if (im instanceof UIResource)
+ SwingUtilities.replaceUIInputMap(list, JComponent.WHEN_FOCUSED, null);
+
+ // Uninstall the ActionMap.
+ if (SwingUtilities.getUIActionMap(list) instanceof UIResource)
+ SwingUtilities.replaceUIActionMap(list, null);
+ }
+
+ /**
+ * Installs the various aspects of the UI in the {@link JList}. In
+ * particular, calls {@link #installDefaults}, {@link #installListeners}
+ * and {@link #installKeyboardActions}. Also saves a reference to the
+ * provided component, cast to a {@link JList}.
+ *
+ * @param c The {@link JList} to install the UI into
+ */
+ public void installUI(final JComponent c)
+ {
+ super.installUI(c);
+ list = (JList) c;
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+ maybeUpdateLayoutState();
+ }
+
+ /**
+ * Uninstalls all the aspects of the UI which were installed in {@link
+ * #installUI}. When finished uninstalling, drops the saved reference to
+ * the {@link JList}.
+ *
+ * @param c Ignored; the UI is uninstalled from the {@link JList}
+ * reference saved during the call to {@link #installUI}
+ */
+ public void uninstallUI(final JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallDefaults();
+ list = null;
+ }
+
+ /**
+ * Gets the size this list would prefer to assume. This is calculated by
+ * calling {@link #getCellBounds} over the entire list.
+ *
+ * @param c Ignored; uses the saved {@link JList} reference
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ maybeUpdateLayoutState();
+ int size = list.getModel().getSize();
+ int visibleRows = list.getVisibleRowCount();
+ int layoutOrientation = list.getLayoutOrientation();
+
+ int h;
+ int w;
+ int maxCellHeight = cellHeight;
+ if (maxCellHeight <= 0)
+ {
+ for (int i = 0; i < cellHeights.length; i++)
+ maxCellHeight = Math.max(maxCellHeight, cellHeights[i]);
+ }
+ if (layoutOrientation == JList.HORIZONTAL_WRAP)
+ {
+ if (visibleRows > 0)
+ {
+ // We cast to double here to force double divisions.
+ double modelSize = size;
+ int neededColumns = (int) Math.ceil(modelSize / visibleRows);
+ int adjustedRows = (int) Math.ceil(modelSize / neededColumns);
+ h = maxCellHeight * adjustedRows;
+ w = cellWidth * neededColumns;
+ }
+ else
+ {
+ int neededColumns = Math.min(1, list.getWidth() / cellWidth);
+ h = size / neededColumns * maxCellHeight;
+ w = neededColumns * cellWidth;
+ }
+ }
+ else if (layoutOrientation == JList.VERTICAL_WRAP)
+ {
+ if (visibleRows > 0)
+ h = visibleRows * maxCellHeight;
+ else
+ h = Math.max(list.getHeight(), maxCellHeight);
+ int neededColumns = h / maxCellHeight;
+ w = cellWidth * neededColumns;
+ }
+ else
+ {
+ if (list.getFixedCellWidth() > 0)
+ w = list.getFixedCellWidth();
+ else
+ w = cellWidth;
+ if (list.getFixedCellHeight() > 0)
+ // FIXME: We need to add some cellVerticalMargins here, according
+ // to the specs.
+ h = list.getFixedCellHeight() * size;
+ else
+ h = maxCellHeight * size;
+ }
+ Insets insets = list.getInsets();
+ Dimension retVal = new Dimension(w + insets.left + insets.right,
+ h + insets.top + insets.bottom);
+ return retVal;
+ }
+
+ /**
+ * Paints a single cell in the list.
+ *
+ * @param g The graphics context to paint in
+ * @param row The row number to paint
+ * @param bounds The bounds of the cell to paint, assuming a coordinate
+ * system beginning at <code>(0,0)</code> in the upper left corner of the
+ * list
+ * @param rend A cell renderer to paint with
+ * @param data The data to provide to the cell renderer
+ * @param sel A selection model to provide to the cell renderer
+ * @param lead The lead selection index of the list
+ */
+ protected void paintCell(Graphics g, int row, Rectangle bounds,
+ ListCellRenderer rend, ListModel data,
+ ListSelectionModel sel, int lead)
+ {
+ boolean isSel = list.isSelectedIndex(row);
+ boolean hasFocus = (list.getLeadSelectionIndex() == row) && BasicListUI.this.list.hasFocus();
+ Component comp = rend.getListCellRendererComponent(list,
+ data.getElementAt(row),
+ row, isSel, hasFocus);
+ rendererPane.paintComponent(g, comp, list, bounds);
+ }
+
+ /**
+ * Paints the list by repeatedly calling {@link #paintCell} for each visible
+ * cell in the list.
+ *
+ * @param g The graphics context to paint with
+ * @param c Ignored; uses the saved {@link JList} reference
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ int nrows = list.getModel().getSize();
+ if (nrows == 0)
+ return;
+
+ maybeUpdateLayoutState();
+ ListCellRenderer render = list.getCellRenderer();
+ ListModel model = list.getModel();
+ ListSelectionModel sel = list.getSelectionModel();
+ int lead = sel.getLeadSelectionIndex();
+ Rectangle clip = g.getClipBounds();
+
+ int startIndex = locationToIndex(list, new Point(clip.x, clip.y));
+ int endIndex = locationToIndex(list, new Point(clip.x + clip.width,
+ clip.y + clip.height));
+
+ for (int row = startIndex; row <= endIndex; ++row)
+ {
+ Rectangle bounds = getCellBounds(list, row, row);
+ if (bounds != null && bounds.intersects(clip))
+ paintCell(g, row, bounds, render, model, sel, lead);
+ }
+ }
+
+ /**
+ * Computes the index of a list cell given a point within the list. If the
+ * location lies outside the bounds of the list, the greatest index in the
+ * list model is returned.
+ *
+ * @param l the list which on which the computation is based on
+ * @param location the coordinates
+ *
+ * @return the index of the list item that is located at the given
+ * coordinates or <code>-1</code> if the list model is empty
+ */
+ public int locationToIndex(JList l, Point location)
+ {
+ int layoutOrientation = list.getLayoutOrientation();
+ int index = -1;
+ switch (layoutOrientation)
+ {
+ case JList.VERTICAL:
+ index = convertYToRow(location.y);
+ break;
+ case JList.HORIZONTAL_WRAP:
+ // determine visible rows and cells per row
+ int maxCellHeight = getCellHeight(0);
+ int visibleRows = list.getHeight() / maxCellHeight;
+ int cellsPerRow = -1;
+ int numberOfItems = list.getModel().getSize();
+ cellsPerRow = numberOfItems / visibleRows + 1;
+
+ // determine index for the given location
+ int cellsPerColumn = numberOfItems / cellsPerRow + 1;
+ int gridX = Math.min(location.x / cellWidth, cellsPerRow - 1);
+ int gridY = Math.min(location.y / maxCellHeight, cellsPerColumn);
+ index = gridX + gridY * cellsPerRow;
+ break;
+ case JList.VERTICAL_WRAP:
+ // determine visible rows and cells per column
+ int maxCellHeight2 = getCellHeight(0);
+ int visibleRows2 = list.getHeight() / maxCellHeight2;
+ int numberOfItems2 = list.getModel().getSize();
+ int cellsPerRow2 = numberOfItems2 / visibleRows2 + 1;
+
+ int gridX2 = Math.min(location.x / cellWidth, cellsPerRow2 - 1);
+ int gridY2 = Math.min(location.y / maxCellHeight2, visibleRows2);
+ index = gridY2 + gridX2 * visibleRows2;
+ break;
+ }
+ return index;
+ }
+
+ public Point indexToLocation(JList l, int index)
+ {
+ int layoutOrientation = list.getLayoutOrientation();
+ Point loc = null;
+ switch (layoutOrientation)
+ {
+ case JList.VERTICAL:
+ loc = new Point(0, convertRowToY(index));
+ break;
+ case JList.HORIZONTAL_WRAP:
+ // determine visible rows and cells per row
+ int maxCellHeight = getCellHeight(0);
+ int visibleRows = list.getHeight() / maxCellHeight;
+ int numberOfCellsPerRow = -1;
+ int numberOfItems = list.getModel().getSize();
+ numberOfCellsPerRow = numberOfItems / visibleRows + 1;
+
+ // compute coordinates inside the grid
+ int gridX = index % numberOfCellsPerRow;
+ int gridY = index / numberOfCellsPerRow;
+ int locX = gridX * cellWidth;
+ int locY;
+ locY = gridY * maxCellHeight;
+ loc = new Point(locX, locY);
+ break;
+ case JList.VERTICAL_WRAP:
+ // determine visible rows and cells per column
+ int maxCellHeight2 = getCellHeight(0);
+ int visibleRows2 = list.getHeight() / maxCellHeight2;
+ // compute coordinates inside the grid
+ if (visibleRows2 > 0)
+ {
+ int gridY2 = index % visibleRows2;
+ int gridX2 = index / visibleRows2;
+ int locX2 = gridX2 * cellWidth;
+ int locY2 = gridY2 * maxCellHeight2;
+ loc = new Point(locX2, locY2);
+ }
+ else
+ loc = new Point(0, convertRowToY(index));
+ break;
+ }
+ return loc;
+ }
+
+ /**
+ * Creates and returns the focus listener for this UI.
+ *
+ * @return the focus listener for this UI
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * Creates and returns the list data listener for this UI.
+ *
+ * @return the list data listener for this UI
+ */
+ protected ListDataListener createListDataListener()
+ {
+ return new ListDataHandler();
+ }
+
+ /**
+ * Creates and returns the list selection listener for this UI.
+ *
+ * @return the list selection listener for this UI
+ */
+ protected ListSelectionListener createListSelectionListener()
+ {
+ return new ListSelectionHandler();
+ }
+
+ /**
+ * Creates and returns the mouse input listener for this UI.
+ *
+ * @return the mouse input listener for this UI
+ */
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * Creates and returns the property change listener for this UI.
+ *
+ * @return the property change listener for this UI
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Selects the next list item and force it to be visible.
+ */
+ protected void selectNextIndex()
+ {
+ int index = list.getSelectionModel().getLeadSelectionIndex();
+ if (index < list.getModel().getSize() - 1)
+ {
+ index++;
+ list.setSelectedIndex(index);
+ }
+ list.ensureIndexIsVisible(index);
+ }
+
+ /**
+ * Selects the previous list item and force it to be visible.
+ */
+ protected void selectPreviousIndex()
+ {
+ int index = list.getSelectionModel().getLeadSelectionIndex();
+ if (index > 0)
+ {
+ index--;
+ list.setSelectedIndex(index);
+ }
+ list.ensureIndexIsVisible(index);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java
new file mode 100644
index 000000000..9a1f544f6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicLookAndFeel.java
@@ -0,0 +1,1734 @@
+/* BasicLookAndFeel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.AWTEvent;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.SystemColor;
+import java.awt.Toolkit;
+import java.awt.event.AWTEventListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BorderFactory;
+import javax.swing.JComponent;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuSelectionManager;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.border.BevelBorder;
+import javax.swing.border.Border;
+import javax.swing.plaf.BorderUIResource;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.DimensionUIResource;
+import javax.swing.plaf.FontUIResource;
+import javax.swing.plaf.IconUIResource;
+import javax.swing.plaf.InsetsUIResource;
+
+/**
+ * A basic implementation of Swing's Look and Feel framework. This can serve
+ * as a base for custom look and feel implementations.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class BasicLookAndFeel extends LookAndFeel
+ implements Serializable
+{
+
+ /**
+ * Helps closing menu popups when the user clicks outside of any menu area.
+ * This is implemented as an AWTEventListener that listens on the event
+ * queue directly, grabs all mouse events from there and finds out of they
+ * are targetted at a menu/submenu/menubar or not. If not,
+ * the MenuSelectionManager is messaged to close the currently opened menus,
+ * if any.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class PopupHelper implements AWTEventListener
+ {
+
+ /**
+ * Receives an event from the event queue.
+ *
+ * @param event
+ */
+ public void eventDispatched(AWTEvent event)
+ {
+ if (event instanceof MouseEvent)
+ {
+ MouseEvent mouseEvent = (MouseEvent) event;
+ if (mouseEvent.getID() == MouseEvent.MOUSE_PRESSED)
+ mousePressed(mouseEvent);
+ }
+ }
+
+ /**
+ * Handles mouse pressed events from the event queue.
+ *
+ * @param ev the mouse pressed event
+ */
+ private void mousePressed(MouseEvent ev)
+ {
+ // Autoclose all menus managed by the MenuSelectionManager.
+ MenuSelectionManager m = MenuSelectionManager.defaultManager();
+ Component target = ev.getComponent();
+ if (target instanceof Container)
+ target = ((Container) target).findComponentAt(ev.getPoint());
+ if (m.getSelectedPath().length > 0
+ && ! m.isComponentPartOfCurrentMenu(target)
+ && (((JComponent)target).getClientProperty(DONT_CANCEL_POPUP) == null
+ || !((JComponent)target).getClientProperty(DONT_CANCEL_POPUP).equals(Boolean.TRUE)))
+ {
+ m.clearSelectedPath();
+ }
+ }
+
+ }
+
+ /**
+ * An action that can play an audio file.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class AudioAction extends AbstractAction
+ {
+ /**
+ * The UIDefaults key that specifies the sound.
+ */
+ Object key;
+
+ /**
+ * Creates a new AudioAction.
+ *
+ * @param key the key that describes the audio action, normally a filename
+ * of an audio file relative to the current package
+ */
+ AudioAction(Object key)
+ {
+ this.key = key;
+ }
+
+ /**
+ * Plays the sound represented by this action.
+ *
+ * @param event the action event that triggers this audio action
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // We only can handle strings for now.
+ if (key instanceof String)
+ {
+ String name = UIManager.getString(key);
+ InputStream stream = getClass().getResourceAsStream(name);
+ try
+ {
+ Clip clip = AudioSystem.getClip();
+ AudioInputStream audioStream =
+ AudioSystem.getAudioInputStream(stream);
+ clip.open(audioStream);
+ }
+ catch (LineUnavailableException ex)
+ {
+ // Nothing we can do about it.
+ }
+ catch (IOException ex)
+ {
+ // Nothing we can do about it.
+ }
+ catch (UnsupportedAudioFileException e)
+ {
+ // Nothing we can do about it.
+ }
+ }
+ }
+ }
+
+ static final long serialVersionUID = -6096995660290287879L;
+
+ /**
+ * This is a key for a client property that tells the PopupHelper that
+ * it shouldn't close popups when the mouse event target has this
+ * property set. This is used when the component handles popup closing
+ * itself.
+ */
+ static final String DONT_CANCEL_POPUP = "noCancelPopup";
+
+ /**
+ * Helps closing menu popups when user clicks outside of the menu area.
+ */
+ private transient PopupHelper popupHelper;
+
+ /**
+ * Maps the audio actions for this l&f.
+ */
+ private ActionMap audioActionMap;
+
+ /**
+ * Creates a new instance of the Basic look and feel.
+ */
+ public BasicLookAndFeel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates and returns a new instance of the default resources for this look
+ * and feel.
+ *
+ * @return The UI defaults.
+ */
+ public UIDefaults getDefaults()
+ {
+ // Variables
+ UIDefaults def = new UIDefaults();
+ // Initialize Class Defaults
+ initClassDefaults(def);
+ // Initialize System Colour Defaults
+ initSystemColorDefaults(def);
+ // Initialize Component Defaults
+ initComponentDefaults(def);
+ // Return UI Defaults
+ return def;
+ }
+
+ /**
+ * Populates the <code>defaults</code> table with mappings between class IDs
+ * and fully qualified class names for the UI delegates.
+ *
+ * @param defaults the defaults table (<code>null</code> not permitted).
+ */
+ protected void initClassDefaults(UIDefaults defaults)
+ {
+ // Variables
+ Object[] uiDefaults;
+ // Initialize Class Defaults
+ uiDefaults = new Object[] {
+ "ButtonUI", "javax.swing.plaf.basic.BasicButtonUI",
+ "CheckBoxMenuItemUI", "javax.swing.plaf.basic.BasicCheckBoxMenuItemUI",
+ "CheckBoxUI", "javax.swing.plaf.basic.BasicCheckBoxUI",
+ "ColorChooserUI", "javax.swing.plaf.basic.BasicColorChooserUI",
+ "ComboBoxUI", "javax.swing.plaf.basic.BasicComboBoxUI",
+ "DesktopIconUI", "javax.swing.plaf.basic.BasicDesktopIconUI",
+ "DesktopPaneUI", "javax.swing.plaf.basic.BasicDesktopPaneUI",
+ "EditorPaneUI", "javax.swing.plaf.basic.BasicEditorPaneUI",
+ "FileChooserUI", "javax.swing.plaf.basic.BasicFileChooserUI",
+ "FormattedTextFieldUI", "javax.swing.plaf.basic.BasicFormattedTextFieldUI",
+ "InternalFrameUI", "javax.swing.plaf.basic.BasicInternalFrameUI",
+ "LabelUI", "javax.swing.plaf.basic.BasicLabelUI",
+ "ListUI", "javax.swing.plaf.basic.BasicListUI",
+ "MenuBarUI", "javax.swing.plaf.basic.BasicMenuBarUI",
+ "MenuItemUI", "javax.swing.plaf.basic.BasicMenuItemUI",
+ "MenuUI", "javax.swing.plaf.basic.BasicMenuUI",
+ "OptionPaneUI", "javax.swing.plaf.basic.BasicOptionPaneUI",
+ "PanelUI", "javax.swing.plaf.basic.BasicPanelUI",
+ "PasswordFieldUI", "javax.swing.plaf.basic.BasicPasswordFieldUI",
+ "PopupMenuSeparatorUI", "javax.swing.plaf.basic.BasicPopupMenuSeparatorUI",
+ "PopupMenuUI", "javax.swing.plaf.basic.BasicPopupMenuUI",
+ "ProgressBarUI", "javax.swing.plaf.basic.BasicProgressBarUI",
+ "RadioButtonMenuItemUI", "javax.swing.plaf.basic.BasicRadioButtonMenuItemUI",
+ "RadioButtonUI", "javax.swing.plaf.basic.BasicRadioButtonUI",
+ "RootPaneUI", "javax.swing.plaf.basic.BasicRootPaneUI",
+ "ScrollBarUI", "javax.swing.plaf.basic.BasicScrollBarUI",
+ "ScrollPaneUI", "javax.swing.plaf.basic.BasicScrollPaneUI",
+ "SeparatorUI", "javax.swing.plaf.basic.BasicSeparatorUI",
+ "SliderUI", "javax.swing.plaf.basic.BasicSliderUI",
+ "SplitPaneUI", "javax.swing.plaf.basic.BasicSplitPaneUI",
+ "SpinnerUI", "javax.swing.plaf.basic.BasicSpinnerUI",
+ "StandardDialogUI", "javax.swing.plaf.basic.BasicStandardDialogUI",
+ "TabbedPaneUI", "javax.swing.plaf.basic.BasicTabbedPaneUI",
+ "TableHeaderUI", "javax.swing.plaf.basic.BasicTableHeaderUI",
+ "TableUI", "javax.swing.plaf.basic.BasicTableUI",
+ "TextPaneUI", "javax.swing.plaf.basic.BasicTextPaneUI",
+ "TextAreaUI", "javax.swing.plaf.basic.BasicTextAreaUI",
+ "TextFieldUI", "javax.swing.plaf.basic.BasicTextFieldUI",
+ "ToggleButtonUI", "javax.swing.plaf.basic.BasicToggleButtonUI",
+ "ToolBarSeparatorUI", "javax.swing.plaf.basic.BasicToolBarSeparatorUI",
+ "ToolBarUI", "javax.swing.plaf.basic.BasicToolBarUI",
+ "ToolTipUI", "javax.swing.plaf.basic.BasicToolTipUI",
+ "TreeUI", "javax.swing.plaf.basic.BasicTreeUI",
+ "ViewportUI", "javax.swing.plaf.basic.BasicViewportUI"
+ };
+ // Add Class Defaults to UI Defaults table
+ defaults.putDefaults(uiDefaults);
+ }
+
+ /**
+ * Populates the <code>defaults</code> table with system color defaults.
+ *
+ * This sets up a couple of default values and passes them to
+ * {@link #loadSystemColors(UIDefaults, String[], boolean)}. If the
+ * look and feel is a native look and feel, these defaults may be overridden
+ * by the corresponding SystemColor constants.
+ *
+ * @param defaults the defaults table (<code>null</code> not permitted).
+ */
+ protected void initSystemColorDefaults(UIDefaults defaults)
+ {
+ String[] defaultColors = new String[] {
+ "activeCaption", "#000080",
+ "activeCaptionBorder", "#C0C0C0",
+ "activeCaptionText", "#FFFFFF",
+ "control", "#C0C0C0",
+ "controlDkShadow", "#000000",
+ "controlHighlight", "#C0C0C0",
+ "controlLtHighlight", "#FFFFFF",
+ "controlShadow", "#808080",
+ "controlText", "#000000",
+ "desktop", "#005C5C",
+ "inactiveCaption", "#808080",
+ "inactiveCaptionBorder", "#C0C0C0",
+ "inactiveCaptionText", "#C0C0C0",
+ "info", "#FFFFE1",
+ "infoText", "#000000",
+ "menu", "#C0C0C0",
+ "menuText", "#000000",
+ "scrollbar", "#E0E0E0",
+ "text", "#C0C0C0",
+ "textHighlight", "#000080",
+ "textHighlightText", "#FFFFFF",
+ "textInactiveText", "#808080",
+ "textText", "#000000",
+ "window", "#FFFFFF",
+ "windowBorder", "#000000",
+ "windowText", "#000000"
+ };
+ loadSystemColors(defaults, defaultColors, isNativeLookAndFeel());
+ }
+
+ /**
+ * Populates the <code>defaults</code> table with the system colors. If
+ * <code>useNative</code> is <code>true</code>, the table is populated
+ * with the constants in {@link SystemColor}, otherwise the
+ * <code>systemColors</code> parameter is decoded into the defaults table.
+ * The system colors array is made up of pairs, where the first entry is the
+ * name of the system color, and the second entry is a string denoting
+ * an RGB color value like &quot;#C0C0C0&quot;, which is decoded using
+ * {@link Color#decode(String)}.
+ *
+ * @param defaults the defaults table (<code>null</code> not permitted).
+ * @param systemColors defaults to use when <code>useNative</code> is
+ * <code>false</code>
+ * @param useNative when <code>true</code>, installs the values of the
+ * SystemColor constants, when <code>false</code>, install the values
+ * from <code>systemColors</code>
+ */
+ protected void loadSystemColors(UIDefaults defaults, String[] systemColors,
+ boolean useNative)
+ {
+ if (useNative)
+ {
+ defaults.put("activeCaption",
+ new ColorUIResource(SystemColor.ACTIVE_CAPTION));
+ defaults.put("activeCaptionBorder",
+ new ColorUIResource(SystemColor.ACTIVE_CAPTION_BORDER));
+ defaults.put("activeCaptionText",
+ new ColorUIResource(SystemColor.ACTIVE_CAPTION_TEXT));
+ defaults.put("control",
+ new ColorUIResource(SystemColor.CONTROL));
+ defaults.put("controlDkShadow",
+ new ColorUIResource(SystemColor.CONTROL_DK_SHADOW));
+ defaults.put("controlHighlight",
+ new ColorUIResource(SystemColor.CONTROL_HIGHLIGHT));
+ defaults.put("controlLtHighlight",
+ new ColorUIResource(SystemColor.CONTROL_LT_HIGHLIGHT));
+ defaults.put("controlShadow",
+ new ColorUIResource(SystemColor.CONTROL_SHADOW));
+ defaults.put("controlText",
+ new ColorUIResource(SystemColor.CONTROL_TEXT));
+ defaults.put("desktop",
+ new ColorUIResource(SystemColor.DESKTOP));
+ defaults.put("inactiveCaption",
+ new ColorUIResource(SystemColor.INACTIVE_CAPTION));
+ defaults.put("inactiveCaptionBorder",
+ new ColorUIResource(SystemColor.INACTIVE_CAPTION_BORDER));
+ defaults.put("inactiveCaptionText",
+ new ColorUIResource(SystemColor.INACTIVE_CAPTION_TEXT));
+ defaults.put("info",
+ new ColorUIResource(SystemColor.INFO));
+ defaults.put("infoText",
+ new ColorUIResource(SystemColor.INFO_TEXT));
+ defaults.put("menu",
+ new ColorUIResource(SystemColor.MENU));
+ defaults.put("menuText",
+ new ColorUIResource(SystemColor.MENU_TEXT));
+ defaults.put("scrollbar",
+ new ColorUIResource(SystemColor.SCROLLBAR));
+ defaults.put("text",
+ new ColorUIResource(SystemColor.TEXT));
+ defaults.put("textHighlight",
+ new ColorUIResource(SystemColor.TEXT_HIGHLIGHT));
+ defaults.put("textHighlightText",
+ new ColorUIResource(SystemColor.TEXT_HIGHLIGHT_TEXT));
+ defaults.put("textInactiveText",
+ new ColorUIResource(SystemColor.TEXT_INACTIVE_TEXT));
+ defaults.put("textText",
+ new ColorUIResource(SystemColor.TEXT_TEXT));
+ defaults.put("window",
+ new ColorUIResource(SystemColor.WINDOW));
+ defaults.put("windowBorder",
+ new ColorUIResource(SystemColor.WINDOW_BORDER));
+ defaults.put("windowText",
+ new ColorUIResource(SystemColor.WINDOW_TEXT));
+ }
+ else
+ {
+ for (int i = 0; i < systemColors.length; i += 2)
+ {
+ Color color = Color.BLACK;
+ try
+ {
+ color = Color.decode(systemColors[i + 1]);
+ }
+ catch (NumberFormatException e)
+ {
+ e.printStackTrace();
+ }
+ defaults.put(systemColors[i], new ColorUIResource(color));
+ }
+ }
+ }
+
+ /**
+ * Loads the resource bundle in 'resources/basic' and adds the contained
+ * key/value pairs to the <code>defaults</code> table.
+ *
+ * @param defaults the UI defaults to load the resources into
+ */
+ // FIXME: This method is not used atm and private and thus could be removed.
+ // However, I consider this method useful for providing localized
+ // descriptions and similar stuff and therefore think that we should use it
+ // instead and provide the resource bundles.
+ private void loadResourceBundle(UIDefaults defaults)
+ {
+ ResourceBundle bundle;
+ Enumeration e;
+ String key;
+ String value;
+ bundle = ResourceBundle.getBundle("resources/basic");
+ // Process Resources
+ e = bundle.getKeys();
+ while (e.hasMoreElements())
+ {
+ key = (String) e.nextElement();
+ value = bundle.getString(key);
+ defaults.put(key, value);
+ }
+ }
+
+ /**
+ * Populates the <code>defaults</code> table with UI default values for
+ * colors, fonts, keybindings and much more.
+ *
+ * @param defaults the defaults table (<code>null</code> not permitted).
+ */
+ protected void initComponentDefaults(UIDefaults defaults)
+ {
+ Object[] uiDefaults;
+
+ Color highLight = new Color(249, 247, 246);
+ Color light = new Color(239, 235, 231);
+ Color shadow = new Color(139, 136, 134);
+ Color darkShadow = new Color(16, 16, 16);
+
+ uiDefaults = new Object[] {
+
+ "AbstractUndoableEdit.undoText", "Undo",
+ "AbstractUndoableEdit.redoText", "Redo",
+ "Button.background", new ColorUIResource(Color.LIGHT_GRAY),
+ "Button.border",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ return BasicBorders.getButtonBorder();
+ }
+ },
+ "Button.darkShadow", new ColorUIResource(Color.BLACK),
+ "Button.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Button.foreground", new ColorUIResource(Color.BLACK),
+ "Button.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("SPACE"), "pressed",
+ KeyStroke.getKeyStroke("released SPACE"), "released"
+ }),
+ "Button.highlight", new ColorUIResource(Color.WHITE),
+ "Button.light", new ColorUIResource(Color.LIGHT_GRAY),
+ "Button.margin", new InsetsUIResource(2, 14, 2, 14),
+ "Button.shadow", new ColorUIResource(Color.GRAY),
+ "Button.textIconGap", new Integer(4),
+ "Button.textShiftOffset", new Integer(0),
+ "CheckBox.background", new ColorUIResource(new Color(204, 204, 204)),
+ "CheckBox.border", new BorderUIResource.CompoundBorderUIResource(null,
+ null),
+ "CheckBox.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("SPACE"), "pressed",
+ KeyStroke.getKeyStroke("released SPACE"), "released"
+ }),
+ "CheckBox.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "CheckBox.foreground", new ColorUIResource(darkShadow),
+ "CheckBox.icon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return BasicIconFactory.getCheckBoxIcon();
+ }
+ },
+ "CheckBox.checkIcon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return BasicIconFactory.getMenuItemCheckIcon();
+ }
+ },
+ "CheckBox.margin", new InsetsUIResource(2, 2, 2, 2),
+ "CheckBox.textIconGap", new Integer(4),
+ "CheckBox.textShiftOffset", new Integer(0),
+ "CheckBoxMenuItem.acceleratorFont", new FontUIResource("Dialog",
+ Font.PLAIN, 12),
+ "CheckBoxMenuItem.acceleratorForeground",
+ new ColorUIResource(new Color(16, 16, 16)),
+ "CheckBoxMenuItem.acceleratorSelectionForeground",
+ new ColorUIResource(Color.white),
+ "CheckBoxMenuItem.arrowIcon", BasicIconFactory.getMenuItemArrowIcon(),
+ "CheckBoxMenuItem.background", new ColorUIResource(light),
+ "CheckBoxMenuItem.border", new BasicBorders.MarginBorder(),
+ "CheckBoxMenuItem.borderPainted", Boolean.FALSE,
+ "CheckBoxMenuItem.checkIcon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return BasicIconFactory.getCheckBoxMenuItemIcon();
+ }
+ },
+ "CheckBoxMenuItem.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "CheckBoxMenuItem.foreground", new ColorUIResource(darkShadow),
+ "CheckBoxMenuItem.margin", new InsetsUIResource(2, 2, 2, 2),
+ "CheckBoxMenuItem.selectionBackground", new ColorUIResource(Color.black),
+ "CheckBoxMenuItem.selectionForeground", new ColorUIResource(Color.white),
+ "ColorChooser.background", new ColorUIResource(light),
+ "ColorChooser.cancelText", "Cancel",
+ "ColorChooser.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ColorChooser.foreground", new ColorUIResource(darkShadow),
+ "ColorChooser.hsbBlueText", "B",
+ "ColorChooser.hsbBrightnessText", "B",
+ "ColorChooser.hsbGreenText", "G",
+ "ColorChooser.hsbHueText", "H",
+ "ColorChooser.hsbNameText", "HSB",
+ "ColorChooser.hsbRedText", "R",
+ "ColorChooser.hsbSaturationText", "S",
+ "ColorChooser.okText", "OK",
+ "ColorChooser.previewText", "Preview",
+ "ColorChooser.resetText", "Reset",
+ "ColorChooser.rgbBlueMnemonic", "66",
+ "ColorChooser.rgbBlueText", "Blue",
+ "ColorChooser.rgbGreenMnemonic", "78",
+ "ColorChooser.rgbGreenText", "Green",
+ "ColorChooser.rgbNameText", "RGB",
+ "ColorChooser.rgbRedMnemonic", "68",
+ "ColorChooser.rgbRedText", "Red",
+ "ColorChooser.sampleText", "Sample Text Sample Text",
+ "ColorChooser.swatchesDefaultRecentColor", new ColorUIResource(light),
+ "ColorChooser.swatchesNameText", "Swatches",
+ "ColorChooser.swatchesRecentSwatchSize", new Dimension(10, 10),
+ "ColorChooser.swatchesRecentText", "Recent:",
+ "ColorChooser.swatchesSwatchSize", new Dimension(10, 10),
+ "ComboBox.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ESCAPE", "hidePopup",
+ "PAGE_UP", "pageUpPassThrough",
+ "PAGE_DOWN", "pageDownPassThrough",
+ "HOME", "homePassThrough",
+ "END", "endPassThrough"
+ }),
+ "ComboBox.background", new ColorUIResource(Color.white),
+ "ComboBox.buttonBackground", new ColorUIResource(light),
+ "ComboBox.buttonDarkShadow", new ColorUIResource(darkShadow),
+ "ComboBox.buttonHighlight", new ColorUIResource(highLight),
+ "ComboBox.buttonShadow", new ColorUIResource(shadow),
+ "ComboBox.disabledBackground", new ColorUIResource(light),
+ "ComboBox.disabledForeground", new ColorUIResource(Color.gray),
+ "ComboBox.font", new FontUIResource("SansSerif", Font.PLAIN, 12),
+ "ComboBox.foreground", new ColorUIResource(Color.black),
+ "ComboBox.selectionBackground", new ColorUIResource(0, 0, 128),
+ "ComboBox.selectionForeground", new ColorUIResource(Color.white),
+ "Desktop.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "KP_LEFT", "left",
+ "KP_RIGHT", "right",
+ "ctrl F5", "restore",
+ "LEFT", "left",
+ "ctrl alt F6", "selectNextFrame",
+ "UP", "up",
+ "ctrl F6", "selectNextFrame",
+ "RIGHT", "right",
+ "DOWN", "down",
+ "ctrl F7", "move",
+ "ctrl F8", "resize",
+ "ESCAPE", "escape",
+ "ctrl TAB", "selectNextFrame",
+ "ctrl F9", "minimize",
+ "KP_UP", "up",
+ "ctrl F4", "close",
+ "KP_DOWN", "down",
+ "ctrl F10", "maximize",
+ "ctrl alt shift F6", "selectPreviousFrame"
+ }),
+ "DesktopIcon.border", new BorderUIResource.CompoundBorderUIResource(null,
+ null),
+ "EditorPane.background", new ColorUIResource(Color.white),
+ "EditorPane.border", BasicBorders.getMarginBorder(),
+ "EditorPane.caretBlinkRate", new Integer(500),
+ "EditorPane.caretForeground", new ColorUIResource(Color.black),
+ "EditorPane.font", new FontUIResource("Serif", Font.PLAIN, 12),
+ "EditorPane.foreground", new ColorUIResource(Color.black),
+ "EditorPane.inactiveForeground", new ColorUIResource(Color.gray),
+ "EditorPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("shift UP"), "selection-up",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selection-up",
+ KeyStroke.getKeyStroke("DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up",
+ KeyStroke.getKeyStroke("KP_UP"), "caret-up",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("ctrl END"), "caret-end",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ENTER"), "insert-break",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left",
+ KeyStroke.getKeyStroke("shift DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("KP_DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selection-end",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl T"), "next-link-action",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("TAB"), "insert-tab",
+ KeyStroke.getKeyStroke("UP"), "caret-up",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("PAGE_UP"), "page-up",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard"
+ }),
+ "EditorPane.margin", new InsetsUIResource(3, 3, 3, 3),
+ "EditorPane.selectionBackground", new ColorUIResource(Color.black),
+ "EditorPane.selectionForeground", new ColorUIResource(Color.white),
+ "FileChooser.acceptAllFileFilterText", "All Files (*.*)",
+ "FileChooser.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ESCAPE", "cancelSelection"
+ }),
+ "FileChooser.cancelButtonMnemonic", "67",
+ "FileChooser.cancelButtonText", "Cancel",
+ "FileChooser.cancelButtonToolTipText", "Abort file chooser dialog",
+ "FileChooser.directoryDescriptionText", "Directory",
+ "FileChooser.fileDescriptionText", "Generic File",
+ "FileChooser.directoryOpenButtonMnemonic", "79",
+ "FileChooser.helpButtonMnemonic", "72",
+ "FileChooser.helpButtonText", "Help",
+ "FileChooser.helpButtonToolTipText", "FileChooser help",
+ "FileChooser.newFolderErrorSeparator", ":",
+ "FileChooser.newFolderErrorText", "Error creating new folder",
+ "FileChooser.openButtonMnemonic", "79",
+ "FileChooser.openButtonText", "Open",
+ "FileChooser.openButtonToolTipText", "Open selected file",
+ "FileChooser.saveButtonMnemonic", "83",
+ "FileChooser.saveButtonText", "Save",
+ "FileChooser.saveButtonToolTipText", "Save selected file",
+ "FileChooser.updateButtonMnemonic", "85",
+ "FileChooser.updateButtonText", "Update",
+ "FileChooser.updateButtonToolTipText", "Update directory listing",
+ "FocusManagerClassName", "TODO",
+ "FormattedTextField.background", new ColorUIResource(light),
+ "FormattedTextField.caretForeground", new ColorUIResource(Color.black),
+ "FormattedTextField.margin", new InsetsUIResource(0, 0, 0, 0),
+ "FormattedTextField.caretBlinkRate", new Integer(500),
+ "FormattedTextField.font",
+ new FontUIResource("SansSerif", Font.PLAIN, 12),
+ "FormattedTextField.foreground", new ColorUIResource(Color.black),
+ "FormattedTextField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("KP_UP"), "increment",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("KP_DOWN"), "decrement",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("UP"), "increment",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("ESCAPE"), "reset-field-edit",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("DOWN"), "decrement",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ENTER"), "notify-field-accept",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward"
+ }),
+ "FormattedTextField.inactiveBackground", new ColorUIResource(light),
+ "FormattedTextField.inactiveForeground", new ColorUIResource(Color.gray),
+ "FormattedTextField.selectionBackground",
+ new ColorUIResource(Color.black),
+ "FormattedTextField.selectionForeground",
+ new ColorUIResource(Color.white),
+ "FormView.resetButtonText", "Reset",
+ "FormView.submitButtonText", "Submit Query",
+ "InternalFrame.activeTitleBackground", new ColorUIResource(0, 0, 128),
+ "InternalFrame.activeTitleForeground", new ColorUIResource(Color.white),
+ "InternalFrame.border",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ Color lineColor = new Color(238, 238, 238);
+ Border inner = BorderFactory.createLineBorder(lineColor, 1);
+ Color shadowInner = new Color(184, 207, 229);
+ Color shadowOuter = new Color(122, 138, 153);
+ Border outer = BorderFactory.createBevelBorder(BevelBorder.RAISED,
+ Color.WHITE,
+ Color.WHITE,
+ shadowOuter,
+ shadowInner);
+ Border border = new BorderUIResource.CompoundBorderUIResource(outer,
+ inner);
+ return border;
+ }
+ },
+ "InternalFrame.borderColor", new ColorUIResource(light),
+ "InternalFrame.borderDarkShadow", new ColorUIResource(Color.BLACK),
+ "InternalFrame.borderHighlight", new ColorUIResource(Color.WHITE),
+ "InternalFrame.borderLight", new ColorUIResource(Color.LIGHT_GRAY),
+ "InternalFrame.borderShadow", new ColorUIResource(Color.GRAY),
+ "InternalFrame.closeIcon", BasicIconFactory.createEmptyFrameIcon(),
+ "InternalFrame.icon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return new IconUIResource(BasicIconFactory.createEmptyFrameIcon());
+ }
+ },
+ "InternalFrame.iconifyIcon", BasicIconFactory.createEmptyFrameIcon(),
+ "InternalFrame.inactiveTitleBackground", new ColorUIResource(Color.gray),
+ "InternalFrame.inactiveTitleForeground",
+ new ColorUIResource(Color.lightGray),
+ "InternalFrame.maximizeIcon", BasicIconFactory.createEmptyFrameIcon(),
+ "InternalFrame.minimizeIcon", BasicIconFactory.createEmptyFrameIcon(),
+ "InternalFrame.titleFont", new FontUIResource("Dialog", Font.BOLD, 12),
+ "InternalFrame.windowBindings", new Object[] {
+ "shift ESCAPE", "showSystemMenu",
+ "ctrl SPACE", "showSystemMenu",
+ "ESCAPE", "showSystemMenu"
+ },
+ "Label.background", new ColorUIResource(light),
+ "Label.disabledForeground", new ColorUIResource(Color.white),
+ "Label.disabledShadow", new ColorUIResource(shadow),
+ "Label.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Label.foreground", new ColorUIResource(darkShadow),
+ "List.background", new ColorUIResource(Color.white),
+ "List.border", new BasicBorders.MarginBorder(),
+ "List.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("ctrl DOWN"), "selectNextRowChangeLead",
+ KeyStroke.getKeyStroke("shift UP"), "selectPreviousRowExtendSelection",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "selectNextColumnChangeLead",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selectPreviousColumnExtendSelection",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selectPreviousRowExtendSelection",
+ KeyStroke.getKeyStroke("DOWN"), "selectNextRow",
+ KeyStroke.getKeyStroke("ctrl UP"), "selectPreviousRowChangeLead",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "selectPreviousColumnChangeLead",
+ KeyStroke.getKeyStroke("CUT"), "cut",
+ KeyStroke.getKeyStroke("END"), "selectLastRow",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "scrollUpExtendSelection",
+ KeyStroke.getKeyStroke("KP_UP"), "selectPreviousRow",
+ KeyStroke.getKeyStroke("shift ctrl UP"), "selectPreviousRowExtendSelection",
+ KeyStroke.getKeyStroke("ctrl HOME"), "selectFirstRowChangeLead",
+ KeyStroke.getKeyStroke("shift LEFT"), "selectPreviousColumnExtendSelection",
+ KeyStroke.getKeyStroke("ctrl END"), "selectLastRowChangeLead",
+ KeyStroke.getKeyStroke("ctrl PAGE_DOWN"), "scrollDownChangeLead",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selectNextColumnExtendSelection",
+ KeyStroke.getKeyStroke("LEFT"), "selectPreviousColumn",
+ KeyStroke.getKeyStroke("ctrl PAGE_UP"), "scrollUpChangeLead",
+ KeyStroke.getKeyStroke("KP_LEFT"), "selectPreviousColumn",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selectNextColumnExtendSelection",
+ KeyStroke.getKeyStroke("SPACE"), "addToSelection",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "toggleAndAnchor",
+ KeyStroke.getKeyStroke("shift SPACE"), "extendTo",
+ KeyStroke.getKeyStroke("shift ctrl SPACE"), "moveSelectionTo",
+ KeyStroke.getKeyStroke("shift ctrl DOWN"), "selectNextRowExtendSelection",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "clearSelection",
+ KeyStroke.getKeyStroke("shift HOME"), "selectFirstRowExtendSelection",
+ KeyStroke.getKeyStroke("RIGHT"), "selectNextColumn",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "scrollUpExtendSelection",
+ KeyStroke.getKeyStroke("shift DOWN"), "selectNextRowExtendSelection",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "scrollDown",
+ KeyStroke.getKeyStroke("shift ctrl KP_UP"), "selectPreviousRowExtendSelection",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selectPreviousColumnExtendSelection",
+ KeyStroke.getKeyStroke("ctrl X"), "cut",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "scrollDownExtendSelection",
+ KeyStroke.getKeyStroke("ctrl SLASH"), "selectAll",
+ KeyStroke.getKeyStroke("ctrl C"), "copy",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "selectNextColumnChangeLead",
+ KeyStroke.getKeyStroke("shift END"), "selectLastRowExtendSelection",
+ KeyStroke.getKeyStroke("shift ctrl KP_DOWN"), "selectNextRowExtendSelection",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "selectPreviousColumnChangeLead",
+ KeyStroke.getKeyStroke("HOME"), "selectFirstRow",
+ KeyStroke.getKeyStroke("ctrl V"), "paste",
+ KeyStroke.getKeyStroke("KP_DOWN"), "selectNextRow",
+ KeyStroke.getKeyStroke("ctrl KP_DOWN"), "selectNextRowChangeLead",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selectNextColumnExtendSelection",
+ KeyStroke.getKeyStroke("ctrl A"), "selectAll",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selectLastRowExtendSelection",
+ KeyStroke.getKeyStroke("COPY"), "copy",
+ KeyStroke.getKeyStroke("ctrl KP_UP"), "selectPreviousRowChangeLead",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selectPreviousColumnExtendSelection",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selectNextRowExtendSelection",
+ KeyStroke.getKeyStroke("UP"), "selectPreviousRow",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selectFirstRowExtendSelection",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "scrollDownExtendSelection",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "selectNextColumn",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selectNextColumnExtendSelection",
+ KeyStroke.getKeyStroke("PAGE_UP"), "scrollUp",
+ KeyStroke.getKeyStroke("PASTE"), "paste"
+ }),
+ "List.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "List.foreground", new ColorUIResource(Color.black),
+ "List.selectionBackground", new ColorUIResource(0, 0, 128),
+ "List.selectionForeground", new ColorUIResource(Color.white),
+ "List.focusCellHighlightBorder",
+ new BorderUIResource.
+ LineBorderUIResource(new ColorUIResource(Color.yellow)),
+ "Menu.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Menu.crossMenuMnemonic", Boolean.TRUE,
+ "Menu.acceleratorForeground", new ColorUIResource(darkShadow),
+ "Menu.acceleratorSelectionForeground", new ColorUIResource(Color.white),
+ "Menu.arrowIcon", BasicIconFactory.getMenuArrowIcon(),
+ "Menu.background", new ColorUIResource(light),
+ "Menu.border", new BasicBorders.MarginBorder(),
+ "Menu.borderPainted", Boolean.FALSE,
+ "Menu.checkIcon", BasicIconFactory.getMenuItemCheckIcon(),
+ "Menu.consumesTabs", Boolean.TRUE,
+ "Menu.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Menu.foreground", new ColorUIResource(darkShadow),
+ "Menu.margin", new InsetsUIResource(2, 2, 2, 2),
+ "Menu.selectedWindowInputMapBindings", new Object[] {
+ "ESCAPE", "cancel",
+ "DOWN", "selectNext",
+ "KP_DOWN", "selectNext",
+ "UP", "selectPrevious",
+ "KP_UP", "selectPrevious",
+ "LEFT", "selectParent",
+ "KP_LEFT", "selectParent",
+ "RIGHT", "selectChild",
+ "KP_RIGHT", "selectChild",
+ "ENTER", "return",
+ "SPACE", "return"
+ },
+ "Menu.menuPopupOffsetX", new Integer(0),
+ "Menu.menuPopupOffsetY", new Integer(0),
+ "Menu.submenuPopupOffsetX", new Integer(0),
+ "Menu.submenuPopupOffsetY", new Integer(0),
+ "Menu.selectionBackground", new ColorUIResource(Color.black),
+ "Menu.selectionForeground", new ColorUIResource(Color.white),
+ "MenuBar.background", new ColorUIResource(light),
+ "MenuBar.border", new BasicBorders.MenuBarBorder(null, null),
+ "MenuBar.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "MenuBar.foreground", new ColorUIResource(darkShadow),
+ "MenuBar.highlight", new ColorUIResource(highLight),
+ "MenuBar.shadow", new ColorUIResource(shadow),
+ "MenuBar.windowBindings", new Object[] {
+ "F10", "takeFocus"
+ },
+ "MenuItem.acceleratorDelimiter", "+",
+ "MenuItem.acceleratorFont", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "MenuItem.acceleratorForeground", new ColorUIResource(darkShadow),
+ "MenuItem.acceleratorSelectionForeground",
+ new ColorUIResource(Color.white),
+ "MenuItem.arrowIcon", BasicIconFactory.getMenuItemArrowIcon(),
+ "MenuItem.background", new ColorUIResource(light),
+ "MenuItem.border", new BasicBorders.MarginBorder(),
+ "MenuItem.borderPainted", Boolean.FALSE,
+ "MenuItem.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "MenuItem.foreground", new ColorUIResource(darkShadow),
+ "MenuItem.margin", new InsetsUIResource(2, 2, 2, 2),
+ "MenuItem.selectionBackground", new ColorUIResource(Color.black),
+ "MenuItem.selectionForeground", new ColorUIResource(Color.white),
+ "OptionPane.background", new ColorUIResource(light),
+ "OptionPane.border",
+ new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0),
+ "OptionPane.buttonAreaBorder",
+ new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0),
+ "OptionPane.buttonClickThreshhold", new Integer(500),
+ "OptionPane.cancelButtonText", "Cancel",
+ "OptionPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "OptionPane.foreground", new ColorUIResource(darkShadow),
+ "OptionPane.messageAreaBorder",
+ new BorderUIResource.EmptyBorderUIResource(0, 0, 0, 0),
+ "OptionPane.messageForeground", new ColorUIResource(darkShadow),
+ "OptionPane.minimumSize",
+ new DimensionUIResource(BasicOptionPaneUI.MinimumWidth,
+ BasicOptionPaneUI.MinimumHeight),
+ "OptionPane.noButtonText", "No",
+ "OptionPane.okButtonText", "OK",
+ "OptionPane.windowBindings", new Object[] {
+ "ESCAPE", "close"
+ },
+ "OptionPane.yesButtonText", "Yes",
+ "Panel.background", new ColorUIResource(light),
+ "Panel.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Panel.foreground", new ColorUIResource(Color.black),
+ "PasswordField.background", new ColorUIResource(light),
+ "PasswordField.border", new BasicBorders.FieldBorder(null, null,
+ null, null),
+ "PasswordField.caretBlinkRate", new Integer(500),
+ "PasswordField.caretForeground", new ColorUIResource(Color.black),
+ "PasswordField.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12),
+ "PasswordField.foreground", new ColorUIResource(Color.black),
+ "PasswordField.inactiveBackground", new ColorUIResource(light),
+ "PasswordField.inactiveForeground", new ColorUIResource(Color.gray),
+ "PasswordField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-end-line",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-begin-line",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-end-line",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-begin-line",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-begin-line",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-end-line",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ENTER"), "notify-field-accept",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward"
+ }),
+ "PasswordField.margin", new InsetsUIResource(0, 0, 0, 0),
+ "PasswordField.selectionBackground", new ColorUIResource(Color.black),
+ "PasswordField.selectionForeground", new ColorUIResource(Color.white),
+ "PopupMenu.background", new ColorUIResource(light),
+ "PopupMenu.border", new BorderUIResource.BevelBorderUIResource(0),
+ "PopupMenu.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "PopupMenu.foreground", new ColorUIResource(darkShadow),
+ "PopupMenu.selectedWindowInputMapBindings",
+ new Object[] {"ESCAPE", "cancel",
+ "DOWN", "selectNext",
+ "KP_DOWN", "selectNext",
+ "UP", "selectPrevious",
+ "KP_UP", "selectPrevious",
+ "LEFT", "selectParent",
+ "KP_LEFT", "selectParent",
+ "RIGHT", "selectChild",
+ "KP_RIGHT", "selectChild",
+ "ENTER", "return",
+ "SPACE", "return"
+ },
+ "PopupMenu.selectedWindowInputMapBindings.RightToLeft",
+ new Object[] {"LEFT", "selectChild",
+ "KP_LEFT", "selectChild",
+ "RIGHT", "selectParent",
+ "KP_RIGHT", "selectParent",
+ },
+ "ProgressBar.background", new ColorUIResource(Color.LIGHT_GRAY),
+ "ProgressBar.border",
+ new BorderUIResource.LineBorderUIResource(Color.GREEN, 2),
+ "ProgressBar.cellLength", new Integer(1),
+ "ProgressBar.cellSpacing", new Integer(0),
+ "ProgressBar.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ProgressBar.foreground", new ColorUIResource(0, 0, 128),
+ "ProgressBar.selectionBackground", new ColorUIResource(0, 0, 128),
+ "ProgressBar.selectionForeground", new ColorUIResource(Color.LIGHT_GRAY),
+ "ProgressBar.repaintInterval", new Integer(50),
+ "ProgressBar.cycleTime", new Integer(3000),
+ "RadioButton.background", new ColorUIResource(light),
+ "RadioButton.border", BasicBorders.getRadioButtonBorder(),
+ "RadioButton.darkShadow", new ColorUIResource(shadow),
+ "RadioButton.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("SPACE"), "pressed",
+ KeyStroke.getKeyStroke("released SPACE"), "released"
+ }),
+ "RadioButton.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "RadioButton.foreground", new ColorUIResource(darkShadow),
+ "RadioButton.highlight", new ColorUIResource(highLight),
+ "RadioButton.icon",
+ new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults def)
+ {
+ return BasicIconFactory.getRadioButtonIcon();
+ }
+ },
+ "RadioButton.light", new ColorUIResource(highLight),
+ "RadioButton.margin", new InsetsUIResource(2, 2, 2, 2),
+ "RadioButton.shadow", new ColorUIResource(shadow),
+ "RadioButton.textIconGap", new Integer(4),
+ "RadioButton.textShiftOffset", new Integer(0),
+ "RadioButtonMenuItem.acceleratorFont",
+ new FontUIResource("Dialog", Font.PLAIN, 12),
+ "RadioButtonMenuItem.acceleratorForeground",
+ new ColorUIResource(darkShadow),
+ "RadioButtonMenuItem.acceleratorSelectionForeground",
+ new ColorUIResource(Color.white),
+ "RadioButtonMenuItem.arrowIcon", BasicIconFactory.getMenuItemArrowIcon(),
+ "RadioButtonMenuItem.background", new ColorUIResource(light),
+ "RadioButtonMenuItem.border", new BasicBorders.MarginBorder(),
+ "RadioButtonMenuItem.borderPainted", Boolean.FALSE,
+ "RadioButtonMenuItem.checkIcon", BasicIconFactory.getRadioButtonMenuItemIcon(),
+ "RadioButtonMenuItem.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "RadioButtonMenuItem.foreground", new ColorUIResource(darkShadow),
+ "RadioButtonMenuItem.margin", new InsetsUIResource(2, 2, 2, 2),
+ "RadioButtonMenuItem.selectionBackground",
+ new ColorUIResource(Color.black),
+ "RadioButtonMenuItem.selectionForeground",
+ new ColorUIResource(Color.white),
+ "RootPane.defaultButtonWindowKeyBindings", new Object[] {
+ "ENTER", "press",
+ "released ENTER", "release",
+ "ctrl ENTER", "press",
+ "ctrl released ENTER", "release"
+ },
+ "ScrollBar.background", new ColorUIResource(224, 224, 224),
+ "ScrollBar.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "PAGE_UP", "negativeBlockIncrement",
+ "PAGE_DOWN", "positiveBlockIncrement",
+ "END", "maxScroll",
+ "HOME", "minScroll",
+ "LEFT", "negativeUnitIncrement",
+ "KP_UP", "negativeUnitIncrement",
+ "KP_DOWN", "positiveUnitIncrement",
+ "UP", "negativeUnitIncrement",
+ "RIGHT", "positiveUnitIncrement",
+ "KP_LEFT", "negativeUnitIncrement",
+ "DOWN", "positiveUnitIncrement",
+ "KP_RIGHT", "positiveUnitIncrement"
+ }),
+ "ScrollBar.foreground", new ColorUIResource(light),
+ "ScrollBar.maximumThumbSize", new DimensionUIResource(4096, 4096),
+ "ScrollBar.minimumThumbSize", new DimensionUIResource(8, 8),
+ "ScrollBar.thumb", new ColorUIResource(light),
+ "ScrollBar.thumbDarkShadow", new ColorUIResource(shadow),
+ "ScrollBar.thumbHighlight", new ColorUIResource(highLight),
+ "ScrollBar.thumbShadow", new ColorUIResource(shadow),
+ "ScrollBar.track", new ColorUIResource(light),
+ "ScrollBar.trackHighlight", new ColorUIResource(shadow),
+ "ScrollBar.width", new Integer(16),
+ "ScrollPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "PAGE_UP", "scrollUp",
+ "KP_LEFT", "unitScrollLeft",
+ "ctrl PAGE_DOWN", "scrollRight",
+ "PAGE_DOWN", "scrollDown",
+ "KP_RIGHT", "unitScrollRight",
+ "LEFT", "unitScrollLeft",
+ "ctrl END", "scrollEnd",
+ "UP", "unitScrollUp",
+ "RIGHT", "unitScrollRight",
+ "DOWN", "unitScrollDown",
+ "ctrl HOME", "scrollHome",
+ "ctrl PAGE_UP", "scrollLeft",
+ "KP_UP", "unitScrollUp",
+ "KP_DOWN", "unitScrollDown"
+ }),
+ "ScrollPane.background", new ColorUIResource(light),
+ "ScrollPane.border", new BorderUIResource.EtchedBorderUIResource(),
+ "ScrollPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ScrollPane.foreground", new ColorUIResource(darkShadow),
+ "Separator.background", new ColorUIResource(highLight),
+ "Separator.foreground", new ColorUIResource(shadow),
+ "Separator.highlight", new ColorUIResource(highLight),
+ "Separator.shadow", new ColorUIResource(shadow),
+ "Slider.background", new ColorUIResource(light),
+ "Slider.focus", new ColorUIResource(shadow),
+ "Slider.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ctrl PAGE_DOWN", "negativeBlockIncrement",
+ "PAGE_DOWN", "negativeBlockIncrement",
+ "PAGE_UP", "positiveBlockIncrement",
+ "ctrl PAGE_UP", "positiveBlockIncrement",
+ "KP_RIGHT", "positiveUnitIncrement",
+ "DOWN", "negativeUnitIncrement",
+ "KP_LEFT", "negativeUnitIncrement",
+ "RIGHT", "positiveUnitIncrement",
+ "KP_DOWN", "negativeUnitIncrement",
+ "UP", "positiveUnitIncrement",
+ "KP_UP", "positiveUnitIncrement",
+ "LEFT", "negativeUnitIncrement",
+ "HOME", "minScroll",
+ "END", "maxScroll"
+ }),
+ "Slider.focusInsets", new InsetsUIResource(2, 2, 2, 2),
+ "Slider.foreground", new ColorUIResource(light),
+ "Slider.highlight", new ColorUIResource(highLight),
+ "Slider.shadow", new ColorUIResource(shadow),
+ "Slider.thumbHeight", new Integer(20),
+ "Slider.thumbWidth", new Integer(11),
+ "Slider.tickHeight", new Integer(12),
+ "Slider.horizontalSize", new Dimension(200, 21),
+ "Slider.verticalSize", new Dimension(21, 200),
+ "Slider.minimumHorizontalSize", new Dimension(36, 21),
+ "Slider.minimumVerticalSize", new Dimension(21, 36),
+ "Spinner.background", new ColorUIResource(light),
+ "Spinner.foreground", new ColorUIResource(light),
+ "Spinner.arrowButtonSize", new DimensionUIResource(16, 5),
+ "Spinner.editorBorderPainted", Boolean.FALSE,
+ "Spinner.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12),
+ "SplitPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "F6", "toggleFocus",
+ "F8", "startResize",
+ "END", "selectMax",
+ "HOME", "selectMin",
+ "LEFT", "negativeIncrement",
+ "KP_UP", "negativeIncrement",
+ "KP_DOWN", "positiveIncrement",
+ "UP", "negativeIncrement",
+ "RIGHT", "positiveIncrement",
+ "KP_LEFT", "negativeIncrement",
+ "DOWN", "positiveIncrement",
+ "KP_RIGHT", "positiveIncrement",
+ "shift ctrl pressed TAB", "focusOutBackward",
+ "ctrl pressed TAB", "focusOutForward"
+ }),
+ "SplitPane.background", new ColorUIResource(light),
+ "SplitPane.border", new BasicBorders.SplitPaneBorder(null, null),
+ "SplitPane.darkShadow", new ColorUIResource(shadow),
+ "SplitPane.dividerSize", new Integer(7),
+ "SplitPane.highlight", new ColorUIResource(highLight),
+ "SplitPane.shadow", new ColorUIResource(shadow),
+ "SplitPaneDivider.border", BasicBorders.getSplitPaneDividerBorder(),
+ "SplitPaneDivider.draggingColor", new ColorUIResource(Color.DARK_GRAY),
+ "TabbedPane.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ctrl PAGE_DOWN", "navigatePageDown",
+ "ctrl PAGE_UP", "navigatePageUp",
+ "ctrl UP", "requestFocus",
+ "ctrl KP_UP", "requestFocus"
+ }),
+ "TabbedPane.background", new ColorUIResource(192, 192, 192),
+ "TabbedPane.contentBorderInsets", new InsetsUIResource(2, 2, 3, 3),
+ "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",
+ KeyStroke.getKeyStroke("LEFT"), "navigateLeft",
+ KeyStroke.getKeyStroke("ctrl KP_DOWN"), "requestFocusForVisibleComponent",
+ KeyStroke.getKeyStroke("UP"), "navigateUp",
+ KeyStroke.getKeyStroke("KP_DOWN"), "navigateDown",
+ KeyStroke.getKeyStroke("KP_LEFT"), "navigateLeft",
+ KeyStroke.getKeyStroke("RIGHT"), "navigateRight",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "navigateRight",
+ KeyStroke.getKeyStroke("DOWN"), "navigateDown"
+ }),
+ "TabbedPane.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "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(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",
+ "ctrl RIGHT", "selectNextColumnChangeLead",
+ "ctrl UP", "selectPreviousRowChangeLead",
+ "ctrl LEFT", "selectPreviousColumnChangeLead",
+ "CUT", "cut",
+ "SPACE", "addToSelection",
+ "ctrl SPACE", "toggleAndAnchor",
+ "shift SPACE", "extendTo",
+ "shift ctrl SPACE", "moveSelectionTo",
+ "ctrl X", "cut",
+ "ctrl C", "copy",
+ "ctrl KP_RIGHT", "selectNextColumnChangeLead",
+ "ctrl KP_LEFT", "selectPreviousColumnChangeLead",
+ "ctrl V", "paste",
+ "ctrl KP_DOWN", "selectNextRowChangeLead",
+ "COPY", "copy",
+ "ctrl KP_UP", "selectPreviousRowChangeLead",
+ "PASTE", "paste",
+ "shift PAGE_DOWN", "scrollDownExtendSelection",
+ "PAGE_DOWN", "scrollDownChangeSelection",
+ "END", "selectLastColumn",
+ "shift END", "selectLastColumnExtendSelection",
+ "HOME", "selectFirstColumn",
+ "ctrl END", "selectLastRow",
+ "ctrl shift END", "selectLastRowExtendSelection",
+ "LEFT", "selectPreviousColumn",
+ "shift HOME", "selectFirstColumnExtendSelection",
+ "UP", "selectPreviousRow",
+ "RIGHT", "selectNextColumn",
+ "ctrl HOME", "selectFirstRow",
+ "shift LEFT", "selectPreviousColumnExtendSelection",
+ "DOWN", "selectNextRow",
+ "ctrl shift HOME", "selectFirstRowExtendSelection",
+ "shift UP", "selectPreviousRowExtendSelection",
+ "F2", "startEditing",
+ "shift RIGHT", "selectNextColumnExtendSelection",
+ "TAB", "selectNextColumnCell",
+ "shift DOWN", "selectNextRowExtendSelection",
+ "ENTER", "selectNextRowCell",
+ "KP_UP", "selectPreviousRow",
+ "KP_DOWN", "selectNextRow",
+ "KP_LEFT", "selectPreviousColumn",
+ "KP_RIGHT", "selectNextColumn",
+ "shift TAB", "selectPreviousColumnCell",
+ "ctrl A", "selectAll",
+ "shift ENTER", "selectPreviousRowCell",
+ "shift KP_DOWN", "selectNextRowExtendSelection",
+ "shift KP_LEFT", "selectPreviousColumnExtendSelection",
+ "ESCAPE", "cancel",
+ "ctrl shift PAGE_UP", "scrollLeftExtendSelection",
+ "shift KP_RIGHT", "selectNextColumnExtendSelection",
+ "ctrl PAGE_UP", "scrollLeftChangeSelection",
+ "shift PAGE_UP", "scrollUpExtendSelection",
+ "ctrl shift PAGE_DOWN", "scrollRightExtendSelection",
+ "ctrl PAGE_DOWN", "scrollRightChangeSelection",
+ "PAGE_UP", "scrollUpChangeSelection",
+ "ctrl shift LEFT", "selectPreviousColumnExtendSelection",
+ "shift KP_UP", "selectPreviousRowExtendSelection",
+ "ctrl shift UP", "selectPreviousRowExtendSelection",
+ "ctrl shift RIGHT", "selectNextColumnExtendSelection",
+ "ctrl shift KP_RIGHT", "selectNextColumnExtendSelection",
+ "ctrl shift DOWN", "selectNextRowExtendSelection",
+ "ctrl BACK_SLASH", "clearSelection",
+ "ctrl shift KP_UP", "selectPreviousRowExtendSelection",
+ "ctrl shift KP_LEFT", "selectPreviousColumnExtendSelection",
+ "ctrl SLASH", "selectAll",
+ "ctrl shift KP_DOWN", "selectNextRowExtendSelection",
+ }),
+ "Table.background", new ColorUIResource(new ColorUIResource(255, 255, 255)),
+ "Table.focusCellBackground", new ColorUIResource(new ColorUIResource(255, 255, 255)),
+ "Table.focusCellForeground", new ColorUIResource(new ColorUIResource(0, 0, 0)),
+ "Table.focusCellHighlightBorder",
+ new BorderUIResource.LineBorderUIResource(
+ new ColorUIResource(255, 255, 0)),
+ "Table.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Table.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)),
+ "Table.gridColor", new ColorUIResource(new ColorUIResource(128, 128, 128)),
+ "Table.scrollPaneBorder", new BorderUIResource.BevelBorderUIResource(0),
+ "Table.selectionBackground", new ColorUIResource(new ColorUIResource(0, 0, 128)),
+ "Table.selectionForeground", new ColorUIResource(new ColorUIResource(255, 255, 255)),
+ "TableHeader.background", new ColorUIResource(new ColorUIResource(192, 192, 192)),
+ "TableHeader.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "TableHeader.foreground", new ColorUIResource(new ColorUIResource(0, 0, 0)),
+
+ "TextArea.background", new ColorUIResource(light),
+ "TextArea.border", new BorderUIResource(BasicBorders.getMarginBorder()),
+ "TextArea.caretBlinkRate", new Integer(500),
+ "TextArea.caretForeground", new ColorUIResource(Color.black),
+ "TextArea.font", new FontUIResource("MonoSpaced", Font.PLAIN, 12),
+ "TextArea.foreground", new ColorUIResource(Color.black),
+ "TextArea.inactiveForeground", new ColorUIResource(Color.gray),
+ "TextArea.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("shift UP"), "selection-up",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selection-up",
+ KeyStroke.getKeyStroke("DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up",
+ KeyStroke.getKeyStroke("KP_UP"), "caret-up",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("ctrl END"), "caret-end",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ENTER"), "insert-break",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left",
+ KeyStroke.getKeyStroke("shift DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("KP_DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selection-end",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl T"), "next-link-action",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("TAB"), "insert-tab",
+ KeyStroke.getKeyStroke("UP"), "caret-up",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("PAGE_UP"), "page-up",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard"
+ }),
+ "TextArea.margin", new InsetsUIResource(0, 0, 0, 0),
+ "TextArea.selectionBackground", new ColorUIResource(Color.black),
+ "TextArea.selectionForeground", new ColorUIResource(Color.white),
+ "TextField.background", new ColorUIResource(light),
+ "TextField.border", new BasicBorders.FieldBorder(null, null, null, null),
+ "TextField.caretBlinkRate", new Integer(500),
+ "TextField.caretForeground", new ColorUIResource(Color.black),
+ "TextField.darkShadow", new ColorUIResource(shadow),
+ "TextField.font", new FontUIResource("SansSerif", Font.PLAIN, 12),
+ "TextField.foreground", new ColorUIResource(Color.black),
+ "TextField.highlight", new ColorUIResource(highLight),
+ "TextField.inactiveBackground", new ColorUIResource(Color.LIGHT_GRAY),
+ "TextField.inactiveForeground", new ColorUIResource(Color.GRAY),
+ "TextField.light", new ColorUIResource(highLight),
+ "TextField.highlight", new ColorUIResource(light),
+ "TextField.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("ENTER"), "notify-field-accept",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word"
+ }),
+ "TextField.margin", new InsetsUIResource(0, 0, 0, 0),
+ "TextField.selectionBackground", new ColorUIResource(Color.black),
+ "TextField.selectionForeground", new ColorUIResource(Color.white),
+ "TextPane.background", new ColorUIResource(Color.white),
+ "TextPane.border", BasicBorders.getMarginBorder(),
+ "TextPane.caretBlinkRate", new Integer(500),
+ "TextPane.caretForeground", new ColorUIResource(Color.black),
+ "TextPane.font", new FontUIResource("Serif", Font.PLAIN, 12),
+ "TextPane.foreground", new ColorUIResource(Color.black),
+ "TextPane.inactiveForeground", new ColorUIResource(Color.gray),
+ "TextPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("shift UP"), "selection-up",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift ctrl LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selection-up",
+ KeyStroke.getKeyStroke("DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("shift ctrl T"), "previous-link-action",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("CUT"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("END"), "caret-end-line",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "selection-page-up",
+ KeyStroke.getKeyStroke("KP_UP"), "caret-up",
+ KeyStroke.getKeyStroke("DELETE"), "delete-next",
+ KeyStroke.getKeyStroke("ctrl HOME"), "caret-begin",
+ KeyStroke.getKeyStroke("shift LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("ctrl END"), "caret-end",
+ KeyStroke.getKeyStroke("BACK_SPACE"), "delete-previous",
+ KeyStroke.getKeyStroke("shift ctrl RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("KP_LEFT"), "caret-backward",
+ KeyStroke.getKeyStroke("shift KP_RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "activate-link-action",
+ KeyStroke.getKeyStroke("ctrl H"), "delete-previous",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "unselect",
+ KeyStroke.getKeyStroke("ENTER"), "insert-break",
+ KeyStroke.getKeyStroke("shift HOME"), "selection-begin-line",
+ KeyStroke.getKeyStroke("RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "selection-page-left",
+ KeyStroke.getKeyStroke("shift DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "page-down",
+ KeyStroke.getKeyStroke("shift KP_LEFT"), "selection-backward",
+ KeyStroke.getKeyStroke("shift ctrl O"), "toggle-componentOrientation",
+ KeyStroke.getKeyStroke("ctrl X"), "cut-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "selection-page-right",
+ KeyStroke.getKeyStroke("ctrl C"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "caret-next-word",
+ KeyStroke.getKeyStroke("shift END"), "selection-end-line",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "caret-previous-word",
+ KeyStroke.getKeyStroke("HOME"), "caret-begin-line",
+ KeyStroke.getKeyStroke("ctrl V"), "paste-from-clipboard",
+ KeyStroke.getKeyStroke("KP_DOWN"), "caret-down",
+ KeyStroke.getKeyStroke("ctrl A"), "select-all",
+ KeyStroke.getKeyStroke("shift RIGHT"), "selection-forward",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selection-end",
+ KeyStroke.getKeyStroke("COPY"), "copy-to-clipboard",
+ KeyStroke.getKeyStroke("shift ctrl KP_LEFT"), "selection-previous-word",
+ KeyStroke.getKeyStroke("ctrl T"), "next-link-action",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selection-down",
+ KeyStroke.getKeyStroke("TAB"), "insert-tab",
+ KeyStroke.getKeyStroke("UP"), "caret-up",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selection-begin",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "selection-page-down",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "caret-forward",
+ KeyStroke.getKeyStroke("shift ctrl KP_RIGHT"), "selection-next-word",
+ KeyStroke.getKeyStroke("PAGE_UP"), "page-up",
+ KeyStroke.getKeyStroke("PASTE"), "paste-from-clipboard"
+ }),
+ "TextPane.margin", new InsetsUIResource(3, 3, 3, 3),
+ "TextPane.selectionBackground", new ColorUIResource(Color.black),
+ "TextPane.selectionForeground", new ColorUIResource(Color.white),
+ "TitledBorder.border", new BorderUIResource.EtchedBorderUIResource(),
+ "TitledBorder.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "TitledBorder.titleColor", new ColorUIResource(darkShadow),
+ "ToggleButton.background", new ColorUIResource(light),
+ "ToggleButton.border",
+ new BorderUIResource.CompoundBorderUIResource(null, null),
+ "ToggleButton.darkShadow", new ColorUIResource(shadow),
+ "ToggleButton.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("SPACE"), "pressed",
+ KeyStroke.getKeyStroke("released SPACE"), "released"
+ }),
+ "ToggleButton.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ToggleButton.foreground", new ColorUIResource(darkShadow),
+ "ToggleButton.highlight", new ColorUIResource(highLight),
+ "ToggleButton.light", new ColorUIResource(light),
+ "ToggleButton.margin", new InsetsUIResource(2, 14, 2, 14),
+ "ToggleButton.shadow", new ColorUIResource(shadow),
+ "ToggleButton.textIconGap", new Integer(4),
+ "ToggleButton.textShiftOffset", new Integer(0),
+ "ToolBar.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "UP", "navigateUp",
+ "KP_UP", "navigateUp",
+ "DOWN", "navigateDown",
+ "KP_DOWN", "navigateDown",
+ "LEFT", "navigateLeft",
+ "KP_LEFT", "navigateLeft",
+ "RIGHT", "navigateRight",
+ "KP_RIGHT", "navigateRight"
+ }),
+ "ToolBar.background", new ColorUIResource(light),
+ "ToolBar.border", new BorderUIResource.EtchedBorderUIResource(),
+ "ToolBar.darkShadow", new ColorUIResource(shadow),
+ "ToolBar.dockingBackground", new ColorUIResource(light),
+ "ToolBar.dockingForeground", new ColorUIResource(Color.red),
+ "ToolBar.floatingBackground", new ColorUIResource(light),
+ "ToolBar.floatingForeground", new ColorUIResource(Color.darkGray),
+ "ToolBar.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "ToolBar.foreground", new ColorUIResource(darkShadow),
+ "ToolBar.highlight", new ColorUIResource(highLight),
+ "ToolBar.light", new ColorUIResource(highLight),
+ "ToolBar.separatorSize", new DimensionUIResource(10, 10),
+ "ToolBar.shadow", new ColorUIResource(shadow),
+ "ToolTip.background", new ColorUIResource(light),
+ "ToolTip.border", new BorderUIResource.LineBorderUIResource(Color.lightGray),
+ "ToolTip.font", new FontUIResource("SansSerif", Font.PLAIN, 12),
+ "ToolTip.foreground", new ColorUIResource(darkShadow),
+ "Tree.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ "ESCAPE", "cancel"
+ }),
+ "Tree.background", new ColorUIResource(new Color(255, 255, 255)),
+ "Tree.changeSelectionWithFocus", Boolean.TRUE,
+ "Tree.drawsFocusBorderAroundIcon", Boolean.FALSE,
+ "Tree.editorBorder", new BorderUIResource.LineBorderUIResource(Color.lightGray),
+ "Tree.focusInputMap", new UIDefaults.LazyInputMap(new Object[] {
+ KeyStroke.getKeyStroke("ctrl DOWN"), "selectNextChangeLead",
+ KeyStroke.getKeyStroke("shift UP"), "selectPreviousExtendSelection",
+ KeyStroke.getKeyStroke("ctrl RIGHT"), "scrollRight",
+ KeyStroke.getKeyStroke("shift KP_UP"), "selectPreviousExtendSelection",
+ KeyStroke.getKeyStroke("DOWN"), "selectNext",
+ KeyStroke.getKeyStroke("ctrl UP"), "selectPreviousChangeLead",
+ KeyStroke.getKeyStroke("ctrl LEFT"), "scrollLeft",
+ KeyStroke.getKeyStroke("CUT"), "cut",
+ KeyStroke.getKeyStroke("END"), "selectLast",
+ KeyStroke.getKeyStroke("shift PAGE_UP"), "scrollUpExtendSelection",
+ KeyStroke.getKeyStroke("KP_UP"), "selectPrevious",
+ KeyStroke.getKeyStroke("shift ctrl UP"), "selectPreviousExtendSelection",
+ KeyStroke.getKeyStroke("ctrl HOME"), "selectFirstChangeLead",
+ KeyStroke.getKeyStroke("ctrl END"), "selectLastChangeLead",
+ KeyStroke.getKeyStroke("ctrl PAGE_DOWN"), "scrollDownChangeLead",
+ KeyStroke.getKeyStroke("LEFT"), "selectParent",
+ KeyStroke.getKeyStroke("ctrl PAGE_UP"), "scrollUpChangeLead",
+ KeyStroke.getKeyStroke("KP_LEFT"), "selectParent",
+ KeyStroke.getKeyStroke("SPACE"), "addToSelection",
+ KeyStroke.getKeyStroke("ctrl SPACE"), "toggleAndAnchor",
+ KeyStroke.getKeyStroke("shift SPACE"), "extendTo",
+ KeyStroke.getKeyStroke("shift ctrl SPACE"), "moveSelectionTo",
+ KeyStroke.getKeyStroke("ADD"), "expand",
+ KeyStroke.getKeyStroke("ctrl BACK_SLASH"), "clearSelection",
+ KeyStroke.getKeyStroke("shift ctrl DOWN"), "selectNextExtendSelection",
+ KeyStroke.getKeyStroke("shift HOME"), "selectFirstExtendSelection",
+ KeyStroke.getKeyStroke("RIGHT"), "selectChild",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_UP"), "scrollUpExtendSelection",
+ KeyStroke.getKeyStroke("shift DOWN"), "selectNextExtendSelection",
+ KeyStroke.getKeyStroke("PAGE_DOWN"), "scrollDownChangeSelection",
+ KeyStroke.getKeyStroke("shift ctrl KP_UP"), "selectPreviousExtendSelection",
+ KeyStroke.getKeyStroke("SUBTRACT"), "collapse",
+ KeyStroke.getKeyStroke("ctrl X"), "cut",
+ KeyStroke.getKeyStroke("shift ctrl PAGE_DOWN"), "scrollDownExtendSelection",
+ KeyStroke.getKeyStroke("ctrl SLASH"), "selectAll",
+ KeyStroke.getKeyStroke("ctrl C"), "copy",
+ KeyStroke.getKeyStroke("ctrl KP_RIGHT"), "scrollRight",
+ KeyStroke.getKeyStroke("shift END"), "selectLastExtendSelection",
+ KeyStroke.getKeyStroke("shift ctrl KP_DOWN"), "selectNextExtendSelection",
+ KeyStroke.getKeyStroke("ctrl KP_LEFT"), "scrollLeft",
+ KeyStroke.getKeyStroke("HOME"), "selectFirst",
+ KeyStroke.getKeyStroke("ctrl V"), "paste",
+ KeyStroke.getKeyStroke("KP_DOWN"), "selectNext",
+ KeyStroke.getKeyStroke("ctrl A"), "selectAll",
+ KeyStroke.getKeyStroke("ctrl KP_DOWN"), "selectNextChangeLead",
+ KeyStroke.getKeyStroke("shift ctrl END"), "selectLastExtendSelection",
+ KeyStroke.getKeyStroke("COPY"), "copy",
+ KeyStroke.getKeyStroke("ctrl KP_UP"), "selectPreviousChangeLead",
+ KeyStroke.getKeyStroke("shift KP_DOWN"), "selectNextExtendSelection",
+ KeyStroke.getKeyStroke("UP"), "selectPrevious",
+ KeyStroke.getKeyStroke("shift ctrl HOME"), "selectFirstExtendSelection",
+ KeyStroke.getKeyStroke("shift PAGE_DOWN"), "scrollDownExtendSelection",
+ KeyStroke.getKeyStroke("KP_RIGHT"), "selectChild",
+ KeyStroke.getKeyStroke("F2"), "startEditing",
+ KeyStroke.getKeyStroke("PAGE_UP"), "scrollUpChangeSelection",
+ KeyStroke.getKeyStroke("PASTE"), "paste"
+ }),
+ "Tree.font", new FontUIResource("Dialog", Font.PLAIN, 12),
+ "Tree.foreground", new ColorUIResource(Color.black),
+ "Tree.hash", new ColorUIResource(new Color(184, 207, 228)),
+ "Tree.leftChildIndent", new Integer(7),
+ "Tree.rightChildIndent", new Integer(13),
+ "Tree.rowHeight", new Integer(16),
+ "Tree.scrollsOnExpand", Boolean.TRUE,
+ "Tree.selectionBackground", new ColorUIResource(Color.black),
+ "Tree.nonSelectionBackground", new ColorUIResource(new Color(255, 255, 255)),
+ "Tree.selectionBorderColor", new ColorUIResource(Color.black),
+ "Tree.selectionBorder", new BorderUIResource.LineBorderUIResource(Color.black),
+ "Tree.selectionForeground", new ColorUIResource(new Color(255, 255, 255)),
+ "Viewport.background", new ColorUIResource(light),
+ "Viewport.foreground", new ColorUIResource(Color.black),
+ "Viewport.font", new FontUIResource("Dialog", Font.PLAIN, 12)
+ };
+ defaults.putDefaults(uiDefaults);
+ }
+
+ /**
+ * Returns the <code>ActionMap</code> that stores all the actions that are
+ * responsibly for rendering auditory cues.
+ *
+ * @return the action map that stores all the actions that are
+ * responsibly for rendering auditory cues
+ *
+ * @see #createAudioAction
+ * @see #playSound
+ *
+ * @since 1.4
+ */
+ protected ActionMap getAudioActionMap()
+ {
+ if (audioActionMap != null)
+ audioActionMap = new ActionMap();
+ return audioActionMap;
+ }
+
+ /**
+ * Creates an <code>Action</code> that can play an auditory cue specified by
+ * the key. The UIDefaults value for the key is normally a String that points
+ * to an audio file relative to the current package.
+ *
+ * @param key a UIDefaults key that specifies the sound
+ *
+ * @return an action that can play the sound
+ *
+ * @see #playSound
+ *
+ * @since 1.4
+ */
+ protected Action createAudioAction(Object key)
+ {
+ return new AudioAction(key);
+ }
+
+ /**
+ * Plays the sound of the action if it is listed in
+ * <code>AuditoryCues.playList</code>.
+ *
+ * @param audioAction the audio action to play
+ *
+ * @since 1.4
+ */
+ protected void playSound(Action audioAction)
+ {
+ if (audioAction instanceof AudioAction)
+ {
+ Object[] playList = (Object[]) UIManager.get("AuditoryCues.playList");
+ for (int i = 0; i < playList.length; ++i)
+ {
+ if (playList[i].equals(((AudioAction) audioAction).key))
+ {
+ ActionEvent ev = new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED,
+ (String) playList[i]);
+ audioAction.actionPerformed(ev);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Initializes the Look and Feel.
+ */
+ public void initialize()
+ {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ popupHelper = new PopupHelper();
+ toolkit.addAWTEventListener(popupHelper, AWTEvent.MOUSE_EVENT_MASK);
+ }
+
+ /**
+ * Uninitializes the Look and Feel.
+ */
+ public void uninitialize()
+ {
+ Toolkit toolkit = Toolkit.getDefaultToolkit();
+ toolkit.removeAWTEventListener(popupHelper);
+ popupHelper = null;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java
new file mode 100644
index 000000000..b9e29128a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuBarUI.java
@@ -0,0 +1,481 @@
+/* BasicMenuBarUI.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BoxLayout;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuBarUI;
+
+/**
+ * UI Delegate for JMenuBar.
+ */
+public class BasicMenuBarUI extends MenuBarUI
+{
+
+ /**
+ * This action is performed for the action command 'takeFocus'.
+ */
+ private static class FocusAction
+ extends AbstractAction
+ {
+
+ /**
+ * Creates a new FocusAction.
+ */
+ FocusAction()
+ {
+ super("takeFocus");
+ }
+
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // In the JDK this action seems to pop up the first menu of the
+ // menu bar.
+ JMenuBar menuBar = (JMenuBar) event.getSource();
+ MenuSelectionManager defaultManager =
+ MenuSelectionManager.defaultManager();
+ MenuElement me[];
+ MenuElement subElements[];
+ JMenu menu = menuBar.getMenu(0);
+ if (menu != null)
+ {
+ me = new MenuElement[3];
+ me[0] = (MenuElement) menuBar;
+ me[1] = (MenuElement) menu;
+ me[2] = (MenuElement) menu.getPopupMenu();
+ defaultManager.setSelectedPath(me);
+ }
+ }
+
+ }
+
+ protected ChangeListener changeListener;
+
+ /*ContainerListener that listens to the ContainerEvents fired from menu bar*/
+ protected ContainerListener containerListener;
+
+ /*Property change listeners that listener to PropertyChangeEvent from menu bar*/
+ private PropertyChangeListener propertyChangeListener;
+
+ /* menu bar for which this UI delegate is for*/
+ protected JMenuBar menuBar;
+
+ /* MouseListener that listens to the mouseEvents fired from menu bar*/
+ private MouseInputListener mouseListener;
+
+ /**
+ * Creates a new BasicMenuBarUI object.
+ */
+ public BasicMenuBarUI()
+ {
+ changeListener = createChangeListener();
+ containerListener = createContainerListener();
+ propertyChangeListener = new PropertyChangeHandler();
+ mouseListener = new MouseInputHandler();
+ }
+
+ /**
+ * Creates ChangeListener
+ *
+ * @return The ChangeListener
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new ChangeHandler();
+ }
+
+ /**
+ * Creates ContainerListener() to listen for ContainerEvents
+ * fired by JMenuBar.
+ *
+ * @return The ContainerListener
+ */
+ protected ContainerListener createContainerListener()
+ {
+ return new ContainerHandler();
+ }
+
+ /**
+ * Factory method to create a BasicMenuBarUI for the given {@link
+ * JComponent}, which should be a {@link JMenuBar}.
+ *
+ * @param x The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicMenuBarUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicMenuBarUI();
+ }
+
+ /**
+ * Returns maximum size for the specified menu bar
+ *
+ * @param c component for which to get maximum size
+ *
+ * @return Maximum size for the specified menu bar
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ // let layout manager calculate its size
+ return null;
+ }
+
+ /**
+ * Returns maximum allowed size of JMenuBar.
+ *
+ * @param c menuBar for which to return maximum size
+ *
+ * @return Maximum size of the give menu bar.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ // let layout manager calculate its size
+ return null;
+ }
+
+ /**
+ * Returns preferred size of JMenuBar.
+ *
+ * @param c menuBar for which to return preferred size
+ *
+ * @return Preferred size of the give menu bar.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ // let layout manager calculate its size
+ return null;
+ }
+
+ /**
+ * Initializes any default properties that this UI has from the defaults for
+ * the Basic look and feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installBorder(menuBar, "MenuBar.border");
+ LookAndFeel.installColorsAndFont(menuBar, "MenuBar.background",
+ "MenuBar.foreground", "MenuBar.font");
+ menuBar.setOpaque(true);
+ }
+
+ /**
+ * This method installs the keyboard actions for the JMenuBar.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install InputMap.
+ Object[] bindings =
+ (Object[]) SharedUIDefaults.get("MenuBar.windowBindings");
+ InputMap inputMap = LookAndFeel.makeComponentInputMap(menuBar, bindings);
+ SwingUtilities.replaceUIInputMap(menuBar,
+ JComponent.WHEN_IN_FOCUSED_WINDOW,
+ inputMap);
+
+ // Install ActionMap.
+ SwingUtilities.replaceUIActionMap(menuBar, getActionMap());
+ }
+
+ /**
+ * Creates and returns the shared action map for JTrees.
+ *
+ * @return the shared action map for JTrees
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("MenuBar.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("MenuBar.actionMap", am);
+ }
+ return am;
+ }
+
+ /**
+ * Creates the default actions when there are none specified by the L&F.
+ *
+ * @return the default actions
+ */
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new FocusAction();
+ am.put(action.getValue(Action.NAME), action);
+ return am;
+ }
+
+ /**
+ * This method installs the listeners needed for this UI to function.
+ */
+ protected void installListeners()
+ {
+ menuBar.addContainerListener(containerListener);
+ menuBar.addPropertyChangeListener(propertyChangeListener);
+ menuBar.addMouseListener(mouseListener);
+ }
+
+ /**
+ * Installs and initializes all fields for this UI delegate. Any properties
+ * of the UI that need to be initialized and/or set to defaults will be
+ * done now. It will also install any listeners necessary.
+ *
+ * @param c The {@link JComponent} that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ menuBar = (JMenuBar) c;
+ menuBar.setLayout(new BoxLayout(menuBar, BoxLayout.X_AXIS));
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * This method uninstalls the defaults and nulls any objects created during
+ * install.
+ */
+ protected void uninstallDefaults()
+ {
+ menuBar.setBackground(null);
+ menuBar.setBorder(null);
+ menuBar.setFont(null);
+ menuBar.setForeground(null);
+ }
+
+ /**
+ * This method reverses the work done in installKeyboardActions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(menuBar,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, null);
+ SwingUtilities.replaceUIActionMap(menuBar, null);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using.
+ */
+ protected void uninstallListeners()
+ {
+ menuBar.removeContainerListener(containerListener);
+ menuBar.removePropertyChangeListener(propertyChangeListener);
+ menuBar.removeMouseListener(mouseListener);
+ }
+
+ /**
+ * Performs the opposite of installUI. Any properties or resources that need
+ * to be cleaned up will be done now. It will also uninstall any listeners
+ * it has. In addition, any properties of this UI will be nulled.
+ *
+ * @param c The {@link JComponent} that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallDefaults();
+ uninstallListeners();
+ uninstallKeyboardActions();
+ menuBar = null;
+ }
+
+ private class ChangeHandler implements ChangeListener
+ {
+ public void stateChanged(ChangeEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * This class handles ContainerEvents fired by JMenuBar. It revalidates
+ * and repaints menu bar whenever menu is added or removed from it.
+ */
+ private class ContainerHandler implements ContainerListener
+ {
+ /**
+ * This method is called whenever menu is added to the menu bar
+ *
+ * @param e The ContainerEvent.
+ */
+ public void componentAdded(ContainerEvent e)
+ {
+ menuBar.revalidate();
+ menuBar.repaint();
+ }
+
+ /**
+ * This method is called whenever menu is removed from the menu bar.
+ *
+ * @param e The ContainerEvent.
+ */
+ public void componentRemoved(ContainerEvent e)
+ {
+ menuBar.revalidate();
+ menuBar.repaint();
+ }
+ }
+
+ /**
+ * This class handles PropertyChangeEvents fired from the JMenuBar
+ */
+ private class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called whenever one of the properties of the MenuBar
+ * changes.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("borderPainted"))
+ menuBar.repaint();
+ if (e.getPropertyName().equals("margin"))
+ menuBar.repaint();
+ }
+ }
+
+ private class MouseInputHandler implements MouseInputListener
+ {
+ /**
+ * Handles mouse clicked event
+ *
+ * @param e Mouse event
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ MenuElement[] me = menuBar.getSubElements();
+
+ for (int i = 0; i < me.length; i++)
+ {
+ JMenu menu = menuBar.getMenu(i);
+ if (menu != null)
+ menu.setSelected(false);
+ }
+ }
+
+ /**
+ * Handles mouse pressed event
+ *
+ * @param e Mouse event
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse released event
+ *
+ * @param e Mouse event
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse exited event
+ *
+ * @param e Mouse event
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse dragged event
+ *
+ * @param e Mouse event
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse moved event
+ *
+ * @param e Mouse event
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * Handles mouse entered event
+ *
+ * @param e Mouse event
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
new file mode 100644
index 000000000..40b539378
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuItemUI.java
@@ -0,0 +1,1339 @@
+/* BasicMenuItemUI.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import gnu.classpath.SystemProperties;
+
+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.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.font.FontRenderContext;
+import java.awt.font.TextLayout;
+import java.awt.geom.AffineTransform;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractButton;
+import javax.swing.ActionMap;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JComponent;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.MenuDragMouseEvent;
+import javax.swing.event.MenuDragMouseListener;
+import javax.swing.event.MenuKeyEvent;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentInputMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.MenuItemUI;
+import javax.swing.text.View;
+
+/**
+ * UI Delegate for JMenuItem.
+ */
+public class BasicMenuItemUI extends MenuItemUI
+{
+ /**
+ * Font to be used when displaying menu item's accelerator.
+ */
+ protected Font acceleratorFont;
+
+ /**
+ * Color to be used when displaying menu item's accelerator.
+ */
+ protected Color acceleratorForeground;
+
+ /**
+ * Color to be used when displaying menu item's accelerator when menu item is
+ * selected.
+ */
+ protected Color acceleratorSelectionForeground;
+
+ /**
+ * Icon that is displayed after the text to indicated that this menu contains
+ * submenu.
+ */
+ protected Icon arrowIcon;
+
+ /**
+ * Icon that is displayed before the text. This icon is only used in
+ * JCheckBoxMenuItem or JRadioBoxMenuItem.
+ */
+ protected Icon checkIcon;
+
+ /**
+ * Number of spaces between icon and text.
+ */
+ protected int defaultTextIconGap = 4;
+
+ /**
+ * Color of the text when menu item is disabled
+ */
+ protected Color disabledForeground;
+
+ /**
+ * The menu Drag mouse listener listening to the menu item.
+ */
+ protected MenuDragMouseListener menuDragMouseListener;
+
+ /**
+ * The menu item itself
+ */
+ protected JMenuItem menuItem;
+
+ /**
+ * Menu Key listener listening to the menu item.
+ */
+ protected MenuKeyListener menuKeyListener;
+
+ /**
+ * mouse input listener listening to menu item.
+ */
+ protected MouseInputListener mouseInputListener;
+
+ /**
+ * Indicates if border should be painted
+ */
+ protected boolean oldBorderPainted;
+
+ /**
+ * Color of text that is used when menu item is selected
+ */
+ protected Color selectionBackground;
+
+ /**
+ * Color of the text that is used when menu item is selected.
+ */
+ protected Color selectionForeground;
+
+ /**
+ * String that separates description of the modifiers and the key
+ */
+ private String acceleratorDelimiter;
+
+ /**
+ * ItemListener to listen for item changes in the menu item
+ */
+ private ItemListener itemListener;
+
+ /**
+ * A PropertyChangeListener to make UI updates after property changes.
+ */
+ private PropertyChangeHandler propertyChangeListener;
+
+ /**
+ * The view rectangle used for layout of the menu item.
+ */
+ private Rectangle viewRect;
+
+ /**
+ * The rectangle that holds the area of the label.
+ */
+ private Rectangle textRect;
+
+ /**
+ * The rectangle that holds the area of the accelerator.
+ */
+ private Rectangle accelRect;
+
+ /**
+ * The rectangle that holds the area of the icon.
+ */
+ private Rectangle iconRect;
+
+ /**
+ * The rectangle that holds the area of the icon.
+ */
+ private Rectangle arrowIconRect;
+
+ /**
+ * The rectangle that holds the area of the check icon.
+ */
+ private Rectangle checkIconRect;
+
+ /**
+ * A rectangle used for temporary storage to avoid creation of new
+ * rectangles.
+ */
+ private Rectangle cachedRect;
+
+ /**
+ * A class to handle PropertChangeEvents for the JMenuItem
+ * @author Anthony Balkissoon abalkiss at redhat dot com.
+ */
+ class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when a property of the menuItem is changed.
+ * Currently it is only used to update the accelerator key bindings.
+ *
+ * @param e
+ * the PropertyChangeEvent
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String property = e.getPropertyName();
+ if (property.equals("accelerator"))
+ {
+ InputMap map = SwingUtilities.getUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (map != null)
+ map.remove((KeyStroke) e.getOldValue());
+ else
+ map = new ComponentInputMapUIResource(menuItem);
+
+ KeyStroke accelerator = (KeyStroke) e.getNewValue();
+ if (accelerator != null)
+ map.put(accelerator, "doClick");
+ }
+ // TextLayout caching for speed-up drawing of text.
+ else if ((property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)
+ || property.equals("font"))
+ && SystemProperties.getProperty("gnu.javax.swing.noGraphics2D")
+ == null)
+ {
+ AbstractButton b = (AbstractButton) e.getSource();
+ String text = b.getText();
+ if (text == null)
+ text = "";
+ FontRenderContext frc = new FontRenderContext(new AffineTransform(),
+ false, false);
+ TextLayout layout = new TextLayout(text, b.getFont(), frc);
+ b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout);
+ }
+ }
+ }
+
+ /**
+ * A class to handle accelerator keys. This is the Action we will
+ * perform when the accelerator key for this JMenuItem is pressed.
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ */
+ class ClickAction extends AbstractAction
+ {
+ /**
+ * This is what is done when the accelerator key for the JMenuItem is
+ * pressed.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ doClick(MenuSelectionManager.defaultManager());
+ }
+ }
+
+ /**
+ * Creates a new BasicMenuItemUI object.
+ */
+ public BasicMenuItemUI()
+ {
+ mouseInputListener = createMouseInputListener(menuItem);
+ menuDragMouseListener = createMenuDragMouseListener(menuItem);
+ menuKeyListener = createMenuKeyListener(menuItem);
+ itemListener = new ItemHandler();
+ propertyChangeListener = new PropertyChangeHandler();
+
+ // Initialize rectangles for layout.
+ viewRect = new Rectangle();
+ textRect = new Rectangle();
+ iconRect = new Rectangle();
+ arrowIconRect = new Rectangle();
+ checkIconRect = new Rectangle();
+ accelRect = new Rectangle();
+ cachedRect = new Rectangle();
+ }
+
+ /**
+ * Create MenuDragMouseListener to listen for mouse dragged events.
+ *
+ * @param c
+ * menu item to listen to
+ * @return The MenuDragMouseListener
+ */
+ protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
+ {
+ return new MenuDragMouseHandler();
+ }
+
+ /**
+ * Creates MenuKeyListener to listen to key events occuring when menu item is
+ * visible on the screen.
+ *
+ * @param c
+ * menu item to listen to
+ * @return The MenuKeyListener
+ */
+ protected MenuKeyListener createMenuKeyListener(JComponent c)
+ {
+ return new MenuKeyHandler();
+ }
+
+ /**
+ * Handles mouse input events occuring for this menu item
+ *
+ * @param c
+ * menu item to listen to
+ * @return The MouseInputListener
+ */
+ protected MouseInputListener createMouseInputListener(JComponent c)
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * Factory method to create a BasicMenuItemUI for the given {@link
+ * JComponent}, which should be a {@link JMenuItem}.
+ *
+ * @param c
+ * The {@link JComponent} a UI is being created for.
+ * @return A BasicMenuItemUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicMenuItemUI();
+ }
+
+ /**
+ * Programatically clicks menu item.
+ *
+ * @param msm
+ * MenuSelectionManager for the menu hierarchy
+ */
+ protected void doClick(MenuSelectionManager msm)
+ {
+ menuItem.doClick(0);
+ msm.clearSelectedPath();
+ }
+
+ /**
+ * Returns maximum size for the specified menu item
+ *
+ * @param c
+ * component for which to get maximum size
+ * @return Maximum size for the specified menu item.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * Returns minimum size for the specified menu item
+ *
+ * @param c
+ * component for which to get minimum size
+ * @return Minimum size for the specified menu item.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * Returns path to this menu item.
+ *
+ * @return $MenuElement[]$ Returns array of menu elements that constitute a
+ * path to this menu item.
+ */
+ public MenuElement[] getPath()
+ {
+ ArrayList path = new ArrayList();
+
+ Component c = menuItem;
+ while (c instanceof MenuElement)
+ {
+ path.add(0, c);
+
+ if (c instanceof JPopupMenu)
+ c = ((JPopupMenu) c).getInvoker();
+ else
+ c = c.getParent();
+ }
+
+ MenuElement[] pathArray = new MenuElement[path.size()];
+ path.toArray(pathArray);
+ return pathArray;
+ }
+
+ /**
+ * Returns preferred size for the given menu item.
+ *
+ * @param c
+ * menu item for which to get preferred size
+ * @param checkIcon
+ * check icon displayed in the given menu item
+ * @param arrowIcon
+ * arrow icon displayed in the given menu item
+ * @param defaultTextIconGap
+ * space between icon and text in the given menuItem
+ * @return $Dimension$ preferred size for the given menu item
+ */
+ protected Dimension getPreferredMenuItemSize(JComponent c, Icon checkIcon,
+ Icon arrowIcon,
+ int defaultTextIconGap)
+ {
+ JMenuItem m = (JMenuItem) c;
+ String accelText = getAcceleratorString(m);
+
+ // 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.
+ cachedRect.setBounds(textRect);
+ Rectangle pref = SwingUtilities.computeUnion(iconRect.x, iconRect.y,
+ iconRect.width,
+ iconRect.height,
+ cachedRect);
+
+ // Find the widest menu item text and accelerator and store it in
+ // client properties of the parent, so that we can align the accelerators
+ // properly. Of course, we only need can do this, if the parent is
+ // a JComponent and this menu item is not a toplevel menu.
+ Container parent = m.getParent();
+ if (parent != null && parent instanceof JComponent
+ && !(m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ JComponent p = (JComponent) parent;
+
+ // The widest text so far.
+ Integer maxTextWidth = (Integer) p.getClientProperty("maxTextWidth");
+ int maxTextValue = maxTextWidth == null ? 0 : maxTextWidth.intValue();
+ if (pref.width < maxTextValue)
+ pref.width = maxTextValue;
+ else
+ p.putClientProperty("maxTextWidth", new Integer(pref.width));
+
+ // The widest accelerator so far.
+ Integer maxAccelWidth = (Integer) p.getClientProperty("maxAccelWidth");
+ int maxAccelValue = maxAccelWidth == null ? 0
+ : maxAccelWidth.intValue();
+ if (accelRect.width > maxAccelValue)
+ {
+ maxAccelValue = accelRect.width;
+ p.putClientProperty("maxAccelWidth", new Integer(accelRect.width));
+ }
+ pref.width += maxAccelValue;
+ pref.width += defaultTextIconGap;
+ }
+
+ // Add arrow and check size if appropriate.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ pref.width += checkIconRect.width;
+ pref.width += defaultTextIconGap;
+ pref.width += arrowIconRect.width;
+ pref.width += defaultTextIconGap;
+ }
+
+ // Add a gap ~2 times as wide as the defaultTextIconGap.
+ pref.width += 2 * defaultTextIconGap;
+
+ // Respect the insets of the menu item.
+ Insets i = m.getInsets();
+ pref.width += i.left + i.right;
+ pref.height += i.top + i.bottom;
+
+ // Return a copy, so that nobody messes with our textRect.
+ return pref.getSize();
+ }
+
+ /**
+ * Returns preferred size of the given component
+ *
+ * @param c
+ * component for which to return preferred size
+ * @return $Dimension$ preferred size for the given component
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return getPreferredMenuItemSize(c, checkIcon, arrowIcon,
+ defaultTextIconGap);
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "MenuItem"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "MenuItem";
+ }
+
+ /**
+ * This method installs the components for this {@link JMenuItem}.
+ *
+ * @param menuItem
+ * The {@link JMenuItem} to install components for.
+ */
+ protected void installComponents(JMenuItem menuItem)
+ {
+ // FIXME: Need to implement
+ }
+
+ /**
+ * This method installs the defaults that are defined in the Basic look and
+ * feel for this {@link JMenuItem}.
+ */
+ protected void installDefaults()
+ {
+ String prefix = getPropertyPrefix();
+ LookAndFeel.installBorder(menuItem, prefix + ".border");
+ LookAndFeel.installColorsAndFont(menuItem, prefix + ".background",
+ prefix + ".foreground", prefix + ".font");
+ menuItem.setMargin(UIManager.getInsets(prefix + ".margin"));
+ acceleratorFont = UIManager.getFont(prefix + ".acceleratorFont");
+ acceleratorForeground = UIManager.getColor(prefix
+ + ".acceleratorForeground");
+ acceleratorSelectionForeground = UIManager.getColor(prefix
+ + ".acceleratorSelectionForeground");
+ selectionBackground = UIManager.getColor(prefix + ".selectionBackground");
+ selectionForeground = UIManager.getColor(prefix + ".selectionForeground");
+ acceleratorDelimiter = UIManager.getString(prefix + ".acceleratorDelimiter");
+ checkIcon = UIManager.getIcon(prefix + ".checkIcon");
+
+ menuItem.setHorizontalTextPosition(SwingConstants.TRAILING);
+ menuItem.setHorizontalAlignment(SwingConstants.LEADING);
+ }
+
+ /**
+ * This method installs the keyboard actions for this {@link JMenuItem}.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap focusedWindowMap = SwingUtilities.getUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (focusedWindowMap == null)
+ focusedWindowMap = new ComponentInputMapUIResource(menuItem);
+ KeyStroke accelerator = menuItem.getAccelerator();
+ if (accelerator != null)
+ focusedWindowMap.put(accelerator, "doClick");
+ SwingUtilities.replaceUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, focusedWindowMap);
+
+ ActionMap UIActionMap = SwingUtilities.getUIActionMap(menuItem);
+ if (UIActionMap == null)
+ UIActionMap = new ActionMapUIResource();
+ UIActionMap.put("doClick", new ClickAction());
+ SwingUtilities.replaceUIActionMap(menuItem, UIActionMap);
+ }
+
+ /**
+ * This method installs the listeners for the {@link JMenuItem}.
+ */
+ protected void installListeners()
+ {
+ menuItem.addMouseListener(mouseInputListener);
+ menuItem.addMouseMotionListener(mouseInputListener);
+ menuItem.addMenuDragMouseListener(menuDragMouseListener);
+ menuItem.addMenuKeyListener(menuKeyListener);
+ menuItem.addItemListener(itemListener);
+ menuItem.addPropertyChangeListener(propertyChangeListener);
+ // Fire synthetic property change event to let the listener update
+ // the TextLayout cache.
+ propertyChangeListener.propertyChange(new PropertyChangeEvent(menuItem,
+ "font", null,
+ menuItem.getFont()));
+ }
+
+ /**
+ * Installs and initializes all fields for this UI delegate. Any properties of
+ * the UI that need to be initialized and/or set to defaults will be done now.
+ * It will also install any listeners necessary.
+ *
+ * @param c
+ * The {@link JComponent} that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ menuItem = (JMenuItem) c;
+ installDefaults();
+ installComponents(menuItem);
+ installListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * Paints given menu item using specified graphics context
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param c
+ * Menu Item to paint
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ paintMenuItem(g, c, checkIcon, arrowIcon, selectionBackground,
+ c.getForeground(), defaultTextIconGap);
+ }
+
+ /**
+ * Paints background of the menu item
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param menuItem
+ * menu item to paint
+ * @param bgColor
+ * Background color to use when painting menu item
+ */
+ protected void paintBackground(Graphics g, JMenuItem menuItem, Color bgColor)
+ {
+ // Menu item is considered to be highlighted when it is selected.
+ // But we don't want to paint the background of JCheckBoxMenuItems
+ ButtonModel mod = menuItem.getModel();
+ Color saved = g.getColor();
+ if (mod.isArmed() || ((menuItem instanceof JMenu) && mod.isSelected()))
+ {
+ g.setColor(bgColor);
+ g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
+ }
+ else if (menuItem.isOpaque())
+ {
+ g.setColor(menuItem.getBackground());
+ g.fillRect(0, 0, menuItem.getWidth(), menuItem.getHeight());
+ }
+ g.setColor(saved);
+ }
+
+ /**
+ * Paints specified menu item
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param c
+ * menu item to paint
+ * @param checkIcon
+ * check icon to use when painting menu item
+ * @param arrowIcon
+ * arrow icon to use when painting menu item
+ * @param background
+ * Background color of the menu item
+ * @param foreground
+ * Foreground color of the menu item
+ * @param defaultTextIconGap
+ * space to use between icon and text when painting menu item
+ */
+ protected void paintMenuItem(Graphics g, JComponent c, Icon checkIcon,
+ Icon arrowIcon, Color background,
+ Color foreground, int defaultTextIconGap)
+ {
+ JMenuItem m = (JMenuItem) c;
+
+ // Fetch fonts.
+ Font oldFont = g.getFont();
+ Font font = c.getFont();
+ g.setFont(font);
+ FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
+
+ // Create accelerator string.
+ String accelText = getAcceleratorString(m);
+
+ // Layout menu item. The result gets stored in the rectangle fields
+ // of this class.
+ resetRectangles(m);
+
+ layoutMenuItem(m, accelText);
+
+ // Paint the background.
+ paintBackground(g, m, background);
+
+ Color oldColor = g.getColor();
+
+ // Paint the check icon.
+ if (checkIcon != null)
+ {
+ checkIcon.paintIcon(m, g, checkIconRect.x, checkIconRect.y);
+ }
+
+ // Paint the icon.
+ ButtonModel model = m.getModel();
+ if (m.getIcon() != null)
+ {
+ // Determine icon depending on the menu item
+ // state (normal/disabled/pressed).
+ Icon icon;
+ if (! m.isEnabled())
+ {
+ icon = m.getDisabledIcon();
+ }
+ else if (model.isPressed() && model.isArmed())
+ {
+ icon = m.getPressedIcon();
+ if (icon == null)
+ {
+ icon = m.getIcon();
+ }
+ }
+ else
+ {
+ icon = m.getIcon();
+ }
+
+ if (icon != null)
+ {
+ icon.paintIcon(m, g, iconRect.x, iconRect.y);
+ }
+ }
+
+ // Paint the text.
+ String text = m.getText();
+ if (text != null)
+ {
+ // Handle HTML.
+ View html = (View) m.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ html.paint(g, textRect);
+ }
+ else
+ {
+ paintText(g, m, textRect, text);
+ }
+ }
+
+ // Paint accelerator text.
+ if (! accelText.equals(""))
+ {
+ // Align the accelerator text. In getPreferredMenuItemSize() we
+ // store a client property 'maxAccelWidth' in the parent which holds
+ // the maximum accelerator width for the children of this parent.
+ // We use this here to align the accelerators properly.
+ int accelOffset = 0;
+ Container parent = m.getParent();
+ if (parent != null && parent instanceof JComponent)
+ {
+ JComponent p = (JComponent) parent;
+ Integer maxAccelWidth =
+ (Integer) p.getClientProperty("maxAccelWidth");
+ int maxAccelValue = maxAccelWidth == null ? 0
+ : maxAccelWidth.intValue();
+ accelOffset = maxAccelValue - accelRect.width;
+ }
+
+ g.setFont(acceleratorFont);
+ if (! m.isEnabled())
+ {
+ // Paint accelerator disabled.
+ g.setColor(disabledForeground);
+ }
+ else
+ {
+ if (m.isArmed() || (m instanceof JMenu && m.isSelected()))
+ g.setColor(acceleratorSelectionForeground);
+ else
+ g.setColor(acceleratorForeground);
+ }
+ g.drawString(accelText, accelRect.x - accelOffset,
+ accelRect.y + accelFm.getAscent());
+ }
+
+ // Paint arrow.
+ if (arrowIcon != null
+ && ! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ arrowIcon.paintIcon(m, g, arrowIconRect.x, arrowIconRect.y);
+ }
+
+ g.setFont(oldFont);
+ g.setColor(oldColor);
+
+ }
+
+ /**
+ * Paints label for the given menu item
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param menuItem
+ * menu item for which to draw its label
+ * @param textRect
+ * rectangle specifiying position of the text relative to the given
+ * menu item
+ * @param text
+ * label of the menu item
+ */
+ protected void paintText(Graphics g, JMenuItem menuItem, Rectangle textRect,
+ String text)
+ {
+ Font f = menuItem.getFont();
+ g.setFont(f);
+ FontMetrics fm = g.getFontMetrics(f);
+
+ if (text != null && !text.equals(""))
+ {
+ if (menuItem.isEnabled())
+ {
+ // Menu item is considered to be highlighted when it is selected.
+ // But not if it's a JCheckBoxMenuItem
+ ButtonModel mod = menuItem.getModel();
+ if ((menuItem.isSelected() && checkIcon == null)
+ || (mod != null && mod.isArmed())
+ && (menuItem.getParent() instanceof MenuElement))
+ g.setColor(selectionForeground);
+ else
+ g.setColor(menuItem.getForeground());
+ }
+ else
+ // FIXME: should fix this to use 'disabledForeground', but its
+ // default value in BasicLookAndFeel is null.
+
+ // FIXME: should there be different foreground colours for selected
+ // or deselected, when disabled?
+ g.setColor(Color.gray);
+
+ int mnemonicIndex = menuItem.getDisplayedMnemonicIndex();
+
+ if (mnemonicIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(menuItem, g, text,
+ mnemonicIndex,
+ textRect.x,
+ textRect.y
+ + fm.getAscent());
+ else
+ BasicGraphicsUtils.drawString(menuItem, g, text, 0, textRect.x,
+ textRect.y + fm.getAscent());
+ }
+ }
+
+ /**
+ * This method uninstalls the components for this {@link JMenuItem}.
+ *
+ * @param menuItem
+ * The {@link JMenuItem} to uninstall components for.
+ */
+ protected void uninstallComponents(JMenuItem menuItem)
+ {
+ // FIXME: need to implement
+ }
+
+ /**
+ * This method uninstalls the defaults and sets any objects created during
+ * install to null
+ */
+ protected void uninstallDefaults()
+ {
+ menuItem.setForeground(null);
+ menuItem.setBackground(null);
+ menuItem.setBorder(null);
+ menuItem.setMargin(null);
+ menuItem.setBackground(null);
+ menuItem.setBorder(null);
+ menuItem.setFont(null);
+ menuItem.setForeground(null);
+ menuItem.setMargin(null);
+ acceleratorFont = null;
+ acceleratorForeground = null;
+ acceleratorSelectionForeground = null;
+ arrowIcon = null;
+ selectionBackground = null;
+ selectionForeground = null;
+ acceleratorDelimiter = null;
+ }
+
+ /**
+ * Uninstalls any keyboard actions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(menuItem,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, null);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using.
+ */
+ protected void uninstallListeners()
+ {
+ menuItem.removeMouseListener(mouseInputListener);
+ menuItem.removeMenuDragMouseListener(menuDragMouseListener);
+ menuItem.removeMenuKeyListener(menuKeyListener);
+ menuItem.removeItemListener(itemListener);
+ menuItem.removePropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Performs the opposite of installUI. Any properties or resources that need
+ * to be cleaned up will be done now. It will also uninstall any listeners it
+ * has. In addition, any properties of this UI will be nulled.
+ *
+ * @param c
+ * The {@link JComponent} that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallComponents(menuItem);
+ c.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, null);
+ menuItem = null;
+ }
+
+ /**
+ * This method calls paint.
+ *
+ * @param g
+ * The graphics context used to paint this menu item
+ * @param c
+ * The menu item to paint
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ paint(g, c);
+ }
+
+ /**
+ * This class handles mouse events occuring inside the menu item. Most of the
+ * events are forwarded for processing to MenuSelectionManager of the current
+ * menu hierarchy.
+ */
+ protected class MouseInputHandler implements MouseInputListener
+ {
+ /**
+ * Creates a new MouseInputHandler object.
+ */
+ protected MouseInputHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when mouse is clicked on the menu item. It forwards
+ * this event to MenuSelectionManager.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse is dragged inside the menu item. It
+ * forwards this event to MenuSelectionManager.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse enters menu item. When this happens menu
+ * item is considered to be selected and selection path in
+ * MenuSelectionManager is set. This event is also forwarded to
+ * MenuSelection Manager for further processing.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ Component source = (Component) e.getSource();
+ if (source.getParent() instanceof MenuElement)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.setSelectedPath(getPath());
+ manager.processMouseEvent(e);
+ }
+ }
+
+ /**
+ * This method is called when mouse exits menu item. The event is forwarded
+ * to MenuSelectionManager for processing.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse is inside the menu item. This event is
+ * forwarder to MenuSelectionManager for further processing.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse is pressed. This event is forwarded to
+ * MenuSelectionManager for further processing.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ /**
+ * This method is called when mouse is released. If the mouse is released
+ * inside this menuItem, then this menu item is considered to be chosen and
+ * the menu hierarchy should be closed.
+ *
+ * @param e
+ * A {@link MouseEvent}.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ int x = e.getX();
+ int y = e.getY();
+ if (x > 0 && x < menuItem.getWidth() && y > 0
+ && y < menuItem.getHeight())
+ {
+ doClick(manager);
+ }
+ else
+ manager.processMouseEvent(e);
+ }
+ }
+
+ /**
+ * This class handles mouse dragged events.
+ */
+ private class MenuDragMouseHandler implements MenuDragMouseListener
+ {
+ /**
+ * Tbis method is invoked when mouse is dragged over the menu item.
+ *
+ * @param e
+ * The MenuDragMouseEvent
+ */
+ public void menuDragMouseDragged(MenuDragMouseEvent e)
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ manager.setSelectedPath(e.getPath());
+ }
+
+ /**
+ * Tbis method is invoked when mouse enters the menu item while it is being
+ * dragged.
+ *
+ * @param e
+ * The MenuDragMouseEvent
+ */
+ public void menuDragMouseEntered(MenuDragMouseEvent e)
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ manager.setSelectedPath(e.getPath());
+ }
+
+ /**
+ * Tbis method is invoked when mouse exits the menu item while it is being
+ * dragged
+ *
+ * @param e the MenuDragMouseEvent
+ */
+ public void menuDragMouseExited(MenuDragMouseEvent e)
+ {
+ // Nothing to do here yet.
+ }
+
+ /**
+ * Tbis method is invoked when mouse was dragged and released inside the
+ * menu item.
+ *
+ * @param e
+ * The MenuDragMouseEvent
+ */
+ public void menuDragMouseReleased(MenuDragMouseEvent e)
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ int x = e.getX();
+ int y = e.getY();
+ if (x >= 0 && x < menuItem.getWidth() && y >= 0
+ && y < menuItem.getHeight())
+ doClick(manager);
+ else
+ manager.clearSelectedPath();
+ }
+ }
+
+ /**
+ * This class handles key events occuring when menu item is visible on the
+ * screen.
+ */
+ private class MenuKeyHandler implements MenuKeyListener
+ {
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e
+ * A {@link MenuKeyEvent}.
+ */
+ public void menuKeyPressed(MenuKeyEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e
+ * A {@link MenuKeyEvent}.
+ */
+ public void menuKeyReleased(MenuKeyEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+
+ /**
+ * This method is invoked when key has been typed It handles the mnemonic
+ * key for the menu item.
+ *
+ * @param e
+ * A {@link MenuKeyEvent}.
+ */
+ public void menuKeyTyped(MenuKeyEvent e)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * Helper class that listens for item changes to the properties of the {@link
+ * JMenuItem}.
+ */
+ private class ItemHandler implements ItemListener
+ {
+ /**
+ * This method is called when one of the menu item changes.
+ *
+ * @param evt A {@link ItemEvent}.
+ */
+ public void itemStateChanged(ItemEvent evt)
+ {
+ boolean state = false;
+ if (menuItem instanceof JCheckBoxMenuItem)
+ {
+ if (evt.getStateChange() == ItemEvent.SELECTED)
+ state = true;
+ ((JCheckBoxMenuItem) menuItem).setState(state);
+ }
+ menuItem.revalidate();
+ menuItem.repaint();
+ }
+ }
+
+ /**
+ * A helper method to create the accelerator string from the menu item's
+ * accelerator property. The returned string is empty if there is
+ * no accelerator defined.
+ *
+ * @param m the menu item
+ *
+ * @return the accelerator string, not null
+ */
+ private String getAcceleratorString(JMenuItem m)
+ {
+ // Create accelerator string.
+ KeyStroke accel = m.getAccelerator();
+ String accelText = "";
+ if (accel != null)
+ {
+ int mods = accel.getModifiers();
+ if (mods > 0)
+ {
+ accelText = KeyEvent.getKeyModifiersText(mods);
+ accelText += acceleratorDelimiter;
+ }
+ int keycode = accel.getKeyCode();
+ if (keycode != 0)
+ accelText += KeyEvent.getKeyText(keycode);
+ else
+ accelText += accel.getKeyChar();
+ }
+ return accelText;
+ }
+
+ /**
+ * 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.
+ *
+ * @param m the menu item to layout
+ * @param accelText the accelerator text
+ */
+ private void layoutMenuItem(JMenuItem m, String accelText)
+ {
+ // Fetch the fonts.
+ Font font = m.getFont();
+ FontMetrics fm = m.getFontMetrics(font);
+ FontMetrics accelFm = m.getFontMetrics(acceleratorFont);
+
+ String text = m.getText();
+ SwingUtilities.layoutCompoundLabel(m, fm, text, m.getIcon(),
+ m.getVerticalAlignment(),
+ m.getHorizontalAlignment(),
+ m.getVerticalTextPosition(),
+ m.getHorizontalTextPosition(),
+ viewRect, iconRect, textRect,
+ defaultTextIconGap);
+
+ // Initialize accelerator width and height.
+ if (! accelText.equals(""))
+ {
+ accelRect.width = accelFm.stringWidth(accelText);
+ accelRect.height = accelFm.getHeight();
+ }
+
+ // Initialize check and arrow icon width and height.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ if (checkIcon != null)
+ {
+ checkIconRect.width = checkIcon.getIconWidth();
+ checkIconRect.height = checkIcon.getIconHeight();
+ }
+ if (arrowIcon != null)
+ {
+ arrowIconRect.width = arrowIcon.getIconWidth();
+ arrowIconRect.height = arrowIcon.getIconHeight();
+ }
+ }
+
+ // The union of the icon and text of the menu item is the 'label area'.
+ cachedRect.setBounds(textRect);
+ Rectangle labelRect = SwingUtilities.computeUnion(iconRect.x,
+ iconRect.y,
+ iconRect.width,
+ iconRect.height,
+ cachedRect);
+ textRect.x += defaultTextIconGap;
+ iconRect.x += defaultTextIconGap;
+
+ // Layout accelerator rect.
+ accelRect.x = viewRect.x + viewRect.width - arrowIconRect.width
+ - defaultTextIconGap - accelRect.width;
+ // Layout check and arrow icons only when not in toplevel menu.
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ checkIconRect.x = viewRect.x + defaultTextIconGap;
+ textRect.x += defaultTextIconGap + checkIconRect.width;
+ iconRect.x += defaultTextIconGap + checkIconRect.width;
+ arrowIconRect.x = viewRect.x + viewRect.width - defaultTextIconGap
+ - arrowIconRect.width;
+ }
+
+ // Align the accelerator text and all the icons vertically centered to
+ // the menu text.
+ accelRect.y = labelRect.y + (labelRect.height / 2)
+ - (accelRect.height / 2);
+ if (! (m instanceof JMenu && ((JMenu) m).isTopLevelMenu()))
+ {
+ arrowIconRect.y = labelRect.y + (labelRect.height / 2)
+ - (arrowIconRect.height / 2);
+ checkIconRect.y = labelRect.y + (labelRect.height / 2)
+ - (checkIconRect.height / 2);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java
new file mode 100644
index 000000000..4897ee4a0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicMenuUI.java
@@ -0,0 +1,678 @@
+/* BasicMenuUI.java
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import gnu.classpath.NotImplementedException;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.JComponent;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JPopupMenu;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.Timer;
+import javax.swing.UIDefaults;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MenuDragMouseEvent;
+import javax.swing.event.MenuDragMouseListener;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuKeyEvent;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.event.MenuListener;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * UI Delegate for JMenu
+ */
+public class BasicMenuUI extends BasicMenuItemUI
+{
+ /**
+ * Selects a menu. This is used to delay menu selection.
+ */
+ class SelectMenuAction
+ extends AbstractAction
+ {
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ JMenu menu = (JMenu) menuItem;
+ MenuSelectionManager defaultManager =
+ MenuSelectionManager.defaultManager();
+ MenuElement path[] = defaultManager.getSelectedPath();
+ if(path.length > 0 && path[path.length - 1] == menu)
+ {
+ MenuElement newPath[] = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ defaultManager.setSelectedPath(newPath);
+ }
+ }
+
+ }
+
+ protected ChangeListener changeListener;
+
+ /* MenuListener listens to MenuEvents fired by JMenu */
+ protected MenuListener menuListener;
+
+ /* PropertyChangeListner that listens to propertyChangeEvents occuring in JMenu*/
+ protected PropertyChangeListener propertyChangeListener;
+
+ /**
+ * Creates a new BasicMenuUI object.
+ */
+ public BasicMenuUI()
+ {
+ mouseInputListener = createMouseInputListener((JMenu) menuItem);
+ menuListener = createMenuListener((JMenu) menuItem);
+ propertyChangeListener = createPropertyChangeListener((JMenu) menuItem);
+ }
+
+ /**
+ * This method creates a new ChangeListener.
+ *
+ * @return A new ChangeListener.
+ */
+ protected ChangeListener createChangeListener(JComponent c)
+ {
+ return new ChangeHandler((JMenu) c, this);
+ }
+
+ /**
+ * This method creates new MenuDragMouseListener to listen to mouse dragged events
+ * occuring in the Menu
+ *
+ * @param c the menu to listen to
+ *
+ * @return The MenuDrageMouseListener
+ */
+ protected MenuDragMouseListener createMenuDragMouseListener(JComponent c)
+ {
+ return new MenuDragMouseHandler();
+ }
+
+ /**
+ * This method creates new MenuDragKeyListener to listen to key events
+ *
+ * @param c the menu to listen to
+ *
+ * @return The MenuKeyListener
+ */
+ protected MenuKeyListener createMenuKeyListener(JComponent c)
+ {
+ return new MenuKeyHandler();
+ }
+
+ /**
+ * This method creates new MenuListener to listen to menu events
+ * occuring in the Menu
+ *
+ * @param c the menu to listen to
+ *
+ * @return The MenuListener
+ */
+ protected MenuListener createMenuListener(JComponent c)
+ {
+ return new MenuHandler();
+ }
+
+ /**
+ * This method creates new MouseInputListener to listen to mouse input events
+ * occuring in the Menu
+ *
+ * @param c the menu to listen to
+ *
+ * @return The MouseInputListener
+ */
+ protected MouseInputListener createMouseInputListener(JComponent c)
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * This method creates newPropertyChangeListener to listen to property changes
+ * occuring in the Menu
+ *
+ * @param c the menu to listen to
+ *
+ * @return The PropertyChangeListener
+ */
+ protected PropertyChangeListener createPropertyChangeListener(JComponent c)
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method creates a new BasicMenuUI.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicMenuUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicMenuUI();
+ }
+
+ /**
+ * Get the component's maximum size.
+ *
+ * @param c The JComponent for which to get maximum size
+ *
+ * @return The maximum size of the component
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return c.getPreferredSize();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "Menu"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "Menu";
+ }
+
+ /**
+ * Initializes any default properties that this UI has from the defaults for
+ * the Basic look and feel.
+ */
+ protected void installDefaults()
+ {
+
+ LookAndFeel.installBorder(menuItem, "Menu.border");
+ LookAndFeel.installColorsAndFont(menuItem, "Menu.background",
+ "Menu.foreground", "Menu.font");
+ menuItem.setMargin(UIManager.getInsets("Menu.margin"));
+ acceleratorFont = UIManager.getFont("Menu.acceleratorFont");
+ acceleratorForeground = UIManager.getColor("Menu.acceleratorForeground");
+ acceleratorSelectionForeground = UIManager.getColor("Menu.acceleratorSelectionForeground");
+ selectionBackground = UIManager.getColor("Menu.selectionBackground");
+ selectionForeground = UIManager.getColor("Menu.selectionForeground");
+ arrowIcon = UIManager.getIcon("Menu.arrowIcon");
+ oldBorderPainted = UIManager.getBoolean("Menu.borderPainted");
+ ((JMenu) menuItem).setDelay(200);
+ }
+
+ /**
+ * Installs any keyboard actions. The list of keys that need to be bound are
+ * listed in Basic look and feel's defaults.
+ *
+ */
+ protected void installKeyboardActions()
+ {
+ super.installKeyboardActions();
+ }
+
+ /**
+ * Creates and registers all the listeners for this UI delegate.
+ */
+ protected void installListeners()
+ {
+ super.installListeners();
+ ((JMenu) menuItem).addMenuListener(menuListener);
+ }
+
+ protected void setupPostTimer(JMenu menu)
+ {
+ Timer timer = new Timer(menu.getDelay(), new SelectMenuAction());
+ timer.setRepeats(false);
+ timer.start();
+ }
+
+ /**
+ * This method uninstalls the defaults and sets any objects created during
+ * install to null
+ */
+ protected void uninstallDefaults()
+ {
+ menuItem.setBackground(null);
+ menuItem.setBorder(null);
+ menuItem.setFont(null);
+ menuItem.setForeground(null);
+ menuItem.setMargin(null);
+ acceleratorFont = null;
+ acceleratorForeground = null;
+ acceleratorSelectionForeground = null;
+ selectionBackground = null;
+ selectionForeground = null;
+ arrowIcon = null;
+ }
+
+ /**
+ * Uninstalls any keyboard actions. The list of keys used are listed in
+ * Basic look and feel's defaults.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ super.installKeyboardActions();
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using. In
+ * addition, it will also null any listeners that it was using.
+ */
+ protected void uninstallListeners()
+ {
+ super.uninstallListeners();
+ ((JMenu) menuItem).removeMenuListener(menuListener);
+ }
+
+ /**
+ * This class is used by menus to handle mouse events occuring in the
+ * menu.
+ */
+ protected class MouseInputHandler implements MouseInputListener
+ {
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseDragged(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ private boolean popupVisible()
+ {
+ JMenuBar mb = (JMenuBar) ((JMenu) menuItem).getParent();
+ // check if mb.isSelected because if no menus are selected
+ // we don't have to look through the list for popup menus
+ if (!mb.isSelected())
+ return false;
+ for (int i = 0; i < mb.getMenuCount(); i++)
+ {
+ JMenu m = mb.getMenu(i);
+ if (m != null && m.isPopupMenuVisible())
+ return true;
+ }
+ return false;
+ }
+
+ public void mouseEntered(MouseEvent e)
+ {
+ JMenu menu = (JMenu) menuItem;
+ if (menu.isEnabled())
+ {
+ MenuSelectionManager manager =
+ MenuSelectionManager.defaultManager();
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if (! menu.isTopLevelMenu())
+ {
+ // Open the menu immediately or delayed, depending on the
+ // delay value.
+ if(! (selectedPath.length > 0
+ && selectedPath[selectedPath.length - 1] == menu.getPopupMenu()))
+ {
+ if(menu.getDelay() == 0)
+ {
+ MenuElement[] path = getPath();
+ MenuElement[] newPath = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ manager.setSelectedPath(getPath());
+ setupPostTimer(menu);
+ }
+ }
+ }
+ else
+ {
+ if(selectedPath.length > 0
+ && selectedPath[0] == menu.getParent())
+ {
+ MenuElement[] newPath = new MenuElement[3];
+ newPath[0] = (MenuElement) menu.getParent();
+ newPath[1] = menu;
+ newPath[2] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ }
+ }
+ }
+
+ public void mouseExited(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mousePressed(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ JMenu menu = (JMenu) menuItem;
+ if (menu.isEnabled())
+ {
+ // Open up the menu immediately if it's a toplevel menu.
+ // But not yet the popup, which might be opened delayed, see below.
+ if (menu.isTopLevelMenu())
+ {
+ if (menu.isSelected())
+ manager.clearSelectedPath();
+ else
+ {
+ Container cnt = menu.getParent();
+ if (cnt != null && cnt instanceof JMenuBar)
+ {
+ MenuElement[] me = new MenuElement[2];
+ me[0] = (MenuElement) cnt;
+ me[1] = menu;
+ manager.setSelectedPath(me);
+ }
+ }
+ }
+
+ // Open the menu's popup. Either do that immediately if delay == 0,
+ // or delayed when delay > 0.
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if (selectedPath.length > 0
+ && selectedPath[selectedPath.length - 1] != menu.getPopupMenu())
+ {
+ if(menu.isTopLevelMenu() || menu.getDelay() == 0)
+ {
+ MenuElement[] newPath =
+ new MenuElement[selectedPath.length + 1];
+ System.arraycopy(selectedPath, 0, newPath, 0,
+ selectedPath.length);
+ newPath[selectedPath.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ setupPostTimer(menu);
+ }
+ }
+
+ }
+ }
+
+ public void mouseReleased(MouseEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.processMouseEvent(e);
+ }
+ }
+
+ /**
+ * This class handles MenuEvents fired by the JMenu
+ */
+ private class MenuHandler implements MenuListener
+ {
+ /**
+ * This method is called when menu is cancelled. The menu is cancelled
+ * when its popup menu is closed without selection. It clears selected index
+ * in the selectionModel of the menu parent.
+ *
+ * @param e The MenuEvent.
+ */
+ public void menuCanceled(MenuEvent e)
+ {
+ menuDeselected(e);
+ }
+
+ /**
+ * This method is called when menu is deselected. It clears selected index
+ * in the selectionModel of the menu parent.
+ *
+ * @param e The MenuEvent.
+ */
+ public void menuDeselected(MenuEvent e)
+ {
+ JMenu menu = (JMenu) menuItem;
+ if (menu.getParent() != null)
+ {
+ if (menu.isTopLevelMenu())
+ ((JMenuBar) menu.getParent()).getSelectionModel().clearSelection();
+ else
+ ((JPopupMenu) menu.getParent()).getSelectionModel().clearSelection();
+ }
+ }
+
+ /**
+ * This method is called when menu is selected. It sets selected index
+ * in the selectionModel of the menu parent.
+ *
+ * @param e The MenuEvent.
+ */
+ public void menuSelected(MenuEvent e)
+ {
+ JMenu menu = (JMenu) menuItem;
+ if (menu.isTopLevelMenu())
+ ((JMenuBar) menu.getParent()).setSelected(menu);
+ else
+ ((JPopupMenu) menu.getParent()).setSelected(menu);
+ }
+ }
+
+ /**
+ * Obsolete as of JDK1.4.
+ */
+ public class ChangeHandler implements ChangeListener
+ {
+ /**
+ * Not used.
+ */
+ public boolean isSelected;
+
+ /**
+ * Not used.
+ */
+ public JMenu menu;
+
+ /**
+ * Not used.
+ */
+ public BasicMenuUI ui;
+
+ /**
+ * Not used.
+ */
+ public Component wasFocused;
+
+ /**
+ * Not used.
+ */
+ public ChangeHandler(JMenu m, BasicMenuUI ui)
+ {
+ menu = m;
+ this.ui = ui;
+ }
+
+ /**
+ * Not used.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Not used.
+ }
+ }
+
+ /**
+ * This class handles mouse dragged events occuring in the menu.
+ */
+ private class MenuDragMouseHandler implements MenuDragMouseListener
+ {
+ /**
+ * This method is invoked when mouse is dragged over the menu item.
+ *
+ * @param e The MenuDragMouseEvent
+ */
+ public void menuDragMouseDragged(MenuDragMouseEvent e)
+ {
+ if (menuItem.isEnabled())
+ {
+ MenuSelectionManager manager = e.getMenuSelectionManager();
+ MenuElement path[] = e.getPath();
+
+ Point p = e.getPoint();
+ if(p.x >= 0 && p.x < menuItem.getWidth()
+ && p.y >= 0 && p.y < menuItem.getHeight())
+ {
+ JMenu menu = (JMenu) menuItem;
+ MenuElement[] selectedPath = manager.getSelectedPath();
+ if(! (selectedPath.length > 0
+ && selectedPath[selectedPath.length-1]
+ == menu.getPopupMenu()))
+ {
+ if(menu.isTopLevelMenu() || menu.getDelay() == 0
+ || e.getID() == MouseEvent.MOUSE_DRAGGED)
+ {
+ MenuElement[] newPath = new MenuElement[path.length + 1];
+ System.arraycopy(path, 0, newPath, 0, path.length);
+ newPath[path.length] = menu.getPopupMenu();
+ manager.setSelectedPath(newPath);
+ }
+ else
+ {
+ manager.setSelectedPath(path);
+ setupPostTimer(menu);
+ }
+ }
+ }
+ else if (e.getID() == MouseEvent.MOUSE_RELEASED)
+ {
+ Component comp = manager.componentForPoint(e.getComponent(),
+ e.getPoint());
+ if (comp == null)
+ manager.clearSelectedPath();
+ }
+ }
+ }
+
+ /**
+ * This method is invoked when mouse enters the menu item while it is
+ * being dragged.
+ *
+ * @param e The MenuDragMouseEvent
+ */
+ public void menuDragMouseEntered(MenuDragMouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked when mouse exits the menu item while
+ * it is being dragged
+ *
+ * @param e The MenuDragMouseEvent
+ */
+ public void menuDragMouseExited(MenuDragMouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked when mouse was dragged and released
+ * inside the menu item.
+ *
+ * @param e The MenuDragMouseEvent
+ */
+ public void menuDragMouseReleased(MenuDragMouseEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * This class handles key events occuring when menu item is visible on the
+ * screen.
+ */
+ private class MenuKeyHandler implements MenuKeyListener
+ {
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e A {@link MenuKeyEvent}.
+ */
+ public void menuKeyPressed(MenuKeyEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked when key has been pressed
+ *
+ * @param e A {@link MenuKeyEvent}.
+ */
+ public void menuKeyReleased(MenuKeyEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is invoked when key has been typed
+ * It handles the mnemonic key for the menu item.
+ *
+ * @param e A {@link MenuKeyEvent}.
+ */
+ public void menuKeyTyped(MenuKeyEvent e)
+ throws NotImplementedException
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java
new file mode 100644
index 000000000..a03d224d2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java
@@ -0,0 +1,1404 @@
+/* BasicOptionPaneUI.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Polygon;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JInternalFrame;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.OptionPaneUI;
+
+/**
+ * This class is the UI delegate for JOptionPane in the Basic Look and Feel.
+ */
+public class BasicOptionPaneUI extends OptionPaneUI
+{
+ /**
+ * Implements the "close" keyboard action.
+ */
+ static class OptionPaneCloseAction
+ extends AbstractAction
+ {
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JOptionPane op = (JOptionPane) event.getSource();
+ op.setValue(new Integer(JOptionPane.CLOSED_OPTION));
+ }
+
+ }
+
+ /**
+ * This is a helper class that listens to the buttons located at the bottom
+ * of the JOptionPane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ButtonActionListener implements ActionListener
+ {
+ /** The index of the option this button represents. */
+ protected int buttonIndex;
+
+ /**
+ * Creates a new ButtonActionListener object with the given buttonIndex.
+ *
+ * @param buttonIndex The index of the option this button represents.
+ */
+ public ButtonActionListener(int buttonIndex)
+ {
+ this.buttonIndex = buttonIndex;
+ }
+
+ /**
+ * This method is called when one of the option buttons are pressed.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ Object value = new Integer(JOptionPane.CLOSED_OPTION);
+ Object[] options = optionPane.getOptions();
+ if (options != null)
+ value = new Integer(buttonIndex);
+ else
+ {
+ String text = ((JButton) e.getSource()).getText();
+ if (text.equals(OK_STRING))
+ value = new Integer(JOptionPane.OK_OPTION);
+ if (text.equals(CANCEL_STRING))
+ value = new Integer(JOptionPane.CANCEL_OPTION);
+ if (text.equals(YES_STRING))
+ value = new Integer(JOptionPane.YES_OPTION);
+ if (text.equals(NO_STRING))
+ value = new Integer(JOptionPane.NO_OPTION);
+ }
+ optionPane.setValue(value);
+ resetInputValue();
+
+ Window owner = SwingUtilities.windowForComponent(optionPane);
+
+ if (owner instanceof JDialog)
+ ((JDialog) owner).dispose();
+
+ //else we probably have some kind of internal frame.
+ JInternalFrame inf = (JInternalFrame) SwingUtilities.getAncestorOfClass(
+ JInternalFrame.class, optionPane);
+ if (inf != null)
+ {
+ try
+ {
+ inf.setClosed(true);
+ }
+ catch (PropertyVetoException pve)
+ {
+ // We do nothing if attempt has been vetoed.
+ }
+ }
+ }
+ }
+
+ /**
+ * This helper layout manager is responsible for the layout of the button
+ * area. The button area is the panel that holds the buttons which
+ * represent the options.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public static class ButtonAreaLayout implements LayoutManager
+ {
+ /** Whether this layout will center the buttons. */
+ protected boolean centersChildren = true;
+
+ /** The space between the buttons. */
+ protected int padding;
+
+ /** Whether the buttons will share the same widths. */
+ protected boolean syncAllWidths;
+
+ /** The width of the widest button. */
+ private transient int widthOfWidestButton;
+
+ /** The height of the tallest button. */
+ private transient int tallestButton;
+
+ /**
+ * Creates a new ButtonAreaLayout object with the given sync widths
+ * property and padding.
+ *
+ * @param syncAllWidths Whether the buttons will share the same widths.
+ * @param padding The padding between the buttons.
+ */
+ public ButtonAreaLayout(boolean syncAllWidths, int padding)
+ {
+ this.syncAllWidths = syncAllWidths;
+ this.padding = padding;
+ }
+
+ /**
+ * This method is called when a component is added to the container.
+ *
+ * @param string The constraints string.
+ * @param comp The component added.
+ */
+ public void addLayoutComponent(String string, Component comp)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method returns whether the children will be centered.
+ *
+ * @return Whether the children will be centered.
+ */
+ public boolean getCentersChildren()
+ {
+ return centersChildren;
+ }
+
+ /**
+ * This method returns the amount of space between components.
+ *
+ * @return The amount of space between components.
+ */
+ public int getPadding()
+ {
+ return padding;
+ }
+
+ /**
+ * This method returns whether all components will share widths (set to
+ * largest width).
+ *
+ * @return Whether all components will share widths.
+ */
+ public boolean getSyncAllWidths()
+ {
+ return syncAllWidths;
+ }
+
+ /**
+ * This method lays out the given container.
+ *
+ * @param container The container to lay out.
+ */
+ public void layoutContainer(Container container)
+ {
+ Component[] buttonList = container.getComponents();
+ int x = container.getInsets().left;
+ if (getCentersChildren())
+ x += (int) ((double) (container.getSize().width) / 2
+ - (double) (buttonRowLength(container)) / 2);
+ for (int i = 0; i < buttonList.length; i++)
+ {
+ Dimension dims = buttonList[i].getPreferredSize();
+ if (syncAllWidths)
+ {
+ buttonList[i].setBounds(x, 0, widthOfWidestButton, dims.height);
+ x += widthOfWidestButton + getPadding();
+ }
+ else
+ {
+ buttonList[i].setBounds(x, 0, dims.width, dims.height);
+ x += dims.width + getPadding();
+ }
+ }
+ }
+
+ /**
+ * This method returns the width of the given container taking into
+ * consideration the padding and syncAllWidths.
+ *
+ * @param c The container to calculate width for.
+ *
+ * @return The width of the given container.
+ */
+ private int buttonRowLength(Container c)
+ {
+ Component[] buttonList = c.getComponents();
+
+ int buttonLength = 0;
+ int widest = 0;
+ int tallest = 0;
+
+ for (int i = 0; i < buttonList.length; i++)
+ {
+ Dimension dims = buttonList[i].getPreferredSize();
+ buttonLength += dims.width + getPadding();
+ widest = Math.max(widest, dims.width);
+ tallest = Math.max(tallest, dims.height);
+ }
+
+ widthOfWidestButton = widest;
+ tallestButton = tallest;
+
+ int width;
+ if (getSyncAllWidths())
+ width = widest * buttonList.length
+ + getPadding() * (buttonList.length - 1);
+ else
+ width = buttonLength;
+
+ Insets insets = c.getInsets();
+ width += insets.left + insets.right;
+
+ return width;
+ }
+
+ /**
+ * This method returns the minimum layout size for the given container.
+ *
+ * @param c The container to measure.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container c)
+ {
+ return preferredLayoutSize(c);
+ }
+
+ /**
+ * This method returns the preferred size of the given container.
+ *
+ * @param c The container to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ int w = buttonRowLength(c);
+
+ return new Dimension(w, tallestButton);
+ }
+
+ /**
+ * This method removes the given component from the layout manager's
+ * knowledge.
+ *
+ * @param c The component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method sets whether the children will be centered.
+ *
+ * @param newValue Whether the children will be centered.
+ */
+ public void setCentersChildren(boolean newValue)
+ {
+ centersChildren = newValue;
+ }
+
+ /**
+ * This method sets the amount of space between each component.
+ *
+ * @param newPadding The padding between components.
+ */
+ public void setPadding(int newPadding)
+ {
+ padding = newPadding;
+ }
+
+ /**
+ * This method sets whether the widths will be synced.
+ *
+ * @param newValue Whether the widths will be synced.
+ */
+ public void setSyncAllWidths(boolean newValue)
+ {
+ syncAllWidths = newValue;
+ }
+ }
+
+ /**
+ * This helper class handles property change events from the JOptionPane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called when one of the properties of the JOptionPane
+ * changes.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String property = e.getPropertyName();
+ if (property.equals(JOptionPane.ICON_PROPERTY)
+ || property.equals(JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY)
+ || property.equals(JOptionPane.INITIAL_VALUE_PROPERTY)
+ || property.equals(JOptionPane.MESSAGE_PROPERTY)
+ || property.equals(JOptionPane.MESSAGE_TYPE_PROPERTY)
+ || property.equals(JOptionPane.OPTION_TYPE_PROPERTY)
+ || property.equals(JOptionPane.OPTIONS_PROPERTY)
+ || property.equals(JOptionPane.WANTS_INPUT_PROPERTY))
+ {
+ uninstallComponents();
+ installComponents();
+ optionPane.validate();
+ }
+ }
+ }
+
+ /**
+ * The minimum width for JOptionPanes.
+ */
+ public static final int MinimumWidth = 262;
+
+ /**
+ * The minimum height for JOptionPanes.
+ */
+ public static final int MinimumHeight = 90;
+
+ /** Whether the JOptionPane contains custom components. */
+ protected boolean hasCustomComponents;
+
+ // The initialFocusComponent seems to always be set to a button (even if
+ // I try to set initialSelectionValue). This is different from what the
+ // javadocs state (which should switch this reference to the input component
+ // if one is present since that is what's going to get focus).
+
+ /**
+ * The button that will receive focus based on initialValue when no input
+ * component is present. If an input component is present, then the input
+ * component will receive focus instead.
+ */
+ protected Component initialFocusComponent;
+
+ /** The component that receives input when the JOptionPane needs it. */
+ protected JComponent inputComponent;
+
+ /** The minimum dimensions of the JOptionPane. */
+ protected Dimension minimumSize;
+
+ /** The propertyChangeListener for the JOptionPane. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The JOptionPane this UI delegate is used for. */
+ protected JOptionPane optionPane;
+
+ /** The size of the icons. */
+ private static final int ICON_SIZE = 36;
+
+ /** The string used to describe OK buttons. */
+ private static final String OK_STRING = "OK";
+
+ /** The string used to describe Yes buttons. */
+ private static final String YES_STRING = "Yes";
+
+ /** The string used to describe No buttons. */
+ private static final String NO_STRING = "No";
+
+ /** The string used to describe Cancel buttons. */
+ private static final String CANCEL_STRING = "Cancel";
+
+ /** The container for the message area.
+ * This is package-private to avoid an accessor method. */
+ transient Container messageAreaContainer;
+
+ /** The container for the buttons.
+ * This is package-private to avoid an accessor method. */
+ transient Container buttonContainer;
+
+ /**
+ * A helper class that implements Icon. This is used temporarily until
+ * ImageIcons are fixed.
+ */
+ private static class MessageIcon implements Icon
+ {
+ /**
+ * This method returns the width of the icon.
+ *
+ * @return The width of the icon.
+ */
+ public int getIconWidth()
+ {
+ return ICON_SIZE;
+ }
+
+ /**
+ * This method returns the height of the icon.
+ *
+ * @return The height of the icon.
+ */
+ public int getIconHeight()
+ {
+ return ICON_SIZE;
+ }
+
+ /**
+ * This method paints the icon as a part of the given component using the
+ * given graphics and the given x and y position.
+ *
+ * @param c The component that owns this icon.
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /** The icon displayed for ERROR_MESSAGE. */
+ private static MessageIcon errorIcon = new MessageIcon()
+ {
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ Polygon oct = new Polygon(new int[] { 0, 0, 9, 27, 36, 36, 27, 9 },
+ new int[] { 9, 27, 36, 36, 27, 9, 0, 0 }, 8);
+ g.translate(x, y);
+
+ Color saved = g.getColor();
+ g.setColor(Color.RED);
+
+ g.fillPolygon(oct);
+
+ g.setColor(Color.BLACK);
+ g.drawRect(13, 16, 10, 4);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ };
+
+ /** The icon displayed for INFORMATION_MESSAGE. */
+ private static MessageIcon infoIcon = new MessageIcon()
+ {
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+
+ // Should be purple.
+ g.setColor(Color.RED);
+
+ g.fillOval(0, 0, ICON_SIZE, ICON_SIZE);
+
+ g.setColor(Color.BLACK);
+ g.drawOval(16, 6, 4, 4);
+
+ Polygon bottomI = new Polygon(new int[] { 15, 15, 13, 13, 23, 23, 21, 21 },
+ new int[] { 12, 28, 28, 30, 30, 28, 28, 12 },
+ 8);
+ g.drawPolygon(bottomI);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ };
+
+ /** The icon displayed for WARNING_MESSAGE. */
+ private static MessageIcon warningIcon = new MessageIcon()
+ {
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+ g.setColor(Color.YELLOW);
+
+ Polygon triangle = new Polygon(new int[] { 0, 18, 36 },
+ new int[] { 36, 0, 36 }, 3);
+ g.fillPolygon(triangle);
+
+ g.setColor(Color.BLACK);
+
+ Polygon excl = new Polygon(new int[] { 15, 16, 20, 21 },
+ new int[] { 8, 26, 26, 8 }, 4);
+ g.drawPolygon(excl);
+ g.drawOval(16, 30, 4, 4);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ };
+
+ /** The icon displayed for MESSAGE_ICON. */
+ private static MessageIcon questionIcon = new MessageIcon()
+ {
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.translate(x, y);
+ Color saved = g.getColor();
+ g.setColor(Color.GREEN);
+
+ g.fillRect(0, 0, ICON_SIZE, ICON_SIZE);
+
+ g.setColor(Color.BLACK);
+
+ g.drawOval(11, 2, 16, 16);
+ g.drawOval(14, 5, 10, 10);
+
+ g.setColor(Color.GREEN);
+ g.fillRect(0, 10, ICON_SIZE, ICON_SIZE - 10);
+
+ g.setColor(Color.BLACK);
+
+ g.drawLine(11, 10, 14, 10);
+
+ g.drawLine(24, 10, 17, 22);
+ g.drawLine(27, 10, 20, 22);
+ g.drawLine(17, 22, 20, 22);
+
+ g.drawOval(17, 25, 3, 3);
+
+ g.setColor(saved);
+ g.translate(-x, -y);
+ }
+ };
+
+ /**
+ * Creates a new BasicOptionPaneUI object.
+ */
+ public BasicOptionPaneUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is messaged to add the buttons to the given container.
+ *
+ * @param container The container to add components to.
+ * @param buttons The buttons to add. (If it is an instance of component,
+ * the Object is added directly. If it is an instance of Icon, it is
+ * packed into a label and added. For all other cases, the string
+ * representation of the Object is retreived and packed into a
+ * label.)
+ * @param initialIndex The index of the component that is the initialValue.
+ */
+ protected void addButtonComponents(Container container, Object[] buttons,
+ int initialIndex)
+ {
+ if (buttons == null)
+ return;
+ for (int i = 0; i < buttons.length; i++)
+ {
+ if (buttons[i] != null)
+ {
+ Component toAdd;
+ if (buttons[i] instanceof Component)
+ toAdd = (Component) buttons[i];
+ else
+ {
+ if (buttons[i] instanceof Icon)
+ toAdd = new JButton((Icon) buttons[i]);
+ else
+ toAdd = new JButton(buttons[i].toString());
+ hasCustomComponents = true;
+ }
+ if (toAdd instanceof JButton)
+ ((JButton) toAdd).addActionListener(createButtonActionListener(i));
+ if (i == initialIndex)
+ initialFocusComponent = toAdd;
+ container.add(toAdd);
+ }
+ }
+ selectInitialValue(optionPane);
+ }
+
+ /**
+ * This method adds the appropriate icon the given container.
+ *
+ * @param top The container to add an icon to.
+ */
+ protected void addIcon(Container top)
+ {
+ JLabel iconLabel = null;
+ Icon icon = getIcon();
+ if (icon != null)
+ {
+ iconLabel = new JLabel(icon);
+ configureLabel(iconLabel);
+ top.add(iconLabel, BorderLayout.WEST);
+ }
+ }
+
+ /**
+ * A helper method that returns an instance of GridBagConstraints to be used
+ * for creating the message area.
+ *
+ * @return An instance of GridBagConstraints.
+ */
+ private static GridBagConstraints createConstraints()
+ {
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.gridx = GridBagConstraints.REMAINDER;
+ constraints.gridy = GridBagConstraints.REMAINDER;
+ constraints.gridwidth = 0;
+ constraints.anchor = GridBagConstraints.LINE_START;
+ constraints.fill = GridBagConstraints.NONE;
+ constraints.insets = new Insets(0, 0, 3, 0);
+
+ return constraints;
+ }
+
+ /**
+ * This method creates the proper object (if necessary) to represent msg.
+ * (If msg is an instance of Component, it will add it directly. If it is
+ * an icon, then it will pack it in a label and add it. Otherwise, it gets
+ * treated as a string. If the string is longer than maxll, a box is
+ * created and the burstStringInto is called with the box as the container.
+ * The box is then added to the given container. Otherwise, the string is
+ * packed in a label and placed in the given container.) This method is
+ * also used for adding the inputComponent to the container.
+ *
+ * @param container The container to add to.
+ * @param cons The constraints when adding.
+ * @param msg The message to add.
+ * @param maxll The max line length.
+ * @param internallyCreated Whether the msg is internally created.
+ */
+ protected void addMessageComponents(Container container,
+ GridBagConstraints cons, Object msg,
+ int maxll, boolean internallyCreated)
+ {
+ if (msg == null)
+ return;
+ hasCustomComponents = internallyCreated;
+ if (msg instanceof Object[])
+ {
+ Object[] arr = (Object[]) msg;
+ for (int i = 0; i < arr.length; i++)
+ addMessageComponents(container, cons, arr[i], maxll,
+ internallyCreated);
+ return;
+ }
+ else if (msg instanceof Component)
+ {
+ container.add((Component) msg, cons);
+ cons.gridy++;
+ }
+ else if (msg instanceof Icon)
+ {
+ JLabel label = new JLabel((Icon) msg);
+ configureLabel(label);
+ container.add(label, cons);
+ cons.gridy++;
+ }
+ else
+ {
+ // Undocumented behaviour.
+ // if msg.toString().length greater than maxll
+ // it will create a box and burst the string.
+ // otherwise, it will just create a label and re-call
+ // this method with the label o.O
+ if (msg.toString().length() > maxll || msg.toString().contains("\n"))
+ {
+ Box tmp = new Box(BoxLayout.Y_AXIS);
+ burstStringInto(tmp, msg.toString(), maxll);
+ addMessageComponents(container, cons, tmp, maxll, true);
+ }
+ else
+ {
+ JLabel label = new JLabel(msg.toString());
+ configureLabel(label);
+ addMessageComponents(container, cons, label, maxll, true);
+ }
+ }
+ }
+
+ /**
+ * This method creates instances of d (recursively if necessary based on
+ * maxll) and adds to c.
+ *
+ * @param c The container to add to.
+ * @param d The string to burst.
+ * @param maxll The max line length.
+ */
+ protected void burstStringInto(Container c, String d, int maxll)
+ {
+ if (d == null || c == null)
+ return;
+
+ int newlineIndex = d.indexOf('\n');
+ String line;
+ String remainder;
+ if (newlineIndex >= 0 && newlineIndex < maxll)
+ {
+ line = d.substring(0, newlineIndex);
+ remainder = d.substring(newlineIndex + 1);
+ }
+ else
+ {
+ line = d.substring(0, maxll);
+ remainder = d.substring(maxll);
+ }
+ JLabel label = new JLabel(line);
+ configureLabel(label);
+ c.add(label);
+
+ // If there is nothing left to burst, then we can stop.
+ if (remainder.length() == 0)
+ return;
+
+ // Recursively call ourselves to burst the remainder of the string,
+ if (remainder.length() > maxll || remainder.contains("\n"))
+ burstStringInto(c, remainder, maxll);
+ else
+ {
+ // Add the remainder to the container and be done.
+ JLabel l = new JLabel(remainder);
+ configureLabel(l);
+ c.add(l);
+ }
+ }
+
+ /**
+ * This method returns true if the given JOptionPane contains custom
+ * components.
+ *
+ * @param op The JOptionPane to check.
+ *
+ * @return True if the JOptionPane contains custom components.
+ */
+ public boolean containsCustomComponents(JOptionPane op)
+ {
+ return hasCustomComponents;
+ }
+
+ /**
+ * This method creates a button action listener for the given button index.
+ *
+ * @param buttonIndex The index of the button in components.
+ *
+ * @return A new ButtonActionListener.
+ */
+ protected ActionListener createButtonActionListener(int buttonIndex)
+ {
+ return new ButtonActionListener(buttonIndex);
+ }
+
+ /**
+ * This method creates the button area.
+ *
+ * @return A new Button Area.
+ */
+ protected Container createButtonArea()
+ {
+ JPanel buttonPanel = new JPanel();
+ Border b = UIManager.getBorder("OptionPane.buttonAreaBorder");
+ if (b != null)
+ buttonPanel.setBorder(b);
+
+ buttonPanel.setLayout(createLayoutManager());
+ addButtonComponents(buttonPanel, getButtons(), getInitialValueIndex());
+
+ return buttonPanel;
+ }
+
+ /**
+ * This method creates a new LayoutManager for the button area.
+ *
+ * @return A new LayoutManager for the button area.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new ButtonAreaLayout(getSizeButtonsToSameWidth(), 6);
+ }
+
+ /**
+ * This method creates the message area.
+ *
+ * @return A new message area.
+ */
+ protected Container createMessageArea()
+ {
+ JPanel messageArea = new JPanel();
+ Border messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder");
+ if (messageBorder != null)
+ messageArea.setBorder(messageBorder);
+
+ messageArea.setLayout(new BorderLayout());
+ addIcon(messageArea);
+
+ JPanel rightSide = new JPanel();
+ rightSide.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+ rightSide.setLayout(new GridBagLayout());
+ GridBagConstraints con = createConstraints();
+
+ addMessageComponents(rightSide, con, getMessage(),
+ getMaxCharactersPerLineCount(), false);
+
+ if (optionPane.getWantsInput())
+ {
+ Object[] selection = optionPane.getSelectionValues();
+
+ if (selection == null)
+ inputComponent = new JTextField(15);
+ else if (selection.length < 20)
+ inputComponent = new JComboBox(selection);
+ else
+ inputComponent = new JList(selection);
+ if (inputComponent != null)
+ {
+ addMessageComponents(rightSide, con, inputComponent,
+ getMaxCharactersPerLineCount(), false);
+ resetSelectedValue();
+ selectInitialValue(optionPane);
+ }
+ }
+
+ messageArea.add(rightSide, BorderLayout.CENTER);
+
+ return messageArea;
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener for listening to the
+ * JOptionPane.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method creates a Container that will separate the message and button
+ * areas.
+ *
+ * @return A Container that will separate the message and button areas.
+ */
+ protected Container createSeparator()
+ {
+ // The reference implementation returns null here. When overriding
+ // to return something non-null, the component gets added between
+ // the message area and the button area. See installComponents().
+ return null;
+ }
+
+ /**
+ * This method creates a new BasicOptionPaneUI for the given component.
+ *
+ * @param x The component to create a UI for.
+ *
+ * @return A new BasicOptionPaneUI.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicOptionPaneUI();
+ }
+
+ /**
+ * This method returns the buttons for the JOptionPane. If no options are
+ * set, a set of options will be created based upon the optionType.
+ *
+ * @return The buttons that will be added.
+ */
+ protected Object[] getButtons()
+ {
+ if (optionPane.getOptions() != null)
+ return optionPane.getOptions();
+ switch (optionPane.getOptionType())
+ {
+ case JOptionPane.YES_NO_OPTION:
+ return new Object[] { YES_STRING, NO_STRING };
+ case JOptionPane.YES_NO_CANCEL_OPTION:
+ return new Object[] { YES_STRING, NO_STRING, CANCEL_STRING };
+ case JOptionPane.OK_CANCEL_OPTION:
+ return new Object[] { OK_STRING, CANCEL_STRING };
+ case JOptionPane.DEFAULT_OPTION:
+ return (optionPane.getWantsInput()) ?
+ new Object[] { OK_STRING, CANCEL_STRING } :
+ (optionPane.getMessageType() == JOptionPane.QUESTION_MESSAGE) ?
+ new Object[] { YES_STRING, NO_STRING, CANCEL_STRING } :
+ // ERROR_MESSAGE, INFORMATION_MESSAGE, WARNING_MESSAGE, PLAIN_MESSAGE
+ new Object[] { OK_STRING };
+ }
+ return null;
+ }
+
+ /**
+ * This method will return the icon the user has set or the icon that will
+ * be used based on message type.
+ *
+ * @return The icon to use in the JOptionPane.
+ */
+ protected Icon getIcon()
+ {
+ if (optionPane.getIcon() != null)
+ return optionPane.getIcon();
+ else
+ return getIconForType(optionPane.getMessageType());
+ }
+
+ /**
+ * This method returns the icon for the given messageType.
+ *
+ * @param messageType The type of message.
+ *
+ * @return The icon for the given messageType.
+ */
+ protected Icon getIconForType(int messageType)
+ {
+ Icon tmp = null;
+ switch (messageType)
+ {
+ case JOptionPane.ERROR_MESSAGE:
+ tmp = errorIcon;
+ break;
+ case JOptionPane.INFORMATION_MESSAGE:
+ tmp = infoIcon;
+ break;
+ case JOptionPane.WARNING_MESSAGE:
+ tmp = warningIcon;
+ break;
+ case JOptionPane.QUESTION_MESSAGE:
+ tmp = questionIcon;
+ break;
+ }
+ return tmp;
+ // FIXME: Don't cast till the default icons are in.
+ // return new IconUIResource(tmp);
+ }
+
+ /**
+ * This method returns the index of the initialValue in the options array.
+ *
+ * @return The index of the initalValue.
+ */
+ protected int getInitialValueIndex()
+ {
+ Object[] buttons = getButtons();
+
+ if (buttons == null)
+ return -1;
+
+ Object select = optionPane.getInitialValue();
+
+ for (int i = 0; i < buttons.length; i++)
+ {
+ if (select == buttons[i])
+ return i;
+ }
+ return 0;
+ }
+
+ /**
+ * This method returns the maximum number of characters that should be
+ * placed on a line.
+ *
+ * @return The maximum number of characteres that should be placed on a
+ * line.
+ */
+ protected int getMaxCharactersPerLineCount()
+ {
+ return optionPane.getMaxCharactersPerLineCount();
+ }
+
+ /**
+ * This method returns the maximum size.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the message of the JOptionPane.
+ *
+ * @return The message.
+ */
+ protected Object getMessage()
+ {
+ return optionPane.getMessage();
+ }
+
+ /**
+ * This method returns the minimum size of the JOptionPane.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumOptionPaneSize()
+ {
+ return minimumSize;
+ }
+
+ /**
+ * This method returns the minimum size.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the preferred size of the JOptionPane. The preferred
+ * size is the maximum of the size desired by the layout and the minimum
+ * size.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension d = optionPane.getLayout().preferredLayoutSize(optionPane);
+ Dimension d2 = getMinimumOptionPaneSize();
+
+ int w = Math.max(d.width, d2.width);
+ int h = Math.max(d.height, d2.height);
+ return new Dimension(w, h);
+ }
+
+ /**
+ * This method returns whether all buttons should have the same width.
+ *
+ * @return Whether all buttons should have the same width.
+ */
+ protected boolean getSizeButtonsToSameWidth()
+ {
+ return true;
+ }
+
+ /**
+ * This method installs components for the JOptionPane.
+ */
+ protected void installComponents()
+ {
+ // First thing is the message area.
+ optionPane.add(createMessageArea());
+
+ // Add separator when createSeparator() is overridden to return
+ // something other than null.
+ Container sep = createSeparator();
+ if (sep != null)
+ optionPane.add(sep);
+
+ // Last thing is the button area.
+ optionPane.add(createButtonArea());
+ }
+
+ /**
+ * This method installs defaults for the JOptionPane.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background",
+ "OptionPane.foreground",
+ "OptionPane.font");
+ LookAndFeel.installBorder(optionPane, "OptionPane.border");
+ optionPane.setOpaque(true);
+
+ minimumSize = UIManager.getDimension("OptionPane.minimumSize");
+
+ // FIXME: Image icons don't seem to work properly right now.
+ // Once they do, replace the synthetic icons with these ones.
+
+ /*
+ warningIcon = (IconUIResource) defaults.getIcon("OptionPane.warningIcon");
+ infoIcon = (IconUIResource) defaults.getIcon("OptionPane.informationIcon");
+ errorIcon = (IconUIResource) defaults.getIcon("OptionPane.errorIcon");
+ questionIcon = (IconUIResource) defaults.getIcon("OptionPane.questionIcon");
+ */
+ }
+
+ /**
+ * This method installs keyboard actions for the JOptionpane.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install the input map.
+ Object[] bindings =
+ (Object[]) SharedUIDefaults.get("OptionPane.windowBindings");
+ InputMap inputMap = LookAndFeel.makeComponentInputMap(optionPane,
+ bindings);
+ SwingUtilities.replaceUIInputMap(optionPane,
+ JComponent.WHEN_IN_FOCUSED_WINDOW,
+ inputMap);
+
+ // FIXME: The JDK uses a LazyActionMap for parentActionMap
+ SwingUtilities.replaceUIActionMap(optionPane, getActionMap());
+ }
+
+ /**
+ * Fetches the action map from the UI defaults, or create a new one
+ * if the action map hasn't been initialized.
+ *
+ * @return the action map
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("OptionPane.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("OptionPane.actionMap", am);
+ }
+ return am;
+ }
+
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new OptionPaneCloseAction();
+
+ am.put("close", action);
+ return am;
+ }
+
+ /**
+ * This method installs listeners for the JOptionPane.
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+
+ optionPane.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * This method installs the UI for the JOptionPane.
+ *
+ * @param c The JComponent to install the UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JOptionPane)
+ {
+ optionPane = (JOptionPane) c;
+
+ installDefaults();
+ installComponents();
+ installListeners();
+ installKeyboardActions();
+ }
+ }
+
+ /**
+ * Changes the inputValue property in the JOptionPane based on the current
+ * value of the inputComponent.
+ */
+ protected void resetInputValue()
+ {
+ if (optionPane.getWantsInput() && inputComponent != null)
+ {
+ Object output = null;
+ if (inputComponent instanceof JTextField)
+ output = ((JTextField) inputComponent).getText();
+ else if (inputComponent instanceof JComboBox)
+ output = ((JComboBox) inputComponent).getSelectedItem();
+ else if (inputComponent instanceof JList)
+ output = ((JList) inputComponent).getSelectedValue();
+
+ if (output != null)
+ optionPane.setInputValue(output);
+ }
+ }
+
+ /**
+ * This method requests focus to the inputComponent (if one is present) and
+ * the initialFocusComponent otherwise.
+ *
+ * @param op The JOptionPane.
+ */
+ public void selectInitialValue(JOptionPane op)
+ {
+ if (inputComponent != null)
+ {
+ inputComponent.requestFocus();
+ return;
+ }
+ if (initialFocusComponent != null)
+ initialFocusComponent.requestFocus();
+ }
+
+ /**
+ * This method resets the value in the inputComponent to the
+ * initialSelectionValue property.
+ * This is package-private to avoid an accessor method.
+ */
+ void resetSelectedValue()
+ {
+ if (inputComponent != null)
+ {
+ Object init = optionPane.getInitialSelectionValue();
+ if (init == null)
+ return;
+ if (inputComponent instanceof JTextField)
+ ((JTextField) inputComponent).setText((String) init);
+ else if (inputComponent instanceof JComboBox)
+ ((JComboBox) inputComponent).setSelectedItem(init);
+ else if (inputComponent instanceof JList)
+ {
+ // ((JList) inputComponent).setSelectedValue(init, true);
+ }
+ }
+ }
+
+ /**
+ * This method uninstalls all the components in the JOptionPane.
+ */
+ protected void uninstallComponents()
+ {
+ optionPane.removeAll();
+ buttonContainer = null;
+ messageAreaContainer = null;
+ }
+
+ /**
+ * This method uninstalls the defaults for the JOptionPane.
+ */
+ protected void uninstallDefaults()
+ {
+ optionPane.setFont(null);
+ optionPane.setForeground(null);
+ optionPane.setBackground(null);
+
+ minimumSize = null;
+
+ // FIXME: ImageIcons don't seem to work properly
+
+ /*
+ warningIcon = null;
+ errorIcon = null;
+ questionIcon = null;
+ infoIcon = null;
+ */
+ }
+
+ /**
+ * This method uninstalls keyboard actions for the JOptionPane.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(optionPane, JComponent.
+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ SwingUtilities.replaceUIActionMap(optionPane, null);
+ }
+
+ /**
+ * This method uninstalls listeners for the JOptionPane.
+ */
+ protected void uninstallListeners()
+ {
+ optionPane.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallComponents();
+ uninstallDefaults();
+
+ optionPane = null;
+ }
+
+ /**
+ * Applies the proper UI configuration to labels that are added to
+ * the OptionPane.
+ *
+ * @param l the label to configure
+ */
+ private void configureLabel(JLabel l)
+ {
+ Color c = UIManager.getColor("OptionPane.messageForeground");
+ if (c != null)
+ l.setForeground(c);
+ Font f = UIManager.getFont("OptionPane.messageFont");
+ if (f != null)
+ l.setFont(f);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java
new file mode 100644
index 000000000..959462a78
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicPanelUI.java
@@ -0,0 +1,132 @@
+/* BasicPanelUI.java
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.LookAndFeel;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.PanelUI;
+
+/**
+ * A UI delegate for the {@link JPanel} component.
+ */
+public class BasicPanelUI extends PanelUI
+{
+ /**
+ * A UI delegate that can be shared by all panels (because the delegate is
+ * stateless).
+ */
+ static BasicPanelUI sharedUI;
+
+ /**
+ * Returns a UI delegate for the specified component.
+ *
+ * @param panel the panel.
+ */
+ public static ComponentUI createUI(JComponent panel)
+ {
+ if (sharedUI == null)
+ sharedUI = new BasicPanelUI();
+ return sharedUI;
+ }
+
+ /**
+ * Installs this UI delegate in the specified component.
+ *
+ * @param c the component (should be a {@link JPanel}, <code>null</code> not
+ * permitted).
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JPanel)
+ {
+ JPanel p = (JPanel) c;
+ installDefaults(p);
+ }
+ }
+
+ /**
+ * Installs the defaults for this UI delegate in the specified panel.
+ *
+ * @param p the panel (<code>null</code> not permitted).
+ */
+ protected void installDefaults(JPanel p)
+ {
+ LookAndFeel.installColorsAndFont(p, "Panel.background", "Panel.foreground",
+ "Panel.font");
+
+ // A test against the reference implementation shows that this method will
+ // install a border if one is defined in the UIDefaults table (even though
+ // the BasicLookAndFeel doesn't actually define a "Panel.border"). This
+ // test was written after discovering that a null argument to
+ // uninstallDefaults throws a NullPointerException in
+ // LookAndFeel.uninstallBorder()...
+ LookAndFeel.installBorder(p, "Panel.border");
+ }
+
+ /**
+ * Uninstalls this UI delegate from the specified component.
+ *
+ * @param c the component (<code>null</code> not permitted).
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallDefaults((JPanel) c);
+ }
+
+ /**
+ * Uninstalls the UI defaults for the specified panel.
+ *
+ * @param p the panel (<code>null</code> not permitted).
+ */
+ protected void uninstallDefaults(JPanel p)
+ {
+ // Tests on the reference implementation showed this method:
+ // (1) doesn't actually remove the installed colors and font installed
+ // by installDefaults(), it isn't necessary;
+ // (2) throws a NullPointerException in LookAndFeel.uninstallBorder() if
+ // p is null. Strangely, no border is installed by the
+ // BasicLookAndFeel - perhaps this is needed by another LAF?
+
+ LookAndFeel.uninstallBorder(p);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java
new file mode 100644
index 000000000..cc839179f
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicPasswordFieldUI.java
@@ -0,0 +1,74 @@
+/* BasicPasswordFieldUI.java
+ Copyright (C) 2004 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.plaf.basic;
+
+import javax.swing.JComponent;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.Element;
+import javax.swing.text.PasswordView;
+import javax.swing.text.View;
+
+public class BasicPasswordFieldUI extends BasicTextFieldUI
+{
+ public BasicPasswordFieldUI()
+ {
+ // Nothing to do here.
+ }
+
+ public View create(Element elem)
+ {
+ return new PasswordView(elem);
+ }
+
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicPasswordFieldUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "PasswordField"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "PasswordField";
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java
new file mode 100644
index 000000000..b62947722
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuSeparatorUI.java
@@ -0,0 +1,114 @@
+/* BasicPopupMenuSeparatorUI.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.JComponent;
+import javax.swing.JPopupMenu;
+import javax.swing.SwingUtilities;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * The Basic Look and Feel UI delegate for JPopupMenu.Separator.
+ */
+public class BasicPopupMenuSeparatorUI extends BasicSeparatorUI
+{
+ /**
+ * Creates a new BasicPopupMenuSeparatorUI object.
+ */
+ public BasicPopupMenuSeparatorUI()
+ {
+ super();
+ }
+
+ /**
+ * Creates a new UI delegate for the given JComponent.
+ *
+ * @param c The JComponent to create a delegate for.
+ *
+ * @return A new BasicPopupMenuSeparatorUI
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicPopupMenuSeparatorUI();
+ }
+
+ /**
+ * The Popup Menu Separator has two lines. The top line will be
+ * painted using highlight color and the bottom using shadow color.
+ *
+ * @param g The Graphics object to paint with
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ if (! (c instanceof JPopupMenu.Separator))
+ return;
+
+ Rectangle r = new Rectangle();
+ SwingUtilities.calculateInnerArea(c, r);
+ Color saved = g.getColor();
+
+ int midAB = r.width / 2 + r.x;
+ int midAD = r.height / 2 + r.y;
+
+ g.setColor(highlight);
+ g.drawLine(r.x, midAD, r.x + r.width, midAD);
+
+ g.setColor(shadow);
+ g.drawLine(r.x, midAD + 1, r.x + r.width, midAD + 1);
+ }
+
+ /**
+ * This method returns the preferred size of the
+ * JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return super.getPreferredSize(c);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java
new file mode 100644
index 000000000..6cd433b33
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicPopupMenuUI.java
@@ -0,0 +1,1019 @@
+/* BasicPopupMenuUI.java
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing.plaf.basic;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.KeyboardFocusManager;
+import java.awt.event.ActionEvent;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.MouseEvent;
+import java.util.EventListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.BoxLayout;
+import javax.swing.InputMap;
+import javax.swing.JApplet;
+import javax.swing.JComponent;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPopupMenu;
+import javax.swing.JRootPane;
+import javax.swing.LookAndFeel;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.PopupMenuUI;
+
+/**
+ * UI Delegate for JPopupMenu
+ */
+public class BasicPopupMenuUI extends PopupMenuUI
+{
+ /**
+ * Handles keyboard navigation through menus.
+ */
+ private static class NavigateAction
+ extends AbstractAction
+ {
+
+ /**
+ * Creates a new NavigateAction instance.
+ *
+ * @param name the name of the action
+ */
+ NavigateAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Actually performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ String name = (String) getValue(Action.NAME);
+ if (name.equals("selectNext"))
+ navigateNextPrevious(true);
+ else if (name.equals("selectPrevious"))
+ navigateNextPrevious(false);
+ else if (name.equals("selectChild"))
+ navigateParentChild(true);
+ else if (name.equals("selectParent"))
+ navigateParentChild(false);
+ else if (name.equals("cancel"))
+ cancel();
+ else if (name.equals("return"))
+ doReturn();
+ else
+ assert false : "Must not reach here";
+ }
+
+ /**
+ * Navigates to the next or previous menu item.
+ *
+ * @param dir <code>true</code>: navigate to next, <code>false</code>:
+ * navigate to previous
+ */
+ private void navigateNextPrevious(boolean dir)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement path[] = msm.getSelectedPath();
+ int len = path.length;
+ if (len >= 2)
+ {
+
+ if (path[0] instanceof JMenuBar &&
+ path[1] instanceof JMenu && len == 2)
+ {
+
+ // A toplevel menu is selected, but its popup not yet shown.
+ // Show the popup and select the first item
+ JPopupMenu popup = ((JMenu)path[1]).getPopupMenu();
+ MenuElement next =
+ findEnabledChild(popup.getSubElements(), -1, true);
+ MenuElement[] newPath;
+
+ if (next != null)
+ {
+ newPath = new MenuElement[4];
+ newPath[3] = next;
+ }
+ else
+ {
+ // Menu has no enabled items, show the popup anyway.
+ newPath = new MenuElement[3];
+ }
+ System.arraycopy(path, 0, newPath, 0, 2);
+ newPath[2] = popup;
+ msm.setSelectedPath(newPath);
+ }
+ else if (path[len - 1] instanceof JPopupMenu &&
+ path[len - 2] instanceof JMenu)
+ {
+ // Select next item in already shown popup menu.
+ JMenu menu = (JMenu) path[len - 2];
+ JPopupMenu popup = menu.getPopupMenu();
+ MenuElement next =
+ findEnabledChild(popup.getSubElements(), -1, dir);
+
+ if (next != null)
+ {
+ MenuElement[] newPath = new MenuElement[len + 1];
+ System.arraycopy(path, 0, newPath, 0, len);
+ newPath[len] = next;
+ msm.setSelectedPath(newPath);
+ }
+ else
+ {
+ // All items in the popup are disabled.
+ // Find the parent popup menu and select
+ // its next item. If there's no parent popup menu , do nothing.
+ if (len > 2 && path[len - 3] instanceof JPopupMenu)
+ {
+ popup = ((JPopupMenu) path[len - 3]);
+ next = findEnabledChild(popup.getSubElements(),
+ menu, dir);
+ if (next != null && next != menu)
+ {
+ MenuElement[] newPath = new MenuElement[len - 1];
+ System.arraycopy(path, 0, newPath, 0, len - 2);
+ newPath[len - 2] = next;
+ msm.setSelectedPath(newPath);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Only select the next item.
+ MenuElement subs[] = path[len - 2].getSubElements();
+ MenuElement nextChild =
+ findEnabledChild(subs, path[len - 1], dir);
+ if (nextChild == null)
+ {
+ nextChild = findEnabledChild(subs, -1, dir);
+ }
+ if (nextChild != null)
+ {
+ path[len-1] = nextChild;
+ msm.setSelectedPath(path);
+ }
+ }
+ }
+ }
+
+ private MenuElement findEnabledChild(MenuElement[] children,
+ MenuElement start, boolean dir)
+ {
+ MenuElement found = null;
+ for (int i = 0; i < children.length && found == null; i++)
+ {
+ if (children[i] == start)
+ {
+ found = findEnabledChild(children, i, dir);
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Searches the next or previous enabled child menu element.
+ *
+ * @param children the children to search through
+ * @param start the index at which to start
+ * @param dir the direction (true == forward, false == backward)
+ *
+ * @return the found element or null
+ */
+ private MenuElement findEnabledChild(MenuElement[] children,
+ int start, boolean dir)
+ {
+ MenuElement result = null;
+ if (dir)
+ {
+ result = findNextEnabledChild(children, start + 1, children.length-1);
+ if (result == null)
+ result = findNextEnabledChild(children, 0, start - 1);
+ }
+ else
+ {
+ result = findPreviousEnabledChild(children, start - 1, 0);
+ if (result == null)
+ result = findPreviousEnabledChild(children, children.length-1,
+ start + 1);
+ }
+ return result;
+ }
+
+ /**
+ * Finds the next child element that is enabled and visible.
+ *
+ * @param children the children to search through
+ * @param start the start index
+ * @param end the end index
+ *
+ * @return the found child, or null
+ */
+ private MenuElement findNextEnabledChild(MenuElement[] children, int start,
+ int end)
+ {
+ MenuElement found = null;
+ for (int i = start; i <= end && found == null; i++)
+ {
+ if (children[i] != null)
+ {
+ Component comp = children[i].getComponent();
+ if (comp != null && comp.isEnabled() && comp.isVisible())
+ {
+ found = children[i];
+ }
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Finds the previous child element that is enabled and visible.
+ *
+ * @param children the children to search through
+ * @param start the start index
+ * @param end the end index
+ *
+ * @return the found child, or null
+ */
+ private MenuElement findPreviousEnabledChild(MenuElement[] children,
+ int start, int end)
+ {
+ MenuElement found = null;
+ for (int i = start; i >= end && found == null; i--)
+ {
+ if (children[i] != null)
+ {
+ Component comp = children[i].getComponent();
+ if (comp != null && comp.isEnabled() && comp.isVisible())
+ {
+ found = children[i];
+ }
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Navigates to the parent or child menu item.
+ *
+ * @param selectChild <code>true</code>: navigate to child,
+ * <code>false</code>: navigate to parent
+ */
+ private void navigateParentChild(boolean selectChild)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement path[] = msm.getSelectedPath();
+ int len = path.length;
+
+ if (selectChild)
+ {
+ if (len > 0 && path[len - 1] instanceof JMenu
+ && ! ((JMenu) path[len-1]).isTopLevelMenu())
+ {
+ // We have a submenu, open it.
+ JMenu menu = (JMenu) path[len - 1];
+ JPopupMenu popup = menu.getPopupMenu();
+ MenuElement[] subs = popup.getSubElements();
+ MenuElement item = findEnabledChild(subs, -1, true);
+ MenuElement[] newPath;
+
+ if (item == null)
+ {
+ newPath = new MenuElement[len + 1];
+ }
+ else
+ {
+ newPath = new MenuElement[len + 2];
+ newPath[len + 1] = item;
+ }
+ System.arraycopy(path, 0, newPath, 0, len);
+ newPath[len] = popup;
+ msm.setSelectedPath(newPath);
+ return;
+ }
+ }
+ else
+ {
+ int popupIndex = len-1;
+ if (len > 2
+ && (path[popupIndex] instanceof JPopupMenu
+ || path[--popupIndex] instanceof JPopupMenu)
+ && ! ((JMenu) path[popupIndex - 1]).isTopLevelMenu())
+ {
+ // We have a submenu, close it.
+ MenuElement newPath[] = new MenuElement[popupIndex];
+ System.arraycopy(path, 0, newPath, 0, popupIndex);
+ msm.setSelectedPath(newPath);
+ return;
+ }
+ }
+
+ // If we got here, we have not selected a child or parent.
+ // Check if we have a toplevel menu selected. If so, then select
+ // another one.
+ if (len > 1 && path[0] instanceof JMenuBar)
+ {
+ MenuElement currentMenu = path[1];
+ MenuElement nextMenu = findEnabledChild(path[0].getSubElements(),
+ currentMenu, selectChild);
+
+ if (nextMenu != null && nextMenu != currentMenu)
+ {
+ MenuElement newSelection[];
+ if (len == 2)
+ {
+ // Menu is selected but its popup not shown.
+ newSelection = new MenuElement[2];
+ newSelection[0] = path[0];
+ newSelection[1] = nextMenu;
+ }
+ else
+ {
+ // Menu is selected and its popup is shown.
+ newSelection = new MenuElement[3];
+ newSelection[0] = path[0];
+ newSelection[1] = nextMenu;
+ newSelection[2] = ((JMenu) nextMenu).getPopupMenu();
+ }
+ msm.setSelectedPath(newSelection);
+ }
+ }
+ }
+
+ /**
+ * Handles cancel requests (ESC key).
+ */
+ private void cancel()
+ {
+ // Fire popup menu cancelled event. Unfortunately the
+ // firePopupMenuCancelled() is protected in JPopupMenu so we work
+ // around this limitation by fetching the listeners and notifying them
+ // directly.
+ JPopupMenu lastPopup = (JPopupMenu) getLastPopup();
+ EventListener[] ll = lastPopup.getListeners(PopupMenuListener.class);
+ for (int i = 0; i < ll.length; i++)
+ {
+ PopupMenuEvent ev = new PopupMenuEvent(lastPopup);
+ ((PopupMenuListener) ll[i]).popupMenuCanceled(ev);
+ }
+
+ // Close the last popup or the whole selection if there's only one
+ // popup left.
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement path[] = msm.getSelectedPath();
+ if(path.length > 4)
+ {
+ MenuElement newPath[] = new MenuElement[path.length - 2];
+ System.arraycopy(path,0,newPath,0,path.length-2);
+ MenuSelectionManager.defaultManager().setSelectedPath(newPath);
+ }
+ else
+ msm.clearSelectedPath();
+ }
+
+ /**
+ * Returns the last popup menu in the current selection or null.
+ *
+ * @return the last popup menu in the current selection or null
+ */
+ private JPopupMenu getLastPopup()
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] p = msm.getSelectedPath();
+ JPopupMenu popup = null;
+ for(int i = p.length - 1; popup == null && i >= 0; i--)
+ {
+ if (p[i] instanceof JPopupMenu)
+ popup = (JPopupMenu) p[i];
+ }
+ return popup;
+ }
+
+ /**
+ * Handles ENTER key requests. This normally opens submenus on JMenu
+ * items, or activates the menu item as if it's been clicked on it.
+ */
+ private void doReturn()
+ {
+ KeyboardFocusManager fmgr =
+ KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ Component focusOwner = fmgr.getFocusOwner();
+ if((focusOwner == null || (focusOwner instanceof JRootPane)))
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement path[] = msm.getSelectedPath();
+ MenuElement lastElement;
+ if(path.length > 0)
+ {
+ lastElement = path[path.length - 1];
+ if(lastElement instanceof JMenu)
+ {
+ MenuElement newPath[] = new MenuElement[path.length + 1];
+ System.arraycopy(path,0,newPath,0,path.length);
+ newPath[path.length] = ((JMenu) lastElement).getPopupMenu();
+ msm.setSelectedPath(newPath);
+ }
+ else if(lastElement instanceof JMenuItem)
+ {
+ JMenuItem mi = (JMenuItem)lastElement;
+ if (mi.getUI() instanceof BasicMenuItemUI)
+ {
+ ((BasicMenuItemUI)mi.getUI()).doClick(msm);
+ }
+ else
+ {
+ msm.clearSelectedPath();
+ mi.doClick(0);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Installs keyboard actions when a popup is opened, and uninstalls the
+ * keyboard actions when closed. This listens on the default
+ * MenuSelectionManager.
+ */
+ private class KeyboardHelper
+ implements ChangeListener
+ {
+ private MenuElement[] lastSelectedPath = new MenuElement[0];
+ private Component lastFocused;
+ private JRootPane invokerRootPane;
+
+ public void stateChanged(ChangeEvent event)
+ {
+ MenuSelectionManager msm = (MenuSelectionManager) event.getSource();
+ MenuElement[] p = msm.getSelectedPath();
+ JPopupMenu popup = getActivePopup(p);
+ if (popup == null || popup.isFocusable())
+ {
+ if (lastSelectedPath.length != 0 && p.length != 0 )
+ {
+ if (! invokerEquals(p[0], lastSelectedPath[0]))
+ {
+ uninstallKeyboardActionsImpl();
+ lastSelectedPath = new MenuElement[0];
+ }
+ }
+
+ if (lastSelectedPath.length == 0 && p.length > 0)
+ {
+ JComponent invoker;
+ if (popup == null)
+ {
+ if (p.length == 2 && p[0] instanceof JMenuBar
+ && p[1] instanceof JMenu)
+ {
+ // A menu has been selected but not opened.
+ invoker = (JComponent)p[1];
+ popup = ((JMenu)invoker).getPopupMenu();
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ Component c = popup.getInvoker();
+ if(c instanceof JFrame)
+ {
+ invoker = ((JFrame) c).getRootPane();
+ }
+ else if(c instanceof JApplet)
+ {
+ invoker = ((JApplet) c).getRootPane();
+ }
+ else
+ {
+ while (!(c instanceof JComponent))
+ {
+ if (c == null)
+ {
+ return;
+ }
+ c = c.getParent();
+ }
+ invoker = (JComponent)c;
+ }
+ }
+
+ // Remember current focus owner.
+ lastFocused = KeyboardFocusManager.
+ getCurrentKeyboardFocusManager().getFocusOwner();
+
+ // Install keybindings used for menu navigation.
+ invokerRootPane = SwingUtilities.getRootPane(invoker);
+ if (invokerRootPane != null)
+ {
+ invokerRootPane.requestFocus(true);
+ installKeyboardActionsImpl();
+ }
+ }
+ else if (lastSelectedPath.length != 0 && p.length == 0)
+ {
+ // menu hidden -- return focus to where it had been before
+ // and uninstall menu keybindings
+ uninstallKeyboardActionsImpl();
+ }
+ }
+
+ // Remember the last path selected
+ lastSelectedPath = p;
+ }
+
+ private JPopupMenu getActivePopup(MenuElement[] path)
+ {
+ JPopupMenu active = null;
+ for (int i = path.length - 1; i >= 0 && active == null; i--)
+ {
+ MenuElement elem = path[i];
+ if (elem instanceof JPopupMenu)
+ {
+ active = (JPopupMenu) elem;
+ }
+ }
+ return active;
+ }
+
+ private boolean invokerEquals(MenuElement el1, MenuElement el2)
+ {
+ Component invoker1 = el1.getComponent();
+ Component invoker2 = el2.getComponent();
+ if (invoker1 instanceof JPopupMenu)
+ invoker1 = ((JPopupMenu) invoker1).getInvoker();
+ if (invoker2 instanceof JPopupMenu)
+ invoker2 = ((JPopupMenu) invoker2).getInvoker();
+ return invoker1 == invoker2;
+ }
+ }
+
+ /* popupMenu for which this UI delegate is for*/
+ protected JPopupMenu popupMenu;
+
+ /* PopupMenuListener listens to popup menu events fired by JPopupMenu*/
+ private transient PopupMenuListener popupMenuListener;
+
+ /* ComponentListener listening to popupMenu's invoker.
+ * This is package-private to avoid an accessor method. */
+ TopWindowListener topWindowListener;
+
+ /**
+ * Counts how many popup menus are handled by this UI or a subclass.
+ * This is used to install a KeyboardHelper on the MenuSelectionManager
+ * for the first popup, and uninstall this same KeyboardHelper when the
+ * last popup is uninstalled.
+ */
+ private static int numPopups;
+
+ /**
+ * This is the KeyboardHelper that listens on the MenuSelectionManager.
+ */
+ private static KeyboardHelper keyboardHelper;
+
+ /**
+ * Creates a new BasicPopupMenuUI object.
+ */
+ public BasicPopupMenuUI()
+ {
+ popupMenuListener = new PopupMenuHandler();
+ topWindowListener = new TopWindowListener();
+ }
+
+ /**
+ * Factory method to create a BasicPopupMenuUI for the given {@link
+ * JComponent}, which should be a {@link JMenuItem}.
+ *
+ * @param x The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicPopupMenuUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicPopupMenuUI();
+ }
+
+ /**
+ * Installs and initializes all fields for this UI delegate. Any properties
+ * of the UI that need to be initialized and/or set to defaults will be
+ * done now. It will also install any listeners necessary.
+ *
+ * @param c The {@link JComponent} that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ // Install KeyboardHelper when the first popup is initialized.
+ if (numPopups == 0)
+ {
+ keyboardHelper = new KeyboardHelper();
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ msm.addChangeListener(keyboardHelper);
+ }
+ numPopups++;
+
+ popupMenu = (JPopupMenu) c;
+ popupMenu.setLayout(new DefaultMenuLayout(popupMenu, BoxLayout.Y_AXIS));
+ popupMenu.setBorderPainted(true);
+ JPopupMenu.setDefaultLightWeightPopupEnabled(true);
+
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * This method installs the defaults that are defined in the Basic look
+ * and feel for this {@link JPopupMenu}.
+ */
+ public void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(popupMenu, "PopupMenu.background",
+ "PopupMenu.foreground", "PopupMenu.font");
+ LookAndFeel.installBorder(popupMenu, "PopupMenu.border");
+ popupMenu.setOpaque(true);
+ }
+
+ /**
+ * This method installs the listeners for the {@link JMenuItem}.
+ */
+ protected void installListeners()
+ {
+ popupMenu.addPopupMenuListener(popupMenuListener);
+ }
+
+ /**
+ * This method installs the keyboard actions for this {@link JPopupMenu}.
+ */
+ protected void installKeyboardActions()
+ {
+ // We can't install the keyboard actions here, because then all
+ // popup menus would have their actions registered in the KeyboardManager.
+ // So we install it when the popup menu is opened, and uninstall it
+ // when it's closed. This is done in the KeyboardHelper class.
+ // Install InputMap.
+ }
+
+ /**
+ * Called by the KeyboardHandler when a popup is made visible.
+ */
+ void installKeyboardActionsImpl()
+ {
+ Object[] bindings;
+ if (popupMenu.getComponentOrientation().isLeftToRight())
+ {
+ bindings = (Object[])
+ SharedUIDefaults.get("PopupMenu.selectedWindowInputMapBindings");
+ }
+ else
+ {
+ bindings = (Object[]) SharedUIDefaults.get
+ ("PopupMenu.selectedWindowInputMapBindings.RightToLeft");
+ }
+ InputMap inputMap = LookAndFeel.makeComponentInputMap(popupMenu, bindings);
+ SwingUtilities.replaceUIInputMap(popupMenu,
+ JComponent.WHEN_IN_FOCUSED_WINDOW,
+ inputMap);
+
+ // Install ActionMap.
+ SwingUtilities.replaceUIActionMap(popupMenu, getActionMap());
+ }
+
+ /**
+ * Creates and returns the shared action map for JTrees.
+ *
+ * @return the shared action map for JTrees
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("PopupMenu.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("PopupMenu.actionMap", am);
+ }
+ return am;
+ }
+
+ /**
+ * Creates the default actions when there are none specified by the L&F.
+ *
+ * @return the default actions
+ */
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new NavigateAction("selectNext");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("selectPrevious");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("selectParent");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("selectChild");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("return");
+ am.put(action.getValue(Action.NAME), action);
+ action = new NavigateAction("cancel");
+ am.put(action.getValue(Action.NAME), action);
+
+ return am;
+ }
+
+ /**
+ * Performs the opposite of installUI. Any properties or resources that need
+ * to be cleaned up will be done now. It will also uninstall any listeners
+ * it has. In addition, any properties of this UI will be nulled.
+ *
+ * @param c The {@link JComponent} that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallKeyboardActions();
+ popupMenu = null;
+
+ // Install KeyboardHelper when the first popup is initialized.
+ numPopups--;
+ if (numPopups == 0)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ msm.removeChangeListener(keyboardHelper);
+ }
+
+ }
+
+ /**
+ * This method uninstalls the defaults and sets any objects created during
+ * install to null
+ */
+ protected void uninstallDefaults()
+ {
+ popupMenu.setBackground(null);
+ popupMenu.setBorder(null);
+ popupMenu.setFont(null);
+ popupMenu.setForeground(null);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using.
+ */
+ protected void uninstallListeners()
+ {
+ popupMenu.removePopupMenuListener(popupMenuListener);
+ }
+
+ /**
+ * Uninstalls any keyboard actions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ // We can't install the keyboard actions here, because then all
+ // popup menus would have their actions registered in the KeyboardManager.
+ // So we install it when the popup menu is opened, and uninstall it
+ // when it's closed. This is done in the KeyboardHelper class.
+ // Install InputMap.
+ }
+
+ /**
+ * Called by the KeyboardHandler when a popup is made invisible.
+ */
+ void uninstallKeyboardActionsImpl()
+ {
+ SwingUtilities.replaceUIInputMap(popupMenu,
+ JComponent.WHEN_IN_FOCUSED_WINDOW, null);
+ SwingUtilities.replaceUIActionMap(popupMenu, null);
+ }
+
+ /**
+ * This method returns the minimum size of the JPopupMenu.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * This method returns the preferred size of the JPopupMenu.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * This method returns the minimum size of the JPopupMenu.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return null;
+ }
+
+ /**
+ * Return true if given mouse event is a platform popup trigger, and false
+ * otherwise
+ *
+ * @param e MouseEvent that is to be checked for popup trigger event
+ *
+ * @return true if given mouse event is a platform popup trigger, and false
+ * otherwise
+ */
+ public boolean isPopupTrigger(MouseEvent e)
+ {
+ return false;
+ }
+
+ /**
+ * This listener handles PopupMenuEvents fired by JPopupMenu
+ */
+ private class PopupMenuHandler implements PopupMenuListener
+ {
+ /**
+ * This method is invoked when JPopupMenu is cancelled.
+ *
+ * @param event the PopupMenuEvent
+ */
+ public void popupMenuCanceled(PopupMenuEvent event)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+
+ /**
+ * This method is invoked when JPopupMenu becomes invisible
+ *
+ * @param event the PopupMenuEvent
+ */
+ public void popupMenuWillBecomeInvisible(PopupMenuEvent event)
+ {
+ // remove listener that listens to component events fired
+ // by the top - level window that this popup belongs to.
+ Component invoker = popupMenu.getInvoker();
+ Component rootContainer = SwingUtilities.getRoot(invoker);
+ if (rootContainer != null)
+ rootContainer.removeComponentListener(topWindowListener);
+ }
+
+ /**
+ * This method is invoked when JPopupMenu becomes visible
+ *
+ * @param event the PopupMenuEvent
+ */
+ public void popupMenuWillBecomeVisible(PopupMenuEvent event)
+ {
+ // Adds topWindowListener to top-level window to listener to
+ // ComponentEvents fired by it. We need to cancel this popup menu
+ // if topWindow to which this popup belongs was resized or moved.
+ Component invoker = popupMenu.getInvoker();
+ Component rootContainer = SwingUtilities.getRoot(invoker);
+ if (rootContainer != null)
+ rootContainer.addComponentListener(topWindowListener);
+
+ // if this popup menu is a free floating popup menu,
+ // then by default its first element should be always selected when
+ // this popup menu becomes visible.
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+
+ if (manager.getSelectedPath().length == 0)
+ {
+ // Set selected path to point to the first item in the popup menu
+ MenuElement[] path = new MenuElement[2];
+ path[0] = popupMenu;
+ Component[] comps = popupMenu.getComponents();
+ if (comps.length != 0 && comps[0] instanceof MenuElement)
+ {
+ path[1] = (MenuElement) comps[0];
+ manager.setSelectedPath(path);
+ }
+ }
+ }
+ }
+
+ /**
+ * ComponentListener that listens to Component Events fired by the top -
+ * level window to which popup menu belongs. If top-level window was
+ * resized, moved or hidded then popup menu will be hidded and selected
+ * path of current menu hierarchy will be set to null.
+ */
+ private class TopWindowListener implements ComponentListener
+ {
+ /**
+ * This method is invoked when top-level window is resized. This method
+ * closes current menu hierarchy.
+ *
+ * @param e The ComponentEvent
+ */
+ public void componentResized(ComponentEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+
+ /**
+ * This method is invoked when top-level window is moved. This method
+ * closes current menu hierarchy.
+ *
+ * @param e The ComponentEvent
+ */
+ public void componentMoved(ComponentEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+
+ /**
+ * This method is invoked when top-level window is shown This method
+ * does nothing by default.
+ *
+ * @param e The ComponentEvent
+ */
+ public void componentShown(ComponentEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+
+ /**
+ * This method is invoked when top-level window is hidden This method
+ * closes current menu hierarchy.
+ *
+ * @param e The ComponentEvent
+ */
+ public void componentHidden(ComponentEvent e)
+ {
+ MenuSelectionManager manager = MenuSelectionManager.defaultManager();
+ manager.clearSelectedPath();
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java
new file mode 100644
index 000000000..bff6385ea
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicProgressBarUI.java
@@ -0,0 +1,962 @@
+/* BasicProgressBarUI.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.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.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.geom.AffineTransform;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JComponent;
+import javax.swing.JProgressBar;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ProgressBarUI;
+
+/**
+ * The Basic Look and Feel UI delegate for the
+ * JProgressBar.
+ */
+public class BasicProgressBarUI extends ProgressBarUI
+{
+ /**
+ * A helper class that listens for ChangeEvents
+ * from the progressBar's model.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ChangeHandler implements ChangeListener
+ {
+ /**
+ * Called every time the state of the model changes.
+ *
+ * @param e The ChangeEvent given by the model.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Nothing to do but repaint.
+ progressBar.repaint();
+ }
+ }
+
+ /**
+ * This helper class is used to listen for
+ * PropertyChangeEvents from the progressBar.
+ */
+ private class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Called every time the properties of the
+ * progressBar change.
+ *
+ * @param e The PropertyChangeEvent given by the progressBar.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // Only need to listen for indeterminate changes.
+ // All other things are done on a repaint.
+ if (e.getPropertyName().equals("indeterminate"))
+ if (((Boolean) e.getNewValue()).booleanValue()
+ && progressBar.isShowing())
+ startAnimationTimer();
+ else
+ stopAnimationTimer();
+ }
+ }
+
+ /**
+ * Receives notification when the progressbar is becoming visible or
+ * invisible and starts/stops the animation timer accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class AncestorHandler implements AncestorListener
+ {
+
+ /**
+ * Receives notification when the progressbar is becoming visible. This
+ * starts the animation timer if the progressbar is indeterminate.
+ *
+ * @param event the ancestor event
+ */
+ public void ancestorAdded(AncestorEvent event)
+ {
+ if (progressBar.isIndeterminate())
+ startAnimationTimer();
+ }
+
+ /**
+ * Receives notification when the progressbar is becoming invisible. This
+ * stops the animation timer if the progressbar is indeterminate.
+ *
+ * @param event the ancestor event
+ */
+ public void ancestorRemoved(AncestorEvent event)
+ {
+ stopAnimationTimer();
+ }
+
+ /**
+ * Receives notification when an ancestor has been moved. We don't need to
+ * do anything here.
+ */
+ public void ancestorMoved(AncestorEvent event)
+ {
+ // Nothing to do here.
+ }
+
+ }
+
+ /**
+ * This helper class is used to listen for
+ * the animationTimer's intervals. On every interval,
+ * the bouncing box should move.
+ */
+ private class Animator implements ActionListener
+ {
+ /**
+ * Called every time the animationTimer reaches
+ * its interval.
+ *
+ * @param e The ActionEvent given by the timer.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ // Incrementing the animation index will cause
+ // a repaint.
+ incrementAnimationIndex();
+ }
+ }
+
+ /**
+ * Receives notification when the size of the progress bar changes and
+ * invalidates the layout information for the box calculation in
+ * {@link BasicProgressBarUI#getBox(Rectangle)}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class ComponentHandler extends ComponentAdapter
+ {
+ /**
+ * Receives notification when the size of the progress bar changes and
+ * invalidates the layout information for the box calculation in
+ * {@link BasicProgressBarUI#getBox}.
+ *
+ * @param e the component event
+ */
+ public void componentResized(ComponentEvent e)
+ {
+ boxDependent = -1;
+ boxIndependent = -1;
+ incr = -1;
+ }
+ }
+
+ /**
+ * Holds the value of the bouncing box that is returned by {@link #getBox}.
+ *
+ * @since 1.5
+ */
+ protected Rectangle boxRect;
+
+ /** The timer used to move the bouncing box. */
+ private transient Timer animationTimer;
+
+ // The total number of frames must be an even number.
+ // The total number of frames is calculated from
+ // the cycleTime and repaintInterval given by
+ // the basic Look and Feel defaults.
+ //
+ // +-----------------------------------------------+
+ // | frame0 | frame1 | frame2 | frame 3 | frame 4 |
+ // | | frame7 | frame6 | frame 5 | |
+ // +-----------------------------------------------+
+
+ /** The current animation index. */
+ private transient int animationIndex;
+
+ /** The total number of frames.*/
+ private transient int numFrames;
+
+ /** The helper that moves the bouncing box. */
+ private transient Animator animation;
+
+ /** The helper that listens for property change events. */
+ private transient PropertyChangeHandler propertyListener;
+
+ /** The Listener for the model. */
+ protected ChangeListener changeListener;
+
+ /** The progressBar for this UI. */
+ protected JProgressBar progressBar;
+
+
+ /**
+ * The size of the box returned by {@link #getBox} in the orientation
+ * direction of the progress bar. This is package private to avoid accessor
+ * method.
+ */
+ transient double boxDependent = - 1;
+
+ /**
+ * The size of the box returned by {@link #getBox} against the orientation
+ * direction of the progress bar. This is package private to avoid accessor
+ * method.
+ */
+ transient int boxIndependent = - 1;
+
+ /**
+ * The increment for box animation. This is package private to avoid accessor
+ * method.
+ */
+ transient double incr = -1;
+
+ /** The length of the cell. The cell is the painted part. */
+ private transient int cellLength;
+
+ /** The gap between cells. */
+ private transient int cellSpacing;
+
+ /** The color of the text when the bar is not over it.*/
+ private transient Color selectionBackground;
+
+ /** The color of the text when the bar is over it. */
+ private transient Color selectionForeground;
+
+ /**
+ * Listens for notification when the component becomes showing and
+ * starts/stops the animation timer.
+ */
+ private AncestorListener ancestorListener;
+
+ /**
+ * Listens for resize events on the progress bar and invalidates some
+ * layout info.
+ */
+ private ComponentListener componentListener;
+
+ /**
+ * Creates a new BasicProgressBarUI object.
+ */
+ public BasicProgressBarUI()
+ {
+ super();
+ }
+
+ /**
+ * Creates a new BasicProgressBarUI for the component.
+ *
+ * @param x The JComponent to create the UI for.
+ *
+ * @return A new BasicProgressBarUI.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicProgressBarUI();
+ }
+
+ /**
+ * This method returns the length of the bar (from the minimum)
+ * in pixels (or units that the Graphics object draws in) based
+ * on the progressBar's getPercentComplete() value.
+ *
+ * @param b The insets of the progressBar.
+ * @param width The width of the progressBar.
+ * @param height The height of the progressBar.
+ *
+ * @return The length of the bar that should be painted in pixels.
+ */
+ protected int getAmountFull(Insets b, int width, int height)
+ {
+ double percentDone = progressBar.getPercentComplete();
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ return (int) (percentDone * (width - b.left - b.right));
+ else
+ return (int) (percentDone * (height - b.top - b.bottom));
+ }
+
+ /**
+ * The current animation index.
+ *
+ * @return The current animation index.
+ */
+ protected int getAnimationIndex()
+ {
+ return animationIndex;
+ }
+
+ /**
+ * This method returns the size and position of the bouncing box
+ * for the current animation index. It stores the values in the
+ * given rectangle and returns it. It returns null if no box should
+ * be drawn.
+ *
+ * @param r The bouncing box rectangle.
+ *
+ * @return The bouncing box rectangle.
+ */
+ protected Rectangle getBox(Rectangle r)
+ {
+ if (!progressBar.isIndeterminate())
+ return null;
+ if (r == null)
+ r = new Rectangle();
+
+ Rectangle vr = new Rectangle();
+ SwingUtilities.calculateInnerArea(progressBar, vr);
+
+ // Recalculate the metrics only when size of the progressbar has changed.
+ if (incr == -1 || boxDependent == -1 || boxIndependent == -1)
+ {
+ //numFrames has to be an even number as defined by spec.
+ int iterations = numFrames / 2;
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ {
+ boxDependent = vr.width / 6.;
+ incr = ((double) (vr.width - boxDependent)) / (double) iterations;
+ boxIndependent = vr.height;
+ }
+ else
+ {
+ boxDependent = vr.height / 6.;
+ incr = ((double) (vr.height - boxDependent)) / (double) iterations;
+ boxIndependent = vr.width;
+ }
+ }
+
+ int index = getAnimationIndex();
+ if (animationIndex > numFrames / 2)
+ index = numFrames - getAnimationIndex();
+
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ {
+ r.x = vr.x + (int) (incr * index);
+ r.y = vr.y;
+ r.width = (int) boxDependent;
+ r.height = (int) boxIndependent;
+ }
+ else
+ {
+ r.x = vr.x;
+ r.y = vr.height - (int) (incr * index) + vr.y - (int) boxDependent;
+ r.width = (int) boxIndependent;
+ r.height = (int) boxDependent;
+ }
+ return r;
+ }
+
+ /**
+ * This method returns the length of the cells.
+ *
+ * @return The cell length.
+ */
+ protected int getCellLength()
+ {
+ return cellLength;
+ }
+
+ /**
+ * This method returns the spacing between cells.
+ *
+ * @return The cell gap.
+ */
+ protected int getCellSpacing()
+ {
+ return cellSpacing;
+ }
+
+ /**
+ * This method returns the maximum size of the JComponent.
+ * If it returns null, it is up to the LayoutManager
+ * to give it a size.
+ *
+ * @param c The component to find a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Insets insets = c.getInsets();
+ Dimension ret;
+ int orientation = progressBar.getOrientation();
+ if (orientation == JProgressBar.VERTICAL)
+ {
+ ret = getPreferredInnerVertical();
+ ret.height = Short.MAX_VALUE;
+ ret.width += insets.left + insets.right;
+ }
+ else
+ {
+ ret = getPreferredInnerHorizontal();
+ ret.width = Short.MAX_VALUE;
+ ret.height += insets.top + insets.bottom;
+ }
+ return ret;
+ }
+
+ /**
+ * This method returns the minimum size of the JComponent.
+ * If it returns null, it is up to the LayoutManager to
+ * give it a size.
+ *
+ * @param c The component to find a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ Insets insets = c.getInsets();
+ Dimension ret;
+ int orientation = progressBar.getOrientation();
+ if (orientation == JProgressBar.VERTICAL)
+ {
+ ret = getPreferredInnerVertical();
+ ret.height = 10;
+ ret.width += insets.left + insets.right;
+ }
+ else
+ {
+ ret = getPreferredInnerHorizontal();
+ ret.width = 10;
+ ret.height += insets.top + insets.bottom;
+ }
+ return ret;
+ }
+
+ /**
+ * This method returns the preferred size of the inner
+ * rectangle (the bounds without the insets) if the
+ * progressBar is horizontal.
+ *
+ * @return The preferred size of the progressBar minus
+ * insets if it's horizontal.
+ */
+ protected Dimension getPreferredInnerHorizontal()
+ {
+ Font font = progressBar.getFont();
+ FontMetrics fm = progressBar.getFontMetrics(font);
+
+ int stringWidth = 0;
+ String str = progressBar.getString();
+ if (str != null)
+ stringWidth = fm.stringWidth(progressBar.getString());
+ Insets i = progressBar.getInsets();
+ int prefWidth = Math.max(200 - i.left - i.right, stringWidth);
+
+ int stringHeight = 0;
+ if (str != null)
+ stringHeight = fm.getHeight();
+ int prefHeight = Math.max(16 - i.top - i.bottom, stringHeight);
+
+ return new Dimension(prefWidth, prefHeight);
+ }
+
+ /**
+ * This method returns the preferred size of the inner
+ * rectangle (the bounds without insets) if the
+ * progressBar is vertical.
+ *
+ * @return The preferred size of the progressBar minus
+ * insets if it's vertical.
+ */
+ protected Dimension getPreferredInnerVertical()
+ {
+ Font font = progressBar.getFont();
+ FontMetrics fm = progressBar.getFontMetrics(font);
+
+ int stringWidth = 0;
+ String str = progressBar.getString();
+ if (str != null)
+ stringWidth = fm.stringWidth(progressBar.getString());
+ Insets i = progressBar.getInsets();
+ int prefHeight = Math.max(200 - i.left - i.right, stringWidth);
+
+ int stringHeight = 0;
+ if (str != null)
+ stringHeight = fm.getHeight();
+ int prefWidth = Math.max(16 - i.top - i.bottom, stringHeight);
+
+ return new Dimension(prefWidth, prefHeight);
+ }
+
+ /**
+ * This method returns the preferred size of the
+ * given JComponent. If it returns null, then it
+ * is up to the LayoutManager to give it a size.
+ *
+ * @param c The component to find the preferred size for.
+ *
+ * @return The preferred size of the component.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Insets insets = c.getInsets();
+ Dimension ret;
+ int orientation = progressBar.getOrientation();
+ if (orientation == JProgressBar.VERTICAL)
+ ret = getPreferredInnerVertical();
+ else
+ ret = getPreferredInnerHorizontal();
+ ret.width += insets.left + insets.right;
+ ret.height += insets.top + insets.bottom;
+ return ret;
+ }
+
+ /**
+ * This method returns the Color that the text is shown in when the bar is
+ * not over the text.
+ *
+ * @return The color of the text when the bar is not over it.
+ */
+ protected Color getSelectionBackground()
+ {
+ return selectionBackground;
+ }
+
+ /**
+ * This method returns the Color that the text is shown in when the bar is
+ * over the text.
+ *
+ * @return The color of the text when the bar is over it.
+ */
+ protected Color getSelectionForeground()
+ {
+ return selectionForeground;
+ }
+
+ /**
+ * This method returns the point (the top left of the bounding box)
+ * where the text should be painted.
+ *
+ * @param g The Graphics object to measure FontMetrics with.
+ * @param progressString The string to paint.
+ * @param x The x coordinate of the overall bounds box.
+ * @param y The y coordinate of the overall bounds box.
+ * @param width The width of the overall bounds box.
+ * @param height The height of the overall bounds box.
+ *
+ * @return The top left of the bounding box where text should be painted.
+ */
+ protected Point getStringPlacement(Graphics g, String progressString, int x,
+ int y, int width, int height)
+ {
+ Rectangle tr = new Rectangle();
+ Rectangle vr = new Rectangle();
+ Rectangle ir = new Rectangle();
+
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ vr.setBounds(x, y, width, height);
+ else
+ vr.setBounds(y, x, height, width);
+
+ Font f = g.getFont();
+ FontMetrics fm = g.getFontMetrics(f);
+
+ SwingUtilities.layoutCompoundLabel(progressBar, fm, progressString, null,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER,
+ SwingConstants.CENTER, vr, ir, tr, 0);
+
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ return new Point(tr.x, tr.y);
+ else
+ return new Point(tr.y, tr.x);
+ }
+
+ /**
+ * This method increments the animation index.
+ */
+ protected void incrementAnimationIndex()
+ {
+ animationIndex++;
+ //numFrames is like string length, it should be named numFrames or something
+ if (animationIndex >= numFrames)
+ animationIndex = 0;
+ progressBar.repaint();
+ }
+
+ /**
+ * This method paints the progressBar. It delegates its responsibilities
+ * to paintDeterminate and paintIndeterminate.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ if (! progressBar.isIndeterminate())
+ paintDeterminate(g, c);
+ else
+ paintIndeterminate(g, c);
+ }
+
+ /**
+ * This method is called if the painting to be done is
+ * for a determinate progressBar.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ protected void paintDeterminate(Graphics g, JComponent c)
+ {
+ Color saved = g.getColor();
+ int space = getCellSpacing();
+ int len = getCellLength();
+ int max = progressBar.getMaximum();
+ int min = progressBar.getMinimum();
+ int value = progressBar.getValue();
+
+ Rectangle vr = SwingUtilities.calculateInnerArea(c, new Rectangle());
+ Rectangle or = progressBar.getBounds();
+ Insets insets = c.getInsets();
+
+ int amountFull = getAmountFull(insets, or.width, or.height);
+
+ if (progressBar.getOrientation() == JProgressBar.HORIZONTAL)
+ {
+ g.setColor(c.getForeground());
+ g.fillRect(vr.x, vr.y, amountFull, vr.height);
+ }
+ else
+ {
+ g.setColor(c.getForeground());
+ g.fillRect(vr.x, vr.y + vr.height - amountFull, vr.width,
+ amountFull);
+ }
+
+ if (progressBar.isStringPainted() && !progressBar.getString().equals(""))
+ paintString(g, 0, 0, or.width, or.height, amountFull, insets);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method is called if the painting to be done is for
+ * an indeterminate progressBar.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ protected void paintIndeterminate(Graphics g, JComponent c)
+ {
+ //need to paint the box at it's current position. no text is painted since
+ //all we're doing is bouncing back and forth
+ Color saved = g.getColor();
+ Insets insets = c.getInsets();
+
+ Rectangle or = c.getBounds();
+ Rectangle vr = new Rectangle();
+ SwingUtilities.calculateInnerArea(c, vr);
+
+ g.setColor(c.getBackground());
+ g.fillRect(vr.x, vr.y, vr.width, vr.height);
+
+ boxRect = getBox(boxRect);
+
+ g.setColor(c.getForeground());
+ g.fillRect(boxRect.x, boxRect.y, boxRect.width, boxRect.height);
+
+ if (progressBar.isStringPainted() && !progressBar.getString().equals(""))
+ paintString(g, 0, 0, or.width, or.height,
+ getAmountFull(insets, or.width, or.height), insets);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the string for the progressBar.
+ *
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate of the progressBar.
+ * @param y The y coordinate of the progressBar.
+ * @param width The width of the progressBar.
+ * @param height The height of the progressBar.
+ * @param amountFull The amount of the progressBar that has its bar filled.
+ * @param b The insets of the progressBar.
+ */
+ protected void paintString(Graphics g, int x, int y, int width, int height,
+ int amountFull, Insets b)
+ {
+ String str = progressBar.getString();
+ int full = getAmountFull(b, width, height);
+ Point placement = getStringPlacement(g, progressBar.getString(),
+ x + b.left, y + b.top,
+ width - b.left - b.right,
+ height - b.top - b.bottom);
+ Color savedColor = g.getColor();
+ Shape savedClip = g.getClip();
+ FontMetrics fm = g.getFontMetrics(progressBar.getFont());
+
+ if (progressBar.getOrientation() == JProgressBar.VERTICAL)
+ {
+ AffineTransform rotate = AffineTransform.getRotateInstance(Math.PI / 2);
+ g.setFont(progressBar.getFont().deriveFont(rotate));
+ placement.x = width - placement.x - fm.getAscent();
+ }
+ else
+ {
+ placement.y += fm.getAscent();
+ }
+
+ g.setColor(getSelectionForeground());
+ g.setClip(0, 0, full + b.left, height);
+ g.drawString(str, placement.x, placement.y);
+ g.setColor(getSelectionBackground());
+ g.setClip(full + b.left, 0, width - full, height);
+ g.drawString(str, placement.x, placement.y);
+ g.setClip(savedClip);
+ g.setColor(savedColor);
+ }
+
+ /**
+ * This method sets the current animation index. If the index is greater than
+ * the number of frames, it resets to 0.
+ *
+ * @param newValue The new animation index.
+ */
+ protected void setAnimationIndex(int newValue)
+ {
+ animationIndex = (newValue <= numFrames) ? newValue : 0;
+ progressBar.repaint();
+ }
+
+ /**
+ * This method sets the cell length.
+ *
+ * @param cellLen The cell length.
+ */
+ protected void setCellLength(int cellLen)
+ {
+ cellLength = cellLen;
+ }
+
+ /**
+ * This method sets the cell spacing.
+ *
+ * @param cellSpace The cell spacing.
+ */
+ protected void setCellSpacing(int cellSpace)
+ {
+ cellSpacing = cellSpace;
+ }
+
+ /**
+ * This method starts the animation timer. It is called
+ * when the propertyChangeListener detects that the progressBar
+ * has changed to indeterminate mode.
+ *
+ * @since 1.4
+ */
+ protected void startAnimationTimer()
+ {
+ if (animationTimer != null)
+ animationTimer.start();
+ }
+
+ /**
+ * This method stops the animation timer. It is called when
+ * the propertyChangeListener detects that the progressBar
+ * has changed to determinate mode.
+ *
+ * @since 1.4
+ */
+ protected void stopAnimationTimer()
+ {
+ if (animationTimer != null)
+ animationTimer.stop();
+ setAnimationIndex(0);
+ }
+
+ /**
+ * This method changes the settings for the progressBar to
+ * the defaults provided by the current Look and Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(progressBar, "ProgressBar.background",
+ "ProgressBar.foreground",
+ "ProgressBar.font");
+ LookAndFeel.installBorder(progressBar, "ProgressBar.border");
+ progressBar.setOpaque(true);
+
+ selectionForeground = UIManager.getColor("ProgressBar.selectionForeground");
+ selectionBackground = UIManager.getColor("ProgressBar.selectionBackground");
+ cellLength = UIManager.getInt("ProgressBar.cellLength");
+ cellSpacing = UIManager.getInt("ProgressBar.cellSpacing");
+
+ int repaintInterval = UIManager.getInt("ProgressBar.repaintInterval");
+ int cycleTime = UIManager.getInt("ProgressBar.cycleTime");
+
+ if (cycleTime % repaintInterval != 0
+ && (cycleTime / repaintInterval) % 2 != 0)
+ {
+ int div = (cycleTime / repaintInterval) + 2;
+ div /= 2;
+ div *= 2;
+ cycleTime = div * repaintInterval;
+ }
+ setAnimationIndex(0);
+ numFrames = cycleTime / repaintInterval;
+ animationTimer.setDelay(repaintInterval);
+ }
+
+ /**
+ * The method uninstalls any defaults that were
+ * set by the current Look and Feel.
+ */
+ protected void uninstallDefaults()
+ {
+ progressBar.setFont(null);
+ progressBar.setForeground(null);
+ progressBar.setBackground(null);
+
+ selectionForeground = null;
+ selectionBackground = null;
+ }
+
+ /**
+ * This method registers listeners to all the
+ * components that this UI delegate needs to listen to.
+ */
+ protected void installListeners()
+ {
+ changeListener = new ChangeHandler();
+ propertyListener = new PropertyChangeHandler();
+ animation = new Animator();
+
+ progressBar.addChangeListener(changeListener);
+ progressBar.addPropertyChangeListener(propertyListener);
+ animationTimer.addActionListener(animation);
+
+ ancestorListener = new AncestorHandler();
+ progressBar.addAncestorListener(ancestorListener);
+
+ componentListener = new ComponentHandler();
+ progressBar.addComponentListener(componentListener);
+ }
+
+ /**
+ * This method unregisters listeners to all the
+ * components that were listened to.
+ */
+ protected void uninstallListeners()
+ {
+ progressBar.removeChangeListener(changeListener);
+ progressBar.removePropertyChangeListener(propertyListener);
+ animationTimer.removeActionListener(animation);
+
+ changeListener = null;
+ propertyListener = null;
+ animation = null;
+
+ if (ancestorListener != null)
+ progressBar.removeAncestorListener(ancestorListener);
+ ancestorListener = null;
+
+ if (componentListener != null)
+ progressBar.removeComponentListener(componentListener);
+ componentListener = null;
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ * This includes setting up defaults and listeners as
+ * well as initializing any values or objects that
+ * the UI may need.
+ *
+ * @param c The JComponent that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JProgressBar)
+ {
+ progressBar = (JProgressBar) c;
+
+ animationTimer = new Timer(200, null);
+ animationTimer.setRepeats(true);
+
+ installDefaults();
+ installListeners();
+ }
+ if (progressBar.isIndeterminate())
+ startAnimationTimer();
+ }
+
+ /**
+ * This method removes the UI for the given JComponent.
+ * This includes removing any listeners or defaults
+ * that the installUI may have set up.
+ *
+ * @param c The JComponent that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ uninstallListeners();
+ uninstallDefaults();
+
+ animationTimer = null;
+ progressBar = null;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java
new file mode 100644
index 000000000..f8f62e156
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonMenuItemUI.java
@@ -0,0 +1,101 @@
+/* BasicRadioButtonMenuItemUI.java --
+ Copyright (C) 2002, 2004 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.plaf.basic;
+
+import java.awt.event.MouseEvent;
+
+import javax.swing.JComponent;
+import javax.swing.JMenuItem;
+import javax.swing.MenuElement;
+import javax.swing.MenuSelectionManager;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * UI Delegator for JRadioButtonMenuItem
+ */
+public class BasicRadioButtonMenuItemUI extends BasicMenuItemUI
+{
+ /**
+ * Creates a new BasicRadioButtonMenuItemUI object.
+ */
+ public BasicRadioButtonMenuItemUI()
+ {
+ super();
+ }
+
+ /**
+ * Factory method to create a BasicRadioButtonMenuItemUI for the given {@link
+ * JComponent}, which should be a JRadioButtonMenuItem.
+ *
+ * @param b The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicRadioButtonMenuItemUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent b)
+ {
+ return new BasicRadioButtonMenuItemUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "RadioButtonMenuItem"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "RadioButtonMenuItem";
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param item DOCUMENT ME!
+ * @param e DOCUMENT ME!
+ * @param path DOCUMENT ME!
+ * @param manager DOCUMENT ME!
+ */
+ public void processMouseEvent(JMenuItem item, MouseEvent e,
+ MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // TODO: May not be implemented properly.
+ item.processMouseEvent(e, path, manager);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java
new file mode 100644
index 000000000..ff374d1ab
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicRadioButtonUI.java
@@ -0,0 +1,301 @@
+/* BasicRadioButtonUI.java
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+
+import javax.swing.AbstractButton;
+import javax.swing.ButtonModel;
+import javax.swing.Icon;
+import javax.swing.JComponent;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.View;
+
+/**
+ * The BasicLookAndFeel UI implementation for
+ * {@link javax.swing.JRadioButton}s.
+ */
+public class BasicRadioButtonUI extends BasicToggleButtonUI
+{
+ /**
+ * The default icon for JRadioButtons. The default icon displays the usual
+ * RadioButton and is sensible to the selection state of the button,
+ * and can be used both as normal icon as well as selectedIcon.
+ */
+ protected Icon icon;
+
+ /**
+ * Creates and returns a new instance of <code>BasicRadioButtonUI</code>.
+ *
+ * @return a new instance of <code>BasicRadioButtonUI</code>
+ */
+ public static ComponentUI createUI(final JComponent c)
+ {
+ return new BasicRadioButtonUI();
+ }
+
+ /**
+ * Creates a new instance of <code>BasicButtonUI</code>.
+ */
+ public BasicRadioButtonUI()
+ {
+ // nothing to do
+ }
+
+ /**
+ * Installs defaults from the Look &amp; Feel table on the specified
+ * button.
+ *
+ * @param b the button on which to install the defaults
+ */
+ protected void installDefaults(AbstractButton b)
+ {
+ super.installDefaults(b);
+ icon = UIManager.getIcon(getPropertyPrefix() + "icon");
+ }
+
+ /**
+ * Returns the prefix used for UIDefaults properties. This is
+ * <code>RadioButton</code> in this case.
+ *
+ * @return the prefix used for UIDefaults properties
+ */
+ protected String getPropertyPrefix()
+ {
+ return "RadioButton.";
+ }
+
+ /**
+ * Returns the default icon for JRadioButtons.
+ * The default icon displays the usual
+ * RadioButton and is sensible to the selection state of the button,
+ * and can be used both as normal icon as well as selectedIcon.
+ *
+ * @return the default icon for JRadioButtons
+ */
+ public Icon getDefaultIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * Paints the RadioButton.
+ *
+ * @param g the Graphics context to paint with
+ * @param c the button to paint
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+ Dimension size = c.getSize();
+ Insets i = b.getInsets();
+ 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 = i.left;
+ viewR.y = i.right;
+ viewR.width = size.width - i.left - i.right;
+ viewR.height = size.height - i.top - i.bottom;
+
+ Font f = c.getFont();
+
+ g.setFont(f);
+
+ // This is the icon that we use for layout.
+ Icon icon = b.getIcon();
+ if (icon == null)
+ icon = getDefaultIcon();
+
+ // Figure out the correct icon.
+ Icon currentIcon = getCurrentIcon(b);
+
+ // Do the layout.
+ String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f),
+ b.getText(), currentIcon == null ? getDefaultIcon() : currentIcon,
+ b.getVerticalAlignment(), b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(), b.getHorizontalTextPosition(),
+ viewR, iconR, textR, b.getIconTextGap());
+
+ // .. and paint it.
+ if (currentIcon != null)
+ currentIcon.paintIcon(c, g, iconR.x, iconR.y);
+
+ // Paint text and focus indicator.
+ if (text != null)
+ {
+ // Maybe render HTML in the radio button.
+ View v = (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (v != null)
+ v.paint(g, textR);
+ else
+ paintText(g, b, textR, text);
+
+ // Paint focus indicator if necessary.
+ if (b.hasFocus() && b.isFocusPainted()
+ && textR.width > 0 && textR.height > 0)
+ paintFocus(g, textR, size);
+ }
+ }
+
+ /**
+ * Determines the icon to be displayed for the specified radio button.
+ *
+ * @param b the radio button
+ *
+ * @return the icon
+ */
+ private Icon getCurrentIcon(AbstractButton b)
+ {
+ ButtonModel m = b.getModel();
+ Icon currentIcon = b.getIcon();
+
+ if (currentIcon == null)
+ {
+ currentIcon = getDefaultIcon();
+ }
+ else
+ {
+ if (! m.isEnabled())
+ {
+ if (m.isSelected())
+ currentIcon = b.getDisabledSelectedIcon();
+ else
+ currentIcon = b.getDisabledIcon();
+ }
+ else if (m.isPressed() && m.isArmed())
+ {
+ currentIcon = b.getPressedIcon();
+ if (currentIcon == null)
+ currentIcon = b.getSelectedIcon();
+ }
+ else if (m.isSelected())
+ {
+ if (b.isRolloverEnabled() && m.isRollover())
+ {
+ currentIcon = b.getRolloverSelectedIcon();
+ if (currentIcon == null)
+ currentIcon = b.getSelectedIcon();
+ }
+ else
+ currentIcon = b.getSelectedIcon();
+ }
+ else if (b.isRolloverEnabled() && m.isRollover())
+ {
+ currentIcon = b.getRolloverIcon();
+ }
+ if (currentIcon == null)
+ currentIcon = b.getIcon();
+ }
+ return currentIcon;
+ }
+
+ public Dimension getPreferredSize(JComponent c)
+ {
+ // This is basically the same code as in
+ // BasicGraphicsUtils.getPreferredButtonSize() but takes the default icon
+ // property into account. JRadioButton and subclasses always have an icon:
+ // the check box. If the user explicitly changes it with setIcon() that
+ // one will be used for layout calculations and painting instead.
+ // The other icon properties are ignored.
+ AbstractButton b = (AbstractButton) c;
+
+ Insets insets = b.getInsets();
+
+ String text = b.getText();
+ Icon i = b.getIcon();
+ if (i == null)
+ i = getDefaultIcon();
+
+ 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
+ b.getFontMetrics(b.getFont()),
+ text, i, b.getVerticalAlignment(),
+ b.getHorizontalAlignment(),
+ b.getVerticalTextPosition(),
+ b.getHorizontalTextPosition(),
+ viewR, iconR, textR,
+ text == null ? 0 : b.getIconTextGap());
+
+ 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);
+ }
+
+ /**
+ * Paints the focus indicator for JRadioButtons.
+ *
+ * @param g the graphics context
+ * @param tr the rectangle for the text label
+ * @param size the size of the <code>JRadioButton</code> component.
+ */
+ protected void paintFocus(Graphics g, Rectangle tr, Dimension size)
+ {
+ Color focusColor = UIManager.getColor(getPropertyPrefix() + ".focus");
+ Color saved = g.getColor();
+ g.setColor(focusColor);
+ g.drawRect(tr.x, tr.y, tr.width, tr.height);
+ g.setColor(saved);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java
new file mode 100644
index 000000000..26c7a532d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicRootPaneUI.java
@@ -0,0 +1,292 @@
+/* BasicRootPaneUI.java --
+ Copyright (C) 2002, 2004 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.plaf.basic;
+
+import java.awt.event.ActionEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ButtonModel;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JRootPane;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentInputMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.RootPaneUI;
+
+public class BasicRootPaneUI extends RootPaneUI
+ implements PropertyChangeListener
+{
+
+ /**
+ * Performed when the user activates the default button inside the JRootPane,
+ * usually by pressing 'ENTER'.
+ */
+ private class DefaultPressAction
+ extends AbstractAction
+ {
+ /**
+ * The JRootPane for which this action should be installed.
+ */
+ private JRootPane rootPane;
+
+ /**
+ * Creates a new DefaultPressAction for the specified JRootPane.
+ */
+ DefaultPressAction(JRootPane rp)
+ {
+ rootPane = rp;
+ }
+
+ /**
+ * Performes the action.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ JButton b = rootPane.getDefaultButton();
+ if (b != null)
+ {
+ ButtonModel m = b.getModel();
+ m.setArmed(true);
+ m.setPressed(true);
+ }
+ }
+ }
+
+ /**
+ * Performed when the user activates the default button inside the JRootPane,
+ * usually by releasing 'ENTER'.
+ */
+ private class DefaultReleaseAction
+ extends AbstractAction
+ {
+ /**
+ * The JRootPane for which this action should be installed.
+ */
+ private JRootPane rootPane;
+
+ /**
+ * Creates a new DefaultReleaseAction for the specified JRootPane.
+ */
+ DefaultReleaseAction(JRootPane rp)
+ {
+ rootPane = rp;
+ }
+
+ /**
+ * Performes the action.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ JButton b = rootPane.getDefaultButton();
+ if (b != null)
+ {
+ ButtonModel m = b.getModel();
+ m.setPressed(false);
+ m.setArmed(false);
+ }
+ }
+ }
+
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicRootPaneUI();
+ }
+
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JRootPane)
+ {
+ JRootPane rp = (JRootPane) c;
+ installDefaults(rp);
+ installComponents(rp);
+ installListeners(rp);
+ installKeyboardActions(rp);
+ }
+ }
+
+ /**
+ * Installs the look and feel defaults for JRootPane.
+ *
+ * @param rp the root pane to install the defaults to
+ */
+ protected void installDefaults(JRootPane rp)
+ {
+ // TODO: What to do here, if anything? (might be a hook method)
+ }
+
+ /**
+ * Installs additional look and feel components to the root pane.
+ *
+ * @param rp the root pane to install the components to
+ */
+ protected void installComponents(JRootPane rp)
+ {
+ // All components are initialized in the JRootPane constructor, and since
+ // the createXXXPane methods are protected, I see no reasonable way,
+ // and no need to initialize them here. This method is here anyway
+ // for compatibility and to provide the necessary hooks to subclasses.
+ }
+
+ /**
+ * Installs any look and feel specific listeners on the root pane.
+ *
+ * @param rp the root pane to install the listeners to
+ */
+ protected void installListeners(JRootPane rp)
+ {
+ rp.addPropertyChangeListener(this);
+ }
+
+ /**
+ * Installs look and feel keyboard actions on the root pane.
+ *
+ * @param rp the root pane to install the keyboard actions to
+ */
+ protected void installKeyboardActions(JRootPane rp)
+ {
+ // Install the keyboard actions.
+ ActionMapUIResource am = new ActionMapUIResource();
+ am.put("press", new DefaultPressAction(rp));
+ am.put("release", new DefaultReleaseAction(rp));
+ SwingUtilities.replaceUIActionMap(rp, am);
+
+ // Install the input map from the UIManager. It seems like the actual
+ // bindings are installed in the JRootPane only when the defaultButton
+ // property receives a value. So we also only install an empty
+ // input map here, and fill it in propertyChange.
+ ComponentInputMapUIResource im = new ComponentInputMapUIResource(rp);
+ SwingUtilities.replaceUIInputMap(rp, JComponent.WHEN_IN_FOCUSED_WINDOW,
+ im);
+ }
+
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ JRootPane source = (JRootPane) event.getSource();
+ String propertyName = event.getPropertyName();
+ if (propertyName.equals("defaultButton"))
+ {
+ Object newValue = event.getNewValue();
+ InputMap im =
+ SwingUtilities.getUIInputMap(source,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ if (newValue != null)
+ {
+ Object[] keybindings = (Object[]) UIManager.get(
+ "RootPane.defaultButtonWindowKeyBindings");
+ LookAndFeel.loadKeyBindings(im, keybindings);
+ }
+ else
+ {
+ im.clear();
+ }
+ }
+ }
+
+ /**
+ * Uninstalls this UI from the root pane. This calls
+ * {@link #uninstallDefaults}, {@link #uninstallComponents},
+ * {@link #uninstallListeners}, {@link #uninstallKeyboardActions}
+ * in this order.
+ *
+ * @param c the root pane to uninstall the UI from
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ if (c instanceof JRootPane)
+ {
+ JRootPane rp = (JRootPane) c;
+ uninstallDefaults(rp);
+ uninstallComponents(rp);
+ uninstallListeners(rp);
+ uninstallKeyboardActions(rp);
+ }
+ }
+
+ /**
+ * Uninstalls the look and feel defaults that have been installed in
+ * {@link #installDefaults}.
+ *
+ * @param rp the root pane to uninstall the defaults from
+ */
+ protected void uninstallDefaults(JRootPane rp)
+ {
+ // We do nothing here.
+ }
+
+ /**
+ * Uninstalls look and feel components from the root pane.
+ *
+ * @param rp the root pane to uninstall the components from
+ */
+ protected void uninstallComponents(JRootPane rp)
+ {
+ // We do nothing here.
+ }
+
+ /**
+ * Uninstalls any look and feel specific listeners from the root pane.
+ *
+ * @param rp the root pane to uninstall the listeners from
+ */
+ protected void uninstallListeners(JRootPane rp)
+ {
+ rp.removePropertyChangeListener(this);
+ }
+
+ /**
+ * Uninstalls look and feel keyboard actions from the root pane.
+ *
+ * @param rp the root pane to uninstall the keyboard actions from
+ */
+ protected void uninstallKeyboardActions(JRootPane rp)
+ {
+ SwingUtilities.replaceUIActionMap(rp, null);
+ SwingUtilities.replaceUIInputMap(rp, JComponent.WHEN_IN_FOCUSED_WINDOW,
+ null);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java
new file mode 100644
index 000000000..9f24f8aca
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollBarUI.java
@@ -0,0 +1,1513 @@
+/* BasicScrollBarUI.java --
+ Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.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.Rectangle;
+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;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.BoundedRangeModel;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JScrollBar;
+import javax.swing.JSlider;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ScrollBarUI;
+
+/**
+ * The Basic Look and Feel UI delegate for JScrollBar.
+ */
+public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager,
+ SwingConstants
+{
+ /**
+ * A helper class that listens to the two JButtons on each end of the
+ * JScrollBar.
+ */
+ protected class ArrowButtonListener extends MouseAdapter
+ {
+
+ /**
+ * Move the thumb in the direction specified by the button's arrow. If
+ * this button is held down, then it should keep moving the thumb.
+ *
+ * @param e The MouseEvent fired by the JButton.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ scrollTimer.stop();
+ scrollListener.setScrollByBlock(false);
+ if (e.getSource() == incrButton)
+ scrollListener.setDirection(POSITIVE_SCROLL);
+ else if (e.getSource() == decrButton)
+ scrollListener.setDirection(NEGATIVE_SCROLL);
+ scrollTimer.setDelay(100);
+ scrollTimer.start();
+ }
+
+ /**
+ * Stops the thumb when the JButton is released.
+ *
+ * @param e The MouseEvent fired by the JButton.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ scrollTimer.stop();
+ scrollTimer.setDelay(300);
+ if (e.getSource() == incrButton)
+ scrollByUnit(POSITIVE_SCROLL);
+ else if (e.getSource() == decrButton)
+ scrollByUnit(NEGATIVE_SCROLL);
+ }
+ }
+
+ /**
+ * A helper class that listens to the ScrollBar's model for ChangeEvents.
+ */
+ protected class ModelListener implements ChangeListener
+ {
+ /**
+ * Called when the model changes.
+ *
+ * @param e The ChangeEvent fired by the model.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ calculatePreferredSize();
+ updateThumbRect();
+ scrollbar.repaint();
+ }
+ }
+
+ /**
+ * A helper class that listens to the ScrollBar's properties.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Called when one of the ScrollBar's properties change.
+ *
+ * @param e The PropertyChangeEvent fired by the ScrollBar.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals("model"))
+ {
+ ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener);
+ scrollbar.getModel().addChangeListener(modelListener);
+ updateThumbRect();
+ }
+ else if (e.getPropertyName().equals("orientation"))
+ {
+ uninstallListeners();
+ uninstallComponents();
+ uninstallDefaults();
+ installDefaults();
+ installComponents();
+ installListeners();
+ }
+ else if (e.getPropertyName().equals("enabled"))
+ {
+ Boolean b = (Boolean) e.getNewValue();
+ if (incrButton != null)
+ incrButton.setEnabled(b.booleanValue());
+ if (decrButton != null)
+ decrButton.setEnabled(b.booleanValue());
+ }
+ }
+ }
+
+ /**
+ * A helper class that listens for events from the timer that is used to
+ * move the thumb.
+ */
+ protected class ScrollListener implements ActionListener
+ {
+ /** The direction the thumb moves in. */
+ private transient int direction;
+
+ /** Whether movement will be in blocks. */
+ private transient boolean block;
+
+ /**
+ * Creates a new ScrollListener object. The default is scrolling
+ * positively with block movement.
+ */
+ public ScrollListener()
+ {
+ direction = POSITIVE_SCROLL;
+ block = true;
+ }
+
+ /**
+ * Creates a new ScrollListener object using the given direction and
+ * block.
+ *
+ * @param dir The direction to move in.
+ * @param block Whether movement will be in blocks.
+ */
+ public ScrollListener(int dir, boolean block)
+ {
+ direction = dir;
+ this.block = block;
+ }
+
+ /**
+ * Sets the direction to scroll in.
+ *
+ * @param direction The direction to scroll in.
+ */
+ public void setDirection(int direction)
+ {
+ this.direction = direction;
+ }
+
+ /**
+ * Sets whether scrolling will be done in blocks.
+ *
+ * @param block Whether scrolling will be in blocks.
+ */
+ public void setScrollByBlock(boolean block)
+ {
+ this.block = block;
+ }
+
+ /**
+ * Called every time the timer reaches its interval.
+ *
+ * @param e The ActionEvent fired by the timer.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (block)
+ {
+ // Only need to check it if it's block scrolling
+ // We only block scroll if the click occurs
+ // in the track.
+ if (!trackListener.shouldScroll(direction))
+ {
+ trackHighlight = NO_HIGHLIGHT;
+ scrollbar.repaint();
+ return;
+ }
+ scrollByBlock(direction);
+ }
+ else
+ scrollByUnit(direction);
+ }
+ }
+
+ /**
+ * Helper class that listens for movement on the track.
+ */
+ protected class TrackListener extends MouseAdapter
+ implements MouseMotionListener
+ {
+ /** The current X coordinate of the mouse. */
+ protected int currentMouseX;
+
+ /** The current Y coordinate of the mouse. */
+ protected int currentMouseY;
+
+ /**
+ * The offset between the current mouse cursor and the current value of
+ * the scrollbar.
+ */
+ protected int offset;
+
+ /**
+ * This method is called when the mouse is being dragged.
+ *
+ * @param e The MouseEvent given.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+ if (scrollbar.getValueIsAdjusting())
+ {
+ int value;
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ value = valueForXPosition(currentMouseX) - offset;
+ else
+ value = valueForYPosition(currentMouseY) - offset;
+
+ scrollbar.setValue(value);
+ }
+ }
+
+ /**
+ * This method is called when the mouse is moved.
+ *
+ * @param e The MouseEvent given.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ if (thumbRect.contains(e.getPoint()))
+ thumbRollover = true;
+ else
+ thumbRollover = false;
+ }
+
+ /**
+ * This method is called when the mouse is pressed. When it is pressed,
+ * the thumb should move in blocks towards the cursor.
+ *
+ * @param e The MouseEvent given.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+
+ int value;
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ value = valueForXPosition(currentMouseX);
+ else
+ value = valueForYPosition(currentMouseY);
+
+ if (! thumbRect.contains(e.getPoint()))
+ {
+ scrollTimer.stop();
+ scrollListener.setScrollByBlock(true);
+ if (value > scrollbar.getValue())
+ {
+ trackHighlight = INCREASE_HIGHLIGHT;
+ scrollListener.setDirection(POSITIVE_SCROLL);
+ }
+ else
+ {
+ trackHighlight = DECREASE_HIGHLIGHT;
+ scrollListener.setDirection(NEGATIVE_SCROLL);
+ }
+ scrollTimer.setDelay(100);
+ scrollTimer.start();
+ }
+ else
+ {
+ // We'd like to keep track of where the cursor
+ // is inside the thumb.
+ // This works because the scrollbar's value represents
+ // "lower" edge of the thumb. The value at which
+ // the cursor is at must be greater or equal
+ // to that value.
+
+ scrollListener.setScrollByBlock(false);
+ scrollbar.setValueIsAdjusting(true);
+ offset = value - scrollbar.getValue();
+ }
+ scrollbar.repaint();
+ }
+
+ /**
+ * This method is called when the mouse is released. It should stop
+ * movement on the thumb
+ *
+ * @param e The MouseEvent given.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ scrollTimer.stop();
+ scrollTimer.setDelay(300);
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+
+ if (shouldScroll(POSITIVE_SCROLL))
+ scrollByBlock(POSITIVE_SCROLL);
+ else if (shouldScroll(NEGATIVE_SCROLL))
+ scrollByBlock(NEGATIVE_SCROLL);
+
+ trackHighlight = NO_HIGHLIGHT;
+ scrollListener.setScrollByBlock(false);
+ scrollbar.setValueIsAdjusting(true);
+ scrollbar.repaint();
+ }
+
+ /**
+ * A helper method that decides whether we should keep scrolling in the
+ * given direction.
+ *
+ * @param direction The direction to check for.
+ *
+ * @return Whether the thumb should keep scrolling.
+ */
+ boolean shouldScroll(int direction)
+ {
+ int value;
+ if (scrollbar.getOrientation() == HORIZONTAL)
+ value = valueForXPosition(currentMouseX);
+ else
+ value = valueForYPosition(currentMouseY);
+
+ if (thumbRect.contains(currentMouseX, currentMouseY))
+ return false;
+
+ if (direction == POSITIVE_SCROLL)
+ return value > scrollbar.getValue();
+ else
+ return value < scrollbar.getValue();
+ }
+ }
+
+ /** The listener that listens to the JButtons. */
+ protected ArrowButtonListener buttonListener;
+
+ /** The listener that listens to the model. */
+ protected ModelListener modelListener;
+
+ /** The listener that listens to the scrollbar for property changes. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The listener that listens to the timer. */
+ protected ScrollListener scrollListener;
+
+ /** The listener that listens for MouseEvents on the track. */
+ protected TrackListener trackListener;
+
+ /** The JButton that decrements the scrollbar's value. */
+ protected JButton decrButton;
+
+ /** The JButton that increments the scrollbar's value. */
+ protected JButton incrButton;
+
+ /** The dimensions of the maximum thumb size. */
+ protected Dimension maximumThumbSize;
+
+ /** The dimensions of the minimum thumb size. */
+ protected Dimension minimumThumbSize;
+
+ /** The color of the thumb. */
+ protected Color thumbColor;
+
+ /** The outer shadow of the thumb. */
+ protected Color thumbDarkShadowColor;
+
+ /** The top and left edge color for the thumb. */
+ protected Color thumbHighlightColor;
+
+ /** The outer light shadow for the thumb. */
+ protected Color thumbLightShadowColor;
+
+ /** The color that is used when the mouse press occurs in the track. */
+ protected Color trackHighlightColor;
+
+ /** The color of the track. */
+ protected Color trackColor;
+
+ /** The size and position of the track. */
+ protected Rectangle trackRect;
+
+ /** The size and position of the thumb. */
+ protected Rectangle thumbRect;
+
+ /** Indicates that the decrease highlight should be painted. */
+ protected static final int DECREASE_HIGHLIGHT = 1;
+
+ /** Indicates that the increase highlight should be painted. */
+ protected static final int INCREASE_HIGHLIGHT = 2;
+
+ /** Indicates that no highlight should be painted. */
+ protected static final int NO_HIGHLIGHT = 0;
+
+ /** Indicates that the scrolling direction is positive. */
+ private static final int POSITIVE_SCROLL = 1;
+
+ /** Indicates that the scrolling direction is negative. */
+ private static final int NEGATIVE_SCROLL = -1;
+
+ /** The cached preferred size for the scrollbar. */
+ private transient Dimension preferredSize;
+
+ /** The current highlight status. */
+ protected int trackHighlight;
+
+ /** FIXME: Use this for something (presumably mouseDragged) */
+ protected boolean isDragging;
+
+ /** The timer used to move the thumb when the mouse is held. */
+ protected Timer scrollTimer;
+
+ /** The scrollbar this UI is acting for. */
+ protected JScrollBar scrollbar;
+
+ /** True if the mouse is over the thumb. */
+ boolean thumbRollover;
+
+ /**
+ * This method adds a component to the layout.
+ *
+ * @param name The name to associate with the component that is added.
+ * @param child The Component to add.
+ */
+ public void addLayoutComponent(String name, Component child)
+ {
+ // You should not be adding stuff to this component.
+ // The contents are fixed.
+ }
+
+ /**
+ * This method configures the scrollbar's colors. This can be done by
+ * looking up the standard colors from the Look and Feel defaults.
+ */
+ protected void configureScrollBarColors()
+ {
+ trackColor = UIManager.getColor("ScrollBar.track");
+ trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight");
+ thumbColor = UIManager.getColor("ScrollBar.thumb");
+ thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight");
+ thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow");
+ thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow");
+ }
+
+ /**
+ * This method creates an ArrowButtonListener.
+ *
+ * @return A new ArrowButtonListener.
+ */
+ protected ArrowButtonListener createArrowButtonListener()
+ {
+ return new ArrowButtonListener();
+ }
+
+ /**
+ * This method creates a new JButton with the appropriate icon for the
+ * orientation.
+ *
+ * @param orientation The orientation this JButton uses.
+ *
+ * @return The increase JButton.
+ */
+ protected JButton createIncreaseButton(int orientation)
+ {
+ return new BasicArrowButton(orientation);
+ }
+
+ /**
+ * This method creates a new JButton with the appropriate icon for the
+ * orientation.
+ *
+ * @param orientation The orientation this JButton uses.
+ *
+ * @return The decrease JButton.
+ */
+ protected JButton createDecreaseButton(int orientation)
+ {
+ return new BasicArrowButton(orientation);
+ }
+
+ /**
+ * This method creates a new ModelListener.
+ *
+ * @return A new ModelListener.
+ */
+ protected ModelListener createModelListener()
+ {
+ return new ModelListener();
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method creates a new ScrollListener.
+ *
+ * @return A new ScrollListener.
+ */
+ protected ScrollListener createScrollListener()
+ {
+ return new ScrollListener();
+ }
+
+ /**
+ * This method creates a new TrackListener.
+ *
+ * @return A new TrackListener.
+ */
+ protected TrackListener createTrackListener()
+ {
+ return new TrackListener();
+ }
+
+ /**
+ * This method returns a new BasicScrollBarUI.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicScrollBarUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicScrollBarUI();
+ }
+
+ /**
+ * This method returns the maximum size for this JComponent.
+ *
+ * @param c The JComponent to measure the maximum size for.
+ *
+ * @return The maximum size for the component.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * This method returns the maximum thumb size.
+ *
+ * @return The maximum thumb size.
+ */
+ protected Dimension getMaximumThumbSize()
+ {
+ return maximumThumbSize;
+ }
+
+ /**
+ * This method returns the minimum size for this JComponent.
+ *
+ * @param c The JComponent to measure the minimum size for.
+ *
+ * @return The minimum size for the component.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the minimum thumb size.
+ *
+ * @return The minimum thumb size.
+ */
+ protected Dimension getMinimumThumbSize()
+ {
+ return minimumThumbSize;
+ }
+
+ /**
+ * This method calculates the preferred size since calling
+ * getPreferredSize() returns a cached value.
+ * This is package-private to avoid an accessor method.
+ */
+ void calculatePreferredSize()
+ {
+ int height;
+ int width;
+ height = width = 0;
+
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ {
+ width += incrButton.getPreferredSize().getWidth();
+ width += decrButton.getPreferredSize().getWidth();
+ width += 16;
+ height = UIManager.getInt("ScrollBar.width");
+ }
+ else
+ {
+ height += incrButton.getPreferredSize().getHeight();
+ height += decrButton.getPreferredSize().getHeight();
+ height += 16;
+ width = UIManager.getInt("ScrollBar.width");
+ }
+
+ Insets insets = scrollbar.getInsets();
+
+ height += insets.top + insets.bottom;
+ width += insets.left + insets.right;
+
+ preferredSize = new Dimension(width, height);
+ }
+
+ /**
+ * This method returns a cached value of the preferredSize. The only
+ * restrictions are: If the scrollbar is horizontal, the height should be
+ * the maximum of the height of the JButtons and the minimum width of the
+ * thumb. For vertical scrollbars, the calculation is similar (swap width
+ * for height and vice versa).
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferredSize.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ calculatePreferredSize();
+ return preferredSize;
+ }
+
+ /**
+ * This method returns the thumb's bounds based on the current value of the
+ * scrollbar. This method updates the cached value and returns that.
+ *
+ * @return The thumb bounds.
+ */
+ protected Rectangle getThumbBounds()
+ {
+ return thumbRect;
+ }
+
+ /**
+ * This method calculates the bounds of the track. This method updates the
+ * cached value and returns it.
+ *
+ * @return The track's bounds.
+ */
+ protected Rectangle getTrackBounds()
+ {
+ return trackRect;
+ }
+
+ /**
+ * This method installs any addition Components that are a part of or
+ * related to this scrollbar.
+ */
+ protected void installComponents()
+ {
+ int orientation = scrollbar.getOrientation();
+ switch (orientation)
+ {
+ case JScrollBar.HORIZONTAL:
+ incrButton = createIncreaseButton(EAST);
+ decrButton = createDecreaseButton(WEST);
+ break;
+ default:
+ incrButton = createIncreaseButton(SOUTH);
+ decrButton = createDecreaseButton(NORTH);
+ break;
+ }
+
+ if (incrButton != null)
+ scrollbar.add(incrButton);
+ if (decrButton != null)
+ scrollbar.add(decrButton);
+ }
+
+ /**
+ * This method installs the defaults for the scrollbar specified by the
+ * Basic Look and Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColors(scrollbar, "ScrollBar.background",
+ "ScrollBar.foreground");
+ LookAndFeel.installBorder(scrollbar, "ScrollBar.border");
+ scrollbar.setOpaque(true);
+ scrollbar.setLayout(this);
+
+ configureScrollBarColors();
+
+ maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize");
+ minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize");
+ }
+
+ /**
+ * Installs the input map from the look and feel defaults, and a
+ * corresponding action map. Note the the keyboard bindings will only
+ * work when the {@link JScrollBar} component has the focus, which is rare.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap keyMap = getInputMap(
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ SwingUtilities.replaceUIInputMap(scrollbar,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(scrollbar, map);
+ }
+
+ /**
+ * Uninstalls the input map and action map installed by
+ * {@link #installKeyboardActions()}.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIActionMap(scrollbar, null);
+ SwingUtilities.replaceUIInputMap(scrollbar,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ }
+
+ InputMap getInputMap(int condition)
+ {
+ if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+ return (InputMap) UIManager.get("ScrollBar.focusInputMap");
+ return null;
+ }
+
+ /**
+ * Returns the action map for the {@link JScrollBar}. All scroll bars
+ * share a single action map which is created the first time this method is
+ * called, then stored in the UIDefaults table for subsequent access.
+ *
+ * @return The shared action map.
+ */
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("ScrollBar.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("ScrollBar.actionMap", map);
+ }
+ return map;
+ }
+
+ /**
+ * Creates the action map shared by all {@link JSlider} instances.
+ * This method is called once by {@link #getActionMap()} when it
+ * finds no action map in the UIDefaults table...after the map is
+ * created, it gets added to the defaults table so that subsequent
+ * calls to {@link #getActionMap()} will return the same shared
+ * instance.
+ *
+ * @return The action map.
+ */
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+ map.put("positiveUnitIncrement",
+ new AbstractAction("positiveUnitIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("positiveBlockIncrement",
+ new AbstractAction("positiveBlockIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("negativeUnitIncrement",
+ new AbstractAction("negativeUnitIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("negativeBlockIncrement",
+ new AbstractAction("negativeBlockIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("minScroll",
+ new AbstractAction("minScroll") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ sb.setValue(sb.getMinimum());
+ }
+ }
+ }
+ );
+ map.put("maxScroll",
+ new AbstractAction("maxScroll") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollBar sb = (JScrollBar) event.getSource();
+ if (sb.isVisible())
+ {
+ sb.setValue(sb.getMaximum());
+ }
+ }
+ }
+ );
+ return map;
+ }
+
+ /**
+ * This method installs any listeners for the scrollbar. This method also
+ * installs listeners for things such as the JButtons and the timer.
+ */
+ protected void installListeners()
+ {
+ scrollListener = createScrollListener();
+ trackListener = createTrackListener();
+ buttonListener = createArrowButtonListener();
+ modelListener = createModelListener();
+ propertyChangeListener = createPropertyChangeListener();
+
+ scrollbar.addMouseMotionListener(trackListener);
+ scrollbar.addMouseListener(trackListener);
+
+ incrButton.addMouseListener(buttonListener);
+ decrButton.addMouseListener(buttonListener);
+
+ scrollbar.addPropertyChangeListener(propertyChangeListener);
+ scrollbar.getModel().addChangeListener(modelListener);
+
+ scrollTimer.addActionListener(scrollListener);
+ }
+
+ /**
+ * This method installs the UI for the component. This can include setting
+ * up listeners, defaults, and components. This also includes initializing
+ * any data objects.
+ *
+ * @param c The JComponent to install.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JScrollBar)
+ {
+ scrollbar = (JScrollBar) c;
+
+ trackRect = new Rectangle();
+ thumbRect = new Rectangle();
+
+ scrollTimer = new Timer(300, null);
+
+ installDefaults();
+ installComponents();
+ configureScrollBarColors();
+ installListeners();
+ installKeyboardActions();
+
+ calculatePreferredSize();
+ }
+ }
+
+ /**
+ * This method lays out the scrollbar.
+ *
+ * @param scrollbarContainer The Container to layout.
+ */
+ public void layoutContainer(Container scrollbarContainer)
+ {
+ if (scrollbarContainer instanceof JScrollBar)
+ {
+ if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL)
+ layoutHScrollbar((JScrollBar) scrollbarContainer);
+ else
+ layoutVScrollbar((JScrollBar) scrollbarContainer);
+ }
+ }
+
+ /**
+ * This method lays out the scrollbar horizontally.
+ *
+ * @param sb The JScrollBar to layout.
+ */
+ protected void layoutHScrollbar(JScrollBar sb)
+ {
+ Rectangle vr = new Rectangle();
+ SwingUtilities.calculateInnerArea(scrollbar, vr);
+
+ Dimension incrDims = incrButton.getPreferredSize();
+ Dimension decrDims = decrButton.getPreferredSize();
+
+ // calculate and update the track bounds
+ SwingUtilities.calculateInnerArea(scrollbar, trackRect);
+ trackRect.width -= incrDims.getWidth();
+ trackRect.width -= decrDims.getWidth();
+ trackRect.x += decrDims.getWidth();
+
+ updateThumbRect();
+
+ decrButton.setBounds(vr.x, vr.y, decrDims.width, trackRect.height);
+ incrButton.setBounds(trackRect.x + trackRect.width, vr.y, incrDims.width,
+ trackRect.height);
+ }
+
+ /**
+ * This method lays out the scrollbar vertically.
+ *
+ * @param sb The JScrollBar to layout.
+ */
+ protected void layoutVScrollbar(JScrollBar sb)
+ {
+ Rectangle vr = new Rectangle();
+ SwingUtilities.calculateInnerArea(scrollbar, vr);
+
+ Dimension incrDims = incrButton.getPreferredSize();
+ Dimension decrDims = decrButton.getPreferredSize();
+
+ // Update rectangles
+ SwingUtilities.calculateInnerArea(scrollbar, trackRect);
+ trackRect.height -= incrDims.getHeight();
+ trackRect.height -= decrDims.getHeight();
+ trackRect.y += decrDims.getHeight();
+
+ updateThumbRect();
+
+ decrButton.setBounds(vr.x, vr.y, trackRect.width, decrDims.height);
+ incrButton.setBounds(vr.x, trackRect.y + trackRect.height,
+ trackRect.width, incrDims.height);
+ }
+
+ /**
+ * Updates the thumb rect.
+ */
+ void updateThumbRect()
+ {
+ int max = scrollbar.getMaximum();
+ int min = scrollbar.getMinimum();
+ int value = scrollbar.getValue();
+ int extent = scrollbar.getVisibleAmount();
+ if (max - extent <= min)
+ {
+ if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL)
+ {
+ thumbRect.x = trackRect.x;
+ thumbRect.y = trackRect.y;
+ thumbRect.width = getMinimumThumbSize().width;
+ thumbRect.height = trackRect.height;
+ }
+ else
+ {
+ thumbRect.x = trackRect.x;
+ thumbRect.y = trackRect.y;
+ thumbRect.width = trackRect.width;
+ thumbRect.height = getMinimumThumbSize().height;
+ }
+ }
+ else
+ {
+ if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL)
+ {
+ thumbRect.x = trackRect.x;
+ thumbRect.width = Math.max(extent * trackRect.width / (max - min),
+ getMinimumThumbSize().width);
+ int availableWidth = trackRect.width - thumbRect.width;
+ thumbRect.x += (value - min) * availableWidth / (max - min - extent);
+ thumbRect.y = trackRect.y;
+ thumbRect.height = trackRect.height;
+ }
+ else
+ {
+ thumbRect.x = trackRect.x;
+ thumbRect.height = Math.max(extent * trackRect.height / (max - min),
+ getMinimumThumbSize().height);
+ int availableHeight = trackRect.height - thumbRect.height;
+ thumbRect.y = trackRect.y
+ + (value - min) * availableHeight / (max - min - extent);
+ thumbRect.width = trackRect.width;
+ }
+ }
+
+ }
+
+ /**
+ * This method returns the minimum size required for the layout.
+ *
+ * @param scrollbarContainer The Container that is laid out.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container scrollbarContainer)
+ {
+ return preferredLayoutSize(scrollbarContainer);
+ }
+
+ /**
+ * This method is called when the component is painted.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ paintTrack(g, c, getTrackBounds());
+ paintThumb(g, c, getThumbBounds());
+
+ if (trackHighlight == INCREASE_HIGHLIGHT)
+ paintIncreaseHighlight(g);
+ else if (trackHighlight == DECREASE_HIGHLIGHT)
+ paintDecreaseHighlight(g);
+ }
+
+ /**
+ * This method is called when repainting and the mouse is pressed in the
+ * track. It paints the track below the thumb with the trackHighlight
+ * color.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintDecreaseHighlight(Graphics g)
+ {
+ Color saved = g.getColor();
+
+ g.setColor(trackHighlightColor);
+ if (scrollbar.getOrientation() == HORIZONTAL)
+ g.fillRect(trackRect.x, trackRect.y, thumbRect.x - trackRect.x,
+ trackRect.height);
+ else
+ g.fillRect(trackRect.x, trackRect.y, trackRect.width,
+ thumbRect.y - trackRect.y);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method is called when repainting and the mouse is pressed in the
+ * track. It paints the track above the thumb with the trackHighlight
+ * color.
+ *
+ * @param g The Graphics objet to paint with.
+ */
+ protected void paintIncreaseHighlight(Graphics g)
+ {
+ Color saved = g.getColor();
+
+ g.setColor(trackHighlightColor);
+ if (scrollbar.getOrientation() == HORIZONTAL)
+ g.fillRect(thumbRect.x + thumbRect.width, trackRect.y,
+ trackRect.x + trackRect.width - thumbRect.x - thumbRect.width,
+ trackRect.height);
+ else
+ g.fillRect(trackRect.x, thumbRect.y + thumbRect.height, trackRect.width,
+ trackRect.y + trackRect.height - thumbRect.y
+ - thumbRect.height);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the thumb.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The Component that is being painted.
+ * @param thumbBounds The thumb bounds.
+ */
+ protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds)
+ {
+ g.setColor(thumbColor);
+ g.fillRect(thumbBounds.x, thumbBounds.y, thumbBounds.width,
+ thumbBounds.height);
+
+ BasicGraphicsUtils.drawBezel(g, thumbBounds.x, thumbBounds.y,
+ thumbBounds.width, thumbBounds.height,
+ false, false, thumbDarkShadowColor,
+ thumbDarkShadowColor, thumbHighlightColor,
+ thumbHighlightColor);
+ }
+
+ /**
+ * This method paints the track.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent being painted.
+ * @param trackBounds The track's bounds.
+ */
+ protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds)
+ {
+ Color saved = g.getColor();
+ g.setColor(trackColor);
+ g.fill3DRect(trackBounds.x, trackBounds.y, trackBounds.width,
+ trackBounds.height, false);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method returns the preferred size for the layout.
+ *
+ * @param scrollbarContainer The Container to find a size for.
+ *
+ * @return The preferred size for the layout.
+ */
+ public Dimension preferredLayoutSize(Container scrollbarContainer)
+ {
+ if (scrollbarContainer instanceof JComponent)
+ return getPreferredSize((JComponent) scrollbarContainer);
+ else
+ return null;
+ }
+
+ /**
+ * This method removes a child component from the layout.
+ *
+ * @param child The child to remove.
+ */
+ public void removeLayoutComponent(Component child)
+ {
+ // You should not be removing stuff from this component.
+ }
+
+ /**
+ * The method scrolls the thumb by a block in the direction specified.
+ *
+ * @param direction The direction to scroll.
+ */
+ protected void scrollByBlock(int direction)
+ {
+ scrollByBlock(scrollbar, direction);
+ }
+
+ /**
+ * Scrolls the specified <code>scrollBar</code> by one block (according
+ * to the scrollable protocol) in the specified <code>direction</code>.
+ *
+ * This method is here statically to support wheel scrolling from the
+ * BasicScrollPaneUI without code duplication.
+ *
+ * @param scrollBar the scrollbar to scroll
+ * @param direction the scroll direction
+ */
+ static final void scrollByBlock(JScrollBar scrollBar, int direction)
+ {
+ int delta;
+ if (direction > 0)
+ delta = scrollBar.getBlockIncrement(direction);
+ else
+ delta = - scrollBar.getBlockIncrement(direction);
+ int oldValue = scrollBar.getValue();
+ int newValue = oldValue + delta;
+
+ // Overflow check.
+ if (delta > 0 && newValue < oldValue)
+ newValue = scrollBar.getMaximum();
+ else if (delta < 0 && newValue > oldValue)
+ newValue = scrollBar.getMinimum();
+
+ scrollBar.setValue(newValue);
+ }
+
+ /**
+ * The method scrolls the thumb by a unit in the direction specified.
+ *
+ * @param direction The direction to scroll.
+ */
+ protected void scrollByUnit(int direction)
+ {
+ scrollByUnits(scrollbar, direction, 1);
+ }
+
+ /**
+ * Scrolls the specified <code>scrollbac/code> by <code>units</code> units
+ * in the specified <code>direction</code>.
+ *
+ * This method is here statically to support wheel scrolling from the
+ * BasicScrollPaneUI without code duplication.
+ *
+ * @param scrollBar the scrollbar to scroll
+ * @param direction the direction
+ * @param units the number of units to scroll
+ */
+ static final void scrollByUnits(JScrollBar scrollBar, int direction,
+ int units)
+ {
+ // Do this inside a loop so that we don't clash with the scrollable
+ // interface, which can return different units at times. For instance,
+ // a Scrollable could return a unit of 2 pixels only to adjust the
+ // visibility of an item. If we would simply multiply this by units,
+ // then we would only get 6 pixels, which is complete crap.
+ for (int i = 0; i < units; i++)
+ {
+ int delta;
+ if (direction > 0)
+ delta = scrollBar.getUnitIncrement(direction);
+ else
+ delta = - scrollBar.getUnitIncrement(direction);
+ int oldValue = scrollBar.getValue();
+ int newValue = oldValue + delta;
+
+ // Overflow check.
+ if (delta > 0 && newValue < oldValue)
+ newValue = scrollBar.getMaximum();
+ else if (delta < 0 && newValue > oldValue)
+ newValue = scrollBar.getMinimum();
+
+ scrollBar.setValue(newValue);
+ }
+ }
+
+ /**
+ * This method sets the thumb's bounds.
+ *
+ * @param x The X position of the thumb.
+ * @param y The Y position of the thumb.
+ * @param width The width of the thumb.
+ * @param height The height of the thumb.
+ */
+ protected void setThumbBounds(int x, int y, int width, int height)
+ {
+ thumbRect.x = x;
+ thumbRect.y = y;
+ thumbRect.width = width;
+ thumbRect.height = height;
+ }
+
+ /**
+ * This method uninstalls any components that are a part of or related to
+ * this scrollbar.
+ */
+ protected void uninstallComponents()
+ {
+ if (incrButton != null)
+ scrollbar.remove(incrButton);
+ if (decrButton != null)
+ scrollbar.remove(decrButton);
+ }
+
+ /**
+ * This method uninstalls any defaults that this scrollbar acquired from the
+ * Basic Look and Feel defaults.
+ */
+ protected void uninstallDefaults()
+ {
+ scrollbar.setForeground(null);
+ scrollbar.setBackground(null);
+ LookAndFeel.uninstallBorder(scrollbar);
+ incrButton = null;
+ decrButton = null;
+ }
+
+ /**
+ * This method uninstalls any listeners that were registered during install.
+ */
+ protected void uninstallListeners()
+ {
+ if (scrollTimer != null)
+ scrollTimer.removeActionListener(scrollListener);
+
+ if (scrollbar != null)
+ {
+ scrollbar.getModel().removeChangeListener(modelListener);
+ scrollbar.removePropertyChangeListener(propertyChangeListener);
+ scrollbar.removeMouseListener(trackListener);
+ scrollbar.removeMouseMotionListener(trackListener);
+ }
+
+ if (decrButton != null)
+ decrButton.removeMouseListener(buttonListener);
+ if (incrButton != null)
+ incrButton.removeMouseListener(buttonListener);
+
+ propertyChangeListener = null;
+ modelListener = null;
+ buttonListener = null;
+ trackListener = null;
+ scrollListener = null;
+ }
+
+ /**
+ * This method uninstalls the UI. This includes removing any defaults,
+ * listeners, and components that this UI may have initialized. It also
+ * nulls any instance data.
+ *
+ * @param c The Component to uninstall for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallComponents();
+
+ scrollTimer = null;
+
+ thumbRect = null;
+ trackRect = null;
+
+ trackColor = null;
+ trackHighlightColor = null;
+ thumbColor = null;
+ thumbHighlightColor = null;
+ thumbDarkShadowColor = null;
+ thumbLightShadowColor = null;
+
+ scrollbar = null;
+ }
+
+ /**
+ * This method returns the value in the scrollbar's range given the y
+ * coordinate. If the value is out of range, it will return the closest
+ * legal value.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param yPos The y coordinate to calculate a value for.
+ *
+ * @return The value for the y coordinate.
+ */
+ int valueForYPosition(int yPos)
+ {
+ int min = scrollbar.getMinimum();
+ int max = scrollbar.getMaximum();
+ int len = trackRect.height;
+
+ int value;
+
+ // If the length is 0, you shouldn't be able to even see where the thumb is.
+ // This really shouldn't ever happen, but just in case, we'll return the middle.
+ if (len == 0)
+ return (max - min) / 2;
+
+ value = (yPos - trackRect.y) * (max - min) / len + min;
+
+ // If this isn't a legal value, then we'll have to move to one now.
+ if (value > max)
+ value = max;
+ else if (value < min)
+ value = min;
+ return value;
+ }
+
+ /**
+ * This method returns the value in the scrollbar's range given the x
+ * coordinate. If the value is out of range, it will return the closest
+ * legal value.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param xPos The x coordinate to calculate a value for.
+ *
+ * @return The value for the x coordinate.
+ */
+ int valueForXPosition(int xPos)
+ {
+ int min = scrollbar.getMinimum();
+ int max = scrollbar.getMaximum();
+ int len = trackRect.width;
+
+ int value;
+
+ // If the length is 0, you shouldn't be able to even see where the slider is.
+ // This really shouldn't ever happen, but just in case, we'll return the middle.
+ if (len == 0)
+ return (max - min) / 2;
+
+ value = (xPos - trackRect.x) * (max - min) / len + min;
+
+ // If this isn't a legal value, then we'll have to move to one now.
+ if (value > max)
+ value = max;
+ else if (value < min)
+ value = min;
+ return value;
+ }
+
+ /**
+ * Returns true if the mouse is over the thumb.
+ *
+ * @return true if the mouse is over the thumb.
+ *
+ * @since 1.5
+ */
+ public boolean isThumbRollover()
+ {
+ return thumbRollover;
+ }
+
+ /**
+ * Set thumbRollover to active. This indicates
+ * whether or not the mouse is over the thumb.
+ *
+ * @param active - true if the mouse is over the thumb.
+ *
+ * @since 1.5
+ */
+ protected void setThumbRollover(boolean active)
+ {
+ thumbRollover = active;
+ }
+
+ /**
+ * Indicates whether the user can position the thumb with
+ * a mouse click (i.e. middle button).
+ *
+ * @return true if the user can position the thumb with a mouse
+ * click.
+ *
+ * @since 1.5
+ */
+ public boolean getSupportsAbsolutePositioning()
+ {
+ // The positioning feature has not been implemented.
+ // So, false is always returned.
+ return false;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java
new file mode 100644
index 000000000..712394830
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicScrollPaneUI.java
@@ -0,0 +1,823 @@
+/* BasicScrollPaneUI.java
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.MouseWheelEvent;
+import java.awt.event.MouseWheelListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.JSlider;
+import javax.swing.JViewport;
+import javax.swing.LookAndFeel;
+import javax.swing.ScrollPaneConstants;
+import javax.swing.ScrollPaneLayout;
+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.
+ */
+public class BasicScrollPaneUI extends ScrollPaneUI
+ implements ScrollPaneConstants
+{
+
+ /**
+ * Listens for changes in the state of the horizontal scrollbar's model and
+ * updates the scrollpane accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class HSBChangeListener implements ChangeListener
+ {
+
+ /**
+ * Receives notification when the state of the horizontal scrollbar
+ * model has changed.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ JScrollBar hsb = scrollpane.getHorizontalScrollBar();
+ JViewport vp = scrollpane.getViewport();
+ Point viewPosition = vp.getViewPosition();
+ viewPosition.x = hsb.getValue();
+ vp.setViewPosition(viewPosition);
+ }
+
+ }
+
+ /**
+ * Listens for changes in the state of the vertical scrollbar's model and
+ * updates the scrollpane accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class VSBChangeListener implements ChangeListener
+ {
+
+ /**
+ * Receives notification when the state of the vertical scrollbar
+ * model has changed.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ JScrollBar vsb = scrollpane.getVerticalScrollBar();
+ JViewport vp = scrollpane.getViewport();
+ Point viewPosition = vp.getViewPosition();
+ viewPosition.y = vsb.getValue();
+ vp.setViewPosition(viewPosition);
+ }
+
+ }
+
+ /**
+ * Listens for changes of the viewport's extent size and updates the
+ * scrollpane accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class ViewportChangeHandler implements ChangeListener
+ {
+
+ /**
+ * Receives notification when the view's size, position or extent size
+ * changes. When the extents size has changed, this method calls
+ * {@link BasicScrollPaneUI#syncScrollPaneWithViewport()} to adjust the
+ * scrollbars extents as well.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ syncScrollPaneWithViewport();
+ }
+
+ }
+
+ /**
+ * Listens for property changes on the scrollpane and update the view
+ * accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+
+ /**
+ * Receives notification when any of the scrollpane's bound property
+ * changes. This method calls the appropriate update method on the
+ * <code>ScrollBarUI</code>.
+ *
+ * @param e the property change event
+ *
+ * @see BasicScrollPaneUI#updateColumnHeader(PropertyChangeEvent)
+ * @see BasicScrollPaneUI#updateRowHeader(PropertyChangeEvent)
+ * @see BasicScrollPaneUI#updateScrollBarDisplayPolicy(PropertyChangeEvent)
+ * @see BasicScrollPaneUI#updateViewport(PropertyChangeEvent)
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String propName = e.getPropertyName();
+ if (propName.equals("viewport"))
+ updateViewport(e);
+ else if (propName.equals("rowHeader"))
+ updateRowHeader(e);
+ else if (propName.equals("columnHeader"))
+ updateColumnHeader(e);
+ else if (propName.equals("horizontalScrollBarPolicy")
+ || e.getPropertyName().equals("verticalScrollBarPolicy"))
+ updateScrollBarDisplayPolicy(e);
+ else if (propName.equals("verticalScrollBar"))
+ {
+ JScrollBar oldSb = (JScrollBar) e.getOldValue();
+ oldSb.getModel().removeChangeListener(vsbChangeListener);
+ JScrollBar newSb = (JScrollBar) e.getNewValue();
+ newSb.getModel().addChangeListener(vsbChangeListener);
+ }
+ else if (propName.equals("horizontalScrollBar"))
+ {
+ JScrollBar oldSb = (JScrollBar) e.getOldValue();
+ oldSb.getModel().removeChangeListener(hsbChangeListener);
+ JScrollBar newSb = (JScrollBar) e.getNewValue();
+ newSb.getModel().addChangeListener(hsbChangeListener);
+ }
+ }
+
+ }
+
+ /**
+ * Listens for mouse wheel events and update the scrollpane accordingly.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ *
+ * @since 1.4
+ */
+ protected class MouseWheelHandler implements MouseWheelListener
+ {
+ /**
+ * Use to compute the visible rectangle.
+ */
+ final Rectangle rect = new Rectangle();
+
+ /**
+ * Scroll with the mouse wheel.
+ *
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+ public void mouseWheelMoved(MouseWheelEvent e)
+ {
+ if (scrollpane.isWheelScrollingEnabled() && e.getScrollAmount() != 0)
+ {
+ // Try to scroll vertically first.
+ JScrollBar scrollBar = scrollpane.getVerticalScrollBar();
+ if (scrollBar == null || ! scrollBar.isVisible())
+ scrollBar = scrollpane.getHorizontalScrollBar();
+ if (scrollBar != null && scrollBar.isVisible())
+ {
+ int direction = e.getWheelRotation() < 0 ? -1 : 1;
+ int scrollType = e.getScrollType();
+ if (scrollType == MouseWheelEvent.WHEEL_UNIT_SCROLL)
+ BasicScrollBarUI.scrollByUnits(scrollBar, direction,
+ e.getScrollAmount());
+ else if (scrollType == MouseWheelEvent.WHEEL_BLOCK_SCROLL)
+ BasicScrollBarUI.scrollByBlock(scrollBar, direction);
+ }
+ }
+ }
+ }
+
+ /**
+ * Adds/removes the mouse wheel listener when the component is added/removed
+ * to/from the scroll pane view port.
+ *
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+ class ViewportContainerListener implements ContainerListener
+ {
+ /**
+ * Add the mouse wheel listener, allowing to scroll with the mouse.
+ */
+ public void componentAdded(ContainerEvent e)
+ {
+ e.getChild().addMouseWheelListener(mouseWheelListener);
+ }
+
+ /**
+ * Remove the mouse wheel listener.
+ */
+ public void componentRemoved(ContainerEvent e)
+ {
+ e.getChild().removeMouseWheelListener(mouseWheelListener);
+ }
+ }
+
+ /**
+ * The number of pixels by that we should scroll the content that does
+ * not implement Scrollable.
+ */
+ static int SCROLL_NON_SCROLLABLES = 10;
+
+ /**
+ * The number of rows to scroll per mouse wheel click. From impression,
+ * Sun seems using the value 3.
+ */
+ static int ROWS_PER_WHEEL_CLICK = 3;
+
+ /** The Scrollpane for which the UI is provided by this class. */
+ protected JScrollPane scrollpane;
+
+ /**
+ * The horizontal scrollbar listener.
+ */
+ protected ChangeListener hsbChangeListener;
+
+ /**
+ * The vertical scrollbar listener.
+ */
+ protected ChangeListener vsbChangeListener;
+
+ /**
+ * The viewport listener.
+ */
+ protected ChangeListener viewportChangeListener;
+
+ /**
+ * The scrollpane property change listener.
+ */
+ protected PropertyChangeListener spPropertyChangeListener;
+
+ /**
+ * The mousewheel listener for the scrollpane.
+ */
+ MouseWheelListener mouseWheelListener;
+
+ /**
+ * The listener to add and remove the mouse wheel listener to/from
+ * the component container.
+ */
+ ContainerListener containerListener;
+
+ public static ComponentUI createUI(final JComponent c)
+ {
+ return new BasicScrollPaneUI();
+ }
+
+ protected void installDefaults(JScrollPane p)
+ {
+ scrollpane = p;
+ LookAndFeel.installColorsAndFont(p, "ScrollPane.background",
+ "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)
+ {
+ LookAndFeel.uninstallBorder(p);
+ Border vpBorder = p.getViewportBorder();
+ if (vpBorder != null && vpBorder instanceof UIResource)
+ p.setViewportBorder(null);
+ }
+
+ public void installUI(final JComponent c)
+ {
+ super.installUI(c);
+ installDefaults((JScrollPane) c);
+ installListeners((JScrollPane) c);
+ installKeyboardActions((JScrollPane) c);
+ }
+
+ /**
+ * Installs the listeners on the scrollbars, the viewport and the scrollpane.
+ *
+ * @param sp the scrollpane on which to install the listeners
+ */
+ protected void installListeners(JScrollPane sp)
+ {
+ if (spPropertyChangeListener == null)
+ spPropertyChangeListener = createPropertyChangeListener();
+ sp.addPropertyChangeListener(spPropertyChangeListener);
+
+ if (hsbChangeListener == null)
+ hsbChangeListener = createHSBChangeListener();
+ sp.getHorizontalScrollBar().getModel().addChangeListener(hsbChangeListener);
+
+ if (vsbChangeListener == null)
+ vsbChangeListener = createVSBChangeListener();
+ sp.getVerticalScrollBar().getModel().addChangeListener(vsbChangeListener);
+
+ if (viewportChangeListener == null)
+ viewportChangeListener = createViewportChangeListener();
+
+ if (mouseWheelListener == null)
+ mouseWheelListener = createMouseWheelListener();
+
+ if (containerListener == null)
+ containerListener = new ViewportContainerListener();
+
+ JViewport v = sp.getViewport();
+ v.addChangeListener(viewportChangeListener);
+ v.addContainerListener(containerListener);
+
+ // Add mouse wheel listeners to the componets that are probably already
+ // in the view port.
+ for (int i = 0; i < v.getComponentCount(); i++)
+ v.getComponent(i).addMouseWheelListener(mouseWheelListener);
+ }
+
+ InputMap getInputMap(int condition)
+ {
+ if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+ return (InputMap) UIManager.get("ScrollPane.ancestorInputMap");
+ return null;
+ }
+
+ /**
+ * Returns the action map for the {@link JScrollPane}. All scroll panes
+ * share a single action map which is created the first time this method is
+ * called, then stored in the UIDefaults table for subsequent access.
+ *
+ * @return The shared action map.
+ */
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("ScrollPane.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("ScrollPane.actionMap", map);
+ }
+ return map;
+ }
+
+ /**
+ * Creates the action map shared by all {@link JSlider} instances.
+ * This method is called once by {@link #getActionMap()} when it
+ * finds no action map in the UIDefaults table...after the map is
+ * created, it gets added to the defaults table so that subsequent
+ * calls to {@link #getActionMap()} will return the same shared
+ * instance.
+ *
+ * @return The action map.
+ */
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+ map.put("scrollLeft",
+ new AbstractAction("scrollLeft") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getHorizontalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("scrollEnd",
+ new AbstractAction("scrollEnd") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb1 = sp.getHorizontalScrollBar();
+ if (sb1.isVisible())
+ {
+ sb1.setValue(sb1.getMaximum());
+ }
+ JScrollBar sb2 = sp.getVerticalScrollBar();
+ if (sb2.isVisible())
+ {
+ sb2.setValue(sb2.getMaximum());
+ }
+ }
+ }
+ );
+ map.put("unitScrollUp",
+ new AbstractAction("unitScrollUp") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getVerticalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("unitScrollLeft",
+ new AbstractAction("unitScrollLeft") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getHorizontalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("scrollUp",
+ new AbstractAction("scrollUp") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getVerticalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(-1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("scrollRight",
+ new AbstractAction("scrollRight") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getHorizontalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("scrollHome",
+ new AbstractAction("scrollHome") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb1 = sp.getHorizontalScrollBar();
+ if (sb1.isVisible())
+ {
+ sb1.setValue(sb1.getMinimum());
+ }
+ JScrollBar sb2 = sp.getVerticalScrollBar();
+ if (sb2.isVisible())
+ {
+ sb2.setValue(sb2.getMinimum());
+ }
+ }
+ }
+ );
+ map.put("scrollDown",
+ new AbstractAction("scrollDown") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getVerticalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getBlockIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("unitScrollDown",
+ new AbstractAction("unitScrollDown") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getVerticalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ map.put("unitScrollRight",
+ new AbstractAction("unitScrollRight") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JScrollPane sp = (JScrollPane) event.getSource();
+ JScrollBar sb = sp.getHorizontalScrollBar();
+ if (sb.isVisible())
+ {
+ int delta = sb.getUnitIncrement(1);
+ sb.setValue(sb.getValue() + delta);
+ }
+ }
+ }
+ );
+ return map;
+ }
+
+ /**
+ * Installs additional keyboard actions on the scrollpane. This is a hook
+ * method provided to subclasses in order to install their own keyboard
+ * actions.
+ *
+ * @param sp the scrollpane to install keyboard actions on
+ */
+ protected void installKeyboardActions(JScrollPane sp)
+ {
+ InputMap keyMap = getInputMap(
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ SwingUtilities.replaceUIInputMap(sp,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(sp, map);
+ }
+
+ /**
+ * Uninstalls all keyboard actions from the JScrollPane that have been
+ * installed by {@link #installKeyboardActions}. This is a hook method
+ * provided to subclasses to add their own keyboard actions.
+ *
+ * @param sp the scrollpane to uninstall keyboard actions from
+ */
+ protected void uninstallKeyboardActions(JScrollPane sp)
+ {
+ SwingUtilities.replaceUIActionMap(sp, null);
+ SwingUtilities.replaceUIInputMap(sp,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ }
+
+ /**
+ * Creates and returns the change listener for the horizontal scrollbar.
+ *
+ * @return the change listener for the horizontal scrollbar
+ */
+ protected ChangeListener createHSBChangeListener()
+ {
+ return new HSBChangeListener();
+ }
+
+ /**
+ * Creates and returns the change listener for the vertical scrollbar.
+ *
+ * @return the change listener for the vertical scrollbar
+ */
+ protected ChangeListener createVSBChangeListener()
+ {
+ return new VSBChangeListener();
+ }
+
+ /**
+ * Creates and returns the change listener for the viewport.
+ *
+ * @return the change listener for the viewport
+ */
+ protected ChangeListener createViewportChangeListener()
+ {
+ return new ViewportChangeHandler();
+ }
+
+ /**
+ * Creates and returns the property change listener for the scrollpane.
+ *
+ * @return the property change listener for the scrollpane
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates and returns the mouse wheel listener for the scrollpane.
+ *
+ * @return the mouse wheel listener for the scrollpane
+ *
+ * @since 1.4
+ */
+ protected MouseWheelListener createMouseWheelListener()
+ {
+ return new MouseWheelHandler();
+ }
+
+ public void uninstallUI(final JComponent c)
+ {
+ uninstallDefaults((JScrollPane) c);
+ uninstallListeners(c);
+ installKeyboardActions((JScrollPane) c);
+ }
+
+ /**
+ * Uninstalls all the listeners that have been installed in
+ * {@link #installListeners(JScrollPane)}.
+ *
+ * @param c the scrollpane from which to uninstall the listeners
+ */
+ protected void uninstallListeners(JComponent c)
+ {
+ JScrollPane sp = (JScrollPane) c;
+ sp.removePropertyChangeListener(spPropertyChangeListener);
+ sp.getHorizontalScrollBar().getModel()
+ .removeChangeListener(hsbChangeListener);
+ sp.getVerticalScrollBar().getModel()
+ .removeChangeListener(vsbChangeListener);
+
+ JViewport v = sp.getViewport();
+ v.removeChangeListener(viewportChangeListener);
+ v.removeContainerListener(containerListener);
+
+ for (int i = 0; i < v.getComponentCount(); i++)
+ v.getComponent(i).removeMouseWheelListener(mouseWheelListener);
+
+ }
+
+ public Dimension getMinimumSize(JComponent c)
+ {
+ JScrollPane p = (JScrollPane) c;
+ ScrollPaneLayout sl = (ScrollPaneLayout) p.getLayout();
+ return sl.minimumLayoutSize(c);
+ }
+
+ public void paint(Graphics g, JComponent c)
+ {
+ Border vpBorder = scrollpane.getViewportBorder();
+ if (vpBorder != null)
+ {
+ Rectangle r = scrollpane.getViewportBorderBounds();
+ vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
+ }
+ }
+
+ /**
+ * Synchronizes the scrollbar and header settings positions and extent
+ * with the viewport's view position and extent.
+ */
+ protected void syncScrollPaneWithViewport()
+ {
+ JViewport vp = scrollpane.getViewport();
+
+ if (vp != null)
+ {
+ Dimension extentSize = vp.getExtentSize();
+ Point viewPos = vp.getViewPosition();
+ Dimension viewSize = vp.getViewSize();
+
+ // Update the vertical scrollbar.
+ JScrollBar vsb = scrollpane.getVerticalScrollBar();
+ if (vsb != null)
+ {
+ int extent = extentSize.height;
+ int max = viewSize.height;
+ int val = Math.max(0, Math.min(viewPos.y, max - extent));
+ vsb.setValues(val, extent, 0, max);
+ }
+
+ // Update the horizontal scrollbar.
+ JScrollBar hsb = scrollpane.getHorizontalScrollBar();
+ if (hsb != null)
+ {
+ int extent = extentSize.width;
+ int max = viewSize.width;
+ int val = Math.max(0, Math.min(viewPos.x, max - extent));
+ hsb.setValues(val, extent, 0, max);
+ }
+
+ // Update the row header.
+ JViewport rowHeader = scrollpane.getRowHeader();
+ if (rowHeader != null)
+ {
+ Point p = new Point(0, viewPos.y);
+ rowHeader.setViewPosition(p);
+ }
+
+ // Update the column header.
+ JViewport colHeader = scrollpane.getColumnHeader();
+ if (colHeader != null)
+ {
+ Point p = new Point(viewPos.x, 0);
+ colHeader.setViewPosition(p);
+ }
+ }
+ }
+
+ /**
+ * Receives notification when the <code>columnHeader</code> property has
+ * changed on the scrollpane.
+ *
+ * @param ev the property change event
+ */
+ protected void updateColumnHeader(PropertyChangeEvent ev)
+ {
+ // TODO: Find out what should be done here. Or is this only a hook?
+ }
+
+ /**
+ * Receives notification when the <code>rowHeader</code> property has changed
+ * on the scrollpane.
+ *
+ * @param ev the property change event
+ */
+ protected void updateRowHeader(PropertyChangeEvent ev)
+ {
+ // TODO: Find out what should be done here. Or is this only a hook?
+ }
+
+ /**
+ * Receives notification when the <code>scrollBarDisplayPolicy</code>
+ * property has changed on the scrollpane.
+ *
+ * @param ev the property change event
+ */
+ protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev)
+ {
+ scrollpane.revalidate();
+ scrollpane.repaint();
+ }
+
+ /**
+ * Receives notification when the <code>viewport</code> property has changed
+ * on the scrollpane.
+ *
+ * This method sets removes the viewportChangeListener from the old viewport
+ * and adds it to the new viewport.
+ *
+ * @param ev the property change event
+ */
+ protected void updateViewport(PropertyChangeEvent ev)
+ {
+ JViewport oldViewport = (JViewport) ev.getOldValue();
+ oldViewport.removeChangeListener(viewportChangeListener);
+ JViewport newViewport = (JViewport) ev.getNewValue();
+ newViewport.addChangeListener(viewportChangeListener);
+ syncScrollPaneWithViewport();
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java
new file mode 100644
index 000000000..2c80822a4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSeparatorUI.java
@@ -0,0 +1,252 @@
+/* BasicSeparatorUI.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.JComponent;
+import javax.swing.JSeparator;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SeparatorUI;
+
+/**
+ * The Basic Look and Feel UI delegate for JSeparator.
+ */
+public class BasicSeparatorUI extends SeparatorUI
+{
+ /** The shadow color. */
+ protected Color shadow;
+
+ /** The highlight color. */
+ protected Color highlight;
+
+ /**
+ * Creates a new UI delegate for the given JComponent.
+ *
+ * @param c The JComponent to create a delegate for.
+ *
+ * @return A new BasicSeparatorUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicSeparatorUI();
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ * This can include installing defaults, listeners, and
+ * initializing any instance data.
+ *
+ * @param c The JComponent that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ if (c instanceof JSeparator)
+ {
+ JSeparator s = (JSeparator) c;
+
+ installDefaults(s);
+ installListeners(s);
+ }
+ }
+
+ /**
+ * Uninstalls the UI for the given JComponent. This
+ * method reverses what was done when installing
+ * the UI on the JComponent.
+ *
+ * @param c The JComponent that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ if (c instanceof JSeparator)
+ {
+ JSeparator s = (JSeparator) c;
+
+ uninstallListeners(s);
+ uninstallDefaults(s);
+ }
+ }
+
+ /**
+ * This method installs the defaults that are given by
+ * the Basic Look and Feel.
+ *
+ * @param s The JSeparator that is being installed.
+ */
+ protected void installDefaults(JSeparator s)
+ {
+ shadow = UIManager.getColor("Separator.shadow");
+ highlight = UIManager.getColor("Separator.highlight");
+ s.setOpaque(false);
+ }
+
+ /**
+ * This method removes the defaults that were given
+ * by the Basic Look and Feel.
+ *
+ * @param s The JSeparator that is being uninstalled.
+ */
+ protected void uninstallDefaults(JSeparator s)
+ {
+ shadow = null;
+ highlight = null;
+ }
+
+ /**
+ * This method installs any listeners that need
+ * to be attached to the JSeparator or any of its
+ * components.
+ *
+ * @param s The JSeparator that is being installed.
+ */
+ protected void installListeners(JSeparator s)
+ {
+ // Separators don't receive events.
+ }
+
+ /**
+ * This method uninstalls any listeners that
+ * were installed during the install UI process.
+ *
+ * @param s The JSeparator that is being uninstalled.
+ */
+ protected void uninstallListeners(JSeparator s)
+ {
+ // Separators don't receive events.
+ }
+
+ /**
+ * The separator is made of two lines. The top line will be
+ * the shadow color (or left line if it's vertical). The bottom
+ * or right line will be the highlight color. The two lines will
+ * be centered inside the bounds box. If the separator is horizontal,
+ * then it will be vertically centered, or if it's vertical, it will
+ * be horizontally centered.
+ *
+ * @param g The Graphics object to paint with
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ Rectangle r = new Rectangle();
+ SwingUtilities.calculateInnerArea(c, r);
+ Color saved = g.getColor();
+
+ JSeparator s;
+ if (c instanceof JSeparator)
+ s = (JSeparator) c;
+ else
+ return;
+
+ if (s.getOrientation() == JSeparator.HORIZONTAL)
+ {
+ int midAB = r.height / 2;
+ g.setColor(shadow);
+ g.drawLine(r.x, r.y + midAB - 1, r.x + r.width, r.y + midAB - 1);
+
+ g.setColor(highlight);
+ g.fillRect(r.x, r.y + midAB, r.x + r.width, r.y + midAB);
+ }
+ else
+ {
+ int midAD = r.height / 2 + r.y;
+ g.setColor(shadow);
+ g.drawLine(r.x, r.y, r.x, r.y + r.height);
+
+ g.setColor(highlight);
+ g.fillRect(r.x + midAD, r.y + r.height, r.x + midAD, r.y + r.height);
+ }
+ g.setColor(saved);
+ }
+
+ /**
+ * This method returns the preferred size of the
+ * JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension pref = new Dimension(2, 0);
+ if (c instanceof JSeparator)
+ {
+ JSeparator s = (JSeparator) c;
+ if (s.getOrientation() == JSeparator.HORIZONTAL)
+ pref = new Dimension(0, 2);
+ }
+ return pref;
+ }
+
+ /**
+ * This method returns the minimum size of the
+ * JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return new Dimension(0, 0);
+ }
+
+ /**
+ * This method returns the maximum size of the
+ * JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(Short.MAX_VALUE,
+ Short.MAX_VALUE);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java
new file mode 100644
index 000000000..b9d564372
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSliderUI.java
@@ -0,0 +1,2339 @@
+/* BasicSliderUI.java --
+ Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Polygon;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.BoundedRangeModel;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JSlider;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MouseInputAdapter;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SliderUI;
+
+/**
+ * <p>
+ * BasicSliderUI.java This is the UI delegate in the Basic look and feel that
+ * paints JSliders.
+ * </p>
+ *
+ * <p>
+ * The UI delegate keeps track of 6 rectangles that place the various parts of
+ * the JSlider inside the component.
+ * </p>
+ *
+ * <p>
+ * The rectangles are organized as follows:
+ * </p>
+ * <pre>
+ * +-------------------------------------------------------+ <-- focusRect
+ * | |
+ * | +==+-------------------+==+--------------------+==+<------ contentRect
+ * | | | | |<---thumbRect | | |
+ * | | | TRACK | | |<--------- trackRect
+ * | | +-------------------+==+--------------------+ | |
+ * | | | | | |
+ * | | | TICKS GO HERE |<-------- tickRect
+ * | | | | | |
+ * | +==+-------------------------------------------+==+ |
+ * | | | | | |
+ * | | | | |<----- labelRect
+ * | | | LABELS GO HERE | | |
+ * | | | | | |
+ * | | | | | |
+ * | | | | | |
+ * | | | | | |
+ * | | | | |
+ * </pre>
+ *
+ * <p>
+ * The space between the contentRect and the focusRect are the FocusInsets.
+ * </p>
+ *
+ * <p>
+ * The space between the focusRect and the component bounds is the insetCache
+ * which are the component's insets.
+ * </p>
+ *
+ * <p>
+ * The top of the thumb is the top of the contentRect. The trackRect has to be
+ * as tall as the thumb.
+ * </p>
+ *
+ * <p>
+ * The trackRect and tickRect do not start from the left edge of the
+ * focusRect. They are trackBuffer away from each side of the focusRect. This
+ * is so that the thumb has room to move.
+ * </p>
+ *
+ * <p>
+ * The labelRect does start right against the contentRect's left and right
+ * edges and it gets all remaining space.
+ * </p>
+ */
+public class BasicSliderUI extends SliderUI
+{
+ /**
+ * Helper class that listens to the {@link JSlider}'s model for changes.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ChangeHandler implements ChangeListener
+ {
+ /**
+ * Called when the slider's model has been altered. The UI delegate should
+ * recalculate any rectangles that are dependent on the model for their
+ * positions and repaint.
+ *
+ * @param e A static {@link ChangeEvent} passed from the model.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Maximum, minimum, and extent values will be taken
+ // care of automatically when the slider is repainted.
+ // Only thing that needs recalculation is the thumb.
+ calculateThumbLocation();
+ slider.repaint();
+ }
+ }
+
+ /**
+ * Helper class that listens for resize events.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ComponentHandler extends ComponentAdapter
+ {
+ /**
+ * Called when the size of the component changes. The UI delegate should
+ * recalculate any rectangles that are dependent on the model for their
+ * positions and repaint.
+ *
+ * @param e A {@link ComponentEvent}.
+ */
+ public void componentResized(ComponentEvent e)
+ {
+ calculateGeometry();
+ slider.repaint();
+ }
+ }
+
+ /**
+ * Helper class that listens for focus events.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class FocusHandler implements FocusListener
+ {
+ /**
+ * Called when the {@link JSlider} has gained focus. It should repaint
+ * the slider with the focus drawn.
+ *
+ * @param e A {@link FocusEvent}.
+ */
+ public void focusGained(FocusEvent e)
+ {
+ slider.repaint();
+ }
+
+ /**
+ * Called when the {@link JSlider} has lost focus. It should repaint the
+ * slider without the focus drawn.
+ *
+ * @param e A {@link FocusEvent}.
+ */
+ public void focusLost(FocusEvent e)
+ {
+ slider.repaint();
+ }
+ }
+
+ /**
+ * Helper class that listens for changes to the properties of the {@link
+ * JSlider}.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Called when one of the properties change. The UI should recalculate any
+ * rectangles if necessary and repaint.
+ *
+ * @param e A {@link PropertyChangeEvent}.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // Check for orientation changes.
+ String prop = e.getPropertyName();
+ if (prop.equals("orientation")
+ || prop.equals("inverted")
+ || prop.equals("labelTable")
+ || prop.equals("majorTickSpacing")
+ || prop.equals("minorTickSpacing")
+ || prop.equals("paintTicks")
+ || prop.equals("paintTrack")
+ || prop.equals("paintLabels"))
+ {
+ calculateGeometry();
+ slider.repaint();
+ }
+ else if (e.getPropertyName().equals("model"))
+ {
+ BoundedRangeModel oldModel = (BoundedRangeModel) e.getOldValue();
+ oldModel.removeChangeListener(changeListener);
+ slider.getModel().addChangeListener(changeListener);
+ calculateThumbLocation();
+ slider.repaint();
+ }
+ }
+ }
+
+ /**
+ * Helper class that listens to our swing timer. This class is responsible
+ * for listening to the timer and moving the thumb in the proper direction
+ * every interval.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class ScrollListener implements ActionListener
+ {
+ /** Indicates which direction the thumb should scroll. */
+ private transient int direction;
+
+ /** Indicates whether we should scroll in blocks or in units. */
+ private transient boolean block;
+
+ /**
+ * Creates a new ScrollListener object.
+ */
+ public ScrollListener()
+ {
+ direction = POSITIVE_SCROLL;
+ block = false;
+ }
+
+ /**
+ * Creates a new ScrollListener object.
+ *
+ * @param dir The direction to scroll in.
+ * @param block If movement will be in blocks.
+ */
+ public ScrollListener(int dir, boolean block)
+ {
+ direction = dir;
+ this.block = block;
+ }
+
+ /**
+ * Called every time the swing timer reaches its interval. If the thumb
+ * needs to move, then this method will move the thumb one block or unit
+ * in the direction desired. Otherwise, the timer can be stopped.
+ *
+ * @param e An {@link ActionEvent}.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (! trackListener.shouldScroll(direction))
+ {
+ scrollTimer.stop();
+ return;
+ }
+
+ if (block)
+ scrollByBlock(direction);
+ else
+ scrollByUnit(direction);
+ }
+
+ /**
+ * Sets the direction to scroll in.
+ *
+ * @param direction The direction to scroll in.
+ */
+ public void setDirection(int direction)
+ {
+ this.direction = direction;
+ }
+
+ /**
+ * Sets whether movement will be in blocks.
+ *
+ * @param block If movement will be in blocks.
+ */
+ public void setScrollByBlock(boolean block)
+ {
+ this.block = block;
+ }
+ }
+
+ /**
+ * Helper class that listens for mouse events.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TrackListener extends MouseInputAdapter
+ {
+ /** The current X position of the mouse. */
+ protected int currentMouseX;
+
+ /** The current Y position of the mouse. */
+ protected int currentMouseY;
+
+ /**
+ * The offset between the current slider value and the cursor's position.
+ */
+ protected int offset;
+
+ /**
+ * Called when the mouse has been dragged. This should find the mouse's
+ * current position and adjust the value of the {@link JSlider}
+ * accordingly.
+ *
+ * @param e A {@link MouseEvent}
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ dragging = true;
+ if (slider.isEnabled())
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+ if (slider.getValueIsAdjusting())
+ {
+ int value;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ value = valueForXPosition(currentMouseX) - offset;
+ else
+ value = valueForYPosition(currentMouseY) - offset;
+
+ slider.setValue(value);
+ }
+ }
+ }
+
+ /**
+ * Called when the mouse has moved over a component but no buttons have
+ * been pressed yet.
+ *
+ * @param e A {@link MouseEvent}
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Don't care that we're moved unless we're dragging.
+ }
+
+ /**
+ * Called when the mouse is pressed. When the press occurs on the thumb
+ * itself, the {@link JSlider} should have its value set to where the
+ * mouse was pressed. If the press occurs on the track, then the thumb
+ * should move one block towards the direction of the mouse.
+ *
+ * @param e A {@link MouseEvent}
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (slider.isEnabled())
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+
+ int value;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ value = valueForXPosition(currentMouseX);
+ else
+ value = valueForYPosition(currentMouseY);
+
+ if (slider.getSnapToTicks())
+ value = findClosestTick(value);
+
+ // If the thumb is hit, then we don't need to set the timers to
+ // move it.
+ if (! thumbRect.contains(e.getPoint()))
+ {
+ // The mouse has hit some other part of the slider.
+ // The value moves no matter where in the slider you hit.
+ if (value > slider.getValue())
+ scrollDueToClickInTrack(POSITIVE_SCROLL);
+ else
+ scrollDueToClickInTrack(NEGATIVE_SCROLL);
+ }
+ else
+ {
+ slider.setValueIsAdjusting(true);
+ offset = value - slider.getValue();
+ }
+ }
+ }
+
+ /**
+ * Called when the mouse is released. This should stop the timer that
+ * scrolls the thumb.
+ *
+ * @param e A {@link MouseEvent}
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ dragging = false;
+ if (slider.isEnabled())
+ {
+ currentMouseX = e.getX();
+ currentMouseY = e.getY();
+
+ if (slider.getValueIsAdjusting())
+ {
+ slider.setValueIsAdjusting(false);
+ if (slider.getSnapToTicks())
+ slider.setValue(findClosestTick(slider.getValue()));
+ }
+ if (scrollTimer != null)
+ scrollTimer.stop();
+ }
+ slider.repaint();
+ }
+
+ /**
+ * Indicates whether the thumb should scroll in the given direction.
+ *
+ * @param direction The direction to check.
+ *
+ * @return True if the thumb should move in that direction.
+ */
+ public boolean shouldScroll(int direction)
+ {
+ int value;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ value = valueForXPosition(currentMouseX);
+ else
+ value = valueForYPosition(currentMouseY);
+
+ if (direction == POSITIVE_SCROLL)
+ return value > slider.getValue();
+ else
+ return value < slider.getValue();
+ }
+ }
+
+ /**
+ * This class is no longer used as of JDK1.3.
+ */
+ public class ActionScroller extends AbstractAction
+ {
+ /**
+ * Not used.
+ *
+ * @param slider not used
+ * @param dir not used
+ * @param block not used
+ */
+ public ActionScroller(JSlider slider, int dir, boolean block)
+ {
+ // Not used.
+ }
+
+ /**
+ * Not used.
+ *
+ * @param event not used
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // Not used.
+ }
+ }
+
+ /** Listener for changes from the model. */
+ protected ChangeListener changeListener;
+
+ /** Listener for changes to the {@link JSlider}. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** Listener for the scrollTimer. */
+ protected ScrollListener scrollListener;
+
+ /** Listener for component resizing. */
+ protected ComponentListener componentListener;
+
+ /** Listener for focus handling. */
+ protected FocusListener focusListener;
+
+ /** Listener for mouse events. */
+ protected TrackListener trackListener;
+
+ /** The insets between the FocusRectangle and the ContentRectangle. */
+ protected Insets focusInsets;
+
+ /** The {@link JSlider}'s insets. */
+ protected Insets insetCache;
+
+ /** Rectangle describing content bounds. See diagram above. */
+ protected Rectangle contentRect;
+
+ /** Rectangle describing focus bounds. See diagram above. */
+ protected Rectangle focusRect;
+
+ /** Rectangle describing the thumb's bounds. See diagram above. */
+ protected Rectangle thumbRect;
+
+ /** Rectangle describing the tick bounds. See diagram above. */
+ protected Rectangle tickRect;
+
+ /** Rectangle describing the label bounds. See diagram above. */
+ protected Rectangle labelRect;
+
+ /** Rectangle describing the track bounds. See diagram above. */
+ protected Rectangle trackRect;
+
+ /** FIXME: use this somewhere. */
+ public static final int MAX_SCROLL = 2;
+
+ /** FIXME: use this somewhere. */
+ public static final int MIN_SCROLL = -2;
+
+ /** A constant describing scrolling towards the minimum. */
+ public static final int NEGATIVE_SCROLL = -1;
+
+ /** A constant describing scrolling towards the maximum. */
+ public static final int POSITIVE_SCROLL = 1;
+
+ /** The gap between the edges of the contentRect and trackRect. */
+ protected int trackBuffer;
+
+ /** Whether this slider is actually drawn left to right. */
+ protected boolean leftToRightCache;
+
+ /** A timer that periodically moves the thumb. */
+ protected Timer scrollTimer;
+
+ /** A reference to the {@link JSlider} that this UI was created for. */
+ protected JSlider slider;
+
+ /** The shadow color. */
+ private transient Color shadowColor;
+
+ /** The highlight color. */
+ private transient Color highlightColor;
+
+ /** The focus color. */
+ private transient Color focusColor;
+
+ /** True if the user is dragging the slider. */
+ boolean dragging;
+
+ /**
+ * Creates a new Basic look and feel Slider UI.
+ *
+ * @param b The {@link JSlider} that this UI was created for.
+ */
+ public BasicSliderUI(JSlider b)
+ {
+ super();
+ }
+
+ /**
+ * Returns true if the user is dragging the slider.
+ *
+ * @return true if the slider is being dragged.
+ *
+ * @since 1.5
+ */
+ protected boolean isDragging()
+ {
+ return dragging;
+ }
+
+ /**
+ * Gets the shadow color to be used for this slider. The shadow color is the
+ * color used for drawing the top and left edges of the track.
+ *
+ * @return The shadow color.
+ */
+ protected Color getShadowColor()
+ {
+ return shadowColor;
+ }
+
+ /**
+ * Gets the highlight color to be used for this slider. The highlight color
+ * is the color used for drawing the bottom and right edges of the track.
+ *
+ * @return The highlight color.
+ */
+ protected Color getHighlightColor()
+ {
+ return highlightColor;
+ }
+
+ /**
+ * Gets the focus color to be used for this slider. The focus color is the
+ * color used for drawing the focus rectangle when the component gains
+ * focus.
+ *
+ * @return The focus color.
+ */
+ protected Color getFocusColor()
+ {
+ return focusColor;
+ }
+
+ /**
+ * Factory method to create a BasicSliderUI for the given {@link
+ * JComponent}, which should be a {@link JSlider}.
+ *
+ * @param b The {@link JComponent} a UI is being created for.
+ *
+ * @return A BasicSliderUI for the {@link JComponent}.
+ */
+ public static ComponentUI createUI(JComponent b)
+ {
+ return new BasicSliderUI((JSlider) b);
+ }
+
+ /**
+ * Installs and initializes all fields for this UI delegate. Any properties
+ * of the UI that need to be initialized and/or set to defaults will be
+ * done now. It will also install any listeners necessary.
+ *
+ * @param c The {@link JComponent} that is having this UI installed.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JSlider)
+ {
+ slider = (JSlider) c;
+
+ focusRect = new Rectangle();
+ contentRect = new Rectangle();
+ thumbRect = new Rectangle();
+ trackRect = new Rectangle();
+ tickRect = new Rectangle();
+ labelRect = new Rectangle();
+
+ insetCache = slider.getInsets();
+ leftToRightCache = ! slider.getInverted();
+
+ scrollTimer = new Timer(200, null);
+ scrollTimer.setRepeats(true);
+
+ installDefaults(slider);
+ installListeners(slider);
+ installKeyboardActions(slider);
+
+ calculateFocusRect();
+
+ calculateContentRect();
+ calculateThumbSize();
+ calculateTrackBuffer();
+ calculateTrackRect();
+ calculateThumbLocation();
+
+ calculateTickRect();
+ calculateLabelRect();
+ }
+ }
+
+ /**
+ * Performs the opposite of installUI. Any properties or resources that need
+ * to be cleaned up will be done now. It will also uninstall any listeners
+ * it has. In addition, any properties of this UI will be nulled.
+ *
+ * @param c The {@link JComponent} that is having this UI uninstalled.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+
+ uninstallKeyboardActions(slider);
+ uninstallListeners(slider);
+
+ scrollTimer = null;
+
+ focusRect = null;
+ contentRect = null;
+ thumbRect = null;
+ trackRect = null;
+ tickRect = null;
+ labelRect = null;
+
+ focusInsets = null;
+ }
+
+ /**
+ * Initializes any default properties that this UI has from the defaults for
+ * the Basic look and feel.
+ *
+ * @param slider The {@link JSlider} that is having this UI installed.
+ */
+ protected void installDefaults(JSlider slider)
+ {
+ LookAndFeel.installColors(slider, "Slider.background",
+ "Slider.foreground");
+ LookAndFeel.installBorder(slider, "Slider.border");
+ shadowColor = UIManager.getColor("Slider.shadow");
+ highlightColor = UIManager.getColor("Slider.highlight");
+ focusColor = UIManager.getColor("Slider.focus");
+ focusInsets = UIManager.getInsets("Slider.focusInsets");
+ slider.setOpaque(true);
+ }
+
+ /**
+ * Creates a new {@link TrackListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link TrackListener} is
+ * created for.
+ *
+ * @return A new {@link TrackListener}.
+ */
+ protected TrackListener createTrackListener(JSlider slider)
+ {
+ return new TrackListener();
+ }
+
+ /**
+ * Creates a new {@link ChangeListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link ChangeListener} is
+ * created for.
+ *
+ * @return A new {@link ChangeListener}.
+ */
+ protected ChangeListener createChangeListener(JSlider slider)
+ {
+ return new ChangeHandler();
+ }
+
+ /**
+ * Creates a new {@link ComponentListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link ComponentListener} is
+ * created for.
+ *
+ * @return A new {@link ComponentListener}.
+ */
+ protected ComponentListener createComponentListener(JSlider slider)
+ {
+ return new ComponentHandler();
+ }
+
+ /**
+ * Creates a new {@link FocusListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link FocusListener} is
+ * created for.
+ *
+ * @return A new {@link FocusListener}.
+ */
+ protected FocusListener createFocusListener(JSlider slider)
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * Creates a new {@link ScrollListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link ScrollListener} is
+ * created for.
+ *
+ * @return A new {@link ScrollListener}.
+ */
+ protected ScrollListener createScrollListener(JSlider slider)
+ {
+ return new ScrollListener();
+ }
+
+ /**
+ * Creates a new {@link PropertyChangeListener}.
+ *
+ * @param slider The {@link JSlider} that this {@link
+ * PropertyChangeListener} is created for.
+ *
+ * @return A new {@link PropertyChangeListener}.
+ */
+ protected PropertyChangeListener createPropertyChangeListener(JSlider slider)
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates and registers all the listeners for this UI delegate. This
+ * includes creating the ScrollListener and registering it to the timer.
+ *
+ * @param slider The {@link JSlider} is having listeners installed.
+ */
+ protected void installListeners(JSlider slider)
+ {
+ propertyChangeListener = createPropertyChangeListener(slider);
+ componentListener = createComponentListener(slider);
+ trackListener = createTrackListener(slider);
+ focusListener = createFocusListener(slider);
+ changeListener = createChangeListener(slider);
+ scrollListener = createScrollListener(slider);
+
+ slider.addPropertyChangeListener(propertyChangeListener);
+ slider.addComponentListener(componentListener);
+ slider.addMouseListener(trackListener);
+ slider.addMouseMotionListener(trackListener);
+ slider.addFocusListener(focusListener);
+ slider.getModel().addChangeListener(changeListener);
+
+ scrollTimer.addActionListener(scrollListener);
+ }
+
+ /**
+ * Unregisters all the listeners that this UI delegate was using. In
+ * addition, it will also null any listeners that it was using.
+ *
+ * @param slider The {@link JSlider} that is having listeners removed.
+ */
+ protected void uninstallListeners(JSlider slider)
+ {
+ slider.removePropertyChangeListener(propertyChangeListener);
+ slider.removeComponentListener(componentListener);
+ slider.removeMouseListener(trackListener);
+ slider.removeMouseMotionListener(trackListener);
+ slider.removeFocusListener(focusListener);
+ slider.getModel().removeChangeListener(changeListener);
+
+ scrollTimer.removeActionListener(scrollListener);
+
+ propertyChangeListener = null;
+ componentListener = null;
+ trackListener = null;
+ focusListener = null;
+ changeListener = null;
+ scrollListener = null;
+ }
+
+ /**
+ * Installs any keyboard actions. The list of keys that need to be bound are
+ * listed in Basic look and feel's defaults.
+ *
+ * @param slider The {@link JSlider} that is having keyboard actions
+ * installed.
+ */
+ protected void installKeyboardActions(JSlider slider)
+ {
+ InputMap keyMap = getInputMap(JComponent.WHEN_FOCUSED);
+ SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, keyMap);
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(slider, map);
+ }
+
+ /**
+ * Uninstalls any keyboard actions. The list of keys used are listed in
+ * Basic look and feel's defaults.
+ *
+ * @param slider The {@link JSlider} that is having keyboard actions
+ * uninstalled.
+ */
+ protected void uninstallKeyboardActions(JSlider slider)
+ {
+ SwingUtilities.replaceUIActionMap(slider, null);
+ SwingUtilities.replaceUIInputMap(slider, JComponent.WHEN_FOCUSED, null);
+ }
+
+ /* XXX: This is all after experimentation with SUN's implementation.
+
+ PreferredHorizontalSize seems to be 200x21.
+ PreferredVerticalSize seems to be 21x200.
+
+ MinimumHorizontalSize seems to be 36x21.
+ MinimumVerticalSize seems to be 21x36.
+
+ PreferredSize seems to be 200x63. Or Components.getBounds?
+
+ MinimumSize seems to be 36x63.
+
+ MaximumSize seems to be 32767x63.
+ */
+
+ /**
+ * This method returns the preferred size when the slider is horizontally
+ * oriented.
+ *
+ * @return The dimensions of the preferred horizontal size.
+ */
+ public Dimension getPreferredHorizontalSize()
+ {
+ Dimension dim = UIManager.getDimension("Slider.horizontalSize");
+ if (dim == null) // Just to be sure we mirror the default.
+ dim = new Dimension(200, 21);
+ return dim;
+ }
+
+ /**
+ * This method returns the preferred size when the slider is vertically
+ * oriented.
+ *
+ * @return The dimensions of the preferred vertical size.
+ */
+ public Dimension getPreferredVerticalSize()
+ {
+ Dimension dim = UIManager.getDimension("Slider.verticalSize");
+ if (dim == null) // Just to be sure we mirror the default.
+ dim = new Dimension(21, 200);
+ return dim;
+ }
+
+ /**
+ * This method returns the minimum size when the slider is horizontally
+ * oriented.
+ *
+ * @return The dimensions of the minimum horizontal size.
+ */
+ public Dimension getMinimumHorizontalSize()
+ {
+ Dimension dim = UIManager.getDimension("Slider.minimumHorizontalSize");
+ if (dim == null) // Just to be sure we mirror the default.
+ dim = new Dimension(36, 21);
+ return dim;
+ }
+
+ /**
+ * This method returns the minimum size of the slider when it is vertically
+ * oriented.
+ *
+ * @return The dimensions of the minimum vertical size.
+ */
+ public Dimension getMinimumVerticalSize()
+ {
+ Dimension dim = UIManager.getDimension("Slider.minimumVerticalSize");
+ if (dim == null) // Just to be sure we mirror the default.
+ dim = new Dimension(21, 36);
+ return dim;
+ }
+
+ /**
+ * This method returns the preferred size of the component. If it returns
+ * null, then it is up to the Layout Manager to give the {@link JComponent}
+ * a size.
+ *
+ * @param c The {@link JComponent} to find the preferred size for.
+ *
+ * @return The dimensions of the preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ recalculateIfInsetsChanged();
+ Dimension dim;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ // Create copy here to protect the UIManager value.
+ dim = new Dimension(getPreferredHorizontalSize());
+ dim.height = insetCache.top + insetCache.bottom;
+ dim.height += focusInsets.top + focusInsets.bottom;
+ dim.height += trackRect.height + tickRect.height + labelRect.height;
+ }
+ else
+ {
+ // Create copy here to protect the UIManager value.
+ dim = new Dimension(getPreferredVerticalSize());
+ dim.width = insetCache.left + insetCache.right;
+ dim.width += focusInsets.left + focusInsets.right;
+ dim.width += trackRect.width + tickRect.width + labelRect.width;
+ }
+ return dim;
+ }
+
+ /**
+ * This method returns the minimum size for this {@link JSlider} for this
+ * look and feel. If it returns null, then it is up to the Layout Manager
+ * to give the {@link JComponent} a size.
+ *
+ * @param c The {@link JComponent} to find the minimum size for.
+ *
+ * @return The dimensions of the minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ recalculateIfInsetsChanged();
+ Dimension dim;
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ // Create copy here to protect the UIManager value.
+ dim = new Dimension(getMinimumHorizontalSize());
+ dim.height = insetCache.top + insetCache.bottom;
+ dim.height += focusInsets.top + focusInsets.bottom;
+ dim.height += trackRect.height + tickRect.height + labelRect.height;
+ }
+ else
+ {
+ // Create copy here to protect the UIManager value.
+ dim = new Dimension(getMinimumVerticalSize());
+ dim.width = insetCache.left + insetCache.right;
+ dim.width += focusInsets.left + focusInsets.right;
+ dim.width += trackRect.width + tickRect.width + labelRect.width;
+ }
+ return dim;
+ }
+
+ /**
+ * This method returns the maximum size for this {@link JSlider} for this
+ * look and feel.
+ *
+ * @param c The {@link JComponent} to find a maximum size for.
+ *
+ * @return The dimensions of the maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ Dimension dim = getPreferredSize(c);
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ dim.width = Short.MAX_VALUE;
+ else
+ dim.height = Short.MAX_VALUE;
+ return dim;
+ }
+
+ /**
+ * This method calculates all the sizes of the rectangles by delegating to
+ * the helper methods calculateXXXRect.
+ */
+ protected void calculateGeometry()
+ {
+ calculateFocusRect();
+ calculateContentRect();
+ calculateThumbSize();
+ calculateTrackBuffer();
+ calculateTrackRect();
+ calculateTickRect();
+ calculateLabelRect();
+ calculateThumbLocation();
+ }
+
+ /**
+ * This method calculates the size and position of the focusRect. This
+ * method does not need to be called if the orientation changes.
+ */
+ protected void calculateFocusRect()
+ {
+ focusRect.x = insetCache.left;
+ focusRect.y = insetCache.top;
+ focusRect.width = slider.getWidth() - insetCache.left - insetCache.right;
+ focusRect.height = slider.getHeight() - insetCache.top - insetCache.bottom;
+ }
+
+ /**
+ * Sets the width and height of the <code>thumbRect</code> field, using the
+ * dimensions returned by {@link #getThumbSize()}.
+ */
+ protected void calculateThumbSize()
+ {
+ Dimension d = getThumbSize();
+ thumbRect.width = d.width;
+ thumbRect.height = d.height;
+ }
+
+ /**
+ * Updates the <code>contentRect</code> field to an area inside the
+ * <code>focusRect</code>. This method does not need to be called if the
+ * orientation changes.
+ */
+ protected void calculateContentRect()
+ {
+ contentRect.x = focusRect.x + focusInsets.left;
+ contentRect.y = focusRect.y + focusInsets.top;
+
+ contentRect.width = focusRect.width - focusInsets.left - focusInsets.right;
+ contentRect.height = focusRect.height - focusInsets.top
+ - focusInsets.bottom;
+ }
+
+ /**
+ * Calculates the position of the thumbRect based on the current value of
+ * the slider. It must take into account the orientation of the slider.
+ */
+ protected void calculateThumbLocation()
+ {
+ int value = slider.getValue();
+
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ thumbRect.x = xPositionForValue(value) - thumbRect.width / 2;
+ thumbRect.y = trackRect.y + 1;
+ }
+ else
+ {
+ thumbRect.x = trackRect.x + 1;
+ thumbRect.y = yPositionForValue(value) - thumbRect.height / 2;
+ }
+ }
+
+ /**
+ * Calculates the gap size between the edge of the <code>contentRect</code>
+ * and the edge of the <code>trackRect</code>, storing the result in the
+ * <code>trackBuffer</code> field. Sufficient space needs to be reserved
+ * for the slider thumb and/or the labels at each end of the slider track.
+ */
+ protected void calculateTrackBuffer()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ int w = Math.max(getWidthOfLowValueLabel(), getWidthOfHighValueLabel());
+ trackBuffer = Math.max(thumbRect.width / 2, w / 2);
+
+ }
+ else
+ {
+ int h = Math.max(getHeightOfLowValueLabel(),
+ getHeightOfHighValueLabel());
+ trackBuffer = Math.max(thumbRect.height / 2, h / 2);
+ }
+ }
+
+ /**
+ * Returns the size of the slider's thumb. The size is hard coded to
+ * <code>11 x 20</code> for horizontal sliders, and <code>20 x 11</code> for
+ * vertical sliders. Note that a new instance of {@link Dimension} is
+ * returned for every call to this method (this seems wasteful, but
+ * {@link Dimension} instances are not immutable, so this is probably
+ * unavoidable).
+ *
+ * @return The size of the slider's thumb.
+ */
+ protected Dimension getThumbSize()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ return new Dimension(11, 20);
+ else
+ return new Dimension(20, 11);
+ }
+
+ /**
+ * Calculates the size and position of the trackRect. It must take into
+ * account the orientation of the slider.
+ */
+ protected void calculateTrackRect()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ int center = thumbRect.height;
+ if (slider.getPaintTicks())
+ center += getTickLength();
+ if (slider.getPaintLabels())
+ center += getHeightOfTallestLabel();
+ trackRect.x = contentRect.x + trackBuffer;
+ trackRect.y = contentRect.y + (contentRect.height - center - 1) / 2;
+ trackRect.width = contentRect.width - 2 * trackBuffer;
+ trackRect.height = thumbRect.height;
+ }
+ else
+ {
+ int center = thumbRect.width;
+ if (slider.getPaintTicks())
+ center += getTickLength();
+ if (slider.getPaintLabels())
+ center += getWidthOfWidestLabel();
+ trackRect.x = contentRect.x + (contentRect.width - center - 1) / 2;
+ trackRect.y = contentRect.y + trackBuffer;
+ trackRect.width = thumbRect.width;
+ trackRect.height = contentRect.height - 2 * trackBuffer;
+ }
+ }
+
+ /**
+ * This method returns the height of the tick area box if the slider is
+ * horizontal and the width of the tick area box is the slider is vertical.
+ * It not necessarily how long the ticks will be. If a gap between the edge
+ * of tick box and the actual tick is desired, then that will need to be
+ * handled in the tick painting methods.
+ *
+ * @return The height (or width if the slider is vertical) of the tick
+ * rectangle.
+ */
+ protected int getTickLength()
+ {
+ return 8;
+ }
+
+ /**
+ * This method calculates the size and position of the tickRect. It must
+ * take into account the orientation of the slider.
+ */
+ protected void calculateTickRect()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ tickRect.x = trackRect.x;
+ tickRect.y = trackRect.y + trackRect.height;
+ tickRect.width = trackRect.width;
+ tickRect.height = getTickLength();
+
+ // this makes our Mauve tests pass...can't explain it!
+ if (!slider.getPaintTicks())
+ {
+ tickRect.y--;
+ tickRect.height = 0;
+ }
+ }
+ else
+ {
+ tickRect.x = trackRect.x + trackRect.width;
+ tickRect.y = trackRect.y;
+ tickRect.width = getTickLength();
+ tickRect.height = trackRect.height;
+
+ // this makes our Mauve tests pass...can't explain it!
+ if (!slider.getPaintTicks())
+ {
+ tickRect.x--;
+ tickRect.width = 0;
+ }
+ }
+ }
+
+ /**
+ * Calculates the <code>labelRect</code> field, taking into account the
+ * orientation of the slider.
+ */
+ protected void calculateLabelRect()
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ if (slider.getPaintLabels())
+ {
+ labelRect.x = tickRect.x - trackBuffer;
+ labelRect.y = tickRect.y + tickRect.height;
+ labelRect.width = tickRect.width + trackBuffer * 2;
+ labelRect.height = getHeightOfTallestLabel();
+ }
+ else
+ {
+ labelRect.x = tickRect.x;
+ labelRect.y = tickRect.y + tickRect.height;
+ labelRect.width = tickRect.width;
+ labelRect.height = 0;
+ }
+ }
+ else
+ {
+ if (slider.getPaintLabels())
+ {
+ labelRect.x = tickRect.x + tickRect.width;
+ labelRect.y = tickRect.y - trackBuffer;
+ labelRect.width = getWidthOfWidestLabel();
+ labelRect.height = tickRect.height + trackBuffer * 2;
+ }
+ else
+ {
+ labelRect.x = tickRect.x + tickRect.width;
+ labelRect.y = tickRect.y;
+ labelRect.width = 0;
+ labelRect.height = tickRect.height;
+ }
+ }
+ }
+
+ /**
+ * This method returns the width of the widest label in the slider's label
+ * table.
+ *
+ * @return The width of the widest label or 0 if no label table exists.
+ */
+ protected int getWidthOfWidestLabel()
+ {
+ int widest = 0;
+ Dictionary table = slider.getLabelTable();
+ if (table != null)
+ {
+ for (Enumeration list = slider.getLabelTable().elements();
+ list.hasMoreElements();)
+ {
+ Component label = (Component) list.nextElement();
+ widest = Math.max(label.getPreferredSize().width, widest);
+ }
+ }
+ return widest;
+ }
+
+ /**
+ * This method returns the height of the tallest label in the slider's label
+ * table.
+ *
+ * @return The height of the tallest label or 0 if no label table exists.
+ */
+ protected int getHeightOfTallestLabel()
+ {
+ int tallest = 0;
+ Component label;
+
+ if (slider.getLabelTable() == null)
+ return 0;
+ Dimension pref;
+ for (Enumeration list = slider.getLabelTable().elements();
+ list.hasMoreElements();)
+ {
+ Object comp = list.nextElement();
+ if (! (comp instanceof Component))
+ continue;
+ label = (Component) comp;
+ pref = label.getPreferredSize();
+ if (pref != null && pref.height > tallest)
+ tallest = pref.height;
+ }
+ return tallest;
+ }
+
+ /**
+ * Returns the width of the label whose key has the highest value, or 0 if
+ * there are no labels.
+ *
+ * @return The width of the label whose key has the highest value.
+ *
+ * @see #getHighestValueLabel()
+ */
+ protected int getWidthOfHighValueLabel()
+ {
+ Component highValueLabel = getHighestValueLabel();
+ if (highValueLabel != null)
+ return highValueLabel.getPreferredSize().width;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns the width of the label whose key has the lowest value, or 0 if
+ * there are no labels.
+ *
+ * @return The width of the label whose key has the lowest value.
+ *
+ * @see #getLowestValueLabel()
+ */
+ protected int getWidthOfLowValueLabel()
+ {
+ Component lowValueLabel = getLowestValueLabel();
+ if (lowValueLabel != null)
+ return lowValueLabel.getPreferredSize().width;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns the height of the label whose key has the highest value, or 0 if
+ * there are no labels.
+ *
+ * @return The height of the high value label or 0 if no label table exists.
+ */
+ protected int getHeightOfHighValueLabel()
+ {
+ Component highValueLabel = getHighestValueLabel();
+ if (highValueLabel != null)
+ return highValueLabel.getPreferredSize().height;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns the height of the label whose key has the lowest value, or 0 if
+ * there are no labels.
+ *
+ * @return The height of the low value label or 0 if no label table exists.
+ */
+ protected int getHeightOfLowValueLabel()
+ {
+ Component lowValueLabel = getLowestValueLabel();
+ if (lowValueLabel != null)
+ return lowValueLabel.getPreferredSize().height;
+ else
+ return 0;
+ }
+
+ /**
+ * Returns <code>true</code> if the slider scale is to be drawn inverted,
+ * and <code>false</code> if not.
+ *
+ * @return <code>true</code> if the slider is to be drawn inverted.
+ */
+ protected boolean drawInverted()
+ {
+ return slider.getInverted();
+ }
+
+ /**
+ * This method returns the label whose key has the lowest value.
+ *
+ * @return The low value label or null if no label table exists.
+ */
+ protected Component getLowestValueLabel()
+ {
+ Integer key = new Integer(Integer.MAX_VALUE);
+ Integer tmpKey;
+ Dictionary labelTable = slider.getLabelTable();
+
+ if (labelTable == null)
+ return null;
+
+ for (Enumeration list = labelTable.keys(); list.hasMoreElements();)
+ {
+ Object value = list.nextElement();
+ if (! (value instanceof Integer))
+ continue;
+ tmpKey = (Integer) value;
+ if (tmpKey.intValue() < key.intValue())
+ key = tmpKey;
+ }
+ Object comp = labelTable.get(key);
+ if (! (comp instanceof Component))
+ return null;
+ return (Component) comp;
+ }
+
+ /**
+ * Returns the label whose key has the highest value.
+ *
+ * @return The label whose key has the highest value or <code>null</code> if
+ * no label table exists.
+ */
+ protected Component getHighestValueLabel()
+ {
+ Integer key = new Integer(Integer.MIN_VALUE);
+ Integer tmpKey;
+ Dictionary labelTable = slider.getLabelTable();
+
+ if (labelTable == null)
+ return null;
+
+ for (Enumeration list = labelTable.keys(); list.hasMoreElements();)
+ {
+ Object value = list.nextElement();
+ if (! (value instanceof Integer))
+ continue;
+ tmpKey = (Integer) value;
+ if (tmpKey.intValue() > key.intValue())
+ key = tmpKey;
+ }
+ Object comp = labelTable.get(key);
+ if (! (comp instanceof Component))
+ return null;
+ return (Component) comp;
+ }
+
+ /**
+ * This method is used to paint the {@link JSlider}. It delegates all its
+ * duties to the various paint methods like paintTicks(), paintTrack(),
+ * paintThumb(), etc.
+ *
+ * @param g The {@link Graphics} object to paint with.
+ * @param c The {@link JComponent} that is being painted.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ recalculateIfInsetsChanged();
+ recalculateIfOrientationChanged();
+ if (slider.getPaintTrack() && hitClip(g, trackRect))
+ paintTrack(g);
+ if (slider.getPaintTicks() && hitClip(g, tickRect))
+ paintTicks(g);
+ if (slider.getPaintLabels() && hitClip(g, labelRect))
+ paintLabels(g);
+ if (slider.hasFocus() && hitClip(g, focusRect))
+ paintFocus(g);
+ if (hitClip(g, thumbRect))
+ paintThumb(g);
+ }
+
+ /**
+ * This method recalculates any rectangles that need to be recalculated
+ * after the insets of the component have changed.
+ */
+ protected void recalculateIfInsetsChanged()
+ {
+ Insets insets = slider.getInsets();
+ if (! insets.equals(insetCache))
+ {
+ insetCache = insets;
+ calculateGeometry();
+ }
+ }
+
+ /**
+ * This method recalculates any rectangles that need to be recalculated
+ * after the orientation of the slider changes.
+ */
+ protected void recalculateIfOrientationChanged()
+ {
+ // Examining a test program shows that either Sun calls private
+ // methods that we don't know about, or these don't do anything.
+ calculateThumbSize();
+ calculateTrackBuffer();
+ calculateTrackRect();
+ calculateThumbLocation();
+
+ calculateTickRect();
+ calculateLabelRect();
+ }
+
+ /**
+ * This method is called during a repaint if the slider has focus. It draws
+ * an outline of the focusRect using the color returned by
+ * getFocusColor().
+ *
+ * @param g The {@link Graphics} object to draw with.
+ */
+ public void paintFocus(Graphics g)
+ {
+ Color saved_color = g.getColor();
+
+ g.setColor(getFocusColor());
+
+ g.drawRect(focusRect.x, focusRect.y, focusRect.width, focusRect.height);
+
+ g.setColor(saved_color);
+ }
+
+ /**
+ * <p>
+ * This method is called during a repaint if the track is to be drawn. It
+ * draws a 3D rectangle to represent the track. The track is not the size
+ * of the trackRect. The top and left edges of the track should be outlined
+ * with the shadow color. The bottom and right edges should be outlined
+ * with the highlight color.
+ * </p>
+ * <pre>
+ * a---d
+ * | |
+ * | | a------------------------d
+ * | | | |
+ * | | b------------------------c
+ * | |
+ * | |
+ * b---c
+ * </pre>
+ *
+ * <p>
+ * The b-a-d path needs to be drawn with the shadow color and the b-c-d path
+ * needs to be drawn with the highlight color.
+ * </p>
+ *
+ * @param g The {@link Graphics} object to draw with.
+ */
+ public void paintTrack(Graphics g)
+ {
+ Color saved_color = g.getColor();
+ int width;
+ int height;
+
+ Point a = new Point(trackRect.x, trackRect.y + 1);
+ Point b = new Point(a);
+ Point c = new Point(a);
+ Point d = new Point(a);
+
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ width = trackRect.width;
+ height = (thumbRect.height / 4 == 0) ? 1 : thumbRect.height / 4;
+
+ a.translate(0, (trackRect.height / 2) - (height / 2));
+ b.translate(0, (trackRect.height / 2) + (height / 2));
+ c.translate(trackRect.width, (trackRect.height / 2) + (height / 2));
+ d.translate(trackRect.width, (trackRect.height / 2) - (height / 2));
+ }
+ else
+ {
+ width = (thumbRect.width / 4 == 0) ? 1 : thumbRect.width / 4;
+ height = trackRect.height;
+
+ a.translate((trackRect.width / 2) - (width / 2), 0);
+ b.translate((trackRect.width / 2) - (width / 2), trackRect.height);
+ c.translate((trackRect.width / 2) + (width / 2), trackRect.height);
+ d.translate((trackRect.width / 2) + (width / 2), 0);
+ }
+ g.setColor(Color.GRAY);
+ g.fillRect(a.x, a.y, width, height);
+
+ g.setColor(getHighlightColor());
+ g.drawLine(b.x, b.y, c.x, c.y);
+ g.drawLine(c.x, c.y, d.x, d.y);
+
+ g.setColor(getShadowColor());
+ g.drawLine(b.x, b.y, a.x, a.y);
+ g.drawLine(a.x, a.y, d.x, d.y);
+
+ g.setColor(saved_color);
+ }
+
+ /**
+ * This method is called during a repaint if the ticks are to be drawn. This
+ * method must still verify that the majorTickSpacing and minorTickSpacing
+ * are greater than zero before drawing the ticks.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ */
+ public void paintTicks(Graphics g)
+ {
+ int max = slider.getMaximum();
+ int min = slider.getMinimum();
+ int majorSpace = slider.getMajorTickSpacing();
+ int minorSpace = slider.getMinorTickSpacing();
+
+ if (majorSpace > 0)
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ g.translate(0, tickRect.y);
+ for (int i = min; i <= max; i += majorSpace)
+ paintMajorTickForHorizSlider(g, tickRect, xPositionForValue(i));
+ g.translate(0, -tickRect.y);
+ }
+ else // JSlider.VERTICAL
+ {
+ g.translate(tickRect.x, 0);
+ for (int i = min; i <= max; i += majorSpace)
+ paintMajorTickForVertSlider(g, tickRect, yPositionForValue(i));
+ g.translate(-tickRect.x, 0);
+ }
+ }
+ if (minorSpace > 0)
+ {
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ g.translate(0, tickRect.y);
+ for (int i = min; i <= max; i += minorSpace)
+ paintMinorTickForHorizSlider(g, tickRect, xPositionForValue(i));
+ g.translate(0, -tickRect.y);
+ }
+ else
+ {
+ g.translate(tickRect.x, 0);
+ for (int i = min; i <= max; i += minorSpace)
+ paintMinorTickForVertSlider(g, tickRect, yPositionForValue(i));
+ g.translate(-tickRect.x, 0);
+ }
+ }
+ }
+
+ /* Minor ticks start at 1/4 of the height (or width) of the tickRect and
+ extend to 1/2 of the tickRect.
+
+ Major ticks start at 1/4 of the height and extend to 3/4.
+ */
+
+ /**
+ * This method paints a minor tick for a horizontal slider at the given x
+ * value. x represents the x coordinate to paint at.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param tickBounds The tickRect rectangle.
+ * @param x The x coordinate to draw the tick at.
+ */
+ protected void paintMinorTickForHorizSlider(Graphics g,
+ Rectangle tickBounds, int x)
+ {
+ int y = tickRect.height / 4;
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+
+ g.drawLine(x, y, x, y + tickRect.height / 4);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints a major tick for a horizontal slider at the given x
+ * value. x represents the x coordinate to paint at.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param tickBounds The tickRect rectangle.
+ * @param x The x coordinate to draw the tick at.
+ */
+ protected void paintMajorTickForHorizSlider(Graphics g,
+ Rectangle tickBounds, int x)
+ {
+ int y = tickRect.height / 4;
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+
+ g.drawLine(x, y, x, y + tickRect.height / 2);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints a minor tick for a vertical slider at the given y
+ * value. y represents the y coordinate to paint at.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param tickBounds The tickRect rectangle.
+ * @param y The y coordinate to draw the tick at.
+ */
+ protected void paintMinorTickForVertSlider(Graphics g, Rectangle tickBounds,
+ int y)
+ {
+ int x = tickRect.width / 4;
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+
+ g.drawLine(x, y, x + tickRect.width / 4, y);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints a major tick for a vertical slider at the given y
+ * value. y represents the y coordinate to paint at.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param tickBounds The tickRect rectangle.
+ * @param y The y coordinate to draw the tick at.
+ */
+ protected void paintMajorTickForVertSlider(Graphics g, Rectangle tickBounds,
+ int y)
+ {
+ int x = tickRect.width / 4;
+ Color saved = g.getColor();
+ g.setColor(Color.BLACK);
+
+ g.drawLine(x, y, x + tickRect.width / 2, y);
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints all the labels from the slider's label table. This
+ * method must make sure that the label table is not null before painting
+ * the labels. Each entry in the label table is a (integer, component)
+ * pair. Every label is painted at the value of the integer.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ */
+ public void paintLabels(Graphics g)
+ {
+ Dictionary table = slider.getLabelTable();
+ if (table != null)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ for (Enumeration list = table.keys(); list.hasMoreElements();)
+ {
+ Integer key = (Integer) list.nextElement();
+ int value = key.intValue();
+ if (value >= min && value <= max)
+ {
+ Component label = (Component) table.get(key);
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ g.translate(0, labelRect.y);
+ paintHorizontalLabel(g, value, label);
+ g.translate(0, -labelRect.y);
+ }
+ else
+ {
+ g.translate(labelRect.x, 0);
+ paintVerticalLabel(g, value, label);
+ g.translate(-labelRect.x, 0);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This method paints the label on the horizontal slider at the value
+ * specified. The value is not a coordinate. It is a value within the range
+ * of the slider. If the value is not within the range of the slider, this
+ * method will do nothing. This method should not paint outside the
+ * boundaries of the labelRect.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param value The value to paint at.
+ * @param label The label to paint.
+ */
+ protected void paintHorizontalLabel(Graphics g, int value, Component label)
+ {
+ int center = xPositionForValue(value);
+ int left = center - label.getPreferredSize().width / 2;
+ g.translate(left, 0);
+ label.paint(g);
+ g.translate(-left, 0);
+ }
+
+ /**
+ * This method paints the label on the vertical slider at the value
+ * specified. The value is not a coordinate. It is a value within the range
+ * of the slider. If the value is not within the range of the slider, this
+ * method will do nothing. This method should not paint outside the
+ * boundaries of the labelRect.
+ *
+ * @param g The {@link Graphics} object to draw with.
+ * @param value The value to paint at.
+ * @param label The label to paint.
+ */
+ protected void paintVerticalLabel(Graphics g, int value, Component label)
+ {
+ int center = yPositionForValue(value);
+ int top = center - label.getPreferredSize().height / 2;
+ g.translate(0, top);
+ label.paint(g);
+ g.translate(0, -top);
+ }
+
+ /**
+ * <p>
+ * This method paints a thumb. There are two types of thumb:
+ * </p>
+ * <pre>
+ * Vertical Horizontal
+ * a---b a-----b
+ * | | | \
+ * e c | c
+ * \ / | /
+ * d e-----d
+ * </pre>
+ *
+ * <p>
+ * In the case of vertical thumbs, we highlight the path b-a-e-d and shadow
+ * the path b-c-d. In the case of horizontal thumbs, we highlight the path
+ * c-b-a-e and shadow the path c-d-e. In both cases we fill the path
+ * a-b-c-d-e before shadows and highlights are drawn.
+ * </p>
+ *
+ * @param g The graphics object to paint with
+ */
+ public void paintThumb(Graphics g)
+ {
+ Color saved_color = g.getColor();
+
+ Point a = new Point(thumbRect.x, thumbRect.y);
+ Point b = new Point(a);
+ Point c = new Point(a);
+ Point d = new Point(a);
+ Point e = new Point(a);
+
+ Polygon bright;
+ Polygon light; // light shadow
+ Polygon dark; // dark shadow
+ Polygon all;
+
+ // This will be in X-dimension if the slider is inverted and y if it isn't.
+ int turnPoint;
+
+ if (slider.getOrientation() == JSlider.HORIZONTAL)
+ {
+ turnPoint = thumbRect.height * 3 / 4;
+
+ b.translate(thumbRect.width - 1, 0);
+ c.translate(thumbRect.width - 1, turnPoint);
+ d.translate(thumbRect.width / 2 - 1, thumbRect.height - 1);
+ e.translate(0, turnPoint);
+
+ bright = new Polygon(new int[] { b.x - 1, a.x, e.x, d.x },
+ new int[] { b.y, a.y, e.y, d.y }, 4);
+
+ dark = new Polygon(new int[] { b.x, c.x, d.x + 1 }, new int[] { b.y,
+ c.y - 1,
+ d.y }, 3);
+
+ light = new Polygon(new int[] { b.x - 1, c.x - 1, d.x + 1 },
+ new int[] { b.y + 1, c.y - 1, d.y - 1 }, 3);
+
+ all = new Polygon(
+ new int[] { a.x + 1, b.x - 2, c.x - 2, d.x, e.x + 1 },
+ new int[] { a.y + 1, b.y + 1, c.y - 1, d.y - 1, e.y },
+ 5);
+ }
+ else
+ {
+ turnPoint = thumbRect.width * 3 / 4 - 1;
+
+ b.translate(turnPoint, 0);
+ c.translate(thumbRect.width - 1, thumbRect.height / 2);
+ d.translate(turnPoint, thumbRect.height - 1);
+ e.translate(0, thumbRect.height - 1);
+
+ bright = new Polygon(new int[] { c.x - 1, b.x, a.x, e.x },
+ new int[] { c.y - 1, b.y, a.y, e.y - 1 }, 4);
+
+ dark = new Polygon(new int[] { c.x, d.x, e.x }, new int[] { c.y, d.y,
+ e.y }, 3);
+
+ light = new Polygon(new int[] { c.x - 1, d.x, e.x + 1 },
+ new int[] { c.y, d.y - 1, e.y - 1 }, 3);
+ all = new Polygon(new int[] { a.x + 1, b.x, c.x - 2, c.x - 2, d.x,
+ e.x + 1 }, new int[] { a.y + 1, b.y + 1,
+ c.y - 1, c.y,
+ d.y - 2, e.y - 2 },
+ 6);
+ }
+
+ g.setColor(Color.WHITE);
+ g.drawPolyline(bright.xpoints, bright.ypoints, bright.npoints);
+
+ g.setColor(Color.BLACK);
+ g.drawPolyline(dark.xpoints, dark.ypoints, dark.npoints);
+
+ g.setColor(Color.GRAY);
+ g.drawPolyline(light.xpoints, light.ypoints, light.npoints);
+
+ g.setColor(Color.LIGHT_GRAY);
+ g.drawPolyline(all.xpoints, all.ypoints, all.npoints);
+ g.fillPolygon(all);
+
+ g.setColor(saved_color);
+ }
+
+ /**
+ * This method sets the position of the thumbRect.
+ *
+ * @param x The new x position.
+ * @param y The new y position.
+ */
+ public void setThumbLocation(int x, int y)
+ {
+ Rectangle union = new Rectangle(thumbRect);
+ thumbRect.setLocation(x, y);
+ SwingUtilities.computeUnion(thumbRect.x, thumbRect.y, thumbRect.width,
+ thumbRect.height, union);
+ slider.repaint(union);
+ }
+
+ /**
+ * Moves the thumb one block in the direction specified (a block is 1/10th
+ * of the slider range). If the slider snaps to ticks, this method is
+ * responsible for snapping it to a tick after the thumb has been moved.
+ *
+ * @param direction the direction (positive values increment the thumb
+ * position by one block, zero/negative values decrement the thumb position
+ * by one block).
+ */
+ public void scrollByBlock(int direction)
+ {
+ int unit = (slider.getMaximum() - slider.getMinimum()) / 10;
+ int moveTo = slider.getValue();
+ if (direction > 0)
+ moveTo += unit;
+ else
+ moveTo -= unit;
+
+ if (slider.getSnapToTicks())
+ moveTo = findClosestTick(moveTo);
+
+ slider.setValue(moveTo);
+ }
+
+ /**
+ * Moves the thumb one unit in the specified direction. If the slider snaps
+ * to ticks, this method is responsible for snapping it to a tick after the
+ * thumb has been moved.
+ *
+ * @param direction the direction (positive values increment the thumb
+ * position by one, zero/negative values decrement the thumb position by
+ * one).
+ */
+ public void scrollByUnit(int direction)
+ {
+ int moveTo = slider.getValue();
+ if (direction > 0)
+ moveTo++;
+ else
+ moveTo--;
+
+ if (slider.getSnapToTicks())
+ moveTo = findClosestTick(moveTo);
+
+ slider.setValue(moveTo);
+ }
+
+ /**
+ * This method is called when there has been a click in the track and the
+ * thumb needs to be scrolled on regular intervals. This method is only
+ * responsible for starting the timer and not for stopping it.
+ *
+ * @param dir The direction to move in.
+ */
+ protected void scrollDueToClickInTrack(int dir)
+ {
+ scrollTimer.stop();
+
+ scrollListener.setDirection(dir);
+ scrollListener.setScrollByBlock(true);
+
+ scrollTimer.start();
+ }
+
+ /**
+ * Returns the x-coordinate (relative to the component) for the given slider
+ * value. This method assumes that the <code>trackRect</code> field is
+ * set up.
+ *
+ * @param value the slider value.
+ *
+ * @return The x-coordinate.
+ */
+ protected int xPositionForValue(int value)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int len = trackRect.width;
+ double range = max - min;
+ double pixPerVal = len / range;
+ int left = trackRect.x;
+ int right = left + trackRect.width - 1;
+ int xpos;
+ if (! drawInverted())
+ xpos = left + (int) Math.round(pixPerVal * ((double) value - min));
+ else
+ xpos = right - (int) Math.round(pixPerVal * ((double) value - min));
+ xpos = Math.max(left, xpos);
+ xpos = Math.min(right, xpos);
+ return xpos;
+ }
+
+ /**
+ * Returns the y-coordinate (relative to the component) for the given slider
+ * value. This method assumes that the <code>trackRect</code> field is
+ * set up.
+ *
+ * @param value the slider value.
+ *
+ * @return The y-coordinate.
+ */
+ protected int yPositionForValue(int value)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int len = trackRect.height;
+ double range = max - min;
+ double pixPerVal = len / range;
+ int top = trackRect.y;
+ int bottom = top + trackRect.height - 1;
+ int ypos;
+ if (! drawInverted())
+ ypos = top + (int) Math.round(pixPerVal * ((double) max - value));
+ else
+ ypos = top + (int) Math.round(pixPerVal * ((double) value - min));
+ ypos = Math.max(top, ypos);
+ ypos = Math.min(bottom, ypos);
+ return ypos;
+ }
+
+ /**
+ * This method returns the value in the slider's range given the y
+ * coordinate. If the value is out of range, it will return the closest
+ * legal value.
+ *
+ * @param yPos The y coordinate to calculate a value for.
+ *
+ * @return The value for the y coordinate.
+ */
+ public int valueForYPosition(int yPos)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int len = trackRect.height;
+
+ int value;
+
+ // If the length is 0, you shouldn't be able to even see where the slider
+ // is. This really shouldn't ever happen, but just in case, we'll return
+ // the middle.
+ if (len == 0)
+ return (max - min) / 2;
+
+ if (! drawInverted())
+ value = (len - (yPos - trackRect.y)) * (max - min) / len + min;
+ else
+ value = (yPos - trackRect.y) * (max - min) / len + min;
+
+ // If this isn't a legal value, then we'll have to move to one now.
+ if (value > max)
+ value = max;
+ else if (value < min)
+ value = min;
+ return value;
+ }
+
+ /**
+ * This method returns the value in the slider's range given the x
+ * coordinate. If the value is out of range, it will return the closest
+ * legal value.
+ *
+ * @param xPos The x coordinate to calculate a value for.
+ *
+ * @return The value for the x coordinate.
+ */
+ public int valueForXPosition(int xPos)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int len = trackRect.width;
+
+ int value;
+
+ // If the length is 0, you shouldn't be able to even see where the slider
+ // is. This really shouldn't ever happen, but just in case, we'll return
+ // the middle.
+ if (len == 0)
+ return (max - min) / 2;
+
+ if (! drawInverted())
+ value = (xPos - trackRect.x) * (max - min) / len + min;
+ else
+ value = (len - (xPos - trackRect.x)) * (max - min) / len + min;
+
+ // If this isn't a legal value, then we'll have to move to one now.
+ if (value > max)
+ value = max;
+ else if (value < min)
+ value = min;
+ return value;
+ }
+
+ /**
+ * This method finds the closest value that has a tick associated with it.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param value The value to search from.
+ *
+ * @return The closest value that has a tick associated with it.
+ */
+ int findClosestTick(int value)
+ {
+ int min = slider.getMinimum();
+ int max = slider.getMaximum();
+ int majorSpace = slider.getMajorTickSpacing();
+ int minorSpace = slider.getMinorTickSpacing();
+
+ // The default value to return is value + minor or
+ // value + major.
+ // Initializing at min - value leaves us with a default
+ // return value of min, which always has tick marks
+ // (if ticks are painted).
+ int minor = min - value;
+ int major = min - value;
+
+ // If there are no major tick marks or minor tick marks
+ // e.g. snap is set to true but no ticks are set, then
+ // we can just return the value.
+ if (majorSpace <= 0 && minorSpace <= 0)
+ return value;
+
+ // First check the major ticks.
+ if (majorSpace > 0)
+ {
+ int lowerBound = (value - min) / majorSpace;
+ int majLower = majorSpace * lowerBound + min;
+ int majHigher = majorSpace * (lowerBound + 1) + min;
+
+ if (majHigher <= max && majHigher - value <= value - majLower)
+ major = majHigher - value;
+ else
+ major = majLower - value;
+ }
+
+ if (minorSpace > 0)
+ {
+ int lowerBound = value / minorSpace;
+ int minLower = minorSpace * lowerBound;
+ int minHigher = minorSpace * (lowerBound + 1);
+
+ if (minHigher <= max && minHigher - value <= value - minLower)
+ minor = minHigher - value;
+ else
+ minor = minLower - value;
+ }
+
+ // Give preference to minor ticks
+ if (Math.abs(minor) > Math.abs(major))
+ return value + major;
+ else
+ return value + minor;
+ }
+
+ InputMap getInputMap(int condition)
+ {
+ if (condition == JComponent.WHEN_FOCUSED)
+ return (InputMap) UIManager.get("Slider.focusInputMap");
+ return null;
+ }
+
+ /**
+ * Returns the action map for the {@link JSlider}. All sliders share
+ * a single action map which is created the first time this method is
+ * called, then stored in the UIDefaults table for subsequent access.
+ *
+ * @return The shared action map.
+ */
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("Slider.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("Slider.actionMap", map);
+ }
+ return map;
+ }
+
+ /**
+ * Creates the action map shared by all {@link JSlider} instances.
+ * This method is called once by {@link #getActionMap()} when it
+ * finds no action map in the UIDefaults table...after the map is
+ * created, it gets added to the defaults table so that subsequent
+ * calls to {@link #getActionMap()} will return the same shared
+ * instance.
+ *
+ * @return The action map.
+ */
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+ map.put("positiveUnitIncrement",
+ new AbstractAction("positiveUnitIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ BasicSliderUI ui = (BasicSliderUI) slider.getUI();
+ if (slider.getInverted())
+ ui.scrollByUnit(BasicSliderUI.NEGATIVE_SCROLL);
+ else
+ ui.scrollByUnit(BasicSliderUI.POSITIVE_SCROLL);
+ }
+ }
+ );
+ map.put("negativeUnitIncrement",
+ new AbstractAction("negativeUnitIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ BasicSliderUI ui = (BasicSliderUI) slider.getUI();
+ if (slider.getInverted())
+ ui.scrollByUnit(BasicSliderUI.POSITIVE_SCROLL);
+ else
+ ui.scrollByUnit(BasicSliderUI.NEGATIVE_SCROLL);
+ }
+ }
+ );
+ map.put("positiveBlockIncrement",
+ new AbstractAction("positiveBlockIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ BasicSliderUI ui = (BasicSliderUI) slider.getUI();
+ if (slider.getInverted())
+ ui.scrollByBlock(BasicSliderUI.NEGATIVE_SCROLL);
+ else
+ ui.scrollByBlock(BasicSliderUI.POSITIVE_SCROLL);
+ }
+ }
+ );
+ map.put("negativeBlockIncrement",
+ new AbstractAction("negativeBlockIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ BasicSliderUI ui = (BasicSliderUI) slider.getUI();
+ if (slider.getInverted())
+ ui.scrollByBlock(BasicSliderUI.POSITIVE_SCROLL);
+ else
+ ui.scrollByBlock(BasicSliderUI.NEGATIVE_SCROLL);
+ }
+ }
+ );
+ map.put("minScroll",
+ new AbstractAction("minScroll") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ if (slider.getInverted())
+ slider.setValue(slider.getMaximum());
+ else
+ slider.setValue(slider.getMinimum());
+ }
+ }
+ );
+ map.put("maxScroll",
+ new AbstractAction("maxScroll") {
+ public void actionPerformed(ActionEvent event)
+ {
+ JSlider slider = (JSlider) event.getSource();
+ if (slider.getInverted())
+ slider.setValue(slider.getMinimum());
+ else
+ slider.setValue(slider.getMaximum());
+ }
+ }
+ );
+ return map;
+ }
+
+ /**
+ * Small utility method to save me from typing the hell out of myself in
+ * paint().
+ */
+ private boolean hitClip(Graphics g, Rectangle r)
+ {
+ return g.hitClip(r.x, r.y, r.width, r.height);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java
new file mode 100644
index 000000000..00c8537cc
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSpinnerUI.java
@@ -0,0 +1,561 @@
+/* BasicSpinnerUI.java --
+ Copyright (C) 2003, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+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.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JSpinner;
+import javax.swing.LookAndFeel;
+import javax.swing.Timer;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SpinnerUI;
+
+/**
+ * A UI delegate for the {@link JSpinner} component.
+ *
+ * @author Ka-Hing Cheung
+ *
+ * @since 1.4
+ */
+public class BasicSpinnerUI extends SpinnerUI
+{
+ /**
+ * Creates a new <code>BasicSpinnerUI</code> for the specified
+ * <code>JComponent</code>
+ *
+ * @param c the component (ignored).
+ *
+ * @return A new instance of {@link BasicSpinnerUI}.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicSpinnerUI();
+ }
+
+ /**
+ * Creates an editor component. Really, it just returns
+ * <code>JSpinner.getEditor()</code>
+ *
+ * @return a JComponent as an editor
+ *
+ * @see javax.swing.JSpinner#getEditor
+ */
+ protected JComponent createEditor()
+ {
+ return spinner.getEditor();
+ }
+
+ /**
+ * Creates a <code>LayoutManager</code> that layouts the sub components. The
+ * subcomponents are identifies by the constraint "Next", "Previous" and
+ * "Editor"
+ *
+ * @return a LayoutManager
+ *
+ * @see java.awt.LayoutManager
+ */
+ protected LayoutManager createLayout()
+ {
+ return new DefaultLayoutManager();
+ }
+
+ /**
+ * Creates the "Next" button
+ *
+ * @return the next button component
+ */
+ protected Component createNextButton()
+ {
+ JButton button = new BasicArrowButton(BasicArrowButton.NORTH);
+ return button;
+ }
+
+ /**
+ * Creates the "Previous" button
+ *
+ * @return the previous button component
+ */
+ protected Component createPreviousButton()
+ {
+ JButton button = new BasicArrowButton(BasicArrowButton.SOUTH);
+ return button;
+ }
+
+ /**
+ * Creates the <code>PropertyChangeListener</code> that will be attached by
+ * <code>installListeners</code>. It should watch for the "editor"
+ * property, when it's changed, replace the old editor with the new one,
+ * probably by calling <code>replaceEditor</code>
+ *
+ * @return a PropertyChangeListener
+ *
+ * @see #replaceEditor
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ // FIXME: Add check for enabled property change. Need to
+ // disable the buttons.
+ if ("editor".equals(event.getPropertyName()))
+ BasicSpinnerUI.this.replaceEditor((JComponent) event.getOldValue(),
+ (JComponent) event.getNewValue());
+ // FIXME: Handle 'font' property change
+ }
+ };
+ }
+
+ /**
+ * Called by <code>installUI</code>. This should set various defaults
+ * obtained from <code>UIManager.getLookAndFeelDefaults</code>, as well as
+ * set the layout obtained from <code>createLayout</code>
+ *
+ * @see javax.swing.UIManager#getLookAndFeelDefaults
+ * @see #createLayout
+ * @see #installUI
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(spinner, "Spinner.background",
+ "Spinner.foreground", "Spinner.font");
+ LookAndFeel.installBorder(spinner, "Spinner.border");
+ JComponent e = spinner.getEditor();
+ if (e instanceof JSpinner.DefaultEditor)
+ {
+ JSpinner.DefaultEditor de = (JSpinner.DefaultEditor) e;
+ de.getTextField().setBorder(null);
+ }
+ spinner.setLayout(createLayout());
+ spinner.setOpaque(true);
+ }
+
+ /*
+ * Called by <code>installUI</code>, which basically adds the
+ * <code>PropertyChangeListener</code> created by
+ * <code>createPropertyChangeListener</code>
+ *
+ * @see #createPropertyChangeListener
+ * @see #installUI
+ */
+ protected void installListeners()
+ {
+ spinner.addPropertyChangeListener(listener);
+ }
+
+ /*
+ * Install listeners to the next button so that it increments the model
+ */
+ protected void installNextButtonListeners(Component c)
+ {
+ c.addMouseListener(new MouseAdapter()
+ {
+ public void mousePressed(MouseEvent evt)
+ {
+ if (! spinner.isEnabled())
+ return;
+ increment();
+ timer.setInitialDelay(500);
+ timer.start();
+ }
+
+ public void mouseReleased(MouseEvent evt)
+ {
+ timer.stop();
+ }
+
+ void increment()
+ {
+ Object next = BasicSpinnerUI.this.spinner.getNextValue();
+ if (next != null)
+ BasicSpinnerUI.this.spinner.getModel().setValue(next);
+ }
+
+ volatile boolean mouseDown;
+ Timer timer = new Timer(50,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ increment();
+ }
+ });
+ });
+ }
+
+ /*
+ * Install listeners to the previous button so that it decrements the model
+ */
+ protected void installPreviousButtonListeners(Component c)
+ {
+ c.addMouseListener(new MouseAdapter()
+ {
+ public void mousePressed(MouseEvent evt)
+ {
+ if (! spinner.isEnabled())
+ return;
+ decrement();
+ timer.setInitialDelay(500);
+ timer.start();
+ }
+
+ public void mouseReleased(MouseEvent evt)
+ {
+ timer.stop();
+ }
+
+ void decrement()
+ {
+ Object prev = BasicSpinnerUI.this.spinner.getPreviousValue();
+ if (prev != null)
+ BasicSpinnerUI.this.spinner.getModel().setValue(prev);
+ }
+
+ volatile boolean mouseDown;
+ Timer timer = new Timer(50,
+ new ActionListener()
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ decrement();
+ }
+ });
+ });
+ }
+
+ /**
+ * Install this UI to the <code>JComponent</code>, which in reality, is a
+ * <code>JSpinner</code>. Calls <code>installDefaults</code>,
+ * <code>installListeners</code>, and also adds the buttons and editor.
+ *
+ * @param c DOCUMENT ME!
+ *
+ * @see #installDefaults
+ * @see #installListeners
+ * @see #createNextButton
+ * @see #createPreviousButton
+ * @see #createEditor
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ spinner = (JSpinner) c;
+
+ installDefaults();
+ installListeners();
+
+ Component next = createNextButton();
+ Component previous = createPreviousButton();
+
+ installNextButtonListeners(next);
+ installPreviousButtonListeners(previous);
+
+ c.add(createEditor(), "Editor");
+ c.add(next, "Next");
+ c.add(previous, "Previous");
+ }
+
+ /**
+ * Replace the old editor with the new one
+ *
+ * @param oldEditor the old editor
+ * @param newEditor the new one to replace with
+ */
+ protected void replaceEditor(JComponent oldEditor, JComponent newEditor)
+ {
+ spinner.remove(oldEditor);
+ spinner.add(newEditor);
+ }
+
+ /**
+ * The reverse of <code>installDefaults</code>. Called by
+ * <code>uninstallUI</code>
+ */
+ protected void uninstallDefaults()
+ {
+ spinner.setLayout(null);
+ }
+
+ /**
+ * The reverse of <code>installListeners</code>, called by
+ * <code>uninstallUI</code>
+ */
+ protected void uninstallListeners()
+ {
+ spinner.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Called when the current L&F is replaced with another one, should call
+ * <code>uninstallDefaults</code> and <code>uninstallListeners</code> as
+ * well as remove the next/previous buttons and the editor
+ *
+ * @param c DOCUMENT ME!
+ */
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+
+ uninstallDefaults();
+ uninstallListeners();
+ c.removeAll();
+ }
+
+ /** The spinner for this UI */
+ protected JSpinner spinner;
+
+ /** DOCUMENT ME! */
+ private PropertyChangeListener listener = createPropertyChangeListener();
+
+ /**
+ * A layout manager for the {@link JSpinner} component. The spinner has
+ * three subcomponents: an editor, a 'next' button and a 'previous' button.
+ */
+ private class DefaultLayoutManager implements LayoutManager
+ {
+ /**
+ * Layout the spinners inner parts.
+ *
+ * @param parent The parent container
+ */
+ public void layoutContainer(Container parent)
+ {
+ synchronized (parent.getTreeLock())
+ {
+ Insets i = parent.getInsets();
+ boolean l2r = parent.getComponentOrientation().isLeftToRight();
+ /*
+ -------------- --------------
+ | | n | | n | |
+ | e | - | or | - | e |
+ | | p | | p | |
+ -------------- --------------
+ */
+ Dimension e = prefSize(editor);
+ Dimension n = prefSize(next);
+ Dimension p = prefSize(previous);
+ Dimension s = parent.getSize();
+
+ int x = l2r ? i.left : i.right;
+ int y = i.top;
+ int w = Math.max(p.width, n.width);
+ int h = (s.height - i.bottom) / 2;
+ int e_width = s.width - w - i.left - i.right;
+
+ if (l2r)
+ {
+ setBounds(editor, x, y, e_width, 2 * h);
+ x += e_width;
+ setBounds(next, x, y, w, h);
+ y += h;
+ setBounds(previous, x, y, w, h);
+ }
+ else
+ {
+ setBounds(next, x, y + (s.height - e.height) / 2, w, h);
+ y += h;
+ setBounds(previous, x, y + (s.height - e.height) / 2, w, h);
+ x += w;
+ y -= h;
+ setBounds(editor, x, y, e_width, e.height);
+ }
+ }
+ }
+
+ /**
+ * Calculates the minimum layout size.
+ *
+ * @param parent the parent.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ Dimension d = new Dimension();
+
+ if (editor != null)
+ {
+ Dimension tmp = editor.getMinimumSize();
+ d.width += tmp.width;
+ d.height = tmp.height;
+ }
+
+ int nextWidth = 0;
+ int previousWidth = 0;
+
+ if (next != null)
+ {
+ Dimension tmp = next.getMinimumSize();
+ nextWidth = tmp.width;
+ }
+ if (previous != null)
+ {
+ Dimension tmp = previous.getMinimumSize();
+ previousWidth = tmp.width;
+ }
+
+ d.width += Math.max(nextWidth, previousWidth);
+
+ return d;
+ }
+
+ /**
+ * Returns the preferred layout size of the container.
+ *
+ * @param parent DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ Dimension d = new Dimension();
+
+ if (editor != null)
+ {
+ Dimension tmp = editor.getPreferredSize();
+ d.width += Math.max(tmp.width, 40);
+ d.height = tmp.height;
+ }
+
+ int nextWidth = 0;
+ int previousWidth = 0;
+
+ if (next != null)
+ {
+ Dimension tmp = next.getPreferredSize();
+ nextWidth = tmp.width;
+ }
+ if (previous != null)
+ {
+ Dimension tmp = previous.getPreferredSize();
+ previousWidth = tmp.width;
+ }
+
+ d.width += Math.max(nextWidth, previousWidth);
+ Insets insets = parent.getInsets();
+ d.width = d.width + insets.left + insets.right;
+ d.height = d.height + insets.top + insets.bottom;
+ return d;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param child DOCUMENT ME!
+ */
+ public void removeLayoutComponent(Component child)
+ {
+ if (child == editor)
+ editor = null;
+ else if (child == next)
+ next = null;
+ else if (previous == child)
+ previous = null;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param name DOCUMENT ME!
+ * @param child DOCUMENT ME!
+ */
+ public void addLayoutComponent(String name, Component child)
+ {
+ if ("Editor".equals(name))
+ editor = child;
+ else if ("Next".equals(name))
+ next = child;
+ else if ("Previous".equals(name))
+ previous = child;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param c DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ private Dimension prefSize(Component c)
+ {
+ if (c == null)
+ return new Dimension();
+ else
+ return c.getPreferredSize();
+ }
+
+ /**
+ * Sets the bounds for the specified component.
+ *
+ * @param c the component.
+ * @param x the x-coordinate for the top-left of the component bounds.
+ * @param y the y-coordinate for the top-left of the component bounds.
+ * @param w the width of the bounds.
+ * @param h the height of the bounds.
+ */
+ private void setBounds(Component c, int x, int y, int w, int h)
+ {
+ if (c != null)
+ c.setBounds(x, y, w, h);
+ }
+
+ /** The editor component. */
+ private Component editor;
+
+ /** The next button. */
+ private Component next;
+
+ /** The previous button. */
+ private Component previous;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java
new file mode 100644
index 000000000..53f7db6e7
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneDivider.java
@@ -0,0 +1,1077 @@
+/* BasicSplitPaneDivider.java --
+ Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.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;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JSplitPane;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+
+/**
+ * The divider that separates the two parts of a JSplitPane in the Basic look
+ * and feel.
+ *
+ * <p>
+ * Implementation status: We do not have a real implementation yet. Currently,
+ * it is mostly a stub to allow compiling other parts of the
+ * javax.swing.plaf.basic package, although some parts are already
+ * functional.
+ * </p>
+ *
+ * @author Sascha Brawer (brawer_AT_dandelis.ch)
+ */
+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.
+ */
+ static final long serialVersionUID = 1463404307042803342L;
+
+ /**
+ * The width and height of the little buttons for showing and hiding parts
+ * of a JSplitPane in a single mouse click.
+ */
+ protected static final int ONE_TOUCH_SIZE = 6;
+
+ /** The distance the one touch buttons will sit from the divider's edges. */
+ protected static final int ONE_TOUCH_OFFSET = 2;
+
+ /**
+ * An object that performs the tasks associated with an ongoing drag
+ * operation, or <code>null</code> if the user is currently not dragging
+ * the divider.
+ */
+ protected DragController dragger;
+
+ /**
+ * The delegate object that is responsible for the UI of the
+ * <code>JSplitPane</code> that contains this divider.
+ */
+ protected BasicSplitPaneUI splitPaneUI;
+
+ /** The thickness of the divider in pixels. */
+ protected int dividerSize;
+
+ /** A divider that is used for layout purposes. */
+ protected Component hiddenDivider;
+
+ /** The JSplitPane containing this divider. */
+ protected JSplitPane splitPane;
+
+ /**
+ * The listener for handling mouse events from both the divider and the
+ * containing <code>JSplitPane</code>.
+ *
+ * <p>
+ * The reason for also handling MouseEvents from the containing
+ * <code>JSplitPane</code> is that users should be able to start a drag
+ * gesture from inside the JSplitPane, but slightly outisde the divider.
+ * </p>
+ */
+ protected MouseHandler mouseHandler = new MouseHandler();
+
+ /**
+ * The current orientation of the containing <code>JSplitPane</code>, which
+ * is either {@link javax.swing.JSplitPane#HORIZONTAL_SPLIT} or {@link
+ * javax.swing.JSplitPane#VERTICAL_SPLIT}.
+ */
+ protected int orientation;
+
+ /**
+ * The button for showing and hiding the left (or top) component of the
+ * <code>JSplitPane</code>.
+ */
+ protected JButton leftButton;
+
+ /**
+ * The button for showing and hiding the right (or bottom) component of the
+ * <code>JSplitPane</code>.
+ */
+ protected JButton rightButton;
+
+ /**
+ * The border of this divider. Typically, this will be an instance of {@link
+ * javax.swing.plaf.basic.BasicBorders.SplitPaneDividerBorder}.
+ *
+ * @see #getBorder()
+ * @see #setBorder(javax.swing.border.Border)
+ */
+ private Border border;
+
+ // This is not a pixel count.
+ // This int should be able to take 3 values.
+ // left (top), middle, right(bottom)
+ // 0 1 2
+
+ /**
+ * Keeps track of where the divider should be placed when using one touch
+ * expand buttons.
+ * This is package-private to avoid an accessor method.
+ */
+ 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>.
+ */
+ public BasicSplitPaneDivider(BasicSplitPaneUI ui)
+ {
+ setLayout(new DividerLayout());
+ setBasicSplitPaneUI(ui);
+ setDividerSize(splitPane.getDividerSize());
+ centerOneTouchButtons =
+ UIManager.getBoolean("SplitPane.centerOneTouchButtons");
+ }
+
+ /**
+ * Sets the delegate object that is responsible for the UI of the {@link
+ * javax.swing.JSplitPane} containing this divider.
+ *
+ * @param newUI the UI delegate, or <code>null</code> to release the
+ * connection to the current delegate.
+ */
+ public void setBasicSplitPaneUI(BasicSplitPaneUI newUI)
+ {
+ /* Remove the connection to the existing JSplitPane. */
+ if (splitPane != null)
+ {
+ splitPane.removePropertyChangeListener(this);
+ removeMouseListener(mouseHandler);
+ removeMouseMotionListener(mouseHandler);
+ splitPane = null;
+ hiddenDivider = null;
+ }
+
+ /* Establish the connection to the new JSplitPane. */
+ splitPaneUI = newUI;
+ if (splitPaneUI != null)
+ splitPane = newUI.getSplitPane();
+ if (splitPane != null)
+ {
+ splitPane.addPropertyChangeListener(this);
+ addMouseListener(mouseHandler);
+ addMouseMotionListener(mouseHandler);
+ hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider();
+ orientation = splitPane.getOrientation();
+ if (splitPane.isOneTouchExpandable())
+ oneTouchExpandableChanged();
+ }
+ }
+
+ /**
+ * Returns the delegate object that is responsible for the UI of the {@link
+ * javax.swing.JSplitPane} containing this divider.
+ *
+ * @return The UI for the JSplitPane.
+ */
+ public BasicSplitPaneUI getBasicSplitPaneUI()
+ {
+ return splitPaneUI;
+ }
+
+ /**
+ * Sets the thickness of the divider.
+ *
+ * @param newSize the new width or height in pixels.
+ */
+ public void setDividerSize(int newSize)
+ {
+ this.dividerSize = newSize;
+ }
+
+ /**
+ * Retrieves the thickness of the divider.
+ *
+ * @return The thickness of the divider.
+ */
+ public int getDividerSize()
+ {
+ return dividerSize;
+ }
+
+ /**
+ * Sets the border of this divider.
+ *
+ * @param border the new border. Typically, this will be an instance of
+ * {@link
+ * javax.swing.plaf.basic.BasicBorders.SplitPaneBorder}.
+ *
+ * @since 1.3
+ */
+ public void setBorder(Border border)
+ {
+ if (border != this.border)
+ {
+ Border oldValue = this.border;
+ this.border = border;
+ firePropertyChange("border", oldValue, border);
+ }
+ }
+
+ /**
+ * Retrieves the border of this divider.
+ *
+ * @return the current border, or <code>null</code> if no border has been
+ * set.
+ *
+ * @since 1.3
+ */
+ public Border getBorder()
+ {
+ return border;
+ }
+
+ /**
+ * Retrieves the insets of the divider. If a border has been installed on
+ * the divider, the result of calling its <code>getBorderInsets</code>
+ * method is returned. Otherwise, the inherited implementation will be
+ * invoked.
+ *
+ * @see javax.swing.border.Border#getBorderInsets(java.awt.Component)
+ */
+ public Insets getInsets()
+ {
+ if (border != null)
+ return border.getBorderInsets(this);
+ else
+ return super.getInsets();
+ }
+
+ /**
+ * Returns the preferred size of this divider, which is
+ * <code>dividerSize</code> by <code>dividerSize</code> pixels.
+ *
+ * @return The preferred size of the divider.
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension d;
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ d = new Dimension(getDividerSize(), 1);
+ else
+ d = new Dimension(1, getDividerSize());
+ return d;
+ }
+
+ /**
+ * Returns the minimal size of this divider, which is
+ * <code>dividerSize</code> by <code>dividerSize</code> pixels.
+ *
+ * @return The minimal size of the divider.
+ */
+ public Dimension getMinimumSize()
+ {
+ return getPreferredSize();
+ }
+
+ /**
+ * Processes events from the <code>JSplitPane</code> that contains this
+ * divider.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(JSplitPane.ONE_TOUCH_EXPANDABLE_PROPERTY))
+ oneTouchExpandableChanged();
+ else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
+ {
+ orientation = splitPane.getOrientation();
+ invalidate();
+ if (splitPane != null)
+ splitPane.revalidate();
+ }
+ else if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
+ dividerSize = splitPane.getDividerSize();
+ }
+
+ /**
+ * Paints the divider by painting its border.
+ *
+ * @param g The Graphics Object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ Dimension dividerSize;
+
+ super.paint(g);
+ if (border != null)
+ {
+ dividerSize = getSize();
+ border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height);
+ }
+ }
+
+ /**
+ * Reacts to changes of the <code>oneToughExpandable</code> property of the
+ * containing <code>JSplitPane</code>.
+ */
+ protected void oneTouchExpandableChanged()
+ {
+ if (splitPane.isOneTouchExpandable())
+ {
+ leftButton = createLeftOneTouchButton();
+ if (leftButton != null)
+ leftButton.addActionListener(new OneTouchAction());
+
+ rightButton = createRightOneTouchButton();
+ if (rightButton != null)
+ rightButton.addActionListener(new OneTouchAction());
+
+ // Only add them when both are non-null.
+ if (leftButton != null && rightButton != null)
+ {
+ add(leftButton);
+ add(rightButton);
+ }
+ }
+ invalidate();
+ if (splitPane != null)
+ splitPane.revalidate();
+ }
+
+ /**
+ * Creates a button for showing and hiding the left (or top) part of a
+ * <code>JSplitPane</code>.
+ *
+ * @return The left one touch button.
+ */
+ protected JButton createLeftOneTouchButton()
+ {
+ JButton button = new BasicOneTouchButton(BasicOneTouchButton.LEFT);
+ button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ button.setRequestFocusEnabled(false);
+ return button;
+ }
+
+ /**
+ * Creates a button for showing and hiding the right (or bottom) part of a
+ * <code>JSplitPane</code>.
+ *
+ * @return The right one touch button.
+ */
+ protected JButton createRightOneTouchButton()
+ {
+ JButton button = new BasicOneTouchButton(BasicOneTouchButton.RIGHT);
+ button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE));
+ button.setRequestFocusEnabled(false);
+ return button;
+ }
+
+ /**
+ * Prepares the divider for dragging by calling the
+ * <code>startDragging</code> method of the UI delegate of the enclosing
+ * <code>JSplitPane</code>.
+ *
+ * @see BasicSplitPaneUI#startDragging()
+ */
+ protected void prepareForDragging()
+ {
+ if (splitPaneUI != null)
+ splitPaneUI.startDragging();
+ }
+
+ /**
+ * Drags the divider to a given location by calling the
+ * <code>dragDividerTo</code> method of the UI delegate of the enclosing
+ * <code>JSplitPane</code>.
+ *
+ * @param location the new location of the divider.
+ *
+ * @see BasicSplitPaneUI#dragDividerTo(int location)
+ */
+ protected void dragDividerTo(int location)
+ {
+ if (splitPaneUI != null)
+ splitPaneUI.dragDividerTo(location);
+ }
+
+ /**
+ * Finishes a dragging gesture by calling the <code>finishDraggingTo</code>
+ * method of the UI delegate of the enclosing <code>JSplitPane</code>.
+ *
+ * @param location the new, final location of the divider.
+ *
+ * @see BasicSplitPaneUI#finishDraggingTo(int location)
+ */
+ protected void finishDraggingTo(int location)
+ {
+ if (splitPaneUI != null)
+ splitPaneUI.finishDraggingTo(location);
+ }
+
+ /**
+ * This helper method moves the divider to one of the three locations when
+ * using one touch expand buttons. Location 0 is the left (or top) most
+ * location. Location 1 is the middle. Location 2 is the right (or bottom)
+ * most location.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param locationIndex The location to move to.
+ */
+ void moveDividerTo(int locationIndex)
+ {
+ Insets insets = splitPane.getInsets();
+ switch (locationIndex)
+ {
+ case 1:
+ splitPane.setDividerLocation(splitPane.getLastDividerLocation());
+ break;
+ case 0:
+ int top = (orientation == JSplitPane.HORIZONTAL_SPLIT) ? insets.left
+ : insets.top;
+ splitPane.setDividerLocation(top);
+ break;
+ case 2:
+ int bottom;
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ bottom = splitPane.getBounds().width - insets.right - dividerSize;
+ else
+ bottom = splitPane.getBounds().height - insets.bottom - dividerSize;
+ splitPane.setDividerLocation(bottom);
+ break;
+ }
+ }
+
+ /**
+ * The listener for handling mouse events from both the divider and the
+ * containing <code>JSplitPane</code>.
+ *
+ * <p>
+ * The reason for also handling MouseEvents from the containing
+ * <code>JSplitPane</code> is that users should be able to start a drag
+ * gesture from inside the JSplitPane, but slightly outisde the divider.
+ * </p>
+ *
+ * @author Sascha Brawer (brawer_AT_dandelis.ch)
+ */
+ protected class MouseHandler extends MouseAdapter
+ implements MouseMotionListener
+ {
+ /** Keeps track of whether a drag is occurring. */
+ private transient boolean isDragging;
+
+ /**
+ * This method is called when the mouse is pressed.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ isDragging = true;
+ currentDividerLocation = 1;
+ if (orientation == JSplitPane.HORIZONTAL_SPLIT)
+ dragger = new DragController(e);
+ else
+ dragger = new VerticalDragController(e);
+ prepareForDragging();
+ }
+
+ /**
+ * This method is called when the mouse is released.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (isDragging)
+ dragger.completeDrag(e);
+ isDragging = false;
+ }
+
+ /**
+ * Repeatedly invoked when the user is dragging the mouse cursor while
+ * having pressed a mouse button.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ if (dragger != null)
+ dragger.continueDrag(e);
+ }
+
+ /**
+ * Repeatedly invoked when the user is dragging the mouse cursor without
+ * having pressed a mouse button.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * Performs the tasks associated with an ongoing drag operation.
+ *
+ * @author Sascha Brawer (brawer_AT_dandelis.ch)
+ */
+ protected class DragController
+ {
+ /**
+ * The difference between where the mouse is clicked and the initial
+ * divider location.
+ */
+ transient int offset;
+
+ /**
+ * Creates a new DragController object.
+ *
+ * @param e The MouseEvent to initialize with.
+ */
+ protected DragController(MouseEvent e)
+ {
+ offset = e.getX();
+ }
+
+ /**
+ * This method returns true if the divider can move.
+ *
+ * @return True if dragging is allowed.
+ */
+ protected boolean isValid()
+ {
+ // Views can always be resized?
+ return true;
+ }
+
+ /**
+ * Returns a position for the divider given the MouseEvent.
+ *
+ * @param e MouseEvent.
+ *
+ * @return The position for the divider to move to.
+ */
+ protected int positionForMouseEvent(MouseEvent e)
+ {
+ return e.getX() + getX() - offset;
+ }
+
+ /**
+ * This method returns one of the two paramters for the orientation. In
+ * this case, it returns x.
+ *
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ *
+ * @return The x coordinate.
+ */
+ protected int getNeededLocation(int x, int y)
+ {
+ return x;
+ }
+
+ /**
+ * This method is called to pass on the drag information to the UI through
+ * dragDividerTo.
+ *
+ * @param newX The x coordinate of the MouseEvent.
+ * @param newY The y coordinate of the MouseEvent.
+ */
+ protected void continueDrag(int newX, int newY)
+ {
+ if (isValid())
+ dragDividerTo(adjust(newX, newY));
+ }
+
+ /**
+ * This method is called to pass on the drag information to the UI
+ * through dragDividerTo.
+ *
+ * @param e The MouseEvent.
+ */
+ protected void continueDrag(MouseEvent e)
+ {
+ if (isValid())
+ dragDividerTo(positionForMouseEvent(e));
+ }
+
+ /**
+ * This method is called to finish the drag session by calling
+ * finishDraggingTo.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ */
+ protected void completeDrag(int x, int y)
+ {
+ finishDraggingTo(adjust(x, y));
+ }
+
+ /**
+ * This method is called to finish the drag session by calling
+ * finishDraggingTo.
+ *
+ * @param e The MouseEvent.
+ */
+ protected void completeDrag(MouseEvent e)
+ {
+ finishDraggingTo(positionForMouseEvent(e));
+ }
+
+ /**
+ * This is a helper method that includes the offset in the needed
+ * location.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ *
+ * @return The needed location adjusted by the offsets.
+ */
+ int adjust(int x, int y)
+ {
+ return getNeededLocation(x, y) + getX() - offset;
+ }
+ }
+
+ /**
+ * This is a helper class that controls dragging when the orientation is
+ * VERTICAL_SPLIT.
+ */
+ protected class VerticalDragController extends DragController
+ {
+ /**
+ * Creates a new VerticalDragController object.
+ *
+ * @param e The MouseEvent to initialize with.
+ */
+ protected VerticalDragController(MouseEvent e)
+ {
+ super(e);
+ offset = e.getY();
+ }
+
+ /**
+ * This method returns one of the two parameters given the orientation. In
+ * this case, it returns y.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ *
+ * @return The y coordinate.
+ */
+ protected int getNeededLocation(int x, int y)
+ {
+ return y;
+ }
+
+ /**
+ * This method returns the new location of the divider given a MouseEvent.
+ *
+ * @param e The MouseEvent.
+ *
+ * @return The new location of the divider.
+ */
+ protected int positionForMouseEvent(MouseEvent e)
+ {
+ return e.getY() + getY() - offset;
+ }
+
+ /**
+ * This is a helper method that includes the offset in the needed
+ * location.
+ *
+ * @param x The x coordinate of the MouseEvent.
+ * @param y The y coordinate of the MouseEvent.
+ *
+ * @return The needed location adjusted by the offsets.
+ */
+ int adjust(int x, int y)
+ {
+ return getNeededLocation(x, y) + getY() - offset;
+ }
+ }
+
+ /**
+ * This helper class acts as the Layout Manager for the divider.
+ */
+ protected class DividerLayout implements LayoutManager
+ {
+ /**
+ * Creates a new DividerLayout object.
+ */
+ protected DividerLayout()
+ {
+ // 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)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the container.
+ *
+ * @param c The container to lay out.
+ */
+ public void layoutContainer(Container c)
+ {
+ if (leftButton != null && rightButton != null
+ && c == BasicSplitPaneDivider.this)
+ {
+ 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);
+ }
+ }
+ }
+
+ /**
+ * 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)
+ {
+ return new Dimension(dividerSize, dividerSize);
+ }
+
+ /**
+ * This method is called when a component is removed.
+ *
+ * @param c The component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // Do nothing.
+ }
+
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java
new file mode 100644
index 000000000..ca9607515
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicSplitPaneUI.java
@@ -0,0 +1,1623 @@
+/* BasicSplitPaneUI.java --
+ Copyright (C) 2003, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Canvas;
+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.LayoutManager2;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+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;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.SplitPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * This is the Basic Look and Feel implementation of the SplitPaneUI class.
+ */
+public class BasicSplitPaneUI extends SplitPaneUI
+{
+ /**
+ * This Layout Manager controls the position and size of the components when
+ * the JSplitPane's orientation is HORIZONTAL_SPLIT.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class BasicHorizontalLayoutManager implements LayoutManager2
+ {
+ // 3 components at a time.
+ // LEFT/TOP = 0
+ // RIGHT/BOTTOM = 1
+ // DIVIDER = 2
+
+ /**
+ * This array contains the components in the JSplitPane. The left/top
+ * component is at index 0, the right/bottom is at 1, and the divider is
+ * at 2.
+ */
+ protected Component[] components = new Component[3];
+
+ // These are the _current_ widths of the associated component.
+
+ /**
+ * This array contains the current width (for HORIZONTAL_SPLIT) or height
+ * (for VERTICAL_SPLIT) of the components. The indices are the same as
+ * for components.
+ */
+ 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()
+ {
+ 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;
+ }
+
+ /**
+ * This method adds the component given to the JSplitPane. The position of
+ * the component is given by the constraints object.
+ *
+ * @param comp The Component to add.
+ * @param constraints The constraints that bind the object.
+ */
+ public void addLayoutComponent(Component comp, Object constraints)
+ {
+ addLayoutComponent((String) constraints, comp);
+ }
+
+ /**
+ * This method is called to add a Component to the JSplitPane. The
+ * placement string determines where the Component will be placed. The
+ * string should be one of LEFT, RIGHT, TOP, BOTTOM or null (signals that
+ * the component is the divider).
+ *
+ * @param place The placement of the Component.
+ * @param component The Component to add.
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
+ */
+ public void addLayoutComponent(String place, Component component)
+ {
+ int i = 0;
+ if (place == null)
+ i = 2;
+ else if (place.equals(JSplitPane.TOP) || place.equals(JSplitPane.LEFT))
+ i = 0;
+ else if (place.equals(JSplitPane.BOTTOM)
+ || place.equals(JSplitPane.RIGHT))
+ i = 1;
+ else
+ throw new IllegalArgumentException("Illegal placement in JSplitPane");
+ components[i] = component;
+ resetSizeAt(i);
+ splitPane.revalidate();
+ splitPane.repaint();
+ }
+
+ /**
+ * This method returns the width of the JSplitPane minus the insets.
+ *
+ * @param containerSize The Dimensions of the JSplitPane.
+ * @param insets The Insets of the JSplitPane.
+ *
+ * @return The width of the JSplitPane minus the insets.
+ */
+ protected int getAvailableSize(Dimension containerSize, Insets insets)
+ {
+ int size;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = containerSize.width - insets.left - insets.right;
+ else
+ size = containerSize.height - insets.top - insets.bottom;
+ return size;
+ }
+
+ /**
+ * This method returns the given insets left value. If the given inset is
+ * null, then 0 is returned.
+ *
+ * @param insets The Insets to use with the JSplitPane.
+ *
+ * @return The inset's left value.
+ */
+ protected int getInitialLocation(Insets insets)
+ {
+ int loc = 0;
+ if (insets != null)
+ {
+ if (axis == SwingConstants.HORIZONTAL)
+ loc = insets.left;
+ else
+ loc = insets.top;
+ }
+ return loc;
+ }
+
+ /**
+ * This specifies how a component is aligned with respect to other
+ * components in the x fdirection.
+ *
+ * @param target The container.
+ *
+ * @return The component's alignment.
+ */
+ public float getLayoutAlignmentX(Container target)
+ {
+ return 0.0f;
+ }
+
+ /**
+ * This specifies how a component is aligned with respect to other
+ * components in the y direction.
+ *
+ * @param target The container.
+ *
+ * @return The component's alignment.
+ */
+ public float getLayoutAlignmentY(Container target)
+ {
+ return 0.0f;
+ }
+
+ /**
+ * This method returns the preferred width of the component.
+ *
+ * @param c The component to measure.
+ *
+ * @return The preferred width of the component.
+ */
+ protected int getPreferredSizeOfComponent(Component c)
+ {
+ int size = 0;
+ Dimension dims = c.getPreferredSize();
+ if (axis == SwingConstants.HORIZONTAL)
+ {
+ if (dims != null)
+ size = dims.width;
+ }
+ else
+ {
+ if (dims != null)
+ size = dims.height;
+ }
+ return size;
+ }
+
+ /**
+ * This method returns the current width of the component.
+ *
+ * @param c The component to measure.
+ *
+ * @return The width of the component.
+ */
+ protected int getSizeOfComponent(Component c)
+ {
+ int size;
+ if (axis == SwingConstants.HORIZONTAL)
+ size = c.getHeight();
+ else
+ size = c.getWidth();
+ return size;
+ }
+
+ /**
+ * This method returns the sizes array.
+ *
+ * @return The sizes array.
+ */
+ protected int[] getSizes()
+ {
+ return sizes;
+ }
+
+ /**
+ * This method invalidates the layout. It does nothing.
+ *
+ * @param c The container to invalidate.
+ */
+ public void invalidateLayout(Container c)
+ {
+ // DO NOTHING
+ }
+
+ /**
+ * This method lays out the components in the container.
+ *
+ * @param container The container to lay out.
+ */
+ public void layoutContainer(Container container)
+ {
+ if (container instanceof JSplitPane)
+ {
+ JSplitPane split = (JSplitPane) container;
+ distributeExtraSpace();
+ Insets insets = split.getInsets();
+ Dimension dims = split.getSize();
+ int loc = getInitialLocation(insets);
+ int available = getAvailableSize(dims, insets);
+ sizes[0] = split.getDividerLocation();
+ sizes[1] = available - sizes[0] - sizes[2];
+
+ // According to a Mauve test we only honour the minimum
+ // size of the components, when the dividerLocation hasn't
+ // been excplicitly set.
+ if (! dividerLocationSet)
+ {
+ sizes[0] = Math.max(sizes[0], minimumSizeOfComponent(0));
+ sizes[1] = Math.max(sizes[1], minimumSizeOfComponent(1));
+ }
+ // The size of the divider won't change.
+
+ // Layout component#1.
+ setComponentToSize(components[0], sizes[0], loc, insets, dims);
+ // Layout divider.
+ loc += sizes[0];
+ setComponentToSize(components[2], sizes[2], loc, insets, dims);
+ // Layout component#2.
+ loc += sizes[2];
+ setComponentToSize(components[1], sizes[1], loc, insets, dims);
+ }
+ }
+
+ /**
+ * This method returns the maximum size for the container given the
+ * components. It returns a new Dimension object that has width and
+ * height equal to Integer.MAX_VALUE.
+ *
+ * @param target The container to measure.
+ *
+ * @return The maximum size.
+ */
+ public Dimension maximumLayoutSize(Container target)
+ {
+ return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * This method returns the container's minimum size. The minimum width is
+ * the sum of all the component's minimum widths. The minimum height is
+ * the maximum of all the components' minimum heights.
+ *
+ * @param target The container to measure.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container target)
+ {
+ Dimension dim = new Dimension();
+ if (target instanceof JSplitPane)
+ {
+ int primary = 0;
+ int secondary = 0;
+ for (int i = 0; i < components.length; i++)
+ {
+ if (components[i] != null)
+ {
+ 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);
+ }
+ }
+ 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 dim;
+ }
+
+ /**
+ * This method returns the container's preferred size. The preferred width
+ * is the sum of all the component's preferred widths. The preferred
+ * height is the maximum of all the components' preferred heights.
+ *
+ * @param target The container to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension preferredLayoutSize(Container target)
+ {
+ Dimension dim = new Dimension();
+ if (target instanceof JSplitPane)
+ {
+ int primary = 0;
+ int secondary = 0;
+ for (int i = 0; i < components.length; i++)
+ {
+ if (components[i] != null)
+ {
+ 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);
+ }
+ }
+ 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 dim;
+ }
+
+ /**
+ * This method removes the component from the layout.
+ *
+ * @param component The component to remove from the layout.
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ for (int i = 0; i < components.length; i++)
+ {
+ if (component == components[i])
+ {
+ components[i] = null;
+ sizes[i] = 0;
+ }
+ }
+ }
+
+ /**
+ * This method resets the size of Component to the preferred size.
+ *
+ * @param index The index of the component to reset.
+ */
+ protected void resetSizeAt(int index)
+ {
+ if (components[index] != null)
+ sizes[index] = getPreferredSizeOfComponent(components[index]);
+ }
+
+ /**
+ * This method resets the sizes of all the components.
+ */
+ public void resetToPreferredSizes()
+ {
+ for (int i = 0; i < components.length; i++)
+ resetSizeAt(i);
+ }
+
+ /**
+ * This methods sets the bounds of the given component. The width is the
+ * size. The height is the container size minus the top and bottom
+ * inset. The x coordinate is the location given. The y coordinate is
+ * the top inset.
+ *
+ * @param c The component to set.
+ * @param size The width of the component.
+ * @param location The x coordinate.
+ * @param insets The insets to use.
+ * @param containerSize The height of the container.
+ */
+ protected void setComponentToSize(Component c, int size, int location,
+ Insets insets, Dimension containerSize)
+ {
+ 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);
+ }
+ }
+
+ /**
+ * This method stores the given int array as the new sizes array.
+ *
+ * @param newSizes The array to use as sizes.
+ */
+ protected void setSizes(int[] newSizes)
+ {
+ sizes = newSizes;
+ }
+
+ /**
+ * This method determines the size of each component. It should be called
+ * when a new Layout Manager is created for an existing JSplitPane.
+ */
+ protected void updateComponents()
+ {
+ Component left = splitPane.getLeftComponent();
+ Component right = splitPane.getRightComponent();
+
+ if (left != null)
+ {
+ components[0] = left;
+ resetSizeAt(0);
+ }
+ if (right != null)
+ {
+ components[1] = right;
+ resetSizeAt(1);
+ }
+ components[2] = divider;
+ }
+
+ /**
+ * This method resizes the left and right components to fit inside the
+ * JSplitPane when there is extra space.
+ */
+ void distributeExtraSpace()
+ {
+ // FIXME: This needs to be reimplemented correctly.
+ }
+
+ /**
+ * This method returns the minimum width of the component at the given
+ * index.
+ *
+ * @param index The index to check.
+ *
+ * @return The minimum width.
+ */
+ int minimumSizeOfComponent(int index)
+ {
+ Dimension dims = components[index].getMinimumSize();
+ int size = 0;
+ if (dims != null)
+ if (axis == SwingConstants.HORIZONTAL)
+ size = dims.width;
+ else
+ size = dims.height;
+ return size;
+ }
+ } //end BasicHorizontalLayoutManager
+
+ /**
+ * This class is the Layout Manager for the JSplitPane when the orientation
+ * is VERTICAL_SPLIT.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class BasicVerticalLayoutManager
+ extends BasicHorizontalLayoutManager
+ {
+ /**
+ * Creates a new instance.
+ */
+ public BasicVerticalLayoutManager()
+ {
+ super(SwingConstants.VERTICAL);
+ }
+ }
+
+ /**
+ * This class handles FocusEvents from the JComponent.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class FocusHandler extends FocusAdapter
+ {
+ /**
+ * This method is called when the JSplitPane gains focus.
+ *
+ * @param ev The FocusEvent.
+ */
+ public void focusGained(FocusEvent ev)
+ {
+ // repaint the divider because its background color may change due to
+ // the focus state...
+ divider.repaint();
+ }
+
+ /**
+ * This method is called when the JSplitPane loses focus.
+ *
+ * @param ev The FocusEvent.
+ */
+ public void focusLost(FocusEvent ev)
+ {
+ // repaint the divider because its background color may change due to
+ // the focus state...
+ divider.repaint();
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handling down
+ * and right key presses.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardDownRightHandler implements ActionListener
+ {
+ /**
+ * This method is called when the down or right keys are pressed.
+ *
+ * @param ev The ActionEvent
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handling end
+ * key presses.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardEndHandler implements ActionListener
+ {
+ /**
+ * This method is called when the end key is pressed.
+ *
+ * @param ev The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handling home
+ * key presses.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardHomeHandler implements ActionListener
+ {
+ /**
+ * This method is called when the home key is pressed.
+ *
+ * @param ev The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handling resize
+ * toggles.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardResizeToggleHandler implements ActionListener
+ {
+ /**
+ * This method is called when a resize is toggled.
+ *
+ * @param ev The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This is a deprecated class. It is supposed to be used for handler up and
+ * left key presses.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class KeyboardUpLeftHandler implements ActionListener
+ {
+ /**
+ * This method is called when the left or up keys are pressed.
+ *
+ * @param ev The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent ev)
+ {
+ // FIXME: implement.
+ }
+ }
+
+ /**
+ * This helper class handles PropertyChangeEvents from the JSplitPane. When
+ * a property changes, this will update the UI accordingly.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class PropertyHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called whenever one of the JSplitPane's properties
+ * change.
+ *
+ * @param e DOCUMENT ME!
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY))
+ {
+ int newSize = splitPane.getDividerSize();
+ int[] tmpSizes = layoutManager.getSizes();
+ dividerSize = tmpSizes[2];
+ int newSpace = newSize - tmpSizes[2];
+ tmpSizes[2] = newSize;
+
+ tmpSizes[0] += newSpace / 2;
+ tmpSizes[1] += newSpace / 2;
+
+ layoutManager.setSizes(tmpSizes);
+ }
+ else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY))
+ {
+ int max = layoutManager.getAvailableSize(splitPane.getSize(),
+ splitPane.getInsets());
+ int dividerLoc = getDividerLocation(splitPane);
+ double prop = ((double) dividerLoc) / max;
+
+ resetLayoutManager();
+ if (prop <= 1 && prop >= 0)
+ splitPane.setDividerLocation(prop);
+ }
+ // Don't have to deal with continuous_layout - only
+ // necessary in dragging modes (and it's checked
+ // every time you drag there)
+ // Don't have to deal with resize_weight (as there
+ // will be no extra space associated with this
+ // event - the changes to the weighting will
+ // be taken into account the next time the
+ // sizes change.)
+ // Don't have to deal with divider_location
+ // The method in JSplitPane calls our setDividerLocation
+ // so we'll know about those anyway.
+ // Don't have to deal with last_divider_location
+ // Although I'm not sure why, it doesn't seem to
+ // have any effect on Sun's JSplitPane.
+ // one_touch_expandable changes are dealt with
+ // by our divider.
+ }
+ }
+
+ /** The location of the divider when dragging began. */
+ protected int beginDragDividerLocation;
+
+ /** The size of the divider while dragging. */
+ protected int dividerSize;
+
+ /** The location where the last drag location ended. */
+ transient int lastDragLocation = -1;
+
+ /** The distance the divider is moved when moved by keyboard actions. */
+ // Sun defines this as 3
+ protected static int KEYBOARD_DIVIDER_MOVE_OFFSET = 3;
+
+ /** The divider that divides this JSplitPane. */
+ protected BasicSplitPaneDivider divider;
+
+ /** The listener that listens for PropertyChangeEvents from the JSplitPane. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The JSplitPane's focus handler. */
+ protected FocusListener focusListener;
+
+ /** @deprecated The handler for down and right key presses. */
+ protected ActionListener keyboardDownRightListener;
+
+ /** @deprecated The handler for end key presses. */
+ protected ActionListener keyboardEndListener;
+
+ /** @deprecated The handler for home key presses. */
+ protected ActionListener keyboardHomeListener;
+
+ /** @deprecated The handler for toggling resizes. */
+ protected ActionListener keyboardResizeToggleListener;
+
+ /** @deprecated The handler for up and left key presses. */
+ protected ActionListener keyboardUpLeftListener;
+
+ /** The JSplitPane's current layout manager. */
+ protected BasicHorizontalLayoutManager layoutManager;
+
+ /** @deprecated The divider resize toggle key. */
+ protected KeyStroke dividerResizeToggleKey;
+
+ /** @deprecated The down key. */
+ protected KeyStroke downKey;
+
+ /** @deprecated The end key. */
+ protected KeyStroke endKey;
+
+ /** @deprecated The home key. */
+ protected KeyStroke homeKey;
+
+ /** @deprecated The left key. */
+ protected KeyStroke leftKey;
+
+ /** @deprecated The right key. */
+ protected KeyStroke rightKey;
+
+ /** @deprecated The up key. */
+ protected KeyStroke upKey;
+
+ /** Set to true when dragging heavy weight components. */
+ protected boolean draggingHW;
+
+ /**
+ * The constraints object used when adding the non-continuous divider to the
+ * JSplitPane.
+ */
+ protected static final String NON_CONTINUOUS_DIVIDER
+ = "nonContinuousDivider";
+
+ /** The dark divider used when dragging in non-continuous layout mode. */
+ protected Component nonContinuousLayoutDivider;
+
+ /** The JSplitPane that this UI draws. */
+ protected JSplitPane splitPane;
+
+ /**
+ * True, when setDividerLocation() has been called at least
+ * once on the JSplitPane, false otherwise.
+ *
+ * This is package private to avoid a synthetic accessor method.
+ */
+ boolean dividerLocationSet;
+
+ /**
+ * Creates a new BasicSplitPaneUI object.
+ */
+ public BasicSplitPaneUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method creates a new BasicSplitPaneUI for the given JComponent.
+ *
+ * @param x The JComponent to create a UI for.
+ *
+ * @return A new BasicSplitPaneUI.
+ */
+ public static ComponentUI createUI(JComponent x)
+ {
+ return new BasicSplitPaneUI();
+ }
+
+ /**
+ * This method installs the BasicSplitPaneUI for the given JComponent.
+ *
+ * @param c The JComponent to install the UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ if (c instanceof JSplitPane)
+ {
+ splitPane = (JSplitPane) c;
+ dividerLocationSet = false;
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+ }
+ }
+
+ /**
+ * This method uninstalls the BasicSplitPaneUI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall the UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallDefaults();
+
+ dividerLocationSet = false;
+ splitPane = null;
+ }
+
+ /**
+ * This method installs the defaults given by the Look and Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColors(splitPane, "SplitPane.background",
+ "SplitPane.foreground");
+ LookAndFeel.installBorder(splitPane, "SplitPane.border");
+ divider = createDefaultDivider();
+ divider.setBorder(UIManager.getBorder("SplitPaneDivider.border"));
+ resetLayoutManager();
+ nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider();
+ splitPane.add(divider, JSplitPane.DIVIDER);
+
+ // There is no need to add the nonContinuousLayoutDivider.
+ dividerSize = UIManager.getInt("SplitPane.dividerSize");
+ splitPane.setDividerSize(dividerSize);
+ divider.setDividerSize(dividerSize);
+ splitPane.setOpaque(true);
+ }
+
+ /**
+ * This method uninstalls the defaults and nulls any objects created during
+ * install.
+ */
+ protected void uninstallDefaults()
+ {
+ layoutManager = null;
+ splitPane.remove(divider);
+ divider = null;
+ nonContinuousLayoutDivider = null;
+
+ if (splitPane.getBackground() instanceof UIResource)
+ splitPane.setBackground(null);
+ if (splitPane.getBorder() instanceof UIResource)
+ splitPane.setBorder(null);
+ }
+
+ /**
+ * This method installs the listeners needed for this UI to function.
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+ focusListener = createFocusListener();
+
+ splitPane.addPropertyChangeListener(propertyChangeListener);
+ splitPane.addFocusListener(focusListener);
+ }
+
+ /**
+ * This method uninstalls all listeners registered for the UI.
+ */
+ protected void uninstallListeners()
+ {
+ splitPane.removePropertyChangeListener(propertyChangeListener);
+ splitPane.removeFocusListener(focusListener);
+
+ focusListener = null;
+ propertyChangeListener = null;
+ }
+
+ /**
+ * Returns the input map for the specified condition.
+ *
+ * @param condition the condition.
+ *
+ * @return The input map.
+ */
+ InputMap getInputMap(int condition)
+ {
+ if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
+ return (InputMap) UIManager.get("SplitPane.ancestorInputMap");
+ return null;
+ }
+
+ /**
+ * Returns the action map for the {@link JSplitPane}. All sliders share
+ * a single action map which is created the first time this method is
+ * called, then stored in the UIDefaults table for subsequent access.
+ *
+ * @return The shared action map.
+ */
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("SplitPane.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("SplitPane.actionMap", map);
+ }
+ return map;
+ }
+
+ /**
+ * Creates the action map shared by all {@link JSlider} instances.
+ * This method is called once by {@link #getActionMap()} when it
+ * finds no action map in the UIDefaults table...after the map is
+ * created, it gets added to the defaults table so that subsequent
+ * calls to {@link #getActionMap()} will return the same shared
+ * instance.
+ *
+ * @return The action map.
+ */
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+ map.put("toggleFocus",
+ new AbstractAction("toggleFocus") {
+ public void actionPerformed(ActionEvent event)
+ {
+ // FIXME: What to do here?
+ }
+ }
+ );
+ map.put("startResize",
+ new AbstractAction("startResize") {
+ public void actionPerformed(ActionEvent event)
+ {
+ splitPane.requestFocus();
+ }
+ }
+ );
+ map.put("selectMax",
+ new AbstractAction("selectMax") {
+ public void actionPerformed(ActionEvent event)
+ {
+ splitPane.setDividerLocation(1.0);
+ }
+ }
+ );
+ map.put("selectMin",
+ new AbstractAction("selectMin") {
+ public void actionPerformed(ActionEvent event)
+ {
+ splitPane.setDividerLocation(0.0);
+ }
+ }
+ );
+ map.put("negativeIncrement",
+ new AbstractAction("negativeIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ int oldLoc = splitPane.getDividerLocation();
+ int newLoc =
+ Math.max(oldLoc - KEYBOARD_DIVIDER_MOVE_OFFSET, 0);
+ splitPane.setDividerLocation(newLoc);
+ }
+ }
+ );
+ map.put("positiveIncrement",
+ new AbstractAction("positiveIncrement") {
+ public void actionPerformed(ActionEvent event)
+ {
+ int oldLoc = splitPane.getDividerLocation();
+ int newLoc =
+ Math.max(oldLoc + KEYBOARD_DIVIDER_MOVE_OFFSET, 0);
+ splitPane.setDividerLocation(newLoc);
+ }
+ }
+ );
+ map.put("focusOutBackward",
+ new AbstractAction("focusOutBackward") {
+ public void actionPerformed(ActionEvent event)
+ {
+ // FIXME: implement this
+ }
+ }
+ );
+ map.put("focusOutForward",
+ new AbstractAction("focusOutForward") {
+ public void actionPerformed(ActionEvent event)
+ {
+ // FIXME: implement this
+ }
+ }
+ );
+ return map;
+ }
+
+ /**
+ * Installs any keyboard actions. The list of keys that need to be bound are
+ * listed in Basic look and feel's defaults.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap keyMap = getInputMap(
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
+ SwingUtilities.replaceUIInputMap(splitPane,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(splitPane, map);
+ }
+
+ /**
+ * This method reverses the work done in installKeyboardActions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIActionMap(splitPane, null);
+ SwingUtilities.replaceUIInputMap(splitPane,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyHandler();
+ }
+
+ /**
+ * This method creates a new FocusListener.
+ *
+ * @return A new FocusListener.
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for up and left key presses.
+ *
+ * @return A new ActionListener for up and left keys.
+ *
+ * @deprecated 1.3
+ */
+ protected ActionListener createKeyboardUpLeftListener()
+ {
+ return new KeyboardUpLeftHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for down and right key presses.
+ *
+ * @return A new ActionListener for down and right keys.
+ *
+ * @deprecated 1.3
+ */
+ protected ActionListener createKeyboardDownRightListener()
+ {
+ return new KeyboardDownRightHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for home key presses.
+ *
+ * @return A new ActionListener for home keys.
+ *
+ * @deprecated
+ */
+ protected ActionListener createKeyboardHomeListener()
+ {
+ return new KeyboardHomeHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for end key presses.i
+ *
+ * @return A new ActionListener for end keys.
+ *
+ * @deprecated 1.3
+ */
+ protected ActionListener createKeyboardEndListener()
+ {
+ return new KeyboardEndHandler();
+ }
+
+ /**
+ * This method creates a new ActionListener for resize toggle key events.
+ *
+ * @return A new ActionListener for resize toggle keys.
+ *
+ * @deprecated 1.3
+ */
+ protected ActionListener createKeyboardResizeToggleListener()
+ {
+ return new KeyboardResizeToggleHandler();
+ }
+
+ /**
+ * This method returns the orientation of the JSplitPane.
+ *
+ * @return The orientation of the JSplitPane.
+ */
+ public int getOrientation()
+ {
+ return splitPane.getOrientation();
+ }
+
+ /**
+ * This method sets the orientation of the JSplitPane.
+ *
+ * @param orientation The new orientation of the JSplitPane.
+ */
+ public void setOrientation(int orientation)
+ {
+ splitPane.setOrientation(orientation);
+ }
+
+ /**
+ * This method returns true if the JSplitPane is using continuous layout.
+ *
+ * @return True if the JSplitPane is using continuous layout.
+ */
+ public boolean isContinuousLayout()
+ {
+ return splitPane.isContinuousLayout();
+ }
+
+ /**
+ * This method sets the continuous layout property of the JSplitPane.
+ *
+ * @param b True if the JsplitPane is to use continuous layout.
+ */
+ public void setContinuousLayout(boolean b)
+ {
+ splitPane.setContinuousLayout(b);
+ }
+
+ /**
+ * This method returns the last location the divider was dragged to.
+ *
+ * @return The last location the divider was dragged to.
+ */
+ public int getLastDragLocation()
+ {
+ return lastDragLocation;
+ }
+
+ /**
+ * This method sets the last location the divider was dragged to.
+ *
+ * @param l The last location the divider was dragged to.
+ */
+ public void setLastDragLocation(int l)
+ {
+ lastDragLocation = l;
+ }
+
+ /**
+ * This method returns the BasicSplitPaneDivider that divides this
+ * JSplitPane.
+ *
+ * @return The divider for the JSplitPane.
+ */
+ public BasicSplitPaneDivider getDivider()
+ {
+ return divider;
+ }
+
+ /**
+ * This method creates a nonContinuousLayoutDivider for use with the
+ * JSplitPane in nonContinousLayout mode. The default divider is a gray
+ * Canvas.
+ *
+ * @return The default nonContinousLayoutDivider.
+ */
+ protected Component createDefaultNonContinuousLayoutDivider()
+ {
+ if (nonContinuousLayoutDivider == null)
+ {
+ nonContinuousLayoutDivider = new Canvas();
+ Color c = UIManager.getColor("SplitPaneDivider.draggingColor");
+ nonContinuousLayoutDivider.setBackground(c);
+ }
+ return nonContinuousLayoutDivider;
+ }
+
+ /**
+ * This method sets the component to use as the nonContinuousLayoutDivider.
+ *
+ * @param newDivider The component to use as the nonContinuousLayoutDivider.
+ */
+ protected void setNonContinuousLayoutDivider(Component newDivider)
+ {
+ setNonContinuousLayoutDivider(newDivider, true);
+ }
+
+ /**
+ * This method sets the component to use as the nonContinuousLayoutDivider.
+ *
+ * @param newDivider The component to use as the nonContinuousLayoutDivider.
+ * @param rememberSizes FIXME: document.
+ */
+ protected void setNonContinuousLayoutDivider(Component newDivider,
+ boolean rememberSizes)
+ {
+ // FIXME: use rememberSizes for something
+ nonContinuousLayoutDivider = newDivider;
+ }
+
+ /**
+ * This method returns the nonContinuousLayoutDivider.
+ *
+ * @return The nonContinuousLayoutDivider.
+ */
+ public Component getNonContinuousLayoutDivider()
+ {
+ return nonContinuousLayoutDivider;
+ }
+
+ /**
+ * This method returns the JSplitPane that this BasicSplitPaneUI draws.
+ *
+ * @return The JSplitPane.
+ */
+ public JSplitPane getSplitPane()
+ {
+ return splitPane;
+ }
+
+ /**
+ * This method creates the divider used normally with the JSplitPane.
+ *
+ * @return The default divider.
+ */
+ public BasicSplitPaneDivider createDefaultDivider()
+ {
+ if (divider == null)
+ divider = new BasicSplitPaneDivider(this);
+ return divider;
+ }
+
+ /**
+ * This method is called when JSplitPane's resetToPreferredSizes is called.
+ * It resets the sizes of all components in the JSplitPane.
+ *
+ * @param jc The JSplitPane to reset.
+ */
+ public void resetToPreferredSizes(JSplitPane jc)
+ {
+ layoutManager.resetToPreferredSizes();
+ }
+
+ /**
+ * This method sets the location of the divider.
+ *
+ * @param jc The JSplitPane to set the divider location in.
+ * @param location The new location of the divider.
+ */
+ public void setDividerLocation(JSplitPane jc, int location)
+ {
+ dividerLocationSet = true;
+ splitPane.revalidate();
+ splitPane.repaint();
+ }
+
+ /**
+ * This method returns the location of the divider.
+ *
+ * @param jc The JSplitPane to retrieve the location for.
+ *
+ * @return The location of the divider.
+ */
+ public int getDividerLocation(JSplitPane jc)
+ {
+ int loc;
+ if (jc.getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
+ loc = divider.getX();
+ else
+ loc = divider.getY();
+ return loc;
+ }
+
+ /**
+ * This method returns the smallest value possible for the location of the
+ * divider.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The minimum divider location.
+ */
+ public int getMinimumDividerLocation(JSplitPane jc)
+ {
+ int value = layoutManager.getInitialLocation(jc.getInsets());
+ if (layoutManager.components[0] != null)
+ value += layoutManager.minimumSizeOfComponent(0);
+ return value;
+ }
+
+ /**
+ * This method returns the largest value possible for the location of the
+ * divider.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The maximum divider location.
+ */
+ public int getMaximumDividerLocation(JSplitPane jc)
+ {
+ int value = layoutManager.getInitialLocation(jc.getInsets())
+ + layoutManager.getAvailableSize(jc.getSize(), jc.getInsets())
+ - splitPane.getDividerSize();
+ if (layoutManager.components[1] != null)
+ value -= layoutManager.minimumSizeOfComponent(1);
+ return value;
+ }
+
+ /**
+ * This method is called after the children of the JSplitPane are painted.
+ *
+ * @param jc The JSplitPane.
+ * @param g The Graphics object to paint with.
+ */
+ public void finishedPaintingChildren(JSplitPane jc, Graphics g)
+ {
+ if (! splitPane.isContinuousLayout() && nonContinuousLayoutDivider != null
+ && nonContinuousLayoutDivider.isVisible())
+ javax.swing.SwingUtilities.paintComponent(g, nonContinuousLayoutDivider,
+ null,
+ nonContinuousLayoutDivider
+ .getBounds());
+ }
+
+ /**
+ * This method is called to paint the JSplitPane.
+ *
+ * @param g The Graphics object to paint with.
+ * @param jc The JSplitPane to paint.
+ */
+ public void paint(Graphics g, JComponent jc)
+ {
+ // TODO: What should be done here?
+ }
+
+ /**
+ * This method returns the preferred size of the JSplitPane.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The preferred size of the JSplitPane.
+ */
+ public Dimension getPreferredSize(JComponent jc)
+ {
+ return layoutManager.preferredLayoutSize(jc);
+ }
+
+ /**
+ * This method returns the minimum size of the JSplitPane.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The minimum size of the JSplitPane.
+ */
+ public Dimension getMinimumSize(JComponent jc)
+ {
+ return layoutManager.minimumLayoutSize(jc);
+ }
+
+ /**
+ * This method returns the maximum size of the JSplitPane.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The maximum size of the JSplitPane.
+ */
+ public Dimension getMaximumSize(JComponent jc)
+ {
+ return layoutManager.maximumLayoutSize(jc);
+ }
+
+ /**
+ * This method returns the border insets of the current border.
+ *
+ * @param jc The JSplitPane.
+ *
+ * @return The current border insets.
+ */
+ public Insets getInsets(JComponent jc)
+ {
+ return splitPane.getBorder().getBorderInsets(splitPane);
+ }
+
+ /**
+ * This method resets the current layout manager. The type of layout manager
+ * is dependent on the current orientation.
+ */
+ protected void resetLayoutManager()
+ {
+ if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
+ layoutManager = new BasicHorizontalLayoutManager();
+ else
+ layoutManager = new BasicVerticalLayoutManager();
+ getSplitPane().setLayout(layoutManager);
+ layoutManager.updateComponents();
+
+ // invalidating by itself does not invalidate the layout.
+ getSplitPane().revalidate();
+ }
+
+ /**
+ * This method is called when dragging starts. It resets lastDragLocation
+ * and dividerSize.
+ */
+ protected void startDragging()
+ {
+ Component left = splitPane.getLeftComponent();
+ Component right = splitPane.getRightComponent();
+ dividerSize = divider.getDividerSize();
+ setLastDragLocation(-1);
+
+ if ((left != null && !left.isLightweight())
+ || (right != null && !right.isLightweight()))
+ draggingHW = true;
+
+ if (splitPane.isContinuousLayout())
+ nonContinuousLayoutDivider.setVisible(false);
+ else
+ {
+ nonContinuousLayoutDivider.setVisible(true);
+ nonContinuousLayoutDivider.setBounds(divider.getBounds());
+ }
+ }
+
+ /**
+ * This method is called whenever the divider is dragged. If the JSplitPane
+ * is in continuousLayout mode, the divider needs to be moved and the
+ * JSplitPane needs to be laid out.
+ *
+ * @param location The new location of the divider.
+ */
+ protected void dragDividerTo(int location)
+ {
+ location = validLocation(location);
+ if (beginDragDividerLocation == -1)
+ beginDragDividerLocation = location;
+
+ if (splitPane.isContinuousLayout())
+ splitPane.setDividerLocation(location);
+ else
+ {
+ Point p = nonContinuousLayoutDivider.getLocation();
+ if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
+ p.x = location;
+ else
+ p.y = location;
+ nonContinuousLayoutDivider.setLocation(p);
+ }
+ setLastDragLocation(location);
+ splitPane.repaint();
+ }
+
+ /**
+ * This method is called when the dragging is finished.
+ *
+ * @param location The location where the drag finished.
+ */
+ protected void finishDraggingTo(int location)
+ {
+ if (nonContinuousLayoutDivider != null)
+ nonContinuousLayoutDivider.setVisible(false);
+ draggingHW = false;
+ location = validLocation(location);
+ splitPane.setDividerLocation(location);
+ splitPane.setLastDividerLocation(beginDragDividerLocation);
+ beginDragDividerLocation = -1;
+ }
+
+ /**
+ * This method returns the width of one of the sides of the divider's border.
+ *
+ * @return The width of one side of the divider's border.
+ *
+ * @deprecated 1.3
+ */
+ protected int getDividerBorderSize()
+ {
+ if (getOrientation() == JSplitPane.HORIZONTAL_SPLIT)
+ return divider.getBorder().getBorderInsets(divider).left;
+ else
+ return divider.getBorder().getBorderInsets(divider).top;
+ }
+
+ /**
+ * This is a helper method that returns a valid location for the divider
+ * when dragging.
+ *
+ * @param location The location to check.
+ *
+ * @return A valid location.
+ */
+ private int validLocation(int location)
+ {
+ int min = getMinimumDividerLocation(splitPane);
+ int max = getMaximumDividerLocation(splitPane);
+ if (min > 0 && location < min)
+ return min;
+ if (max > 0 && location > max)
+ return max;
+ return location;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java
new file mode 100644
index 000000000..c42f9caed
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTabbedPaneUI.java
@@ -0,0 +1,4003 @@
+/* BasicTabbedPaneUI.java --
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+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.LayoutManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.ActionMap;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
+import javax.swing.JViewport;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+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.ComponentUI;
+import javax.swing.plaf.TabbedPaneUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.text.View;
+
+/**
+ * This is the Basic Look and Feel's UI delegate for JTabbedPane.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ * @author Kim Ho (kho@redhat.com)
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ */
+public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants
+{
+
+ static class NavigateAction extends AbstractAction
+ {
+ int direction;
+
+ NavigateAction(String name, int dir)
+ {
+ super(name);
+ direction = dir;
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTabbedPane tp = (JTabbedPane) event.getSource();
+ BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
+
+ ui.navigateSelectedTab(direction);
+ }
+
+ }
+
+ static class NavigatePageDownAction extends AbstractAction
+ {
+
+ public NavigatePageDownAction()
+ {
+ super("navigatePageDown");
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTabbedPane tp = (JTabbedPane) event.getSource();
+ BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
+
+ int i = tp.getSelectedIndex();
+
+ if (i < 0)
+ i = 0;
+
+ ui.selectNextTabInRun(i);
+ }
+
+ }
+
+ static class NavigatePageUpAction extends AbstractAction
+ {
+
+ public NavigatePageUpAction()
+ {
+ super("navigatePageUp");
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTabbedPane tp = (JTabbedPane) event.getSource();
+ BasicTabbedPaneUI ui = (BasicTabbedPaneUI) tp.getUI();
+
+ int i = tp.getSelectedIndex();
+
+ if (i < 0)
+ i = 0;
+
+ ui.selectPreviousTabInRun(i);
+
+ }
+ }
+
+ static class RequestFocusAction extends AbstractAction
+ {
+
+ public RequestFocusAction()
+ {
+ super("requestFocus");
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ ((JTabbedPane) event.getSource()).requestFocus();
+ }
+
+ }
+
+ static class RequestFocusForVisibleComponentAction extends AbstractAction
+ {
+
+ public RequestFocusForVisibleComponentAction()
+ {
+ super("requestFocusForVisibleComponent");
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JTabbedPane tp = (JTabbedPane) event.getSource();
+
+ // FIXME: This should select a suitable component within
+ // the tab content. However I dont know whether we have
+ // to search for this component or wether the called is
+ // supposed to do that.
+ tp.getSelectedComponent().requestFocus();
+ }
+
+ }
+
+ /**
+ * A helper class that handles focus.
+ * <p>The purpose of this class is to implement a more flexible focus
+ * handling for the tabbed pane, which is used to determine whether the
+ * focus indicator should be painted or not. When in scrolling layout
+ * mode the area containing the tabs is a scrollpane, so simply testing
+ * whether the tabbed pane has the focus does not work.</p>
+ * <p>The <code>FocusHandler</code> is installed on the scrollpane and
+ * the tabbed pane and sets the variable <code>hasFocus</code> to
+ * <code>false</code> only when both components do not hold the focus.</p>
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class FocusHandler extends FocusAdapter
+ {
+ /**
+ * This method is called when the component gains focus.
+ *
+ * @param e The FocusEvent.
+ */
+ public void focusGained(FocusEvent e)
+ {
+ Object source = e.getSource();
+ if (source == panel )
+ tabPane.requestFocus();
+ else if (source == tabPane)
+ tabPane.repaint();
+ }
+
+ /**
+ * This method is called when the component loses focus.
+ *
+ * @param e The FocusEvent.
+ */
+ public void focusLost(FocusEvent e)
+ {
+ if (e.getOppositeComponent() == tabPane.getSelectedComponent())
+ tabPane.requestFocus();
+ else if (e.getSource() == tabPane)
+ tabPane.repaint();
+ }
+ }
+
+ /**
+ * A helper class for determining if mouse presses occur inside tabs and
+ * sets the index appropriately. In SCROLL_TAB_MODE, this class also
+ * handles the mouse clicks on the scrolling buttons.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class MouseHandler extends MouseAdapter
+ {
+ public void mouseReleased(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);
+ }
+ }
+
+ /**
+ * This method is called when the mouse is pressed. The index cannot
+ * change to a tab that is not enabled.
+ *
+ * @param e The MouseEvent.
+ */
+ 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)
+ {
+ if(!incrButton.isEnabled())
+ return;
+
+ currentScrollLocation++;
+
+ switch (placement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ currentScrollOffset = getTabAreaInsets(placement).left;
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].width;
+ break;
+ default:
+ currentScrollOffset = getTabAreaInsets(placement).top;
+ for (int i = 0; i < currentScrollLocation; i++)
+ currentScrollOffset += rects[i].height;
+ break;
+ }
+
+ updateViewPosition();
+ updateButtons();
+
+ tabPane.repaint();
+ }
+ else if (s == decrButton)
+ {
+ 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--;
+
+ // 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;
+ }
+
+ updateViewPosition();
+ updateButtons();
+
+ 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();
+ }
+
+ }
+
+ }
+
+ /**
+ * Receives notification when the mouse pointer has entered the tabbed
+ * pane.
+ *
+ * @param e the mouse event
+ */
+ public void mouseEntered(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 tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY());
+ setRolloverTab(tabIndex);
+ }
+
+ /**
+ * Receives notification when the mouse pointer has exited the tabbed
+ * pane.
+ *
+ * @param e the mouse event
+ */
+ 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);
+ }
+
+ /**
+ * Receives notification when the mouse pointer has moved over the tabbed
+ * pane.
+ *
+ * @param ev the mouse event
+ */
+ 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);
+ }
+
+ }
+
+ /**
+ * This class handles PropertyChangeEvents fired from the JTabbedPane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * This method is called whenever one of the properties of the JTabbedPane
+ * changes.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ out:
+ {
+ 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();
+ }
+ }
+
+ /**
+ * A LayoutManager responsible for placing all the tabs and the visible
+ * component inside the JTabbedPane. This class is only used for
+ * WRAP_TAB_LAYOUT.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TabbedPaneLayout implements LayoutManager
+ {
+ /**
+ * This method is called when a component is added to the JTabbedPane.
+ *
+ * @param name The name of the component.
+ * @param comp The component being added.
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called when the rectangles need to be calculated. It
+ * also fixes the size of the visible component.
+ */
+ public void calculateLayoutInfo()
+ {
+ int count = tabPane.getTabCount();
+ assureRectsCreated(count);
+ calculateTabRects(tabPane.getTabPlacement(), count);
+ tabRunsDirty = false;
+ }
+
+ /**
+ * This method calculates the size of the the JTabbedPane.
+ *
+ * @param minimum Whether the JTabbedPane will try to be as small as it
+ * can.
+ *
+ * @return The desired size of the JTabbedPane.
+ */
+ protected Dimension calculateSize(boolean minimum)
+ {
+ int tabPlacement = tabPane.getTabPlacement();
+
+ int width = 0;
+ int height = 0;
+ Component c;
+ Dimension dims;
+
+ // Find out the minimum/preferred size to display the largest child
+ // of the tabbed pane.
+ int count = tabPane.getTabCount();
+ for (int i = 0; i < count; i++)
+ {
+ c = tabPane.getComponentAt(i);
+ if (c == null)
+ continue;
+ dims = minimum ? c.getMinimumSize() : c.getPreferredSize();
+ if (dims != null)
+ {
+ height = Math.max(height, dims.height);
+ width = Math.max(width, dims.width);
+ }
+ }
+
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ width = Math.max(calculateMaxTabWidth(tabPlacement), width);
+
+ height += preferredTabAreaHeight(tabPlacement,
+ width - tabAreaInsets.left
+ - tabAreaInsets.right);
+ }
+ else
+ {
+ height = Math.max(calculateMaxTabHeight(tabPlacement), height);
+
+ width += preferredTabAreaWidth(tabPlacement,
+ height - tabAreaInsets.top
+ - tabAreaInsets.bottom);
+ }
+
+ Insets tabPaneInsets = tabPane.getInsets();
+ return new Dimension(width + tabPaneInsets.left + tabPaneInsets.right,
+ height + tabPaneInsets.top + tabPaneInsets.bottom);
+ }
+
+ // if tab placement is LEFT OR RIGHT, they share width.
+ // if tab placement is TOP OR BOTTOM, they share height
+ // PRE STEP: finds the default sizes for the labels as well as their
+ // locations.
+ // AND where they will be placed within the run system.
+ // 1. calls normalizeTab Runs.
+ // 2. calls rotate tab runs.
+ // 3. pads the tab runs.
+ // 4. pads the selected tab.
+
+ /**
+ * This method is called to calculate the tab rectangles. This method
+ * will calculate the size and position of all rectangles (taking into
+ * account which ones should be in which tab run). It will pad them and
+ * normalize them as necessary.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabCount The run the current selection is in.
+ */
+ protected void calculateTabRects(int tabPlacement, int tabCount)
+ {
+ Insets insets = tabPane.getInsets();
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ Dimension size = tabPane.getSize();
+
+ // The coordinates of the upper left corner of the tab area.
+ int x;
+ int y;
+ // The location at which the runs must be broken.
+ int breakAt;
+
+ // Calculate the bounds for the tab area.
+ switch (tabPlacement)
+ {
+ case LEFT:
+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ x = insets.left + tabAreaInsets.left;
+ y = insets.top + tabAreaInsets.top;
+ breakAt = size.height - (insets.bottom + tabAreaInsets.bottom);
+ break;
+ case RIGHT:
+ maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ x = size.width - (insets.right + tabAreaInsets.right)
+ - maxTabWidth - 1;
+ y = insets.top + tabAreaInsets.top;
+ breakAt = size.height - (insets.bottom + tabAreaInsets.bottom);
+ break;
+ case BOTTOM:
+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ x = insets.left + tabAreaInsets.left;
+ y = size.height - (insets.bottom + tabAreaInsets.bottom)
+ - maxTabHeight - 1;
+ breakAt = size.width - (insets.right + tabAreaInsets.right);
+ break;
+ case TOP:
+ default:
+ maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ x = insets.left + tabAreaInsets.left;
+ y = insets.top + tabAreaInsets.top;
+ breakAt = size.width - (insets.right + tabAreaInsets.right);
+ break;
+ }
+
+ if (tabCount == 0)
+ return;
+
+ FontMetrics fm = getFontMetrics();
+ runCount = 0;
+ selectedRun = -1;
+ int selectedIndex = tabPane.getSelectedIndex();
+ if (selectedIndex < 0)
+ selectedIndex = 0;
+
+ Rectangle rect;
+
+ // Go through all the tabs and build the tab runs.
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ for (int i = 0; i < tabCount; i++)
+ {
+ rect = rects[i];
+ if (i > 0)
+ {
+ rect.x = rects[i - 1].x + rects[i - 1].width;
+ }
+ else
+ {
+ tabRuns[0] = 0;
+ runCount = 1;
+ maxTabWidth = 0;
+ rect.x = x;
+ }
+ rect.width = calculateTabWidth(tabPlacement, i, fm);
+ maxTabWidth = Math.max(maxTabWidth, rect.width);
+
+ if (rect.x != 2 + insets.left && rect.x + rect.width > breakAt)
+ {
+ if (runCount > tabRuns.length - 1)
+ expandTabRunsArray();
+ tabRuns[runCount] = i;
+ runCount++;
+ rect.x = x;
+ }
+
+ rect.y = y;
+ rect.height = maxTabHeight;
+ if (i == selectedIndex)
+ selectedRun = runCount - 1;
+ }
+ }
+ else
+ {
+ for (int i = 0; i < tabCount; i++)
+ {
+ rect = rects[i];
+ if (i > 0)
+ {
+ rect.y = rects[i - 1].y + rects[i - 1].height;
+ }
+ else
+ {
+ tabRuns[0] = 0;
+ runCount = 1;
+ maxTabHeight = 0;
+ rect.y = y;
+ }
+ rect.height = calculateTabHeight(tabPlacement, i,
+ fm.getHeight());
+ maxTabHeight = Math.max(maxTabHeight, rect.height);
+
+ if (rect.y != 2 + insets.top && rect.y + rect.height > breakAt)
+ {
+ if (runCount > tabRuns.length - 1)
+ expandTabRunsArray();
+ tabRuns[runCount] = i;
+ runCount++;
+ rect.y = y;
+ }
+
+ rect.x = x;
+ rect.width = maxTabWidth;
+
+ if (i == selectedIndex)
+ selectedRun = runCount - 1;
+ }
+ }
+
+ if (runCount > 1)
+ {
+ int start;
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ start = x;
+ else
+ start = y;
+ normalizeTabRuns(tabPlacement, tabCount, start, breakAt);
+ selectedRun = getRunForTab(tabCount, selectedIndex);
+ if (shouldRotateTabRuns(tabPlacement))
+ {
+ rotateTabRuns(tabPlacement, selectedRun);
+ }
+ }
+
+ // Suppress padding if we have only one tab run.
+ if (runCount == 1)
+ return;
+
+ // Pad the runs.
+ int tabRunOverlay = getTabRunOverlay(tabPlacement);
+ for (int i = runCount - 1; i >= 0; --i)
+ {
+ int start = tabRuns[i];
+ int nextIndex;
+ if (i == runCount - 1)
+ nextIndex = 0;
+ else
+ nextIndex = i + 1;
+ int next = tabRuns[nextIndex];
+ int end = next != 0 ? next - 1 : tabCount - 1;
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ for (int j = start; j <= end; ++j)
+ {
+ rect = rects[j];
+ rect.y = y;
+ rect.x += getTabRunIndent(tabPlacement, i);
+ }
+ if (shouldPadTabRun(tabPlacement, i))
+ {
+ padTabRun(tabPlacement, start, end, breakAt);
+ }
+ if (tabPlacement == BOTTOM)
+ y -= maxTabHeight - tabRunOverlay;
+ else
+ y += maxTabHeight - tabRunOverlay;
+ }
+ else
+ {
+ for (int j = start; j <= end; ++j)
+ {
+ rect = rects[j];
+ rect.x = x;
+ rect.y += getTabRunIndent(tabPlacement, i);
+ }
+ if (shouldPadTabRun(tabPlacement, i))
+ {
+ padTabRun(tabPlacement, start, end, breakAt);
+ }
+ if (tabPlacement == RIGHT)
+ x -= maxTabWidth - tabRunOverlay;
+ else
+ x += maxTabWidth - tabRunOverlay;
+
+ }
+ }
+ padSelectedTab(tabPlacement, selectedIndex);
+ }
+
+ /**
+ * This method is called when the JTabbedPane is laid out in
+ * WRAP_TAB_LAYOUT. It calls calculateLayoutInfo to find the positions
+ * of all its components.
+ *
+ * @param parent The Container to lay out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ calculateLayoutInfo();
+
+ int tabPlacement = tabPane.getTabPlacement();
+ Insets insets = tabPane.getInsets();
+
+ int selectedIndex = tabPane.getSelectedIndex();
+
+ Component selectedComponent = null;
+ if (selectedIndex >= 0)
+ selectedComponent = tabPane.getComponentAt(selectedIndex);
+ // The RI doesn't seem to change the component if the new selected
+ // component == null. This is probably so that applications can add
+ // one single component for every tab.
+ if (selectedComponent != null)
+ {
+ setVisibleComponent(selectedComponent);
+ }
+
+ int childCount = tabPane.getComponentCount();
+ if (childCount > 0)
+ {
+ int compX;
+ int compY;
+ int tabAreaWidth = 0;
+ int tabAreaHeight = 0;
+ switch (tabPlacement)
+ {
+ case LEFT:
+ tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount,
+ maxTabWidth);
+ compX = tabAreaWidth + insets.left + contentBorderInsets.left;
+ compY = insets.top + contentBorderInsets.top;
+ break;
+ case RIGHT:
+ tabAreaWidth = calculateTabAreaWidth(tabPlacement, runCount,
+ maxTabWidth);
+ compX = insets.left + contentBorderInsets.left;
+ compY = insets.top + contentBorderInsets.top;
+ break;
+ case BOTTOM:
+ tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount,
+ maxTabHeight);
+ compX = insets.left + contentBorderInsets.left;
+ compY = insets.top + contentBorderInsets.top;
+ break;
+ case TOP:
+ default:
+ tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount,
+ maxTabHeight);
+
+ compX = insets.left + contentBorderInsets.left;
+ compY = tabAreaHeight + insets.top + contentBorderInsets.top;
+ }
+ Rectangle bounds = tabPane.getBounds();
+ int compWidth = bounds.width - tabAreaWidth - insets.left
+ - insets.right - contentBorderInsets.left
+ - contentBorderInsets.right;
+ int compHeight = bounds.height - tabAreaHeight - insets.top
+ - insets.bottom - contentBorderInsets.top
+ - contentBorderInsets.bottom;
+
+
+ for (int i = 0; i < childCount; ++i)
+ {
+ Component c = tabPane.getComponent(i);
+ c.setBounds(compX, compY, compWidth, compHeight);
+ }
+ }
+ }
+
+ /**
+ * This method returns the minimum layout size for the given container.
+ *
+ * @param parent The container that is being sized.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return calculateSize(true);
+ }
+
+ // If there is more free space in an adjacent run AND the tab
+ // in the run can fit in the adjacent run, move it. This method
+ // is not perfect, it is merely an approximation.
+ // If you play around with Sun's JTabbedPane, you'll see that
+ // it does do some pretty strange things with regards to not moving tabs
+ // that should be moved.
+ // start = the x position where the tabs will begin
+ // max = the maximum position of where the tabs can go to
+ // (tabAreaInsets.left + the width of the tab area)
+
+ /**
+ * This method tries to "even out" the number of tabs in each run based on
+ * their widths.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabCount The number of tabs.
+ * @param start The x position where the tabs will begin.
+ * @param max The maximum x position where the tab can run to.
+ */
+ protected void normalizeTabRuns(int tabPlacement, int tabCount, int start,
+ int max)
+ {
+ boolean horizontal = tabPlacement == TOP || tabPlacement == BOTTOM;
+ int currentRun = runCount - 1;
+ double weight = 1.25;
+ for (boolean adjust = true; adjust == true;)
+ {
+ int last = lastTabInRun(tabCount, currentRun);
+ int prevLast = lastTabInRun(tabCount, currentRun - 1);
+ int end;
+ int prevLength;
+ if (horizontal)
+ {
+ end = rects[last].x + rects[last].width;
+ prevLength = (int) (maxTabWidth * weight);
+ }
+ else
+ {
+ end = rects[last].y + rects[last].height;
+ prevLength = (int) (maxTabWidth * weight * 2);
+ }
+ if (max - end > prevLength)
+ {
+ tabRuns[currentRun] = prevLast;
+ if (horizontal)
+ rects[prevLast].x = start;
+ else
+ rects[prevLast].y = start;
+ for (int i = prevLast + 1; i <= last; i++)
+ {
+ if (horizontal)
+ rects[i].x = rects[i - 1].x + rects[i - 1].width;
+ else
+ rects[i].y = rects[i - 1].y + rects[i - 1].height;
+ }
+ }
+ else if (currentRun == runCount - 1)
+ adjust = false;
+ if (currentRun - 1 > 0)
+ currentRun -= 1;
+ else
+ {
+ // Check again, but with higher ratio to avoid
+ // clogging up the last run.
+ currentRun = runCount - 1;
+ weight += 0.25;
+ }
+ }
+ }
+
+ /**
+ * This method pads the tab at the selected index by the selected tab pad
+ * insets (so that it looks larger).
+ *
+ * @param tabPlacement The placement of the tabs.
+ * @param selectedIndex The selected index.
+ */
+ protected void padSelectedTab(int tabPlacement, int selectedIndex)
+ {
+ Insets insets = getSelectedTabPadInsets(tabPlacement);
+ rects[selectedIndex].x -= insets.left;
+ rects[selectedIndex].y -= insets.top;
+ rects[selectedIndex].width += insets.left + insets.right;
+ rects[selectedIndex].height += insets.top + insets.bottom;
+ }
+
+ // If the tabs on the run don't fill the width of the window, make it
+ // fit now.
+ // start = starting index of the run
+ // end = last index of the run
+ // max = tabAreaInsets.left + width (or equivalent)
+ // assert start <= end.
+
+ /**
+ * This method makes each tab in the run larger so that the tabs expand
+ * to fill the runs width/height (depending on tabPlacement).
+ *
+ * @param tabPlacement The placement of the tabs.
+ * @param start The index of the first tab.
+ * @param end The last index of the tab
+ * @param max The amount of space in the run (width for TOP and BOTTOM
+ * tabPlacement).
+ */
+ protected void padTabRun(int tabPlacement, int start, int end, int max)
+ {
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ int runWidth = rects[end].x + rects[end].width;
+ int spaceRemaining = max - runWidth;
+ int numTabs = end - start + 1;
+
+ // now divvy up the space.
+ int spaceAllocated = spaceRemaining / numTabs;
+ int currX = rects[start].x;
+ for (int i = start; i <= end; i++)
+ {
+ rects[i].x = currX;
+ rects[i].width += spaceAllocated;
+
+ currX += rects[i].width;
+ // This is used because since the spaceAllocated
+ // variable is an int, it rounds down. Sometimes,
+ // we don't fill an entire row, so we make it do
+ // so now.
+
+ if (i == end && rects[i].x + rects[i].width != max)
+ rects[i].width = max - rects[i].x;
+ }
+ }
+ else
+ {
+ int runHeight = rects[end].y + rects[end].height;
+ int spaceRemaining = max - runHeight;
+ int numTabs = end - start + 1;
+
+ int spaceAllocated = spaceRemaining / numTabs;
+ int currY = rects[start].y;
+ for (int i = start; i <= end; i++)
+ {
+ rects[i].y = currY;
+ rects[i].height += spaceAllocated;
+ currY += rects[i].height;
+ if (i == end && rects[i].y + rects[i].height != max)
+ rects[i].height = max - rects[i].y;
+ }
+ }
+ }
+
+ /**
+ * This method returns the preferred layout size for the given container.
+ *
+ * @param parent The container to size.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ return calculateSize(false);
+ }
+
+ /**
+ * This method returns the preferred tab height given a tabPlacement and
+ * width.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param width The expected width.
+ *
+ * @return The preferred tab area height.
+ */
+ protected int preferredTabAreaHeight(int tabPlacement, int width)
+ {
+ if (tabPane.getTabCount() == 0)
+ return calculateTabAreaHeight(tabPlacement, 0, 0);
+
+ int runs = 0;
+ int runWidth = 0;
+ int tabWidth = 0;
+
+ FontMetrics fm = getFontMetrics();
+
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ Insets insets = tabPane.getInsets();
+
+ // Only interested in width, this is a messed up rectangle now.
+ width -= tabAreaInsets.left + tabAreaInsets.right + insets.left
+ + insets.right;
+
+ // The reason why we can't use runCount:
+ // This method is only called to calculate the size request
+ // for the tabbedPane. However, this size request is dependent on
+ // our desired width. We need to find out what the height would
+ // be IF we got our desired width.
+ for (int i = 0; i < tabPane.getTabCount(); i++)
+ {
+ tabWidth = calculateTabWidth(tabPlacement, i, fm);
+ if (runWidth + tabWidth > width)
+ {
+ runWidth = tabWidth;
+ runs++;
+ }
+ else
+ runWidth += tabWidth;
+ }
+ runs++;
+
+ int maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
+ maxTabHeight);
+ return tabAreaHeight;
+ }
+
+ /**
+ * This method calculates the preferred tab area width given a tab
+ * placement and height.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param height The expected height.
+ *
+ * @return The preferred tab area width.
+ */
+ protected int preferredTabAreaWidth(int tabPlacement, int height)
+ {
+ if (tabPane.getTabCount() == 0)
+ return calculateTabAreaHeight(tabPlacement, 0, 0);
+
+ int runs = 0;
+ int runHeight = 0;
+ int tabHeight = 0;
+
+ FontMetrics fm = getFontMetrics();
+
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ Insets insets = tabPane.getInsets();
+
+ height -= tabAreaInsets.top + tabAreaInsets.bottom + insets.top
+ + insets.bottom;
+ int fontHeight = fm.getHeight();
+
+ for (int i = 0; i < tabPane.getTabCount(); i++)
+ {
+ tabHeight = calculateTabHeight(tabPlacement, i, fontHeight);
+ if (runHeight + tabHeight > height)
+ {
+ runHeight = tabHeight;
+ runs++;
+ }
+ else
+ runHeight += tabHeight;
+ }
+ runs++;
+
+ int maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs,
+ maxTabWidth);
+ return tabAreaWidth;
+ }
+
+ /**
+ * This method rotates the places each run in the correct place the
+ * tabRuns array. See the comment for tabRuns for how the runs are placed
+ * in the array.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedRun The run the current selection is in.
+ */
+ protected void rotateTabRuns(int tabPlacement, int selectedRun)
+ {
+ if (runCount == 1 || selectedRun == 0 || selectedRun == -1)
+ return;
+ int[] newTabRuns = new int[tabRuns.length];
+ int currentRun = selectedRun;
+ int i = 0;
+ do
+ {
+ newTabRuns[i] = tabRuns[currentRun];
+ currentRun = getNextTabRun(currentRun);
+ i++;
+ }
+ while (i < runCount);
+
+ tabRuns = newTabRuns;
+ BasicTabbedPaneUI.this.selectedRun = 1;
+ }
+
+ /**
+ * This method is called when a component is removed from the
+ * JTabbedPane.
+ *
+ * @param comp The component removed.
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * This class acts as the LayoutManager for the JTabbedPane in
+ * SCROLL_TAB_MODE.
+ */
+ private class TabbedPaneScrollLayout extends TabbedPaneLayout
+ {
+ /**
+ * This method returns the preferred layout size for the given container.
+ *
+ * @param parent The container to calculate a size for.
+ *
+ * @return The preferred layout size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ return super.calculateSize(false);
+ }
+
+ /**
+ * This method returns the minimum layout size for the given container.
+ *
+ * @param parent The container to calculate a size for.
+ *
+ * @return The minimum layout size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return super.calculateSize(true);
+ }
+
+ /**
+ * This method calculates the tab area height given a desired width.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param width The expected width.
+ *
+ * @return The tab area height given the width.
+ */
+ protected int preferredTabAreaHeight(int tabPlacement, int width)
+ {
+ if (tabPane.getTabCount() == 0)
+ return calculateTabAreaHeight(tabPlacement, 0, 0);
+
+ int runs = 1;
+
+ int maxTabHeight = calculateMaxTabHeight(tabPlacement);
+ int tabAreaHeight = calculateTabAreaHeight(tabPlacement, runs,
+ maxTabHeight);
+ return tabAreaHeight;
+ }
+
+ /**
+ * This method calculates the tab area width given a desired height.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param height The expected height.
+ *
+ * @return The tab area width given the height.
+ */
+ protected int preferredTabAreaWidth(int tabPlacement, int height)
+ {
+ if (tabPane.getTabCount() == 0)
+ return calculateTabAreaHeight(tabPlacement, 0, 0);
+
+ int runs = 1;
+
+ int maxTabWidth = calculateMaxTabWidth(tabPlacement);
+ int tabAreaWidth = calculateTabAreaWidth(tabPlacement, runs, maxTabWidth);
+ return tabAreaWidth;
+ }
+
+ /**
+ * This method is called to calculate the tab rectangles. This method
+ * will calculate the size and position of all rectangles (taking into
+ * account which ones should be in which tab run). It will pad them and
+ * normalize them as necessary.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabCount The number of tabs.
+ */
+ protected void calculateTabRects(int tabPlacement, int tabCount)
+ {
+ if (tabCount == 0)
+ return;
+
+ FontMetrics fm = getFontMetrics();
+ SwingUtilities.calculateInnerArea(tabPane, calcRect);
+ Insets tabAreaInsets = getTabAreaInsets(tabPlacement);
+ Insets insets = tabPane.getInsets();
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ int maxHeight = calculateMaxTabHeight(tabPlacement);
+ calcRect.width -= tabAreaInsets.left + tabAreaInsets.right;
+ int width = 0;
+ int runWidth = tabAreaInsets.left + insets.left;
+ int top = insets.top + tabAreaInsets.top;
+ for (int i = 0; i < tabCount; i++)
+ {
+ width = calculateTabWidth(tabPlacement, i, fm);
+
+ // The proper instances should exists because
+ // assureRectsCreated() was being run already.
+ rects[i].setBounds(runWidth, top, width, maxHeight);
+
+ runWidth += width;
+ }
+ tabAreaRect.width = tabPane.getWidth() - insets.left - insets.right;
+ tabAreaRect.height = maxTabHeight + tabAreaInsets.top
+ + tabAreaInsets.bottom;
+ contentRect.width = tabAreaRect.width;
+ contentRect.height = tabPane.getHeight() - insets.top
+ - insets.bottom - tabAreaRect.height;
+ contentRect.x = insets.left;
+ tabAreaRect.x = insets.left;
+ if (tabPlacement == SwingConstants.BOTTOM)
+ {
+ contentRect.y = insets.top;
+ tabAreaRect.y = contentRect.y + contentRect.height;
+ }
+ else
+ {
+ tabAreaRect.y = insets.top;
+ contentRect.y = tabAreaRect.y + tabAreaRect.height;
+ }
+ }
+ else
+ {
+ int maxWidth = calculateMaxTabWidth(tabPlacement);
+
+ calcRect.height -= tabAreaInsets.top + tabAreaInsets.bottom;
+ int height = 0;
+ int runHeight = tabAreaInsets.top + insets.top;
+ int fontHeight = fm.getHeight();
+ int left = insets.left + tabAreaInsets.left;
+ for (int i = 0; i < tabCount; i++)
+ {
+ height = calculateTabHeight(tabPlacement, i, fontHeight);
+
+ // The proper instances should exists because
+ // assureRectsCreated() was being run already.
+ rects[i].setBounds(left, runHeight, maxWidth, height);
+ runHeight += height;
+ }
+ tabAreaRect.width = maxTabWidth + tabAreaInsets.left
+ + tabAreaInsets.right;
+ tabAreaRect.height = tabPane.getHeight() - insets.top
+ - insets.bottom;
+ tabAreaRect.y = insets.top;
+ contentRect.width = tabPane.getWidth() - insets.left - insets.right
+ - tabAreaRect.width;
+ contentRect.height = tabAreaRect.height;
+ contentRect.y = insets.top;
+ if (tabPlacement == SwingConstants.LEFT)
+ {
+ tabAreaRect.x = insets.left;
+ contentRect.x = tabAreaRect.x + tabAreaRect.width;
+ }
+ else
+ {
+ contentRect.x = insets.left;
+ tabAreaRect.x = contentRect.x + contentRect.width;
+ }
+ }
+
+ // Unlike the behavior in the WRAP_TAB_LAYOUT the selected
+ // tab is not padded specially.
+ }
+
+ /**
+ * This method is called when the JTabbedPane is laid out in
+ * SCROLL_TAB_LAYOUT. It finds the position for all components in the
+ * JTabbedPane.
+ *
+ * @param pane The JTabbedPane to be laid out.
+ */
+ public void layoutContainer(Container pane)
+ {
+ super.layoutContainer(pane);
+ int tabCount = tabPane.getTabCount();
+ if (tabCount == 0)
+ return;
+ int tabPlacement = tabPane.getTabPlacement();
+
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ if (tabAreaRect.x + tabAreaRect.width < rects[tabCount - 1].x
+ + rects[tabCount - 1].width)
+ {
+ Dimension incrDims = incrButton.getPreferredSize();
+ Dimension decrDims = decrButton.getPreferredSize();
+
+ if (tabPlacement == SwingConstants.BOTTOM)
+ {
+ // Align scroll buttons with the bottom border of the tabbed
+ // pane's content area.
+ decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width - decrDims.width,
+ tabAreaRect.y, decrDims.width,
+ decrDims.height);
+ incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width, tabAreaRect.y,
+ incrDims.width, incrDims.height);
+ }
+ else
+ {
+ // Align scroll buttons with the top border of the tabbed
+ // pane's content area.
+ decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width - decrDims.width,
+ tabAreaRect.y + tabAreaRect.height
+ - decrDims.height, decrDims.width,
+ decrDims.height);
+ incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height,
+ incrDims.width, incrDims.height);
+ }
+
+ tabAreaRect.width -= decrDims.width + incrDims.width;
+
+ updateButtons();
+
+ incrButton.setVisible(true);
+ decrButton.setVisible(true);
+ }
+ else
+ {
+ incrButton.setVisible(false);
+ decrButton.setVisible(false);
+
+ currentScrollOffset = 0;
+ currentScrollLocation = 0;
+ }
+ }
+
+ if (tabPlacement == SwingConstants.LEFT
+ || tabPlacement == SwingConstants.RIGHT)
+ {
+ if (tabAreaRect.y + tabAreaRect.height < rects[tabCount - 1].y
+ + rects[tabCount - 1].height)
+ {
+ Dimension incrDims = incrButton.getPreferredSize();
+ Dimension decrDims = decrButton.getPreferredSize();
+
+ if (tabPlacement == SwingConstants.RIGHT)
+ {
+ // Align scroll buttons with the right border of the tabbed
+ // pane's content area.
+ decrButton.setBounds(tabAreaRect.x,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height - decrDims.height,
+ decrDims.width, decrDims.height);
+ incrButton.setBounds(tabAreaRect.x,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height, incrDims.width,
+ incrDims.height);
+ }
+ else
+ {
+ // Align scroll buttons with the left border of the tabbed
+ // pane's content area.
+ decrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - decrDims.width,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height - decrDims.height,
+ decrDims.width, decrDims.height);
+ incrButton.setBounds(tabAreaRect.x + tabAreaRect.width
+ - incrDims.width,
+ tabAreaRect.y + tabAreaRect.height
+ - incrDims.height, incrDims.width,
+ incrDims.height);
+ }
+
+ tabAreaRect.height -= decrDims.height + incrDims.height;
+
+ incrButton.setVisible(true);
+ decrButton.setVisible(true);
+ }
+ else
+ {
+ incrButton.setVisible(false);
+ decrButton.setVisible(false);
+
+ currentScrollOffset = 0;
+ currentScrollLocation = 0;
+ }
+ }
+ viewport.setBounds(tabAreaRect.x, tabAreaRect.y, tabAreaRect.width,
+ tabAreaRect.height);
+
+ updateViewPosition();
+
+ viewport.repaint();
+ }
+ }
+
+ /**
+ * This class handles ChangeEvents from the JTabbedPane.
+ *
+ * @specnote Apparently this class was intended to be protected,
+ * but was made public by a compiler bug and is now
+ * public for compatibility.
+ */
+ public class TabSelectionHandler implements ChangeListener
+ {
+ /**
+ * This method is called whenever a ChangeEvent is fired from the
+ * JTabbedPane.
+ *
+ * @param e The ChangeEvent fired.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ selectedRun = getRunForTab(tabPane.getTabCount(),
+ tabPane.getSelectedIndex());
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
+ tabPane.revalidate();
+ tabPane.repaint();
+ }
+ }
+
+ /**
+ * This helper class is a JPanel that fits inside the ScrollViewport. This
+ * panel's sole job is to paint the tab rectangles inside the viewport so
+ * that it's clipped correctly.
+ */
+ private class ScrollingPanel extends JPanel
+ {
+ /**
+ * This is a private UI class for our panel.
+ */
+ private class ScrollingPanelUI extends BasicPanelUI
+ {
+ /**
+ * This method overrides the default paint method. It paints the tab
+ * rectangles for the JTabbedPane in the panel.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ int placement = tabPane.getTabPlacement();
+ g.setColor(highlight);
+ if (placement == SwingUtilities.TOP
+ || placement == SwingUtilities.BOTTOM)
+ g.fillRect(currentScrollOffset, 0,
+ tabAreaRect.width, tabAreaRect.height);
+ else
+ g.fillRect(0, currentScrollOffset,
+ tabAreaRect.width, tabAreaRect.height);
+
+ paintTabArea(g, placement, tabPane.getSelectedIndex());
+ }
+ }
+
+ /**
+ * This method overrides the updateUI method. It makes the default UI for
+ * this ScrollingPanel to be a ScrollingPanelUI.
+ */
+ public void updateUI()
+ {
+ setUI(new ScrollingPanelUI());
+ }
+ }
+
+ /**
+ * This is a helper class that paints the panel that paints tabs. This
+ * custom JViewport is used so that the tabs painted in the panel will be
+ * clipped. This class implements UIResource so tabs are not added when
+ * this objects of this class are added to the JTabbedPane.
+ */
+ private class ScrollingViewport extends JViewport implements UIResource
+ {
+ // TODO: Maybe remove this inner class.
+ }
+
+ /**
+ * This is a helper class that implements UIResource so it is not added as a
+ * tab when an object of this class is added to the JTabbedPane.
+ */
+ private class ScrollingButton extends BasicArrowButton implements UIResource
+ {
+ /**
+ * Creates a ScrollingButton given the direction.
+ *
+ * @param dir The direction to point in.
+ */
+ public ScrollingButton(int dir)
+ {
+ super(dir);
+ }
+ }
+
+ /** The button that increments the current scroll location.
+ * This is package-private to avoid an accessor method. */
+ transient ScrollingButton incrButton;
+
+ /** The button that decrements the current scroll location.
+ * This is package-private to avoid an accessor method. */
+ transient ScrollingButton decrButton;
+
+ /** The viewport used to display the tabs.
+ * This is package-private to avoid an accessor method. */
+ transient ScrollingViewport viewport;
+
+ /** The panel inside the viewport that paints the tabs.
+ * This is package-private to avoid an accessor method. */
+ transient ScrollingPanel panel;
+
+ /** The starting visible tab in the run in SCROLL_TAB_MODE.
+ * This is package-private to avoid an accessor method. */
+ transient int currentScrollLocation;
+
+ transient int currentScrollOffset;
+
+ /** A reusable rectangle. */
+ protected Rectangle calcRect;
+
+ /** An array of Rectangles keeping track of the tabs' area and position. */
+ protected Rectangle[] rects;
+
+ /** The insets around the content area. */
+ protected Insets contentBorderInsets;
+
+ /** The extra insets around the selected tab. */
+ protected Insets selectedTabPadInsets;
+
+ /** The insets around the tab area. */
+ protected Insets tabAreaInsets;
+
+ /** The insets around each and every tab. */
+ protected Insets tabInsets;
+
+ /**
+ * The outer bottom and right edge color for both the tab and content
+ * border.
+ */
+ protected Color darkShadow;
+
+ /** The color of the focus outline on the selected tab. */
+ protected Color focus;
+
+ /** FIXME: find a use for this. */
+ protected Color highlight;
+
+ /** The top and left edge color for both the tab and content border. */
+ protected Color lightHighlight;
+
+ /** The inner bottom and right edge color for the tab and content border. */
+ protected Color shadow;
+
+ /** The maximum tab height. */
+ protected int maxTabHeight;
+
+ /** The maximum tab width. */
+ protected int maxTabWidth;
+
+ /** The number of runs in the JTabbedPane. */
+ protected int runCount;
+
+ /** The index of the run that the selected index is in. */
+ protected int selectedRun;
+
+ /** The amount of space each run overlaps the previous by. */
+ protected int tabRunOverlay;
+
+ /** The gap between text and label */
+ protected int textIconGap;
+
+ /** This array keeps track of which tabs are in which run.
+ * <p>The value at index i denotes the index of the first tab in run i.</p>
+ * <p>If the value for any index (i > 0) is 0 then (i - 1) is the last
+ * run.</p>
+ */
+ protected int[] tabRuns;
+
+ /**
+ * Indicates if the layout of the tab runs is ok or not. This is package
+ * private to avoid a synthetic accessor method.
+ */
+ boolean tabRunsDirty;
+
+ /**
+ * This is the keystroke for moving down.
+ *
+ * @deprecated 1.3
+ */
+ protected KeyStroke downKey;
+
+ /**
+ * This is the keystroke for moving left.
+ *
+ * @deprecated 1.3
+ */
+ protected KeyStroke leftKey;
+
+ /**
+ * This is the keystroke for moving right.
+ *
+ * @deprecated 1.3
+ */
+ protected KeyStroke rightKey;
+
+ /**
+ * This is the keystroke for moving up.
+ *
+ * @deprecated 1.3
+ */
+ protected KeyStroke upKey;
+
+ /** The listener that listens for focus events. */
+ protected FocusListener focusListener;
+
+ /** The listener that listens for mouse events. */
+ protected MouseListener mouseListener;
+
+ /** The listener that listens for property change events. */
+ protected PropertyChangeListener propertyChangeListener;
+
+ /** The listener that listens for change events. */
+ protected ChangeListener tabChangeListener;
+
+ /** The tab pane that this UI paints. */
+ protected JTabbedPane tabPane;
+
+ /** The current layout manager for the tabPane.
+ * This is package-private to avoid an accessor method. */
+ transient LayoutManager layoutManager;
+
+ /** The rectangle that describes the tab area's position and size.
+ * This is package-private to avoid an accessor method. */
+ transient Rectangle tabAreaRect;
+
+ /** The rectangle that describes the content area's position and
+ * size. This is package-private to avoid an accessor method. */
+ transient Rectangle contentRect;
+
+ /**
+ * The index over which the mouse is currently moving.
+ */
+ private int rolloverTab;
+
+ /**
+ * Determines if tabs are painted opaque or not. This can be adjusted using
+ * the UIManager property 'TabbedPane.tabsOpaque'.
+ */
+ private boolean tabsOpaque;
+
+ /**
+ * The currently visible component.
+ */
+ private Component visibleComponent;
+
+ private Color selectedColor;
+
+ private Rectangle tempTextRect = new Rectangle();
+
+ private Rectangle tempIconRect = new Rectangle();
+
+ /**
+ * Creates a new BasicTabbedPaneUI object.
+ */
+ public BasicTabbedPaneUI()
+ {
+ super();
+ rects = new Rectangle[0];
+ tabRuns = new int[10];
+ }
+
+ /**
+ * This method creates a ScrollingButton that points in the appropriate
+ * direction for an increasing button.
+ * This is package-private to avoid an accessor method.
+ *
+ * @return The increase ScrollingButton.
+ */
+ ScrollingButton createIncreaseButton()
+ {
+ if (incrButton == null)
+ incrButton = new ScrollingButton(SwingConstants.NORTH);
+ if (tabPane.getTabPlacement() == SwingConstants.TOP
+ || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
+ incrButton.setDirection(SwingConstants.EAST);
+ else
+ incrButton.setDirection(SwingConstants.SOUTH);
+ return incrButton;
+ }
+
+ /**
+ * This method creates a ScrollingButton that points in the appropriate
+ * direction for a decreasing button.
+ * This is package-private to avoid an accessor method.
+ *
+ * @return The decrease ScrollingButton.
+ */
+ ScrollingButton createDecreaseButton()
+ {
+ if (decrButton == null)
+ decrButton = new ScrollingButton(SwingConstants.SOUTH);
+ if (tabPane.getTabPlacement() == SwingConstants.TOP
+ || tabPane.getTabPlacement() == SwingConstants.BOTTOM)
+ decrButton.setDirection(SwingConstants.WEST);
+ else
+ decrButton.setDirection(SwingConstants.NORTH);
+ return decrButton;
+ }
+
+ /**
+ * This method finds the point to set the view position at given the index
+ * of a tab. The tab will be the first visible tab in the run.
+ * This is package-private to avoid an accessor method.
+ *
+ * @param index The index of the first visible tab.
+ *
+ * @return The position of the first visible tab.
+ */
+ Point findPointForIndex(int index)
+ {
+ int tabPlacement = tabPane.getTabPlacement();
+ int selectedIndex = tabPane.getSelectedIndex();
+ Insets insets = getSelectedTabPadInsets(tabPlacement);
+ int w = 0;
+ int h = 0;
+
+ if (tabPlacement == TOP || tabPlacement == BOTTOM)
+ {
+ if (index > 0)
+ {
+ w += rects[index - 1].x + rects[index - 1].width;
+ if (index > selectedIndex)
+ w -= insets.left + insets.right;
+ }
+ }
+
+ else
+ {
+ if (index > 0)
+ {
+ h += rects[index - 1].y + rects[index - 1].height;
+ if (index > selectedIndex)
+ h -= insets.top + insets.bottom;
+ }
+ }
+
+ Point p = new Point(w, h);
+ return p;
+ }
+
+ /** TabbedPanes in scrolling mode should use this method to
+ * scroll properly to the tab given by the index argument.
+ *
+ * @param index The tab to scroll to.
+ * @param placement The tab's placement.
+ */
+ final void scrollTab(int index, int placement)
+ {
+ int diff;
+ if (index >= 0 && tabPane.isEnabledAt(index))
+ {
+ // If the user clicked on the last tab and that one was
+ // only partially visible shift the scroll offset to make
+ // it completely visible.
+ switch (placement)
+ {
+ case JTabbedPane.TOP:
+ case JTabbedPane.BOTTOM:
+ if ((diff = rects[index].x
+ + rects[index].width
+ - decrButton.getX() - currentScrollOffset) > 0)
+ currentScrollOffset += diff;
+ else if ((diff = rects[index].x - currentScrollOffset) < 0)
+ {
+ if (index == 0)
+ currentScrollOffset = 0;
+ else
+ currentScrollOffset += diff;
+ }
+
+ currentScrollLocation = tabForCoordinate(tabPane,
+ currentScrollOffset,
+ rects[index].y);
+ break;
+ default:
+ if ((diff = rects[index].y + rects[index].height
+ - decrButton.getY() - currentScrollOffset) > 0)
+ currentScrollOffset += diff;
+ else if ((diff = rects[index].y - currentScrollOffset) < 0)
+ {
+ if (index == 0)
+ currentScrollOffset = 0;
+ else
+ currentScrollOffset += diff;
+ }
+
+ currentScrollLocation = tabForCoordinate(tabPane,
+ rects[index].x,
+ currentScrollOffset);
+ }
+
+ updateViewPosition();
+ updateButtons();
+ }
+ }
+
+ /** Sets the enabled state of the increase and decrease button
+ * according to the current scrolling offset and tab pane width
+ * (or height in TOP/BOTTOM placement).
+ */
+ final void updateButtons()
+ {
+ int tc = tabPane.getTabCount();
+
+ // The increase button should be enabled as long as the
+ // right/bottom border of the last tab is under the left/top
+ // border of the decrease button.
+ switch (tabPane.getTabPlacement())
+ {
+ case JTabbedPane.BOTTOM:
+ case JTabbedPane.TOP:
+ incrButton.setEnabled(currentScrollLocation + 1 < tc
+ && rects[tc-1].x + rects[tc-1].width
+ - currentScrollOffset > decrButton.getX());
+ break;
+ default:
+ incrButton.setEnabled(currentScrollLocation + 1 < tc
+ && rects[tc-1].y + rects[tc-1].height
+ - currentScrollOffset > decrButton.getY());
+ }
+
+ // The decrease button is enabled when the tab pane is scrolled in any way.
+ decrButton.setEnabled(currentScrollOffset > 0);
+
+ }
+
+ /**
+ * Updates the position of the scrolling viewport's view
+ * according to the current scroll offset.
+ */
+ 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);
+ }
+
+ /**
+ * This method creates a new BasicTabbedPaneUI.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A new BasicTabbedPaneUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicTabbedPaneUI();
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install the UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ if (c instanceof JTabbedPane)
+ {
+ tabPane = (JTabbedPane) c;
+
+ installComponents();
+ installDefaults();
+ installListeners();
+ installKeyboardActions();
+
+ layoutManager = createLayoutManager();
+ tabPane.setLayout(layoutManager);
+ }
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall the UI for.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ layoutManager = null;
+
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallDefaults();
+ uninstallComponents();
+
+ tabPane = null;
+ }
+
+ /**
+ * This method creates the appropriate layout manager for the JTabbedPane's
+ * current tab layout policy. If the tab layout policy is
+ * SCROLL_TAB_LAYOUT, then all the associated components that need to be
+ * created will be done so now.
+ *
+ * @return A layout manager given the tab layout policy.
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
+ return new TabbedPaneLayout();
+ else
+ {
+ runCount = 1;
+ tabRuns[0] = 0;
+
+ incrButton = createIncreaseButton();
+ incrButton.addMouseListener(mouseListener);
+
+ decrButton = createDecreaseButton();
+ decrButton.addMouseListener(mouseListener);
+ decrButton.setEnabled(false);
+
+ panel = new ScrollingPanel();
+ panel.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ panel.addMouseListener(mouseListener);
+ panel.addFocusListener(focusListener);
+
+ viewport = new ScrollingViewport();
+ viewport.setBackground(Color.LIGHT_GRAY);
+ viewport.setView(panel);
+ viewport.setLayout(null);
+
+ tabPane.add(incrButton);
+ tabPane.add(decrButton);
+ tabPane.add(viewport);
+
+ return new TabbedPaneScrollLayout();
+ }
+ }
+
+ /**
+ * This method installs components for this JTabbedPane.
+ */
+ protected void installComponents()
+ {
+ // Nothing to be done.
+ }
+
+ /**
+ * This method uninstalls components for this JTabbedPane.
+ */
+ protected void uninstallComponents()
+ {
+ if (incrButton != null)
+ tabPane.remove(incrButton);
+
+ if (decrButton != null)
+ tabPane.remove(decrButton);
+
+ if (viewport != null)
+ tabPane.remove(viewport);
+ }
+
+ /**
+ * This method installs defaults for the Look and Feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(tabPane, "TabbedPane.background",
+ "TabbedPane.foreground",
+ "TabbedPane.font");
+ tabPane.setOpaque(false);
+
+ lightHighlight = UIManager.getColor("TabbedPane.highlight");
+ highlight = UIManager.getColor("TabbedPane.light");
+
+ shadow = UIManager.getColor("TabbedPane.shadow");
+ darkShadow = UIManager.getColor("TabbedPane.darkShadow");
+
+ focus = UIManager.getColor("TabbedPane.focus");
+
+ textIconGap = UIManager.getInt("TabbedPane.textIconGap");
+ tabRunOverlay = UIManager.getInt("TabbedPane.tabRunOverlay");
+
+ tabInsets = UIManager.getInsets("TabbedPane.tabInsets");
+ selectedTabPadInsets
+ = UIManager.getInsets("TabbedPane.selectedTabPadInsets");
+ tabAreaInsets = UIManager.getInsets("TabbedPane.tabAreaInsets");
+ contentBorderInsets
+ = UIManager.getInsets("TabbedPane.contentBorderInsets");
+ tabsOpaque = UIManager.getBoolean("TabbedPane.tabsOpaque");
+
+ // Although 'TabbedPane.contentAreaColor' is not defined in the defaults
+ // of BasicLookAndFeel it is used by this class.
+ selectedColor = UIManager.getColor("TabbedPane.contentAreaColor");
+ if (selectedColor == null)
+ selectedColor = UIManager.getColor("control");
+
+ calcRect = new Rectangle();
+ tabRuns = new int[10];
+ tabAreaRect = new Rectangle();
+ contentRect = new Rectangle();
+ }
+
+ /**
+ * This method uninstalls defaults for the Look and Feel.
+ */
+ protected void uninstallDefaults()
+ {
+ calcRect = null;
+ tabAreaRect = null;
+ contentRect = null;
+ tabRuns = null;
+
+ tempIconRect = null;
+ tempTextRect = null;
+
+ contentBorderInsets = null;
+ tabAreaInsets = null;
+ selectedTabPadInsets = null;
+ tabInsets = null;
+
+ focus = null;
+ darkShadow = null;
+ shadow = null;
+ lightHighlight = null;
+ highlight = null;
+
+ selectedColor = null;
+ }
+
+ /**
+ * This method creates and installs the listeners for this UI.
+ */
+ protected void installListeners()
+ {
+ mouseListener = createMouseListener();
+ tabChangeListener = createChangeListener();
+ propertyChangeListener = createPropertyChangeListener();
+ focusListener = createFocusListener();
+
+ tabPane.addMouseListener(mouseListener);
+ tabPane.addChangeListener(tabChangeListener);
+ tabPane.addPropertyChangeListener(propertyChangeListener);
+ tabPane.addFocusListener(focusListener);
+ }
+
+ /**
+ * This method removes and nulls the listeners for this UI.
+ */
+ protected void uninstallListeners()
+ {
+ tabPane.removeFocusListener(focusListener);
+ tabPane.removePropertyChangeListener(propertyChangeListener);
+ tabPane.removeChangeListener(tabChangeListener);
+ tabPane.removeMouseListener(mouseListener);
+
+ if (incrButton != null)
+ incrButton.removeMouseListener(mouseListener);
+
+ if (decrButton != null)
+ decrButton.removeMouseListener(mouseListener);
+
+ if (panel != null)
+ {
+ panel.removeMouseListener(mouseListener);
+ panel.removeFocusListener(focusListener);
+ }
+
+ focusListener = null;
+ propertyChangeListener = null;
+ tabChangeListener = null;
+ mouseListener = null;
+ }
+
+ /**
+ * This method creates a new MouseListener.
+ *
+ * @return A new MouseListener.
+ */
+ protected MouseListener createMouseListener()
+ {
+ return new MouseHandler();
+ }
+
+ /**
+ * This method creates a new FocusListener.
+ *
+ * @return A new FocusListener.
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * This method creates a new ChangeListener.
+ *
+ * @return A new ChangeListener.
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new TabSelectionHandler();
+ }
+
+ /**
+ * This method creates a new PropertyChangeListener.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * This method installs keyboard actions for the JTabbedPane.
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap keyMap = (InputMap) UIManager.get("TabbedPane.focusInputMap");
+ SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, keyMap);
+
+ keyMap = (InputMap) UIManager.get("TabbedPane.ancestorInputMap");
+ SwingUtilities
+ .replaceUIInputMap(tabPane,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ keyMap);
+
+ ActionMap map = getActionMap();
+ SwingUtilities.replaceUIActionMap(tabPane, map);
+ }
+
+ /**
+ * This method uninstalls keyboard actions for the JTabbedPane.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIActionMap(tabPane, null);
+ SwingUtilities.replaceUIInputMap(tabPane, JComponent.WHEN_FOCUSED, null);
+ SwingUtilities
+ .replaceUIInputMap(tabPane,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ null);
+ }
+
+ /**
+ * This method returns the minimum size of the JTabbedPane.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return layoutManager.minimumLayoutSize(tabPane);
+ }
+
+ /**
+ * This method returns the maximum size of the JTabbedPane.
+ *
+ * @param c The JComponent to find a size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return new Dimension(Short.MAX_VALUE, Short.MAX_VALUE);
+ }
+
+ /**
+ * This method paints the JTabbedPane.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ if (!tabPane.isValid())
+ tabPane.validate();
+
+ if (tabPane.getTabCount() == 0)
+ return;
+
+ int index = tabPane.getSelectedIndex();
+ if (index < 0)
+ index = 0;
+
+ int tabPlacement = tabPane.getTabPlacement();
+
+ // Paint the tab area only in WRAP_TAB_LAYOUT Mode from this method
+ // because it is done through the ScrollingViewport.paint() method
+ // for the SCROLL_TAB_LAYOUT mode.
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.WRAP_TAB_LAYOUT)
+ {
+ g.setColor(highlight);
+ g.fillRect(tabAreaRect.x, tabAreaRect.y,
+ tabAreaRect.width, tabAreaRect.height);
+ paintTabArea(g, tabPlacement, index);
+ }
+
+ paintContentBorder(g, tabPlacement, index);
+ }
+
+ /**
+ * This method paints the tab area. This includes painting the rectangles
+ * that make up the tabs.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected index.
+ */
+ protected void paintTabArea(Graphics g, int tabPlacement, int selectedIndex)
+ {
+ // Please note: the ordering of the painting is important.
+ // we WANT to paint the outermost run first and then work our way in.
+
+ // The following drawing code works for both tab layouts.
+ int tabCount = tabPane.getTabCount();
+
+ for (int i = runCount - 1; i >= 0; --i)
+ {
+ int start = tabRuns[i];
+ int next;
+ if (i == runCount - 1)
+ next = tabRuns[0];
+ else
+ next = tabRuns[i + 1];
+ int end = next != 0 ? next - 1 : tabCount - 1;
+ for (int j = start; j <= end; ++j)
+ {
+ if (j != selectedIndex)
+ {
+ paintTab(g, tabPlacement, rects, j,
+ tempIconRect, tempTextRect);
+ }
+ }
+ }
+
+ // Paint selected tab in front of every other tab.
+ if (selectedIndex >= 0)
+ paintTab(g, tabPlacement, rects, selectedIndex,
+ tempIconRect, tempTextRect);
+ }
+
+ /**
+ * This method paints an individual tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param rects The array of rectangles that keep the size and position of
+ * the tabs.
+ * @param tabIndex The tab index to paint.
+ * @param iconRect The rectangle to use for the icon.
+ * @param textRect The rectangle to use for the text.
+ */
+ protected void paintTab(Graphics g, int tabPlacement, Rectangle[] rects,
+ int tabIndex, Rectangle iconRect, Rectangle textRect)
+ {
+ Rectangle rect = rects[tabIndex];
+ boolean isSelected = tabIndex == tabPane.getSelectedIndex();
+ // Paint background if necessary.
+ if (tabsOpaque || tabPane.isOpaque())
+ {
+ paintTabBackground(g, tabPlacement, tabIndex, rect.x, rect.y,
+ rect.width, rect.height, isSelected);
+ }
+
+ // Paint border.
+ paintTabBorder(g, tabPlacement, tabIndex, rect.x, rect.y, rect.width,
+ rect.height, isSelected);
+
+ // Layout label.
+ FontMetrics fm = getFontMetrics();
+ Icon icon = getIconForTab(tabIndex);
+ String title = tabPane.getTitleAt(tabIndex);
+ layoutLabel(tabPlacement, fm, tabIndex, title, icon, rect, iconRect,
+ textRect, isSelected);
+ // Paint the text.
+ paintText(g, tabPlacement, tabPane.getFont(), fm, tabIndex, title,
+ textRect, isSelected);
+
+ // Paint icon if necessary.
+ paintIcon(g, tabPlacement, tabIndex, icon, iconRect, isSelected);
+
+ // Paint focus indicator.
+ paintFocusIndicator(g, tabPlacement, rects, tabIndex, iconRect, textRect,
+ isSelected);
+ }
+
+ /**
+ * This method lays out the tab and finds the location to paint the icon
+ * and text.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param metrics The font metrics for the font to paint with.
+ * @param tabIndex The tab index to paint.
+ * @param title The string painted.
+ * @param icon The icon painted.
+ * @param tabRect The tab bounds.
+ * @param iconRect The calculated icon bounds.
+ * @param textRect The calculated text bounds.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void layoutLabel(int tabPlacement, FontMetrics metrics,
+ int tabIndex, String title, Icon icon,
+ Rectangle tabRect, Rectangle iconRect,
+ Rectangle textRect, boolean isSelected)
+ {
+ // 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,
+ SwingConstants.RIGHT, tabRect,
+ iconRect, textRect, textIconGap);
+
+ int shiftX = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
+ int shiftY = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
+
+ iconRect.x += shiftX;
+ iconRect.y += shiftY;
+
+ textRect.x += shiftX;
+ textRect.y += shiftY;
+ }
+
+ /**
+ * This method paints the icon.
+ *
+ * @param g The Graphics object to paint.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index to paint.
+ * @param icon The icon to paint.
+ * @param iconRect The bounds of the icon.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void paintIcon(Graphics g, int tabPlacement, int tabIndex,
+ Icon icon, Rectangle iconRect, boolean isSelected)
+ {
+ if (icon != null)
+ icon.paintIcon(tabPane, g, iconRect.x, iconRect.y);
+ }
+
+ /**
+ * This method paints the text for the given tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param font The font to paint with.
+ * @param metrics The fontmetrics of the given font.
+ * @param tabIndex The tab index.
+ * @param title The string to paint.
+ * @param textRect The bounds of the string.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void paintText(Graphics g, int tabPlacement, Font font,
+ FontMetrics metrics, int tabIndex, String title,
+ Rectangle textRect, boolean isSelected)
+ {
+ g.setFont(font);
+ View textView = getTextViewForTab(tabIndex);
+ if (textView != null)
+ {
+ textView.paint(g, textRect);
+ return;
+ }
+
+ int ascent = metrics.getAscent();
+
+ int mnemIndex = tabPane.getDisplayedMnemonicIndexAt(tabIndex);
+ if (tabPane.isEnabled() && tabPane.isEnabledAt(tabIndex))
+ {
+ Color fg = tabPane.getForegroundAt(tabIndex);
+ if (isSelected && (fg instanceof UIResource))
+ {
+ Color selectionForeground =
+ UIManager.getColor("TabbedPane.selectionForeground");
+ if (selectionForeground != null)
+ fg = selectionForeground;
+ }
+ g.setColor(fg);
+
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
+ textRect.x,
+ textRect.y + ascent);
+ else
+ g.drawString(title, textRect.x, textRect.y + ascent);
+ }
+ else
+ {
+ Color bg = tabPane.getBackgroundAt(tabIndex);
+ g.setColor(bg.brighter());
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
+ textRect.x, textRect.y
+ + ascent);
+ else
+ g.drawString(title, textRect.x, textRect.y + ascent);
+
+ g.setColor(bg.darker());
+ if (mnemIndex != -1)
+ BasicGraphicsUtils.drawStringUnderlineCharAt(g, title, mnemIndex,
+ textRect.x + 1,
+ textRect.y + 1
+ + ascent);
+ else
+ g.drawString(title, textRect.x + 1, textRect.y + 1 + ascent);
+ }
+ }
+
+ /**
+ * This method returns how much the label for the tab should shift in the X
+ * direction.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index being painted.
+ * @param isSelected Whether this tab is selected.
+ *
+ * @return The amount the label should shift by in the X direction.
+ */
+ protected int getTabLabelShiftX(int tabPlacement, int tabIndex,
+ boolean isSelected)
+ {
+ switch (tabPlacement)
+ {
+ default:
+ case SwingUtilities.TOP:
+ case SwingUtilities.BOTTOM:
+ return 1;
+ case SwingUtilities.LEFT:
+ return (isSelected) ? -1 : 1;
+ case SwingUtilities.RIGHT:
+ return (isSelected) ? 1 : -1;
+ }
+ }
+
+ /**
+ * This method returns how much the label for the tab should shift in the Y
+ * direction.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index being painted.
+ * @param isSelected Whether this tab is selected.
+ *
+ * @return The amount the label should shift by in the Y direction.
+ */
+ protected int getTabLabelShiftY(int tabPlacement, int tabIndex,
+ boolean isSelected)
+ {
+ switch (tabPlacement)
+ {
+ default:
+ case SwingUtilities.TOP:
+ return (isSelected) ? -1 : 1;
+ case SwingUtilities.BOTTOM:
+ return (isSelected) ? 1 : -1;
+ case SwingUtilities.LEFT:
+ case SwingUtilities.RIGHT:
+ return 0;
+ }
+ }
+
+ /**
+ * This method paints the focus rectangle around the selected tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param rects The array of rectangles keeping track of size and position.
+ * @param tabIndex The tab index.
+ * @param iconRect The icon bounds.
+ * @param textRect The text bounds.
+ * @param isSelected Whether this tab is selected.
+ */
+ protected void paintFocusIndicator(Graphics g, int tabPlacement,
+ Rectangle[] rects, int tabIndex,
+ Rectangle iconRect, Rectangle textRect,
+ boolean isSelected)
+ {
+ if (tabPane.hasFocus() && isSelected)
+ {
+ Rectangle rect = rects[tabIndex];
+ // The focus rectangle.
+ int x;
+ int y;
+ int w;
+ int h;
+
+ g.setColor(focus);
+ switch (tabPlacement)
+ {
+ case LEFT:
+ x = rect.x + 3;
+ y = rect.y + 3;
+ w = rect.width - 5;
+ h = rect.height - 6;
+ break;
+ case RIGHT:
+ x = rect.x + 2;
+ y = rect.y + 3;
+ w = rect.width - 6;
+ h = rect.height - 5;
+ break;
+ case BOTTOM:
+ x = rect.x + 3;
+ y = rect.y + 2;
+ w = rect.width - 6;
+ h = rect.height - 5;
+ break;
+ case TOP:
+ default:
+ x = rect.x + 3;
+ y = rect.y + 3;
+ w = rect.width - 6;
+ h = rect.height - 5;
+ }
+
+ BasicGraphicsUtils.drawDashedRect(g, x, y, w, h);
+ }
+ }
+
+ /**
+ * This method paints the border for an individual tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index.
+ * @param x The x position of the tab.
+ * @param y The y position of the tab.
+ * @param w The width of the tab.
+ * @param h The height of the tab.
+ * @param isSelected Whether the tab is selected.
+ */
+ protected void paintTabBorder(Graphics g, int tabPlacement, int tabIndex,
+ int x, int y, int w, int h, boolean isSelected)
+ {
+ Color saved = g.getColor();
+
+ switch (tabPlacement)
+ {
+ case SwingConstants.TOP:
+ g.setColor(shadow);
+ // Inner right line.
+ g.drawLine(x + w - 2, y + 2, x + w - 2, y + h);
+
+ g.setColor(darkShadow);
+ // Outer right line.
+ g.drawLine(x + w - 1, y + 2, x + w - 1, y + h);
+
+ // Upper right corner.
+ g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2);
+
+ g.setColor(lightHighlight);
+
+ // Left line.
+ g.drawLine(x, y + 3, x, y + h);
+
+ // Upper line.
+ g.drawLine(x + 3, y, x + w - 3, y);
+
+ // Upper left corner.
+ g.drawLine(x, y + 2, x + 2, y);
+
+ break;
+ case SwingConstants.LEFT:
+ g.setColor(lightHighlight);
+ // Top line.
+ g.drawLine(x + 3, y, x + w - 1, y);
+
+ // Top left border.
+ g.drawLine(x + 2, y, x, y + 2);
+
+ // Left line.
+ g.drawLine(x, y + 3, x, y + h - 4);
+
+ // Bottom left corner.
+ g.drawLine(x, y + h - 3, x + 1, y + h - 2);
+
+ g.setColor(darkShadow);
+ // Outer bottom line.
+ g.drawLine(x + 2, y + h - 1, x + w - 1, y + h - 1);
+
+ g.setColor(shadow);
+ // Inner bottom line.
+ g.drawLine(x + 2, y + h - 2, x + w - 1, y + h - 2);
+
+ break;
+ case SwingConstants.BOTTOM:
+ g.setColor(shadow);
+ // Inner right line.
+ g.drawLine(x + w - 2, y, x + w - 2, y + h - 2);
+
+ // Inner bottom line.
+ g.drawLine(x + 2, y + h - 1, x + w - 3, y + h - 1);
+
+ g.setColor(darkShadow);
+ // Outer right line.
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 3);
+
+ // Bottom right corner.
+ g.drawLine(x + w - 1, y + h - 2, x + w - 3, y + h);
+
+ // Bottom line.
+ g.drawLine(x + 2, y + h, x + w - 4, y + h);
+
+ g.setColor(lightHighlight);
+ // Left line.
+ g.drawLine(x, y, x, y + h - 3);
+
+ // Bottom left corner.
+ g.drawLine(x, y + h - 2, x + 1, y + h - 1);
+ break;
+ case SwingConstants.RIGHT:
+ g.setColor(lightHighlight);
+ // Top line.
+ g.drawLine(x, y, x + w - 3, y);
+
+ g.setColor(darkShadow);
+ // Top right corner.
+ g.drawLine(x + w - 2, y + 1, x + w - 1, y + 2);
+
+ // Outer right line.
+ g.drawLine(x + w - 1, y + 3, x + w - 1, y + h - 3);
+
+ // Bottom right corner.
+ g.drawLine(x + w - 2, y + h - 2, x + w - 3, y + h - 1);
+
+ // Bottom line.
+ g.drawLine(x, y + h - 1, x + w - 4, y + h - 1);
+
+ g.setColor(shadow);
+
+ // Inner right line.
+ g.drawLine(x + w - 2, y + 2, x + w - 2, y + h - 3);
+
+ // Inner bottom line.
+ g.drawLine(x, y + h - 2, x + w - 3, y + h - 2);
+
+ break;
+ }
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the background for an individual tab.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index.
+ * @param x The x position of the tab.
+ * @param y The y position of the tab.
+ * @param w The width of the tab.
+ * @param h The height of the tab.
+ * @param isSelected Whether the tab is selected.
+ */
+ protected void paintTabBackground(Graphics g, int tabPlacement,
+ int tabIndex, int x, int y, int w, int h,
+ boolean isSelected)
+ {
+ Color saved = g.getColor();
+
+ if (isSelected)
+ g.setColor(selectedColor);
+ else
+ {
+ Color bg = tabPane.getBackgroundAt(tabIndex);
+ if (bg == null)
+ bg = Color.LIGHT_GRAY;
+ g.setColor(bg);
+ }
+
+ switch (tabPlacement)
+ {
+ case SwingConstants.TOP:
+ g.fillRect(x + 1, y + 1, w - 1, h - 1);
+ break;
+ case SwingConstants.BOTTOM:
+ g.fillRect(x, y, w - 1, h - 1);
+ break;
+ case SwingConstants.LEFT:
+ g.fillRect(x + 1, y + 1, w - 1, h - 2);
+ break;
+ case SwingConstants.RIGHT:
+ g.fillRect(x, y + 1, w - 1, h - 2);
+ break;
+ }
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the border around the content area.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The index of the selected tab.
+ */
+ protected void paintContentBorder(Graphics g, int tabPlacement,
+ int selectedIndex)
+ {
+ int width = tabPane.getWidth();
+ int height = tabPane.getHeight();
+ Insets insets = tabPane.getInsets();
+
+ // Calculate coordinates of content area.
+ int x = insets.left;
+ int y = insets.top;
+ int w = width - insets.left - insets.right;
+ int h = height - insets.top - insets.bottom;
+
+ switch (tabPlacement)
+ {
+ case LEFT:
+ x += calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
+ w -= x - insets.left;
+ break;
+ case RIGHT:
+ w -= calculateTabAreaWidth(tabPlacement, runCount, maxTabWidth);
+ break;
+ case BOTTOM:
+ h -= calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
+ break;
+ case TOP:
+ default:
+ y += calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight);
+ h -= y - insets.top;
+ }
+
+ // Fill background if necessary.
+ if (tabPane.isOpaque())
+ {
+ Color bg = UIManager.getColor("TabbedPane.contentAreaColor");
+ g.setColor(bg);
+ g.fillRect(x, y, w, h);
+ }
+
+ // Paint border.
+ paintContentBorderTopEdge(g, tabPlacement, selectedIndex, x, y, w, h);
+ paintContentBorderLeftEdge(g, tabPlacement, selectedIndex, x, y, w, h);
+ paintContentBorderBottomEdge(g, tabPlacement, selectedIndex, x, y, w, h);
+ paintContentBorderRightEdge(g, tabPlacement, selectedIndex, x, y, w, h);
+ }
+
+ /**
+ * This method paints the top edge of the content border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected tab index.
+ * @param x The x coordinate for the content area.
+ * @param y The y coordinate for the content area.
+ * @param w The width of the content area.
+ * @param h The height of the content area.
+ */
+ protected void paintContentBorderTopEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color saved = g.getColor();
+ g.setColor(lightHighlight);
+
+ int startgap = rects[selectedIndex].x - currentScrollOffset;
+ int endgap = rects[selectedIndex].x + rects[selectedIndex].width
+ - currentScrollOffset;
+
+ // Paint the highlight line with a gap if the tabs are at the top
+ // and the selected tab is inside the visible area.
+ if (tabPlacement == SwingConstants.TOP && startgap >= 0)
+ {
+ g.drawLine(x, y, startgap, y);
+ g.drawLine(endgap, y, x + w - 1, y);
+
+ g.setColor(selectedColor);
+ g.drawLine(startgap, y, endgap - 1, y);
+ }
+ else
+ g.drawLine(x, y, x + w, y);
+
+ g.setColor(selectedColor);
+ g.drawLine(x, y + 1, x + w - 1, y + 1);
+ g.drawLine(x, y + 2, x + w - 1, y + 2);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the left edge of the content border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected tab index.
+ * @param x The x coordinate for the content area.
+ * @param y The y coordinate for the content area.
+ * @param w The width of the content area.
+ * @param h The height of the content area.
+ */
+ protected void paintContentBorderLeftEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color saved = g.getColor();
+ g.setColor(lightHighlight);
+
+ int startgap = rects[selectedIndex].y - currentScrollOffset;
+ int endgap = rects[selectedIndex].y + rects[selectedIndex].height
+ - currentScrollOffset;
+
+ if (tabPlacement == SwingConstants.LEFT && startgap >= 0)
+ {
+ g.drawLine(x, y, x, startgap);
+ g.drawLine(x, endgap, x, y + h - 1);
+
+ g.setColor(selectedColor);
+ g.drawLine(x, startgap, x, endgap - 1);
+ }
+ else
+ g.drawLine(x, y, x, y + h - 1);
+
+ g.setColor(selectedColor);
+ g.drawLine(x + 1, y + 1, x + 1, y + h - 4);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the bottom edge of the content border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected tab index.
+ * @param x The x coordinate for the content area.
+ * @param y The y coordinate for the content area.
+ * @param w The width of the content area.
+ * @param h The height of the content area.
+ */
+ protected void paintContentBorderBottomEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color saved = g.getColor();
+
+ int startgap = rects[selectedIndex].x - currentScrollOffset;
+ int endgap = rects[selectedIndex].x + rects[selectedIndex].width
+ - currentScrollOffset;
+
+ if (tabPlacement == SwingConstants.BOTTOM && startgap >= 0)
+ {
+ g.setColor(shadow);
+ g.drawLine(x + 1, y + h - 2, startgap, y + h - 2);
+ g.drawLine(endgap, y + h - 2, x + w - 2, y + h - 2);
+
+ g.setColor(darkShadow);
+ g.drawLine(x, y + h - 1, startgap , y + h - 1);
+ g.drawLine(endgap, y + h - 1, x + w - 1, y + h - 1);
+
+ g.setColor(selectedColor);
+ g.drawLine(startgap, y + h - 1, endgap - 1, y + h - 1);
+ g.drawLine(startgap, y + h - 2, endgap - 1, y + h - 2);
+ }
+ else
+ {
+ g.setColor(shadow);
+ g.drawLine(x + 1, y + h - 2, x + w - 1, y + h - 2);
+ g.setColor(darkShadow);
+ g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
+ }
+
+ g.setColor(selectedColor);
+ g.drawLine(x + 1, y + h - 3, x + w - 2, y + h - 3);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the right edge of the content border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param selectedIndex The selected tab index.
+ * @param x The x coordinate for the content area.
+ * @param y The y coordinate for the content area.
+ * @param w The width of the content area.
+ * @param h The height of the content area.
+ */
+ protected void paintContentBorderRightEdge(Graphics g, int tabPlacement,
+ int selectedIndex, int x, int y,
+ int w, int h)
+ {
+ Color saved = g.getColor();
+ int startgap = rects[selectedIndex].y - currentScrollOffset;
+ int endgap = rects[selectedIndex].y + rects[selectedIndex].height
+ - currentScrollOffset;
+
+ if (tabPlacement == SwingConstants.RIGHT && startgap >= 0)
+ {
+ g.setColor(shadow);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, startgap);
+ g.drawLine(x + w - 2, endgap, x + w - 2, y + h - 2);
+
+ g.setColor(darkShadow);
+ g.drawLine(x + w - 1, y, x + w - 1, startgap);
+ g.drawLine(x + w - 1, endgap, x + w - 1, y + h - 2);
+
+ g.setColor(selectedColor);
+ g.drawLine(x + w - 2, startgap, x + w - 2, endgap - 1);
+ g.drawLine(x + w - 1, startgap, x + w - 1, endgap - 1);
+ }
+ else
+ {
+ g.setColor(shadow);
+ g.drawLine(x + w - 2, y + 1, x + w - 2, y + h - 2);
+ g.setColor(darkShadow);
+ g.drawLine(x + w - 1, y, x + w - 1, y + h - 2);
+ }
+
+ g.setColor(selectedColor);
+ g.drawLine(x + w - 3, y + 1, x + w - 3, y + h - 4);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * <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.
+ *
+ * @return The bounds of the tab with the given index.
+ */
+ public Rectangle getTabBounds(JTabbedPane pane, int i)
+ {
+ // 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];
+ }
+
+ /**
+ * This method returns the number of runs.
+ *
+ * @param pane The JTabbedPane.
+ *
+ * @return The number of runs.
+ */
+ public int getTabRunCount(JTabbedPane pane)
+ {
+ return runCount;
+ }
+
+ /**
+ * This method returns the tab index given a coordinate.
+ *
+ * @param pane The JTabbedPane.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ *
+ * @return The tab index that the coordinate lands in.
+ */
+ public int tabForCoordinate(JTabbedPane pane, int x, int y)
+ {
+ // Note: This code is tab layout mode agnostic.
+ if (! tabPane.isValid())
+ tabPane.validate();
+
+ int tabCount = tabPane.getTabCount();
+
+ // If the user clicked outside of any tab rect the
+ // selection should not change.
+ int index = tabPane.getSelectedIndex();
+ for (int i = 0; i < tabCount; ++i)
+ {
+ if (rects[i].contains(x, y))
+ {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * <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.
+ *
+ * @return The rectangle passed in.
+ */
+ protected Rectangle getTabBounds(int tabIndex, Rectangle dest)
+ {
+ dest.setBounds(getTabBounds(tabPane, tabIndex));
+ return dest;
+ }
+
+ /**
+ * This method returns the component that is shown in the content area.
+ *
+ * @return The component that is shown in the content area.
+ */
+ protected Component getVisibleComponent()
+ {
+ return visibleComponent;
+ }
+
+ /**
+ * This method sets the visible component.
+ *
+ * @param component The component to be set visible.
+ */
+ protected void setVisibleComponent(Component component)
+ {
+ // Make old component invisible.
+ if (visibleComponent != null && visibleComponent != component
+ && visibleComponent.getParent() == tabPane)
+ {
+ visibleComponent.setVisible(false);
+ }
+
+ // Make new component visible.
+ if (component != null && ! component.isVisible())
+ {
+ component.setVisible(true);
+ }
+ visibleComponent = component;
+ }
+
+ /**
+ * This method assures that enough rectangles are created given the
+ * tabCount. The old array is copied to the new one.
+ *
+ * @param tabCount The number of tabs.
+ */
+ protected void assureRectsCreated(int tabCount)
+ {
+ if (rects.length < tabCount)
+ {
+ Rectangle[] old = rects;
+ rects = new Rectangle[tabCount];
+ System.arraycopy(old, 0, rects, 0, old.length);
+ for (int i = old.length; i < rects.length; i++)
+ rects[i] = new Rectangle();
+ }
+ }
+
+ /**
+ * This method expands the tabRuns array to give it more room. The old array
+ * is copied to the new one.
+ */
+ protected void expandTabRunsArray()
+ {
+ // This method adds another 10 index positions to the tabRuns array.
+ if (tabRuns == null)
+ tabRuns = new int[10];
+ else
+ {
+ int[] newRuns = new int[tabRuns.length + 10];
+ System.arraycopy(tabRuns, 0, newRuns, 0, tabRuns.length);
+ tabRuns = newRuns;
+ }
+ }
+
+ /**
+ * This method returns which run a particular tab belongs to.
+ *
+ * @param tabCount The number of tabs.
+ * @param tabIndex The tab to find.
+ *
+ * @return The tabRuns index that it belongs to.
+ */
+ protected int getRunForTab(int tabCount, int tabIndex)
+ {
+ if (runCount == 1 && tabIndex < tabCount && tabIndex >= 0)
+ return 0;
+ for (int i = 0; i < runCount; i++)
+ {
+ int first = lastTabInRun(tabCount, getPreviousTabRun(i)) + 1;
+ if (first == tabCount)
+ first = 0;
+ int last = lastTabInRun(tabCount, i);
+ if (last >= tabIndex && first <= tabIndex)
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * This method returns the index of the last tab in a run.
+ *
+ * @param tabCount The number of tabs.
+ * @param run The run to check.
+ *
+ * @return The last tab in the given run.
+ */
+ protected int lastTabInRun(int tabCount, int run)
+ {
+ int lastTab;
+ if (runCount == 1)
+ lastTab = tabCount - 1;
+ else
+ {
+ int nextRun;
+ if (run == runCount - 1)
+ nextRun = 0;
+ else
+ nextRun = run + 1;
+
+ if (tabRuns[nextRun] == 0)
+ lastTab = tabCount - 1;
+ else
+ lastTab = tabRuns[nextRun] - 1;
+ }
+ return lastTab;
+ }
+
+ /**
+ * This method returns the tab run overlay.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The tab run overlay.
+ */
+ protected int getTabRunOverlay(int tabPlacement)
+ {
+ return tabRunOverlay;
+ }
+
+ /**
+ * This method returns the tab run indent. It is used in WRAP_TAB_LAYOUT and
+ * makes each tab run start indented by a certain amount.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param run The run to get indent for.
+ *
+ * @return The amount a run should be indented.
+ */
+ protected int getTabRunIndent(int tabPlacement, int run)
+ {
+ return 0;
+ }
+
+ /**
+ * This method returns whether a tab run should be padded.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param run The run to check.
+ *
+ * @return Whether the given run should be padded.
+ */
+ protected boolean shouldPadTabRun(int tabPlacement, int run)
+ {
+ return true;
+ }
+
+ /**
+ * This method returns whether the tab runs should be rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return Whether runs should be rotated.
+ */
+ protected boolean shouldRotateTabRuns(int tabPlacement)
+ {
+ return true;
+ }
+
+ /**
+ * This method returns an icon for the tab. If the tab is disabled, it
+ * should return the disabledIcon. If it is enabled, then it should return
+ * the default icon.
+ *
+ * @param tabIndex The tab index to get an icon for.
+ *
+ * @return The icon for the tab index.
+ */
+ protected Icon getIconForTab(int tabIndex)
+ {
+ if (tabPane.isEnabledAt(tabIndex))
+ return tabPane.getIconAt(tabIndex);
+ else
+ return tabPane.getDisabledIconAt(tabIndex);
+ }
+
+ /**
+ * This method returns a view that can paint the text for the label.
+ *
+ * @param tabIndex The tab index to get a view for.
+ *
+ * @return The view for the tab index.
+ */
+ protected View getTextViewForTab(int tabIndex)
+ {
+ // FIXME: When the label contains HTML this should return something
+ // non-null.
+ return null;
+ }
+
+ /**
+ * This method returns the tab height, including insets, for the given index
+ * and fontheight.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The index of the tab to calculate.
+ * @param fontHeight The font height.
+ *
+ * @return This tab's height.
+ */
+ protected int calculateTabHeight(int tabPlacement, int tabIndex,
+ int fontHeight)
+ {
+ // FIXME: Handle HTML by using the view (see getTextViewForTab).
+
+ int height = fontHeight;
+ Icon icon = getIconForTab(tabIndex);
+ Insets tabInsets = getTabInsets(tabPlacement, tabIndex);
+ if (icon != null)
+ height = Math.max(height, icon.getIconHeight());
+ height += tabInsets.top + tabInsets.bottom + 2;
+ return height;
+ }
+
+ /**
+ * This method returns the max tab height.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The maximum tab height.
+ */
+ protected int calculateMaxTabHeight(int tabPlacement)
+ {
+ maxTabHeight = 0;
+
+ FontMetrics fm = getFontMetrics();
+ int fontHeight = fm.getHeight();
+
+ for (int i = 0; i < tabPane.getTabCount(); i++)
+ maxTabHeight = Math.max(calculateTabHeight(tabPlacement, i, fontHeight),
+ maxTabHeight);
+
+ return maxTabHeight;
+ }
+
+ /**
+ * This method calculates the tab width, including insets, for the given tab
+ * index and font metrics.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index to calculate for.
+ * @param metrics The font's metrics.
+ *
+ * @return The tab width for the given index.
+ */
+ protected int calculateTabWidth(int tabPlacement, int tabIndex,
+ FontMetrics metrics)
+ {
+ Icon icon = getIconForTab(tabIndex);
+ Insets insets = getTabInsets(tabPlacement, tabIndex);
+
+ int width = insets.bottom + insets.right + 3;
+ if (icon != null)
+ {
+ width += icon.getIconWidth() + textIconGap;
+ }
+
+ 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;
+ }
+
+ /**
+ * This method calculates the max tab width.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The maximum tab width.
+ */
+ protected int calculateMaxTabWidth(int tabPlacement)
+ {
+ maxTabWidth = 0;
+
+ FontMetrics fm = getFontMetrics();
+
+ for (int i = 0; i < tabPane.getTabCount(); i++)
+ maxTabWidth = Math.max(calculateTabWidth(tabPlacement, i, fm),
+ maxTabWidth);
+
+ return maxTabWidth;
+ }
+
+ /**
+ * This method calculates the tab area height, including insets, for the
+ * given amount of runs and tab height.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param horizRunCount The number of runs.
+ * @param maxTabHeight The max tab height.
+ *
+ * @return The tab area height.
+ */
+ protected int calculateTabAreaHeight(int tabPlacement, int horizRunCount,
+ int maxTabHeight)
+ {
+ Insets insets = getTabAreaInsets(tabPlacement);
+ int tabAreaHeight = horizRunCount * maxTabHeight
+ - (horizRunCount - 1)
+ * getTabRunOverlay(tabPlacement);
+
+ tabAreaHeight += insets.top + insets.bottom;
+
+ return tabAreaHeight;
+ }
+
+ /**
+ * This method calculates the tab area width, including insets, for the
+ * given amount of runs and tab width.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param vertRunCount The number of runs.
+ * @param maxTabWidth The max tab width.
+ *
+ * @return The tab area width.
+ */
+ protected int calculateTabAreaWidth(int tabPlacement, int vertRunCount,
+ int maxTabWidth)
+ {
+ Insets insets = getTabAreaInsets(tabPlacement);
+ int tabAreaWidth = vertRunCount * maxTabWidth
+ - (vertRunCount - 1)
+ * getTabRunOverlay(tabPlacement);
+
+ tabAreaWidth += insets.left + insets.right;
+
+ return tabAreaWidth;
+ }
+
+ /**
+ * This method returns the tab insets appropriately rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab index.
+ *
+ * @return The tab insets for the given index.
+ */
+ protected Insets getTabInsets(int tabPlacement, int tabIndex)
+ {
+ return tabInsets;
+ }
+
+ /**
+ * This method returns the selected tab pad insets appropriately rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The selected tab pad insets.
+ */
+ protected Insets getSelectedTabPadInsets(int tabPlacement)
+ {
+ Insets target = new Insets(0, 0, 0, 0);
+ rotateInsets(selectedTabPadInsets, target, tabPlacement);
+ return target;
+ }
+
+ /**
+ * This method returns the tab area insets appropriately rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The tab area insets.
+ */
+ protected Insets getTabAreaInsets(int tabPlacement)
+ {
+ Insets target = new Insets(0, 0, 0, 0);
+ rotateInsets(tabAreaInsets, target, tabPlacement);
+ return target;
+ }
+
+ /**
+ * This method returns the content border insets appropriately rotated.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ *
+ * @return The content border insets.
+ */
+ protected Insets getContentBorderInsets(int tabPlacement)
+ {
+ Insets target = new Insets(0, 0, 0, 0);
+ rotateInsets(contentBorderInsets, target, tabPlacement);
+ return target;
+ }
+
+ /**
+ * This method returns the fontmetrics for the font of the JTabbedPane.
+ *
+ * @return The font metrics for the JTabbedPane.
+ */
+ protected FontMetrics getFontMetrics()
+ {
+ FontMetrics fm = tabPane.getFontMetrics(tabPane.getFont());
+ return fm;
+ }
+
+ /**
+ * This method navigates from the selected tab into the given direction. As
+ * a result, a new tab will be selected (if possible).
+ *
+ * @param direction The direction to navigate in.
+ */
+ protected void navigateSelectedTab(int direction)
+ {
+ int tabPlacement = tabPane.getTabPlacement();
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ {
+ if (direction == SwingConstants.WEST)
+ selectPreviousTabInRun(tabPane.getSelectedIndex());
+ else if (direction == SwingConstants.EAST)
+ selectNextTabInRun(tabPane.getSelectedIndex());
+
+ else
+ {
+ int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
+ tabPane.getSelectedIndex(),
+ (tabPlacement == SwingConstants.TOP)
+ ? direction == SwingConstants.NORTH
+ : direction == SwingConstants.SOUTH);
+ selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
+ offset);
+ }
+ }
+ if (tabPlacement == SwingConstants.LEFT
+ || tabPlacement == SwingConstants.RIGHT)
+ {
+ if (direction == SwingConstants.NORTH)
+ selectPreviousTabInRun(tabPane.getSelectedIndex());
+ else if (direction == SwingConstants.SOUTH)
+ selectNextTabInRun(tabPane.getSelectedIndex());
+ else
+ {
+ int offset = getTabRunOffset(tabPlacement, tabPane.getTabCount(),
+ tabPane.getSelectedIndex(),
+ (tabPlacement == SwingConstants.LEFT)
+ ? direction == SwingConstants.WEST
+ : direction == SwingConstants.EAST);
+ selectAdjacentRunTab(tabPlacement, tabPane.getSelectedIndex(),
+ offset);
+ }
+ }
+ }
+
+ /**
+ * This method selects the next tab in the run.
+ *
+ * @param current The current selected index.
+ */
+ protected void selectNextTabInRun(int current)
+ {
+ current = getNextTabIndexInRun(tabPane.getTabCount(),
+ current);
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(current, tabPane.getTabPlacement());
+
+ tabPane.setSelectedIndex(current);
+ }
+
+ /**
+ * This method selects the previous tab in the run.
+ *
+ * @param current The current selected index.
+ */
+ protected void selectPreviousTabInRun(int current)
+ {
+ current = getPreviousTabIndexInRun(tabPane.getTabCount(),
+ current);
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(current, tabPane.getTabPlacement());
+
+ tabPane.setSelectedIndex(current);
+ }
+
+ /**
+ * This method selects the next tab (regardless of runs).
+ *
+ * @param current The current selected index.
+ */
+ protected void selectNextTab(int current)
+ {
+ current = getNextTabIndex(current);
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(current, tabPane.getTabPlacement());
+
+ tabPane.setSelectedIndex(current);
+ }
+
+ /**
+ * This method selects the previous tab (regardless of runs).
+ *
+ * @param current The current selected index.
+ */
+ protected void selectPreviousTab(int current)
+ {
+ current = getPreviousTabIndex(current);
+
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(current, tabPane.getTabPlacement());
+
+ tabPane.setSelectedIndex(current);
+ }
+
+ /**
+ * This method selects the correct tab given an offset from the current tab
+ * index. If the tab placement is TOP or BOTTOM, the offset will be in the
+ * y direction, otherwise, it will be in the x direction. A new coordinate
+ * will be found by adding the offset to the current location of the tab.
+ * The tab that the new location will be selected.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabIndex The tab to start from.
+ * @param offset The coordinate offset.
+ */
+ protected void selectAdjacentRunTab(int tabPlacement, int tabIndex,
+ int offset)
+ {
+ int x = rects[tabIndex].x + rects[tabIndex].width / 2;
+ int y = rects[tabIndex].y + rects[tabIndex].height / 2;
+
+ switch (tabPlacement)
+ {
+ case SwingConstants.TOP:
+ case SwingConstants.BOTTOM:
+ y += offset;
+ break;
+ case SwingConstants.RIGHT:
+ case SwingConstants.LEFT:
+ x += offset;
+ break;
+ }
+
+ int index = tabForCoordinate(tabPane, x, y);
+ if (index != -1)
+ {
+ if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT)
+ scrollTab(index, tabPlacement);
+ tabPane.setSelectedIndex(index);
+ }
+ }
+
+ // This method is called when you press up/down to cycle through tab runs.
+ // it returns the distance (between the two runs' x/y position.
+ // where one run is the current selected run and the other run is the run in the
+ // direction of the scroll (dictated by the forward flag)
+ // the offset is an absolute value of the difference
+
+ /**
+ * This method calculates the offset distance for use in
+ * selectAdjacentRunTab. The offset returned will be a difference in the y
+ * coordinate between the run in the desired direction and the current run
+ * (for tabPlacement in TOP or BOTTOM). Use x coordinate for LEFT and
+ * RIGHT.
+ *
+ * @param tabPlacement The JTabbedPane's tab placement.
+ * @param tabCount The number of tabs.
+ * @param tabIndex The starting index.
+ * @param forward If forward, the run in the desired direction will be the
+ * next run.
+ *
+ * @return The offset between the two runs.
+ */
+ protected int getTabRunOffset(int tabPlacement, int tabCount, int tabIndex,
+ boolean forward)
+ {
+ int currRun = getRunForTab(tabCount, tabIndex);
+ int offset;
+ int nextRun = forward ? getNextTabRun(currRun) : getPreviousTabRun(currRun);
+ if (tabPlacement == SwingConstants.TOP
+ || tabPlacement == SwingConstants.BOTTOM)
+ offset = rects[lastTabInRun(tabCount, nextRun)].y
+ - rects[lastTabInRun(tabCount, currRun)].y;
+ else
+ offset = rects[lastTabInRun(tabCount, nextRun)].x
+ - rects[lastTabInRun(tabCount, currRun)].x;
+
+ return offset;
+ }
+
+ /**
+ * This method returns the previous tab index.
+ *
+ * @param base The index to start from.
+ *
+ * @return The previous tab index.
+ */
+ protected int getPreviousTabIndex(int base)
+ {
+ base--;
+ if (base < 0)
+ return tabPane.getTabCount() - 1;
+ return base;
+ }
+
+ /**
+ * This method returns the next tab index.
+ *
+ * @param base The index to start from.
+ *
+ * @return The next tab index.
+ */
+ protected int getNextTabIndex(int base)
+ {
+ base++;
+ if (base == tabPane.getTabCount())
+ return 0;
+ return base;
+ }
+
+ /**
+ * This method returns the next tab index in the run. If the next index is
+ * out of this run, it will return the starting tab index for the run.
+ *
+ * @param tabCount The number of tabs.
+ * @param base The index to start from.
+ *
+ * @return The next tab index in the run.
+ */
+ protected int getNextTabIndexInRun(int tabCount, int base)
+ {
+ int index = getNextTabIndex(base);
+ int run = getRunForTab(tabCount, base);
+ if (base == lastTabInRun(tabCount, run))
+ index = (run > 0)
+ ? lastTabInRun(tabCount, getPreviousTabRun(run)) + 1
+ : 0;
+
+ return index;
+ }
+
+ /**
+ * This method returns the previous tab index in the run. If the previous
+ * index is out of this run, it will return the last index for the run.
+ *
+ * @param tabCount The number of tabs.
+ * @param base The index to start from.
+ *
+ * @return The previous tab index in the run.
+ */
+ protected int getPreviousTabIndexInRun(int tabCount, int base)
+ {
+ int index = getPreviousTabIndex(base);
+ int run = getRunForTab(tabCount, base);
+ if (index == lastTabInRun(tabCount, getPreviousTabRun(run)))
+ index = lastTabInRun(tabCount, run);
+
+ return index;
+ }
+
+ /**
+ * This method returns the index of the previous run.
+ *
+ * @param baseRun The run to start from.
+ *
+ * @return The index of the previous run.
+ */
+ protected int getPreviousTabRun(int baseRun)
+ {
+ if (getTabRunCount(tabPane) == 1)
+ return 1;
+
+ int prevRun = --baseRun;
+ if (prevRun < 0)
+ prevRun = getTabRunCount(tabPane) - 1;
+ return prevRun;
+ }
+
+ /**
+ * This method returns the index of the next run.
+ *
+ * @param baseRun The run to start from.
+ *
+ * @return The index of the next run.
+ */
+ protected int getNextTabRun(int baseRun)
+ {
+ if (getTabRunCount(tabPane) == 1)
+ return 1;
+
+ int nextRun = ++baseRun;
+ if (nextRun == getTabRunCount(tabPane))
+ nextRun = 0;
+ return nextRun;
+ }
+
+ /**
+ * This method rotates the insets given a direction to rotate them in.
+ * Target placement should be one of TOP, LEFT, BOTTOM, RIGHT. The rotated
+ * insets will be stored in targetInsets. Passing in TOP as the direction
+ * does nothing. Passing in LEFT switches top and left, right and bottom.
+ * Passing in BOTTOM switches top and bottom. Passing in RIGHT switches top
+ * for left, left for bottom, bottom for right, and right for top.
+ *
+ * @param topInsets The reference insets.
+ * @param targetInsets An Insets object to store the new insets.
+ * @param targetPlacement The rotation direction.
+ */
+ protected static void rotateInsets(Insets topInsets, Insets targetInsets,
+ int targetPlacement)
+ {
+ // Sun's version will happily throw an NPE if params are null,
+ // so I won't check it either.
+ switch (targetPlacement)
+ {
+ default:
+ case SwingConstants.TOP:
+ targetInsets.top = topInsets.top;
+ targetInsets.left = topInsets.left;
+ targetInsets.right = topInsets.right;
+ targetInsets.bottom = topInsets.bottom;
+ break;
+ case SwingConstants.LEFT:
+ targetInsets.left = topInsets.top;
+ targetInsets.top = topInsets.left;
+ targetInsets.right = topInsets.bottom;
+ targetInsets.bottom = topInsets.right;
+ break;
+ case SwingConstants.BOTTOM:
+ targetInsets.top = topInsets.bottom;
+ targetInsets.bottom = topInsets.top;
+ targetInsets.left = topInsets.left;
+ targetInsets.right = topInsets.right;
+ break;
+ case SwingConstants.RIGHT:
+ targetInsets.top = topInsets.left;
+ targetInsets.left = topInsets.bottom;
+ targetInsets.bottom = topInsets.right;
+ targetInsets.right = topInsets.top;
+ break;
+ }
+ }
+
+ ActionMap getActionMap()
+ {
+ ActionMap map = (ActionMap) UIManager.get("TabbedPane.actionMap");
+
+ if (map == null) // first time here
+ {
+ map = createActionMap();
+ if (map != null)
+ UIManager.put("TabbedPane.actionMap", map);
+ }
+ return map;
+ }
+
+ ActionMap createActionMap()
+ {
+ ActionMap map = new ActionMapUIResource();
+
+ map.put("navigatePageDown", new NavigatePageDownAction());
+ map.put("navigatePageUp", new NavigatePageUpAction());
+ map.put("navigateDown",
+ new NavigateAction("navigateDown", SwingConstants.SOUTH));
+
+ map.put("navigateUp",
+ new NavigateAction("navigateUp", SwingConstants.NORTH));
+
+ map.put("navigateLeft",
+ new NavigateAction("navigateLeft", SwingConstants.WEST));
+
+ map.put("navigateRight",
+ new NavigateAction("navigateRight", SwingConstants.EAST));
+
+ map.put("requestFocusForVisibleComponent",
+ new RequestFocusForVisibleComponentAction());
+ map.put("requestFocus", new RequestFocusAction());
+
+ return map;
+ }
+
+ /**
+ * Sets the tab which should be highlighted when in rollover mode. And
+ * <code>index</code> of <code>-1</code> means that the rollover tab
+ * is deselected (i.e. the mouse is outside of the tabarea).
+ *
+ * @param index the index of the tab that is under the mouse, <code>-1</code>
+ * for no tab
+ *
+ * @since 1.5
+ */
+ protected void setRolloverTab(int index)
+ {
+ rolloverTab = index;
+ }
+
+ /**
+ * Retunrs the index of the tab over which the mouse is currently moving,
+ * or <code>-1</code> for no tab.
+ *
+ * @return the index of the tab over which the mouse is currently moving,
+ * or <code>-1</code> for no tab
+ *
+ * @since 1.5
+ */
+ protected int getRolloverTab()
+ {
+ return rolloverTab;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
new file mode 100644
index 000000000..7ff234e6d
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableHeaderUI.java
@@ -0,0 +1,566 @@
+/* BasicTableHeaderUI.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseEvent;
+
+import javax.swing.CellRendererPane;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TableHeaderUI;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+/**
+ * Basic pluggable look and feel interface for JTableHeader.
+ */
+public class BasicTableHeaderUI extends TableHeaderUI
+{
+ /**
+ * The width of the space (in both direction) around the column boundary,
+ * where mouse cursor changes shape into "resize"
+ */
+ static int COLUMN_BOUNDARY_TOLERANCE = 3;
+
+ public static ComponentUI createUI(JComponent h)
+ {
+ return new BasicTableHeaderUI();
+ }
+
+ /**
+ * The table header that is using this interface.
+ */
+ protected JTableHeader header;
+
+ /**
+ * The mouse input listener, responsible for mouse manipulations with
+ * the table header.
+ */
+ protected MouseInputListener mouseInputListener;
+
+ /**
+ * Paint the header cell.
+ */
+ protected CellRendererPane rendererPane;
+
+ /**
+ * The header cell border.
+ */
+ private Border cellBorder;
+
+ /**
+ * Original mouse cursor prior to resizing.
+ */
+ private Cursor originalCursor;
+
+ /**
+ * If not null, one of the columns is currently being dragged.
+ */
+ Rectangle draggingHeaderRect;
+
+ /**
+ * Handles column movement and rearrangement by mouse. The same instance works
+ * both as mouse listener and the mouse motion listner.
+ */
+ public class MouseInputHandler
+ implements MouseInputListener
+ {
+ /**
+ * If true, the cursor is being already shown in the alternative "resize"
+ * shape.
+ */
+ boolean showingResizeCursor;
+
+ /**
+ * The position, from where the cursor is dragged during resizing. Double
+ * purpose field (absolute value during resizing and relative offset during
+ * column dragging).
+ */
+ int draggingFrom = - 1;
+
+ /**
+ * The number of the column being dragged.
+ */
+ int draggingColumnNumber;
+
+ /**
+ * The previous preferred width of the column.
+ */
+ int prevPrefWidth = - 1;
+
+ /**
+ * The timer to coalesce column resizing events.
+ */
+ Timer timer;
+
+ /**
+ * Returns without action, part of the MouseInputListener interface.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * If being in the resizing mode, handle resizing.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ TableColumn resizeIt = header.getResizingColumn();
+ if (resizeIt != null && header.getResizingAllowed())
+ {
+ // The timer is intialised on demand.
+ if (timer == null)
+ {
+ // The purpose of timer is to coalesce events. If the queue
+ // is free, the repaint event is fired immediately.
+ timer = new Timer(1, new ActionListener()
+ {
+ public void actionPerformed(ActionEvent e)
+ {
+ header.getTable().doLayout();
+ }
+ });
+ timer.setRepeats(false);
+ timer.setCoalesce(true);
+ }
+ resizeIt.setPreferredWidth(prevPrefWidth + e.getX() - draggingFrom);
+ timer.restart();
+ }
+ else if (draggingHeaderRect != null && header.getReorderingAllowed())
+ {
+ draggingHeaderRect.x = e.getX() + draggingFrom;
+ header.repaint();
+ }
+ }
+
+ /**
+ * Returns without action, part of the MouseInputListener interface.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * Reset drag information of the column resizing.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ // Nothing to do.
+ }
+
+ /**
+ * Change the mouse cursor if the mouse if above the column boundary.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // When dragging, the functionality is handled by the mouseDragged.
+ if (e.getButton() == 0 && header.getResizingAllowed())
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n < 2)
+ // It must be at least two columns to have at least one boundary.
+ // Otherwise, nothing to do.
+ return;
+
+ boolean onBoundary = false;
+
+ int x = e.getX();
+ int a = x - COLUMN_BOUNDARY_TOLERANCE;
+ int b = x + COLUMN_BOUNDARY_TOLERANCE;
+
+ int p = 0;
+
+ Scan: for (int i = 0; i < n - 1; i++)
+ {
+ p += model.getColumn(i).getWidth();
+
+ if (p >= a && p <= b)
+ {
+ TableColumn column = model.getColumn(i);
+ onBoundary = true;
+
+ draggingFrom = x;
+ prevPrefWidth = column.getWidth();
+ header.setResizingColumn(column);
+ break Scan;
+ }
+ }
+
+ if (onBoundary != showingResizeCursor)
+ {
+ // Change the cursor shape, if needed.
+ if (onBoundary)
+ {
+
+ originalCursor = header.getCursor();
+ if (p < x)
+ header.setCursor(Cursor.getPredefinedCursor(
+ Cursor.W_RESIZE_CURSOR));
+ else
+ header.setCursor(Cursor.getPredefinedCursor(
+ Cursor.E_RESIZE_CURSOR));
+ }
+ else
+ {
+ header.setCursor(originalCursor);
+ header.setResizingColumn(null);
+ }
+
+ showingResizeCursor = onBoundary;
+ }
+ }
+ }
+
+ /**
+ * Starts the dragging/resizing procedure.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (header.getResizingAllowed())
+ {
+ TableColumn resizingColumn = header.getResizingColumn();
+ if (resizingColumn != null)
+ {
+ resizingColumn.setPreferredWidth(resizingColumn.getWidth());
+ return;
+ }
+ }
+
+ if (header.getReorderingAllowed())
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n < 2)
+ // It must be at least two columns to change the column location.
+ return;
+
+ boolean onBoundary = false;
+
+ int x = e.getX();
+ int p = 0;
+ int col = - 1;
+
+ Scan: for (int i = 0; i < n; i++)
+ {
+ p += model.getColumn(i).getWidth();
+ if (p > x)
+ {
+ col = i;
+ break Scan;
+ }
+ }
+ if (col < 0)
+ return;
+
+ TableColumn dragIt = model.getColumn(col);
+ header.setDraggedColumn(dragIt);
+
+ draggingFrom = (p - dragIt.getWidth()) - x;
+ draggingHeaderRect = new Rectangle(header.getHeaderRect(col));
+ draggingColumnNumber = col;
+ }
+ }
+
+ /**
+ * Set all column preferred width to the current width to prevend abrupt
+ * width changes during the next resize.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (header.getResizingColumn() != null && header.getResizingAllowed())
+ endResizing();
+ if (header.getDraggedColumn() != null && header.getReorderingAllowed())
+ endDragging(e);
+ }
+
+ /**
+ * Stop resizing session.
+ */
+ void endResizing()
+ {
+ TableColumnModel model = header.getColumnModel();
+ int n = model.getColumnCount();
+ if (n > 2)
+ {
+ TableColumn c;
+ for (int i = 0; i < n; i++)
+ {
+ c = model.getColumn(i);
+ c.setPreferredWidth(c.getWidth());
+ }
+ }
+ header.setResizingColumn(null);
+ showingResizeCursor = false;
+ if (timer != null)
+ timer.stop();
+ header.setCursor(originalCursor);
+ }
+
+ /**
+ * Stop the dragging session.
+ *
+ * @param e the "mouse release" mouse event, needed to determing the final
+ * location for the dragged column.
+ */
+ void endDragging(MouseEvent e)
+ {
+ header.setDraggedColumn(null);
+ draggingHeaderRect = null;
+
+ TableColumnModel model = header.getColumnModel();
+
+ // Find where have we dragged the column.
+ int x = e.getX();
+ int p = 0;
+
+ int col = model.getColumnCount() - 1;
+ int n = model.getColumnCount();
+
+ // This loop does not find the column if the mouse if out of the
+ // right boundary of the table header. Then we make this column the
+ // rightmost column.
+ Scan: for (int i = 0; i < n; i++)
+ {
+ p += model.getColumn(i).getWidth();
+ if (p > x)
+ {
+ col = i;
+ break Scan;
+ }
+ }
+
+ header.getTable().moveColumn(draggingColumnNumber, col);
+ }
+ }
+
+ /**
+ * Create and return the mouse input listener.
+ *
+ * @return the mouse listener ({@link MouseInputHandler}, if not overridden.
+ */
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+ /**
+ * Construct a new BasicTableHeaderUI, create mouse listeners.
+ */
+ public BasicTableHeaderUI()
+ {
+ mouseInputListener = createMouseInputListener();
+ }
+
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(header, "TableHeader.background",
+ "TableHeader.foreground",
+ "TableHeader.font");
+ cellBorder = UIManager.getBorder("TableHeader.cellBorder");
+ }
+
+ protected void installKeyboardActions()
+ {
+ // AFAICS, the RI does nothing here.
+ }
+
+ /**
+ * Add the mouse listener and the mouse motion listener to the table
+ * header. The listeners support table column resizing and rearrangement
+ * by mouse.
+ */
+ protected void installListeners()
+ {
+ header.addMouseListener(mouseInputListener);
+ header.addMouseMotionListener(mouseInputListener);
+ }
+
+ public void installUI(JComponent c)
+ {
+ header = (JTableHeader) c;
+ rendererPane = new CellRendererPane();
+ installDefaults();
+ installKeyboardActions();
+ installListeners();
+ }
+
+ protected void uninstallDefaults()
+ {
+ header.setBackground(null);
+ header.setForeground(null);
+ header.setFont(null);
+ }
+
+ protected void uninstallKeyboardActions()
+ {
+ // AFAICS, the RI does nothing here.
+ }
+
+ /**
+ * Remove the previously installed listeners.
+ */
+ protected void uninstallListeners()
+ {
+ header.removeMouseListener(mouseInputListener);
+ header.removeMouseMotionListener(mouseInputListener);
+ }
+
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallKeyboardActions();
+ uninstallDefaults();
+ }
+
+ /**
+ * Repaint the table header.
+ */
+ public void paint(Graphics gfx, JComponent c)
+ {
+ TableColumnModel cmod = header.getColumnModel();
+ int ncols = cmod.getColumnCount();
+ if (ncols == 0)
+ return;
+
+ Rectangle clip = gfx.getClipBounds();
+ TableCellRenderer defaultRend = header.getDefaultRenderer();
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ Rectangle bounds = header.getHeaderRect(i);
+ if (bounds.intersects(clip))
+ {
+ Rectangle oldClip = gfx.getClipBounds();
+ TableColumn col = cmod.getColumn(i);
+ TableCellRenderer rend = col.getHeaderRenderer();
+ if (rend == null)
+ rend = defaultRend;
+ Object val = col.getHeaderValue();
+ Component comp = rend.getTableCellRendererComponent(header.getTable(),
+ val,
+ false, // isSelected
+ false, // isFocused
+ -1, i);
+ // FIXME: The following settings should be performed in
+ // rend.getTableCellRendererComponent().
+ comp.setFont(header.getFont());
+ comp.setBackground(header.getBackground());
+ comp.setForeground(header.getForeground());
+ if (comp instanceof JComponent)
+ ((JComponent) comp).setBorder(cellBorder);
+ rendererPane.paintComponent(gfx, comp, header, bounds.x, bounds.y,
+ bounds.width, bounds.height);
+ }
+ }
+
+ // This displays a running rectangle that is much simplier than the total
+ // animation, as it is seen in Sun's application.
+ // TODO animate the collumn dragging like in Sun's jre.
+ if (draggingHeaderRect != null)
+ {
+ gfx.setColor(header.getForeground());
+ gfx.drawRect(draggingHeaderRect.x, draggingHeaderRect.y + 2,
+ draggingHeaderRect.width - 1, draggingHeaderRect.height - 6);
+ }
+ }
+
+ /**
+ * Get the preferred header size.
+ *
+ * @param ignored unused
+ *
+ * @return the preferred size of the associated header.
+ */
+ public Dimension getPreferredSize(JComponent ignored)
+ {
+ TableColumnModel cmod = header.getColumnModel();
+ TableCellRenderer defaultRend = header.getDefaultRenderer();
+ int ncols = cmod.getColumnCount();
+ Dimension ret = new Dimension(0, 0);
+ int spacing = 0;
+
+ if (header.getTable() != null
+ && header.getTable().getIntercellSpacing() != null)
+ spacing = header.getTable().getIntercellSpacing().width;
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ TableColumn col = cmod.getColumn(i);
+ TableCellRenderer rend = col.getHeaderRenderer();
+ if (rend == null)
+ rend = defaultRend;
+ Object val = col.getHeaderValue();
+ Component comp = rend.getTableCellRendererComponent(header.getTable(),
+ val,
+ false, // isSelected
+ false, // isFocused
+ -1, i);
+ comp.setFont(header.getFont());
+ comp.setBackground(header.getBackground());
+ comp.setForeground(header.getForeground());
+ if (comp instanceof JComponent)
+ ((JComponent) comp).setBorder(cellBorder);
+
+ Dimension d = comp.getPreferredSize();
+ ret.width += spacing;
+ ret.height = Math.max(d.height, ret.height);
+ }
+ ret.width = cmod.getTotalColumnWidth();
+ return ret;
+ }
+
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java
new file mode 100644
index 000000000..f5a4bcb67
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTableUI.java
@@ -0,0 +1,1410 @@
+/* BasicTableUI.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.CellRendererPane;
+import javax.swing.DefaultCellEditor;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.TransferHandler;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TableUI;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+import javax.swing.table.TableModel;
+
+public class BasicTableUI extends TableUI
+{
+ public static ComponentUI createUI(JComponent comp)
+ {
+ return new BasicTableUI();
+ }
+
+ protected FocusListener focusListener;
+ protected KeyListener keyListener;
+ protected MouseInputListener mouseInputListener;
+ protected CellRendererPane rendererPane;
+ protected JTable table;
+
+ /** The normal cell border. */
+ Border cellBorder;
+
+ /** The action bound to KeyStrokes. */
+ TableAction action;
+
+ /**
+ * Listens for changes to the tables properties.
+ */
+ private PropertyChangeListener propertyChangeListener;
+
+ /**
+ * Handles key events for the JTable. Key events should be handled through
+ * the InputMap/ActionMap mechanism since JDK1.3. This class is only there
+ * for backwards compatibility.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class KeyHandler implements KeyListener
+ {
+
+ /**
+ * Receives notification that a key has been pressed and released.
+ * Activates the editing session for the focused cell by pressing the
+ * character keys.
+ *
+ * @param event the key event
+ */
+ public void keyTyped(KeyEvent event)
+ {
+ // Key events should be handled through the InputMap/ActionMap mechanism
+ // since JDK1.3. This class is only there for backwards compatibility.
+
+ // Editor activation is a specific kind of response to ''any''
+ // character key. Hence it is handled here.
+ if (!table.isEditing() && table.isEnabled())
+ {
+ int r = table.getSelectedRow();
+ int c = table.getSelectedColumn();
+ if (table.isCellEditable(r, c))
+ table.editCellAt(r, c);
+ }
+ }
+
+ /**
+ * Receives notification that a key has been pressed.
+ *
+ * @param event the key event
+ */
+ public void keyPressed(KeyEvent event)
+ {
+ // Key events should be handled through the InputMap/ActionMap mechanism
+ // since JDK1.3. This class is only there for backwards compatibility.
+ }
+
+ /**
+ * Receives notification that a key has been released.
+ *
+ * @param event the key event
+ */
+ public void keyReleased(KeyEvent event)
+ {
+ // Key events should be handled through the InputMap/ActionMap mechanism
+ // since JDK1.3. This class is only there for backwards compatibility.
+ }
+ }
+
+ public class FocusHandler implements FocusListener
+ {
+ public void focusGained(FocusEvent e)
+ {
+ // The only thing that is affected by a focus change seems to be
+ // how the lead cell is painted. So we repaint this cell.
+ repaintLeadCell();
+ }
+
+ public void focusLost(FocusEvent e)
+ {
+ // The only thing that is affected by a focus change seems to be
+ // how the lead cell is painted. So we repaint this cell.
+ repaintLeadCell();
+ }
+
+ /**
+ * Repaints the lead cell in response to a focus change, to refresh
+ * the display of the focus indicator.
+ */
+ private void repaintLeadCell()
+ {
+ int rowCount = table.getRowCount();
+ int columnCount = table.getColumnCount();
+ int rowLead = table.getSelectionModel().getLeadSelectionIndex();
+ int columnLead = table.getColumnModel().getSelectionModel().
+ getLeadSelectionIndex();
+ if (rowLead >= 0 && rowLead < rowCount && columnLead >= 0
+ && columnLead < columnCount)
+ {
+ Rectangle dirtyRect = table.getCellRect(rowLead, columnLead, false);
+ table.repaint(dirtyRect);
+ }
+ }
+ }
+
+ public class MouseInputHandler implements MouseInputListener
+ {
+ Point begin, curr;
+
+ private void updateSelection(boolean controlPressed)
+ {
+ // Update the rows
+ int lo_row = table.rowAtPoint(begin);
+ int hi_row = table.rowAtPoint(curr);
+ ListSelectionModel rowModel = table.getSelectionModel();
+ if (lo_row != -1 && hi_row != -1)
+ {
+ if (controlPressed && rowModel.getSelectionMode()
+ != ListSelectionModel.SINGLE_SELECTION)
+ rowModel.addSelectionInterval(lo_row, hi_row);
+ else
+ rowModel.setSelectionInterval(lo_row, hi_row);
+ }
+
+ // Update the columns
+ int lo_col = table.columnAtPoint(begin);
+ int hi_col = table.columnAtPoint(curr);
+ ListSelectionModel colModel = table.getColumnModel().
+ getSelectionModel();
+ if (lo_col != -1 && hi_col != -1)
+ {
+ if (controlPressed && colModel.getSelectionMode() !=
+ ListSelectionModel.SINGLE_SELECTION)
+ colModel.addSelectionInterval(lo_col, hi_col);
+ else
+ colModel.setSelectionInterval(lo_col, hi_col);
+ }
+ }
+
+ /**
+ * For the double click, start the cell editor.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ Point p = e.getPoint();
+ int row = table.rowAtPoint(p);
+ int col = table.columnAtPoint(p);
+ if (table.isCellEditable(row, col))
+ {
+ // If the cell editor is the default editor, we request the
+ // number of the required clicks from it. Otherwise,
+ // require two clicks (double click).
+ TableCellEditor editor = table.getCellEditor(row, col);
+ if (editor instanceof DefaultCellEditor)
+ {
+ DefaultCellEditor ce = (DefaultCellEditor) editor;
+ if (e.getClickCount() < ce.getClickCountToStart())
+ return;
+ }
+ table.editCellAt(row, col);
+ }
+ }
+
+ public void mouseDragged(MouseEvent e)
+ {
+ if (table.isEnabled())
+ {
+ curr = new Point(e.getX(), e.getY());
+ updateSelection(e.isControlDown());
+ }
+ }
+
+ public void mouseEntered(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseExited(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void mousePressed(MouseEvent e)
+ {
+ if (table.isEnabled())
+ {
+ ListSelectionModel rowModel = table.getSelectionModel();
+ ListSelectionModel colModel = table.getColumnModel().getSelectionModel();
+ int rowLead = rowModel.getLeadSelectionIndex();
+ int colLead = colModel.getLeadSelectionIndex();
+
+ begin = new Point(e.getX(), e.getY());
+ curr = new Point(e.getX(), e.getY());
+ //if control is pressed and the cell is already selected, deselect it
+ if (e.isControlDown() && table.isCellSelected(
+ table.rowAtPoint(begin), table.columnAtPoint(begin)))
+ {
+ table.getSelectionModel().
+ removeSelectionInterval(table.rowAtPoint(begin),
+ table.rowAtPoint(begin));
+ table.getColumnModel().getSelectionModel().
+ removeSelectionInterval(table.columnAtPoint(begin),
+ table.columnAtPoint(begin));
+ }
+ else
+ updateSelection(e.isControlDown());
+
+ // If we were editing, but the moved to another cell, stop editing
+ if (rowLead != rowModel.getLeadSelectionIndex() ||
+ colLead != colModel.getLeadSelectionIndex())
+ if (table.isEditing())
+ table.editingStopped(new ChangeEvent(e));
+
+ // Must request focus explicitly.
+ table.requestFocusInWindow();
+ }
+ }
+
+ public void mouseReleased(MouseEvent e)
+ {
+ if (table.isEnabled())
+ {
+ begin = null;
+ curr = null;
+ }
+ }
+ }
+
+ /**
+ * Listens for changes to the model property of the JTable and adjusts some
+ * settings.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private class PropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Receives notification if one of the JTable's properties changes.
+ *
+ * @param ev the property change event
+ */
+ public void propertyChange(PropertyChangeEvent ev)
+ {
+ String propName = ev.getPropertyName();
+ if (propName.equals("model"))
+ {
+ ListSelectionModel rowSel = table.getSelectionModel();
+ rowSel.clearSelection();
+ ListSelectionModel colSel = table.getColumnModel().getSelectionModel();
+ colSel.clearSelection();
+ TableModel model = table.getModel();
+
+ // Adjust lead and anchor selection indices of the row and column
+ // selection models.
+ if (model.getRowCount() > 0)
+ {
+ rowSel.setAnchorSelectionIndex(0);
+ rowSel.setLeadSelectionIndex(0);
+ }
+ else
+ {
+ rowSel.setAnchorSelectionIndex(-1);
+ rowSel.setLeadSelectionIndex(-1);
+ }
+ if (model.getColumnCount() > 0)
+ {
+ colSel.setAnchorSelectionIndex(0);
+ colSel.setLeadSelectionIndex(0);
+ }
+ else
+ {
+ colSel.setAnchorSelectionIndex(-1);
+ colSel.setLeadSelectionIndex(-1);
+ }
+ }
+ }
+ }
+
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ protected MouseInputListener createMouseInputListener()
+ {
+ return new MouseInputHandler();
+ }
+
+
+ /**
+ * Creates and returns a key listener for the JTable.
+ *
+ * @return a key listener for the JTable
+ */
+ protected KeyListener createKeyListener()
+ {
+ return new KeyHandler();
+ }
+
+ /**
+ * Return the maximum size of the table. The maximum height is the row
+ * height times the number of rows. The maximum width is the sum of
+ * the maximum widths of each column.
+ *
+ * @param comp the component whose maximum size is being queried,
+ * this is ignored.
+ * @return a Dimension object representing the maximum size of the table,
+ * or null if the table has no elements.
+ */
+ public Dimension getMaximumSize(JComponent comp)
+ {
+ int maxTotalColumnWidth = 0;
+ for (int i = 0; i < table.getColumnCount(); i++)
+ maxTotalColumnWidth += table.getColumnModel().getColumn(i).getMaxWidth();
+
+ return new Dimension(maxTotalColumnWidth, getHeight());
+ }
+
+ /**
+ * Return the minimum size of the table. The minimum height is the row
+ * height times the number of rows. The minimum width is the sum of
+ * the minimum widths of each column.
+ *
+ * @param comp the component whose minimum size is being queried,
+ * this is ignored.
+ * @return a Dimension object representing the minimum size of the table,
+ * or null if the table has no elements.
+ */
+ public Dimension getMinimumSize(JComponent comp)
+ {
+ int minTotalColumnWidth = 0;
+ for (int i = 0; i < table.getColumnCount(); i++)
+ minTotalColumnWidth += table.getColumnModel().getColumn(i).getMinWidth();
+
+ return new Dimension(minTotalColumnWidth, getHeight());
+ }
+
+ /**
+ * Returns the preferred size for the table of that UI.
+ *
+ * @param comp ignored, the <code>table</code> field is used instead
+ *
+ * @return the preferred size for the table of that UI
+ */
+ public Dimension getPreferredSize(JComponent comp)
+ {
+ int prefTotalColumnWidth = 0;
+ TableColumnModel tcm = table.getColumnModel();
+
+ for (int i = 0; i < tcm.getColumnCount(); i++)
+ {
+ TableColumn col = tcm.getColumn(i);
+ prefTotalColumnWidth += col.getPreferredWidth();
+ }
+
+ return new Dimension(prefTotalColumnWidth, getHeight());
+ }
+
+ /**
+ * Returns the table height. This helper method is used by
+ * {@link #getMinimumSize(JComponent)}, {@link #getPreferredSize(JComponent)}
+ * and {@link #getMaximumSize(JComponent)} to determine the table height.
+ *
+ * @return the table height
+ */
+ private int getHeight()
+ {
+ int height = 0;
+ int rowCount = table.getRowCount();
+ if (rowCount > 0 && table.getColumnCount() > 0)
+ {
+ Rectangle r = table.getCellRect(rowCount - 1, 0, true);
+ height = r.y + r.height;
+ }
+ return height;
+ }
+
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(table, "Table.background",
+ "Table.foreground", "Table.font");
+ table.setGridColor(UIManager.getColor("Table.gridColor"));
+ table.setSelectionForeground(UIManager.getColor("Table.selectionForeground"));
+ table.setSelectionBackground(UIManager.getColor("Table.selectionBackground"));
+ table.setOpaque(true);
+ }
+
+ /**
+ * Installs keyboard actions on the table.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install the input map.
+ InputMap inputMap =
+ (InputMap) SharedUIDefaults.get("Table.ancestorInputMap");
+ SwingUtilities.replaceUIInputMap(table,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ inputMap);
+
+ // FIXME: The JDK uses a LazyActionMap for parentActionMap
+ SwingUtilities.replaceUIActionMap(table, getActionMap());
+
+ }
+
+ /**
+ * Fetches the action map from the UI defaults, or create a new one
+ * if the action map hasn't been initialized.
+ *
+ * @return the action map
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("Table.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("Table.actionMap", am);
+ }
+ return am;
+ }
+
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new TableAction();
+
+ am.put("cut", TransferHandler.getCutAction());
+ am.put("copy", TransferHandler.getCopyAction());
+ am.put("paste", TransferHandler.getPasteAction());
+
+ am.put("cancel", action);
+ am.put("selectAll", action);
+ am.put("clearSelection", action);
+ am.put("startEditing", action);
+
+ am.put("selectNextRow", action);
+ am.put("selectNextRowCell", action);
+ am.put("selectNextRowExtendSelection", action);
+ am.put("selectNextRowChangeLead", action);
+
+ am.put("selectPreviousRow", action);
+ am.put("selectPreviousRowCell", action);
+ am.put("selectPreviousRowExtendSelection", action);
+ am.put("selectPreviousRowChangeLead", action);
+
+ am.put("selectNextColumn", action);
+ am.put("selectNextColumnCell", action);
+ am.put("selectNextColumnExtendSelection", action);
+ am.put("selectNextColumnChangeLead", action);
+
+ am.put("selectPreviousColumn", action);
+ am.put("selectPreviousColumnCell", action);
+ am.put("selectPreviousColumnExtendSelection", action);
+ am.put("selectPreviousColumnChangeLead", action);
+
+ am.put("scrollLeftChangeSelection", action);
+ am.put("scrollLeftExtendSelection", action);
+ am.put("scrollRightChangeSelection", action);
+ am.put("scrollRightExtendSelection", action);
+
+ am.put("scrollUpChangeSelection", action);
+ am.put("scrollUpExtendSelection", action);
+ am.put("scrollDownChangeSelection", action);
+ am.put("scrolldownExtendSelection", action);
+
+ am.put("selectFirstColumn", action);
+ am.put("selectFirstColumnExtendSelection", action);
+ am.put("selectLastColumn", action);
+ am.put("selectLastColumnExtendSelection", action);
+
+ am.put("selectFirstRow", action);
+ am.put("selectFirstRowExtendSelection", action);
+ am.put("selectLastRow", action);
+ am.put("selectLastRowExtendSelection", action);
+
+ am.put("addToSelection", action);
+ am.put("toggleAndAnchor", action);
+ am.put("extendTo", action);
+ am.put("moveSelectionTo", action);
+
+ return am;
+ }
+
+ /**
+ * This class implements the actions that we want to happen
+ * when specific keys are pressed for the JTable. The actionPerformed
+ * method is called when a key that has been registered for the JTable
+ * is received.
+ */
+ private static class TableAction
+ extends AbstractAction
+ {
+ /**
+ * What to do when this action is called.
+ *
+ * @param e the ActionEvent that caused this action.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ JTable table = (JTable) e.getSource();
+
+ DefaultListSelectionModel rowModel
+ = (DefaultListSelectionModel) table.getSelectionModel();
+ DefaultListSelectionModel colModel
+ = (DefaultListSelectionModel) table.getColumnModel().getSelectionModel();
+
+ int rowLead = rowModel.getLeadSelectionIndex();
+ int rowMax = table.getModel().getRowCount() - 1;
+
+ int colLead = colModel.getLeadSelectionIndex();
+ int colMax = table.getModel().getColumnCount() - 1;
+
+ // The command with which the action has been called is stored
+ // in this undocumented action value. This allows us to have only
+ // one Action instance to serve all keyboard input for JTable.
+ String command = (String) getValue("__command__");
+ if (command.equals("selectPreviousRowExtendSelection"))
+ {
+ rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0));
+ }
+ else if (command.equals("selectLastColumn"))
+ {
+ colModel.setSelectionInterval(colMax, colMax);
+ }
+ else if (command.equals("startEditing"))
+ {
+ if (table.isCellEditable(rowLead, colLead))
+ table.editCellAt(rowLead, colLead);
+ }
+ else if (command.equals("selectFirstRowExtendSelection"))
+ {
+ rowModel.setLeadSelectionIndex(0);
+ }
+ else if (command.equals("selectFirstColumn"))
+ {
+ colModel.setSelectionInterval(0, 0);
+ }
+ else if (command.equals("selectFirstColumnExtendSelection"))
+ {
+ colModel.setLeadSelectionIndex(0);
+ }
+ else if (command.equals("selectLastRow"))
+ {
+ rowModel.setSelectionInterval(rowMax, rowMax);
+ }
+ else if (command.equals("selectNextRowExtendSelection"))
+ {
+ rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
+ }
+ else if (command.equals("selectFirstRow"))
+ {
+ rowModel.setSelectionInterval(0, 0);
+ }
+ else if (command.equals("selectNextColumnExtendSelection"))
+ {
+ colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax));
+ }
+ else if (command.equals("selectLastColumnExtendSelection"))
+ {
+ colModel.setLeadSelectionIndex(colMax);
+ }
+ else if (command.equals("selectPreviousColumnExtendSelection"))
+ {
+ colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0));
+ }
+ else if (command.equals("selectNextRow"))
+ {
+ rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
+ Math.min(rowLead + 1, rowMax));
+ }
+ else if (command.equals("scrollUpExtendSelection"))
+ {
+ int target;
+ if (rowLead == getFirstVisibleRowIndex(table))
+ target = Math.max(0, rowLead - (getLastVisibleRowIndex(table)
+ - getFirstVisibleRowIndex(table) + 1));
+ else
+ target = getFirstVisibleRowIndex(table);
+
+ rowModel.setLeadSelectionIndex(target);
+ colModel.setLeadSelectionIndex(colLead);
+ }
+ else if (command.equals("selectPreviousRow"))
+ {
+ rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
+ Math.max(rowLead - 1, 0));
+ }
+ else if (command.equals("scrollRightChangeSelection"))
+ {
+ int target;
+ if (colLead == getLastVisibleColumnIndex(table))
+ target = Math.min(colMax, colLead
+ + (getLastVisibleColumnIndex(table)
+ - getFirstVisibleColumnIndex(table) + 1));
+ else
+ target = getLastVisibleColumnIndex(table);
+
+ colModel.setSelectionInterval(target, target);
+ rowModel.setSelectionInterval(rowLead, rowLead);
+ }
+ else if (command.equals("selectPreviousColumn"))
+ {
+ colModel.setSelectionInterval(Math.max(colLead - 1, 0),
+ Math.max(colLead - 1, 0));
+ }
+ else if (command.equals("scrollLeftChangeSelection"))
+ {
+ int target;
+ if (colLead == getFirstVisibleColumnIndex(table))
+ target = Math.max(0, colLead - (getLastVisibleColumnIndex(table)
+ - getFirstVisibleColumnIndex(table) + 1));
+ else
+ target = getFirstVisibleColumnIndex(table);
+
+ colModel.setSelectionInterval(target, target);
+ rowModel.setSelectionInterval(rowLead, rowLead);
+ }
+ else if (command.equals("clearSelection"))
+ {
+ table.clearSelection();
+ }
+ else if (command.equals("cancel"))
+ {
+ // FIXME: implement other parts of "cancel" like undo-ing last
+ // selection. Right now it just calls editingCancelled if
+ // we're currently editing.
+ if (table.isEditing())
+ table.editingCanceled(new ChangeEvent("cancel"));
+ }
+ else if (command.equals("selectNextRowCell")
+ || command.equals("selectPreviousRowCell")
+ || command.equals("selectNextColumnCell")
+ || command.equals("selectPreviousColumnCell"))
+ {
+ // If nothing is selected, select the first cell in the table
+ if (table.getSelectedRowCount() == 0 &&
+ table.getSelectedColumnCount() == 0)
+ {
+ rowModel.setSelectionInterval(0, 0);
+ colModel.setSelectionInterval(0, 0);
+ return;
+ }
+
+ // If the lead selection index isn't selected (ie a remove operation
+ // happened, then set the lead to the first selected cell in the
+ // table
+ if (!table.isCellSelected(rowLead, colLead))
+ {
+ rowModel.addSelectionInterval(rowModel.getMinSelectionIndex(),
+ rowModel.getMinSelectionIndex());
+ colModel.addSelectionInterval(colModel.getMinSelectionIndex(),
+ colModel.getMinSelectionIndex());
+ return;
+ }
+
+ // multRowsSelected and multColsSelected tell us if multiple rows or
+ // columns are selected, respectively
+ boolean multRowsSelected, multColsSelected;
+ multRowsSelected = table.getSelectedRowCount() > 1 &&
+ table.getRowSelectionAllowed();
+
+ multColsSelected = table.getSelectedColumnCount() > 1 &&
+ table.getColumnSelectionAllowed();
+
+ // If there is just one selection, select the next cell, and wrap
+ // when you get to the edges of the table.
+ if (!multColsSelected && !multRowsSelected)
+ {
+ if (command.indexOf("Column") != -1)
+ advanceSingleSelection(colModel, colMax, rowModel, rowMax,
+ command.equals("selectPreviousColumnCell"));
+ else
+ advanceSingleSelection(rowModel, rowMax, colModel, colMax,
+ command.equals("selectPreviousRowCell"));
+ return;
+ }
+
+
+ // rowMinSelected and rowMaxSelected are the minimum and maximum
+ // values respectively of selected cells in the row selection model
+ // Similarly for colMinSelected and colMaxSelected.
+ int rowMaxSelected = table.getRowSelectionAllowed() ?
+ rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1;
+ int rowMinSelected = table.getRowSelectionAllowed() ?
+ rowModel.getMinSelectionIndex() : 0;
+ int colMaxSelected = table.getColumnSelectionAllowed() ?
+ colModel.getMaxSelectionIndex() :
+ table.getModel().getColumnCount() - 1;
+ int colMinSelected = table.getColumnSelectionAllowed() ?
+ colModel.getMinSelectionIndex() : 0;
+
+ // If there are multiple rows and columns selected, select the next
+ // cell and wrap at the edges of the selection.
+ if (command.indexOf("Column") != -1)
+ advanceMultipleSelection(table, colModel, colMinSelected,
+ colMaxSelected, rowModel, rowMinSelected,
+ rowMaxSelected,
+ command.equals("selectPreviousColumnCell"),
+ true);
+
+ else
+ advanceMultipleSelection(table, rowModel, rowMinSelected,
+ rowMaxSelected, colModel, colMinSelected,
+ colMaxSelected,
+ command.equals("selectPreviousRowCell"),
+ false);
+ }
+ else if (command.equals("selectNextColumn"))
+ {
+ colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
+ Math.min(colLead + 1, colMax));
+ }
+ else if (command.equals("scrollLeftExtendSelection"))
+ {
+ int target;
+ if (colLead == getFirstVisibleColumnIndex(table))
+ target = Math.max(0, colLead - (getLastVisibleColumnIndex(table)
+ - getFirstVisibleColumnIndex(table) + 1));
+ else
+ target = getFirstVisibleColumnIndex(table);
+
+ colModel.setLeadSelectionIndex(target);
+ rowModel.setLeadSelectionIndex(rowLead);
+ }
+ else if (command.equals("scrollDownChangeSelection"))
+ {
+ int target;
+ if (rowLead == getLastVisibleRowIndex(table))
+ target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table)
+ - getFirstVisibleRowIndex(table) + 1));
+ else
+ target = getLastVisibleRowIndex(table);
+
+ rowModel.setSelectionInterval(target, target);
+ colModel.setSelectionInterval(colLead, colLead);
+ }
+ else if (command.equals("scrollRightExtendSelection"))
+ {
+ int target;
+ if (colLead == getLastVisibleColumnIndex(table))
+ target = Math.min(colMax, colLead + (getLastVisibleColumnIndex(table)
+ - getFirstVisibleColumnIndex(table) + 1));
+ else
+ target = getLastVisibleColumnIndex(table);
+
+ colModel.setLeadSelectionIndex(target);
+ rowModel.setLeadSelectionIndex(rowLead);
+ }
+ else if (command.equals("selectAll"))
+ {
+ table.selectAll();
+ }
+ else if (command.equals("selectLastRowExtendSelection"))
+ {
+ rowModel.setLeadSelectionIndex(rowMax);
+ colModel.setLeadSelectionIndex(colLead);
+ }
+ else if (command.equals("scrollDownExtendSelection"))
+ {
+ int target;
+ if (rowLead == getLastVisibleRowIndex(table))
+ target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table)
+ - getFirstVisibleRowIndex(table) + 1));
+ else
+ target = getLastVisibleRowIndex(table);
+
+ rowModel.setLeadSelectionIndex(target);
+ colModel.setLeadSelectionIndex(colLead);
+ }
+ else if (command.equals("scrollUpChangeSelection"))
+ {
+ int target;
+ if (rowLead == getFirstVisibleRowIndex(table))
+ target = Math.max(0, rowLead - (getLastVisibleRowIndex(table)
+ - getFirstVisibleRowIndex(table) + 1));
+ else
+ target = getFirstVisibleRowIndex(table);
+
+ rowModel.setSelectionInterval(target, target);
+ colModel.setSelectionInterval(colLead, colLead);
+ }
+ else if (command.equals("selectNextRowChangeLead"))
+ {
+ if (rowModel.getSelectionMode()
+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ {
+ // just "selectNextRow"
+ rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax),
+ Math.min(rowLead + 1, rowMax));
+ colModel.setSelectionInterval(colLead, colLead);
+ }
+ else
+ rowModel.moveLeadSelectionIndex(Math.min(rowLead + 1, rowMax));
+ }
+ else if (command.equals("selectPreviousRowChangeLead"))
+ {
+ if (rowModel.getSelectionMode()
+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ {
+ // just selectPreviousRow
+ rowModel.setSelectionInterval(Math.max(rowLead - 1, 0),
+ Math.min(rowLead - 1, 0));
+ colModel.setSelectionInterval(colLead, colLead);
+ }
+ else
+ rowModel.moveLeadSelectionIndex(Math.max(rowLead - 1, 0));
+ }
+ else if (command.equals("selectNextColumnChangeLead"))
+ {
+ if (colModel.getSelectionMode()
+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ {
+ // just selectNextColumn
+ rowModel.setSelectionInterval(rowLead, rowLead);
+ colModel.setSelectionInterval(Math.min(colLead + 1, colMax),
+ Math.min(colLead + 1, colMax));
+ }
+ else
+ colModel.moveLeadSelectionIndex(Math.min(colLead + 1, colMax));
+ }
+ else if (command.equals("selectPreviousColumnChangeLead"))
+ {
+ if (colModel.getSelectionMode()
+ != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ {
+ // just selectPreviousColumn
+ rowModel.setSelectionInterval(rowLead, rowLead);
+ colModel.setSelectionInterval(Math.max(colLead - 1, 0),
+ Math.max(colLead - 1, 0));
+
+ }
+ else
+ colModel.moveLeadSelectionIndex(Math.max(colLead - 1, 0));
+ }
+ else if (command.equals("addToSelection"))
+ {
+ if (!table.isEditing())
+ {
+ int oldRowAnchor = rowModel.getAnchorSelectionIndex();
+ int oldColAnchor = colModel.getAnchorSelectionIndex();
+ rowModel.addSelectionInterval(rowLead, rowLead);
+ colModel.addSelectionInterval(colLead, colLead);
+ rowModel.setAnchorSelectionIndex(oldRowAnchor);
+ colModel.setAnchorSelectionIndex(oldColAnchor);
+ }
+ }
+ else if (command.equals("extendTo"))
+ {
+ rowModel.setSelectionInterval(rowModel.getAnchorSelectionIndex(),
+ rowLead);
+ colModel.setSelectionInterval(colModel.getAnchorSelectionIndex(),
+ colLead);
+ }
+ else if (command.equals("toggleAndAnchor"))
+ {
+ if (rowModel.isSelectedIndex(rowLead))
+ rowModel.removeSelectionInterval(rowLead, rowLead);
+ else
+ rowModel.addSelectionInterval(rowLead, rowLead);
+
+ if (colModel.isSelectedIndex(colLead))
+ colModel.removeSelectionInterval(colLead, colLead);
+ else
+ colModel.addSelectionInterval(colLead, colLead);
+
+ rowModel.setAnchorSelectionIndex(rowLead);
+ colModel.setAnchorSelectionIndex(colLead);
+ }
+ else if (command.equals("stopEditing"))
+ {
+ table.editingStopped(new ChangeEvent(command));
+ }
+ else
+ {
+ // If we're here that means we bound this TableAction class
+ // to a keyboard input but we either want to ignore that input
+ // or we just haven't implemented its action yet.
+
+ // Uncomment the following line to print the names of unused bindings
+ // when their keys are pressed
+
+ // System.out.println ("not implemented: "+e.getActionCommand());
+ }
+
+ // Any commands whose keyStrokes should be used by the Editor should not
+ // cause editing to be stopped: ie, the SPACE sends "addToSelection" but
+ // if the table is in editing mode, the space should not cause us to stop
+ // editing because it should be used by the Editor.
+ if (table.isEditing() && command != "startEditing"
+ && command != "addToSelection")
+ table.editingStopped(new ChangeEvent("update"));
+
+ table.scrollRectToVisible(table.getCellRect(
+ rowModel.getLeadSelectionIndex(), colModel.getLeadSelectionIndex(),
+ false));
+ }
+
+ /**
+ * Returns the column index of the first visible column.
+ * @return the column index of the first visible column.
+ */
+ int getFirstVisibleColumnIndex(JTable table)
+ {
+ ComponentOrientation or = table.getComponentOrientation();
+ Rectangle r = table.getVisibleRect();
+ if (!or.isLeftToRight())
+ r.translate((int) r.getWidth() - 1, 0);
+ return table.columnAtPoint(r.getLocation());
+ }
+
+ /**
+ * Returns the column index of the last visible column.
+ *
+ */
+ int getLastVisibleColumnIndex(JTable table)
+ {
+ ComponentOrientation or = table.getComponentOrientation();
+ Rectangle r = table.getVisibleRect();
+ if (or.isLeftToRight())
+ r.translate((int) r.getWidth() - 1, 0);
+ return table.columnAtPoint(r.getLocation());
+ }
+
+ /**
+ * Returns the row index of the first visible row.
+ *
+ */
+ int getFirstVisibleRowIndex(JTable table)
+ {
+ ComponentOrientation or = table.getComponentOrientation();
+ Rectangle r = table.getVisibleRect();
+ if (!or.isLeftToRight())
+ r.translate((int) r.getWidth() - 1, 0);
+ return table.rowAtPoint(r.getLocation());
+ }
+
+ /**
+ * Returns the row index of the last visible row.
+ *
+ */
+ int getLastVisibleRowIndex(JTable table)
+ {
+ ComponentOrientation or = table.getComponentOrientation();
+ Rectangle r = table.getVisibleRect();
+ r.translate(0, (int) r.getHeight() - 1);
+ if (or.isLeftToRight())
+ r.translate((int) r.getWidth() - 1, 0);
+ // The next if makes sure that we don't return -1 simply because
+ // there is white space at the bottom of the table (ie, the display
+ // area is larger than the table)
+ if (table.rowAtPoint(r.getLocation()) == -1)
+ {
+ if (getFirstVisibleRowIndex(table) == -1)
+ return -1;
+ else
+ return table.getModel().getRowCount() - 1;
+ }
+ return table.rowAtPoint(r.getLocation());
+ }
+
+ /**
+ * A helper method for the key bindings. Used because the actions
+ * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
+ *
+ * Selects the next (previous if SHIFT pressed) column for TAB, or row for
+ * ENTER from within the currently selected cells.
+ *
+ * @param firstModel the ListSelectionModel for columns (TAB) or
+ * rows (ENTER)
+ * @param firstMin the first selected index in firstModel
+ * @param firstMax the last selected index in firstModel
+ * @param secondModel the ListSelectionModel for rows (TAB) or
+ * columns (ENTER)
+ * @param secondMin the first selected index in secondModel
+ * @param secondMax the last selected index in secondModel
+ * @param reverse true if shift was held for the event
+ * @param eventIsTab true if TAB was pressed, false if ENTER pressed
+ */
+ void advanceMultipleSelection(JTable table, ListSelectionModel firstModel,
+ int firstMin,
+ int firstMax, ListSelectionModel secondModel,
+ int secondMin, int secondMax, boolean reverse,
+ boolean eventIsTab)
+ {
+ // If eventIsTab, all the "firsts" correspond to columns, otherwise, to
+ // rows "seconds" correspond to the opposite
+ int firstLead = firstModel.getLeadSelectionIndex();
+ int secondLead = secondModel.getLeadSelectionIndex();
+ int numFirsts = eventIsTab ?
+ table.getModel().getColumnCount() : table.getModel().getRowCount();
+ int numSeconds = eventIsTab ?
+ table.getModel().getRowCount() : table.getModel().getColumnCount();
+
+ // check if we have to wrap the "firsts" around, going to the other side
+ if ((firstLead == firstMax && !reverse) ||
+ (reverse && firstLead == firstMin))
+ {
+ firstModel.addSelectionInterval(reverse ? firstMax : firstMin,
+ reverse ? firstMax : firstMin);
+
+ // check if we have to wrap the "seconds"
+ if ((secondLead == secondMax && !reverse) ||
+ (reverse && secondLead == secondMin))
+ secondModel.addSelectionInterval(reverse ? secondMax : secondMin,
+ reverse ? secondMax : secondMin);
+
+ // if we're not wrapping the seconds, we have to find out where we
+ // are within the secondModel and advance to the next cell (or
+ // go back to the previous cell if reverse == true)
+ else
+ {
+ int[] secondsSelected;
+ if (eventIsTab && table.getRowSelectionAllowed() ||
+ !eventIsTab && table.getColumnSelectionAllowed())
+ secondsSelected = eventIsTab ?
+ table.getSelectedRows() : table.getSelectedColumns();
+ else
+ {
+ // if row selection is not allowed, then the entire column gets
+ // selected when you click on it, so consider ALL rows selected
+ secondsSelected = new int[numSeconds];
+ for (int i = 0; i < numSeconds; i++)
+ secondsSelected[i] = i;
+ }
+
+ // and now find the "next" index within the model
+ int secondIndex = reverse ? secondsSelected.length - 1 : 0;
+ if (!reverse)
+ while (secondsSelected[secondIndex] <= secondLead)
+ secondIndex++;
+ else
+ while (secondsSelected[secondIndex] >= secondLead)
+ secondIndex--;
+
+ // and select it - updating the lead selection index
+ secondModel.addSelectionInterval(secondsSelected[secondIndex],
+ secondsSelected[secondIndex]);
+ }
+ }
+ // We didn't have to wrap the firsts, so just find the "next" first
+ // and select it, we don't have to change "seconds"
+ else
+ {
+ int[] firstsSelected;
+ if (eventIsTab && table.getColumnSelectionAllowed() ||
+ !eventIsTab && table.getRowSelectionAllowed())
+ firstsSelected = eventIsTab ?
+ table.getSelectedColumns() : table.getSelectedRows();
+ else
+ {
+ // if selection not allowed, consider ALL firsts to be selected
+ firstsSelected = new int[numFirsts];
+ for (int i = 0; i < numFirsts; i++)
+ firstsSelected[i] = i;
+ }
+ int firstIndex = reverse ? firstsSelected.length - 1 : 0;
+ if (!reverse)
+ while (firstsSelected[firstIndex] <= firstLead)
+ firstIndex++;
+ else
+ while (firstsSelected[firstIndex] >= firstLead)
+ firstIndex--;
+ firstModel.addSelectionInterval(firstsSelected[firstIndex],
+ firstsSelected[firstIndex]);
+ secondModel.addSelectionInterval(secondLead, secondLead);
+ }
+ }
+
+ /**
+ * A helper method for the key bindings. Used because the actions
+ * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar.
+ *
+ * Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER)
+ * in the table, changing the current selection. All cells in the table
+ * are eligible, not just the ones that are currently selected.
+ * @param firstModel the ListSelectionModel for columns (TAB) or rows
+ * (ENTER)
+ * @param firstMax the last index in firstModel
+ * @param secondModel the ListSelectionModel for rows (TAB) or columns
+ * (ENTER)
+ * @param secondMax the last index in secondModel
+ * @param reverse true if SHIFT was pressed for the event
+ */
+
+ void advanceSingleSelection(ListSelectionModel firstModel, int firstMax,
+ ListSelectionModel secondModel, int secondMax,
+ boolean reverse)
+ {
+ // for TABs, "first" corresponds to columns and "seconds" to rows.
+ // the opposite is true for ENTERs
+ int firstLead = firstModel.getLeadSelectionIndex();
+ int secondLead = secondModel.getLeadSelectionIndex();
+
+ // if we are going backwards subtract 2 because we later add 1
+ // for a net change of -1
+ if (reverse && (firstLead == 0))
+ {
+ // check if we have to wrap around
+ if (secondLead == 0)
+ secondLead += secondMax + 1;
+ secondLead -= 2;
+ }
+
+ // do we have to wrap the "seconds"?
+ if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax))
+ secondModel.setSelectionInterval((secondLead + 1) % (secondMax + 1),
+ (secondLead + 1) % (secondMax + 1));
+ // if not, just reselect the current lead
+ else
+ secondModel.setSelectionInterval(secondLead, secondLead);
+
+ // if we are going backwards, subtract 2 because we add 1 later
+ // for net change of -1
+ if (reverse)
+ {
+ // check for wraparound
+ if (firstLead == 0)
+ firstLead += firstMax + 1;
+ firstLead -= 2;
+ }
+ // select the next "first"
+ firstModel.setSelectionInterval((firstLead + 1) % (firstMax + 1),
+ (firstLead + 1) % (firstMax + 1));
+ }
+ }
+
+ protected void installListeners()
+ {
+ if (focusListener == null)
+ focusListener = createFocusListener();
+ table.addFocusListener(focusListener);
+ if (keyListener == null)
+ keyListener = createKeyListener();
+ table.addKeyListener(keyListener);
+ if (mouseInputListener == null)
+ mouseInputListener = createMouseInputListener();
+ table.addMouseListener(mouseInputListener);
+ table.addMouseMotionListener(mouseInputListener);
+ if (propertyChangeListener == null)
+ propertyChangeListener = new PropertyChangeHandler();
+ table.addPropertyChangeListener(propertyChangeListener);
+ }
+
+ /**
+ * Uninstalls UI defaults that have been installed by
+ * {@link #installDefaults()}.
+ */
+ protected void uninstallDefaults()
+ {
+ // Nothing to do here for now.
+ }
+
+ /**
+ * Uninstalls the keyboard actions that have been installed by
+ * {@link #installKeyboardActions()}.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(table, JComponent.
+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ SwingUtilities.replaceUIActionMap(table, null);
+ }
+
+ protected void uninstallListeners()
+ {
+ table.removeFocusListener(focusListener);
+ table.removeKeyListener(keyListener);
+ table.removeMouseListener(mouseInputListener);
+ table.removeMouseMotionListener(mouseInputListener);
+ table.removePropertyChangeListener(propertyChangeListener);
+ propertyChangeListener = null;
+ }
+
+ public void installUI(JComponent comp)
+ {
+ table = (JTable) comp;
+ rendererPane = new CellRendererPane();
+ table.add(rendererPane);
+
+ installDefaults();
+ installKeyboardActions();
+ installListeners();
+ }
+
+ public void uninstallUI(JComponent c)
+ {
+ uninstallListeners();
+ uninstallKeyboardActions();
+ uninstallDefaults();
+
+ table.remove(rendererPane);
+ rendererPane = null;
+ table = null;
+ }
+
+ /**
+ * Paints a single cell in the table.
+ *
+ * @param g The graphics context to paint in
+ * @param row The row number to paint
+ * @param col The column number to paint
+ * @param bounds The bounds of the cell to paint, assuming a coordinate
+ * system beginning at <code>(0,0)</code> in the upper left corner of the
+ * table
+ * @param rend A cell renderer to paint with
+ */
+ void paintCell(Graphics g, int row, int col, Rectangle bounds,
+ TableCellRenderer rend)
+ {
+ Component comp = table.prepareRenderer(rend, row, col);
+ rendererPane.paintComponent(g, comp, table, bounds);
+ }
+
+ /**
+ * Paint the associated table.
+ */
+ public void paint(Graphics gfx, JComponent ignored)
+ {
+ int ncols = table.getColumnCount();
+ int nrows = table.getRowCount();
+ if (nrows == 0 || ncols == 0)
+ return;
+
+ Rectangle clip = gfx.getClipBounds();
+
+ // Determine the range of cells that are within the clip bounds.
+ Point p1 = new Point(clip.x, clip.y);
+ int c0 = table.columnAtPoint(p1);
+ if (c0 == -1)
+ c0 = 0;
+ int r0 = table.rowAtPoint(p1);
+ if (r0 == -1)
+ r0 = 0;
+ Point p2 = new Point(clip.x + clip.width, clip.y + clip.height);
+ int cn = table.columnAtPoint(p2);
+ if (cn == -1)
+ cn = table.getColumnCount() - 1;
+ int rn = table.rowAtPoint(p2);
+ if (rn == -1)
+ rn = table.getRowCount() - 1;
+
+ int columnMargin = table.getColumnModel().getColumnMargin();
+ int rowMargin = table.getRowMargin();
+
+ TableColumnModel cmodel = table.getColumnModel();
+ int[] widths = new int[cn + 1];
+ for (int i = c0; i <= cn; i++)
+ {
+ widths[i] = cmodel.getColumn(i).getWidth() - columnMargin;
+ }
+
+ Rectangle bounds = table.getCellRect(r0, c0, false);
+ // The left boundary of the area being repainted.
+ int left = bounds.x;
+
+ // The top boundary of the area being repainted.
+ int top = bounds.y;
+
+ // The bottom boundary of the area being repainted.
+ int bottom;
+
+ // paint the cell contents
+ Color grid = table.getGridColor();
+ for (int r = r0; r <= rn; ++r)
+ {
+ for (int c = c0; c <= cn; ++c)
+ {
+ bounds.width = widths[c];
+ paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c));
+ bounds.x += widths[c] + columnMargin;
+ }
+ bounds.x = left;
+ bounds.y += table.getRowHeight(r);
+ // Update row height for tables with custom heights.
+ bounds.height = table.getRowHeight(r + 1) - rowMargin;
+ }
+
+ bottom = bounds.y - rowMargin;
+
+ // paint vertical grid lines
+ if (grid != null && table.getShowVerticalLines())
+ {
+ Color save = gfx.getColor();
+ gfx.setColor(grid);
+ int x = left - columnMargin;
+ for (int c = c0; c <= cn; ++c)
+ {
+ // The vertical grid is draw right from the cells, so we
+ // add before drawing.
+ x += widths[c] + columnMargin;
+ gfx.drawLine(x, top, x, bottom);
+ }
+ gfx.setColor(save);
+ }
+
+ // paint horizontal grid lines
+ if (grid != null && table.getShowHorizontalLines())
+ {
+ Color save = gfx.getColor();
+ gfx.setColor(grid);
+ int y = top - rowMargin;
+ for (int r = r0; r <= rn; ++r)
+ {
+ // The horizontal grid is draw below the cells, so we
+ // add before drawing.
+ y += table.getRowHeight(r);
+ gfx.drawLine(left, y, p2.x, y);
+ }
+ gfx.setColor(save);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java
new file mode 100644
index 000000000..b2541b453
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextAreaUI.java
@@ -0,0 +1,116 @@
+/* BasicTextAreaUI.java --
+ Copyright (C) 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+
+import java.beans.PropertyChangeEvent;
+
+import javax.swing.JComponent;
+import javax.swing.JTextArea;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.Element;
+import javax.swing.text.PlainView;
+import javax.swing.text.View;
+import javax.swing.text.WrappedPlainView;
+
+public class BasicTextAreaUI extends BasicTextUI
+{
+ public static ComponentUI createUI(JComponent comp)
+ {
+ return new BasicTextAreaUI();
+ }
+
+ public BasicTextAreaUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Create the view. Returns a WrappedPlainView if the text area
+ * has lineWrap set to true, otherwise returns a PlainView. If
+ * lineWrap is true has to check whether the wrap style is word
+ * or character and return an appropriate WrappedPlainView.
+ *
+ * @param elem the element to create a View for
+ * @return an appropriate View for the element
+ */
+ public View create(Element elem)
+ {
+ JTextArea comp = (JTextArea) getComponent();
+ if (comp.getLineWrap())
+ {
+ if (comp.getWrapStyleWord())
+ return new WrappedPlainView(elem, true);
+ else
+ return new WrappedPlainView(elem, false);
+ }
+ else
+ return new PlainView(elem);
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "TextArea"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "TextArea";
+ }
+
+ /**
+ * Receives notification whenever one of the text component's bound
+ * properties changes. This changes the view to WrappedPlainView
+ * if setLineWrap(true) is called, and back to PlainView if
+ * setLineWrap(false) is called.
+ *
+ * @param ev the property change event
+ */
+ protected void propertyChange(PropertyChangeEvent ev)
+ {
+ JTextArea comp = (JTextArea) getComponent();
+ if (ev.getPropertyName() == "lineWrap"
+ || ev.getPropertyName() == "wrapStyleWord")
+ {
+ // Changes the View (without modifying the document or it's listeners).
+ setView(create(textComponent.getDocument().getDefaultRootElement()));
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java
new file mode 100644
index 000000000..5f6a92757
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextFieldUI.java
@@ -0,0 +1,118 @@
+/* BasicTextFieldUI.java
+ Copyright (C) 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Color;
+import java.beans.PropertyChangeEvent;
+
+import javax.swing.JComponent;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.Element;
+import javax.swing.text.FieldView;
+import javax.swing.text.View;
+
+public class BasicTextFieldUI extends BasicTextUI
+{
+ public BasicTextFieldUI()
+ {
+ super();
+ }
+
+ public View create(Element elem)
+ {
+ return new FieldView(elem);
+ }
+
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicTextFieldUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "TextField"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "TextField";
+ }
+
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ }
+
+ /**
+ * Receives notification whenever one of the text component's bound
+ * properties changes. Here we check for the editable and enabled
+ * properties and adjust the background color accordingly.
+ *
+ * <p>The colors are only changed if they are not a
+ * <code>ColorUIResource</code>.</p>
+ *
+ * @param event the property change event
+ */
+ protected void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getPropertyName().equals("editable"))
+ {
+ // Changing the color only if the current background is an instance of
+ // ColorUIResource is the behavior of the RI.
+ if (textComponent.getBackground() instanceof ColorUIResource)
+ {
+ Color c = null;
+ Color old = textComponent.getBackground();
+ String prefix = getPropertyPrefix();
+ if (! textComponent.isEnabled())
+ c = SharedUIDefaults.getColor(prefix + ".disabledBackground");
+ if (c == null && ! textComponent.isEditable())
+ c = SharedUIDefaults.getColor(prefix + ".inactiveBackground");
+ if (c == null)
+ c = SharedUIDefaults.getColor(prefix + ".background");
+ if (c != null && c != old)
+ {
+ textComponent.setBackground(c);
+ }
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java
new file mode 100644
index 000000000..507e0a169
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextPaneUI.java
@@ -0,0 +1,92 @@
+/* BasicTextPaneUI.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import java.awt.Color;
+
+import javax.swing.JComponent;
+import javax.swing.JTextPane;
+import javax.swing.plaf.ColorUIResource;
+import javax.swing.UIDefaults;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+
+public class BasicTextPaneUI extends BasicEditorPaneUI
+{
+ public BasicTextPaneUI()
+ {
+ // Do nothing here.
+ }
+
+ public static ComponentUI createUI(JComponent comp)
+ {
+ return new BasicTextPaneUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIDefaults} table.
+ *
+ * @return "TextPane"
+ */
+ protected String getPropertyPrefix()
+ {
+ return "TextPane";
+ }
+
+ /**
+ * Installs this UI on the specified <code>JTextPane</code>. This calls the
+ * super implementation and then adds a default style to the text pane.
+ *
+ * @param c the text pane to install the UI to
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ JTextPane tp = (JTextPane) c;
+ Style defaultStyle = tp.getStyle(StyleContext.DEFAULT_STYLE);
+ defaultStyle.addAttribute(StyleConstants.Foreground,
+ new ColorUIResource(Color.BLACK));
+ defaultStyle.addAttribute(StyleConstants.FontFamily, "Serif");
+ defaultStyle.addAttribute(StyleConstants.Italic, Boolean.FALSE);
+ defaultStyle.addAttribute(StyleConstants.Bold, Boolean.FALSE);
+ defaultStyle.addAttribute(StyleConstants.FontSize, new Integer(12));
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java
new file mode 100644
index 000000000..bd7cc48b9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTextUI.java
@@ -0,0 +1,1538 @@
+/* BasicTextUI.java --
+ Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import gnu.classpath.SystemProperties;
+
+import java.awt.Color;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.HeadlessException;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.StringSelection;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.TransferHandler;
+import javax.swing.UIManager;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.plaf.ActionMapUIResource;
+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;
+import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.DefaultHighlighter;
+import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.Highlighter;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.Keymap;
+import javax.swing.text.Position;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * The abstract base class from which the UI classes for Swings text
+ * components are derived. This provides most of the functionality for
+ * the UI classes.
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public abstract class BasicTextUI extends TextUI
+ implements ViewFactory
+{
+ /**
+ * A {@link DefaultCaret} that implements {@link UIResource}.
+ */
+ public static class BasicCaret extends DefaultCaret implements UIResource
+ {
+ public BasicCaret()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A {@link DefaultHighlighter} that implements {@link UIResource}.
+ */
+ public static class BasicHighlighter extends DefaultHighlighter
+ implements UIResource
+ {
+ public BasicHighlighter()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ 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.
+ * The purpose is to make sure that all Views in the hierarchy, including
+ * the (real) root have a well-defined parent to which they can delegate
+ * calls like {@link #preferenceChanged}, {@link #getViewFactory} and
+ * {@link #getContainer}.
+ */
+ private class RootView extends View
+ {
+ /** The real root view. */
+ private View view;
+
+ /**
+ * Creates a new RootView.
+ */
+ public RootView()
+ {
+ super(null);
+ }
+
+ /**
+ * Returns the ViewFactory for this RootView. If the current EditorKit
+ * provides a ViewFactory, this is used. Otherwise the TextUI itself
+ * is returned as a ViewFactory.
+ *
+ * @return the ViewFactory for this RootView
+ */
+ public ViewFactory getViewFactory()
+ {
+ ViewFactory factory = null;
+ EditorKit editorKit = BasicTextUI.this.getEditorKit(getComponent());
+ factory = editorKit.getViewFactory();
+ if (factory == null)
+ factory = BasicTextUI.this;
+ return factory;
+ }
+
+ /**
+ * Indicates that the preferences of one of the child view has changed.
+ * This calls revalidate on the text component.
+ *
+ * @param v the child view which's preference has changed
+ * @param width <code>true</code> if the width preference has changed
+ * @param height <code>true</code> if the height preference has changed
+ */
+ public void preferenceChanged(View v, boolean width, boolean height)
+ {
+ textComponent.revalidate();
+ }
+
+ /**
+ * Sets the real root view.
+ *
+ * @param v the root view to set
+ */
+ public void setView(View v)
+ {
+ if (view != null)
+ view.setParent(null);
+
+ if (v != null)
+ v.setParent(this);
+
+ view = v;
+ }
+
+ /**
+ * Returns the real root view, regardless of the index.
+ *
+ * @param index not used here
+ *
+ * @return the real root view, regardless of the index.
+ */
+ public View getView(int index)
+ {
+ return view;
+ }
+
+ /**
+ * Returns <code>1</code> since the RootView always contains one
+ * child, that is the real root of the View hierarchy.
+ *
+ * @return <code>1</code> since the RootView always contains one
+ * child, that is the real root of the View hierarchy
+ */
+ public int getViewCount()
+ {
+ int count = 0;
+ if (view != null)
+ count = 1;
+ return count;
+ }
+
+ /**
+ * Returns the <code>Container</code> that contains this view. This
+ * normally will be the text component that is managed by this TextUI.
+ *
+ * @return the <code>Container</code> that contains this view
+ */
+ public Container getContainer()
+ {
+ return textComponent;
+ }
+
+ /**
+ * Sets the size of the renderer. This is synchronized because that
+ * potentially triggers layout and we don't want more than one thread
+ * playing with the layout information.
+ */
+ public synchronized void setSize(float w, float h)
+ {
+ if (view != null)
+ view.setSize(w, h);
+ }
+
+ /**
+ * Paints the view. This is delegated to the real root view.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ * @param s the allocation for the View
+ */
+ public void paint(Graphics g, Shape s)
+ {
+ if (view != null)
+ {
+ Rectangle b = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
+ setSize(b.width, b.height);
+ view.paint(g, s);
+ }
+ }
+
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * This is delegated to the real root view.
+ *
+ * @param position the position of the character in the model
+ * @param a the area that is occupied by the view
+ * @param bias either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Shape modelToView(int position, Shape a, Position.Bias bias)
+ throws BadLocationException
+ {
+ return view.modelToView(position, a, bias);
+ }
+
+ /**
+ * Maps coordinates from the <code>View</code>'s space into a position
+ * in the document model.
+ *
+ * @param x the x coordinate in the view space
+ * @param y the y coordinate in the view space
+ * @param a the allocation of this <code>View</code>
+ * @param b the bias to use
+ *
+ * @return the position in the document that corresponds to the screen
+ * coordinates <code>x, y</code>
+ */
+ public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
+ {
+ return view.viewToModel(x, y, a, b);
+ }
+
+ /**
+ * Notification about text insertions. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ if (view != null)
+ view.insertUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Notification about text removals. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ if (view != null)
+ view.removeUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Notification about text changes. These are forwarded to the
+ * real root view.
+ *
+ * @param ev the DocumentEvent describing the change
+ * @param shape the current allocation of the view's display
+ * @param vf the ViewFactory to use for creating new Views
+ */
+ public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
+ {
+ if (view != null)
+ view.changedUpdate(ev, shape, vf);
+ }
+
+ /**
+ * Returns the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction <code>d</code>.
+ *
+ * @param pos the document position
+ * @param b the bias for <code>pos</code>
+ * @param a the allocation for the view
+ * @param d the direction, must be either {@link SwingConstants#NORTH},
+ * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
+ * {@link SwingConstants#EAST}
+ * @param biasRet an array of {@link Position.Bias} that can hold at least
+ * one element, which is filled with the bias of the return position
+ * on method exit
+ *
+ * @return the document position that is (visually) nearest to the given
+ * document position <code>pos</code> in the given direction
+ * <code>d</code>
+ *
+ * @throws BadLocationException if <code>pos</code> is not a valid offset in
+ * the document model
+ */
+ public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
+ int d, Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ return view.getNextVisualPositionFrom(pos, b, a, d, biasRet);
+ }
+
+ /**
+ * Returns the startOffset of this view, which is always the beginning
+ * of the document.
+ *
+ * @return the startOffset of this view
+ */
+ public int getStartOffset()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the endOffset of this view, which is always the end
+ * of the document.
+ *
+ * @return the endOffset of this view
+ */
+ public int getEndOffset()
+ {
+ return getDocument().getLength();
+ }
+
+ /**
+ * Returns the document associated with this view.
+ *
+ * @return the document associated with this view
+ */
+ public Document getDocument()
+ {
+ return textComponent.getDocument();
+ }
+
+ /**
+ * Returns the attributes, which is null for the RootView.
+ */
+ public AttributeSet getAttributes()
+ {
+ return null;
+ }
+
+ /**
+ * Overridden to forward to the view.
+ */
+ public float getPreferredSpan(int axis)
+ {
+ // The RI returns 10 in the degenerate case.
+ float span = 10;
+ if (view != null)
+ span = view.getPreferredSpan(axis);
+ return span;
+ }
+
+ /**
+ * Overridden to forward to the real view.
+ */
+ public float getMinimumSpan(int axis)
+ {
+ // The RI returns 10 in the degenerate case.
+ float span = 10;
+ if (view != null)
+ span = view.getMinimumSpan(axis);
+ return span;
+ }
+
+ /**
+ * Overridden to return Integer.MAX_VALUE.
+ */
+ public float getMaximumSpan(int axis)
+ {
+ // 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.
+ */
+ private static EditorKit kit;
+
+ /**
+ * The combined event handler for text components.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Handler handler;
+
+ /**
+ * The root view.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ RootView rootView;
+
+ /**
+ * The text component that we handle.
+ */
+ JTextComponent textComponent;
+
+ /**
+ * Creates a new <code>BasicTextUI</code> instance.
+ */
+ public BasicTextUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a {@link Caret} that should be installed into the text component.
+ *
+ * @return a caret that should be installed into the text component
+ */
+ protected Caret createCaret()
+ {
+ return new BasicCaret();
+ }
+
+ /**
+ * Creates a {@link Highlighter} that should be installed into the text
+ * component.
+ *
+ * @return a <code>Highlighter</code> for the text component
+ */
+ protected Highlighter createHighlighter()
+ {
+ return new BasicHighlighter();
+ }
+
+ /**
+ * The text component that is managed by this UI.
+ *
+ * @return the text component that is managed by this UI
+ */
+ protected final JTextComponent getComponent()
+ {
+ return textComponent;
+ }
+
+ /**
+ * Installs this UI on the text component.
+ *
+ * @param c the text component on which to install the UI
+ */
+ public void installUI(final JComponent c)
+ {
+ textComponent = (JTextComponent) c;
+
+ if (rootView == null)
+ rootView = new RootView();
+
+ installDefaults();
+ 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
+ {
+ // Must install the document listener.
+ doc.addDocumentListener(handler);
+ modelChanged();
+ }
+
+ installListeners();
+ installKeyboardActions();
+ }
+
+ /**
+ * Installs UI defaults on the text components.
+ */
+ protected void installDefaults()
+ {
+ String prefix = getPropertyPrefix();
+ // Install the standard properties.
+ LookAndFeel.installColorsAndFont(textComponent, prefix + ".background",
+ prefix + ".foreground", prefix + ".font");
+ LookAndFeel.installBorder(textComponent, prefix + ".border");
+
+ // Some additional text component only properties.
+ Color color = textComponent.getCaretColor();
+ if (color == null || color instanceof UIResource)
+ {
+ color = UIManager.getColor(prefix + ".caretForeground");
+ textComponent.setCaretColor(color);
+ }
+
+ // Fetch the colors for enabled/disabled text components.
+ color = textComponent.getDisabledTextColor();
+ if (color == null || color instanceof UIResource)
+ {
+ color = UIManager.getColor(prefix + ".inactiveForeground");
+ textComponent.setDisabledTextColor(color);
+ }
+ color = textComponent.getSelectedTextColor();
+ if (color == null || color instanceof UIResource)
+ {
+ color = UIManager.getColor(prefix + ".selectionForeground");
+ textComponent.setSelectedTextColor(color);
+ }
+ color = textComponent.getSelectionColor();
+ if (color == null || color instanceof UIResource)
+ {
+ color = UIManager.getColor(prefix + ".selectionBackground");
+ textComponent.setSelectionColor(color);
+ }
+
+ Insets margin = textComponent.getMargin();
+ if (margin == null || margin instanceof UIResource)
+ {
+ margin = UIManager.getInsets(prefix + ".margin");
+ 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)
+ {
+ caret = createCaret();
+ textComponent.setCaret(caret);
+ caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate"));
+ }
+
+ Highlighter highlighter = textComponent.getHighlighter();
+ if (highlighter == null || highlighter instanceof UIResource)
+ textComponent.setHighlighter(createHighlighter());
+
+ }
+
+ /**
+ * Install all listeners on the text component.
+ */
+ protected void installListeners()
+ {
+ //
+ if (SystemProperties.getProperty("gnu.swing.text.no-xlike-clipboard")
+ == null)
+ {
+ if (focusListener == null)
+ focusListener = new FocusHandler();
+ textComponent.addFocusListener(focusListener);
+ }
+ }
+
+ /**
+ * Returns the name of the keymap for this type of TextUI.
+ *
+ * This is implemented so that the classname of this TextUI
+ * without the package prefix is returned. This way subclasses
+ * don't have to override this method.
+ *
+ * @return the name of the keymap for this TextUI
+ */
+ protected String getKeymapName()
+ {
+ String fullClassName = getClass().getName();
+ int index = fullClassName.lastIndexOf('.');
+ String className = fullClassName.substring(index + 1);
+ return className;
+ }
+
+ /**
+ * Creates the {@link Keymap} that is installed on the text component.
+ *
+ * @return the {@link Keymap} that is installed on the text component
+ */
+ protected Keymap createKeymap()
+ {
+ String keymapName = getKeymapName();
+ Keymap keymap = JTextComponent.getKeymap(keymapName);
+ if (keymap == null)
+ {
+ Keymap parentMap =
+ JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
+ keymap = JTextComponent.addKeymap(keymapName, parentMap);
+ Object val = UIManager.get(getPropertyPrefix() + ".keyBindings");
+ if (val != null && val instanceof JTextComponent.KeyBinding[])
+ {
+ JTextComponent.KeyBinding[] bindings =
+ (JTextComponent.KeyBinding[]) val;
+ JTextComponent.loadKeymap(keymap, bindings,
+ getComponent().getActions());
+ }
+ }
+ return keymap;
+ }
+
+ /**
+ * Installs the keyboard actions on the text components.
+ */
+ protected void installKeyboardActions()
+ {
+ // This is only there for backwards compatibility.
+ textComponent.setKeymap(createKeymap());
+
+ // load any bindings for the newer InputMap / ActionMap interface
+ SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED,
+ getInputMap());
+ SwingUtilities.replaceUIActionMap(textComponent, getActionMap());
+ }
+
+ /**
+ * Creates an ActionMap to be installed on the text component.
+ *
+ * @return an ActionMap to be installed on the text component
+ */
+ private ActionMap getActionMap()
+ {
+ // Note: There are no .actionMap entries in the standard L&Fs. However,
+ // with the RI it is possible to install action maps via such keys, so
+ // we must load them too. It can be observed that when there is no
+ // .actionMap entry in the UIManager, one gets installed after a text
+ // component of that type has been loaded.
+ String prefix = getPropertyPrefix();
+ String amName = prefix + ".actionMap";
+ ActionMap am = (ActionMap) UIManager.get(amName);
+ if (am == null)
+ {
+ am = createActionMap();
+ UIManager.put(amName, am);
+ }
+
+ ActionMap map = new ActionMapUIResource();
+ map.setParent(am);
+
+ return map;
+ }
+
+ /**
+ * Creates a default ActionMap for text components that have no UI default
+ * for this (the standard for the built-in L&Fs). The ActionMap is copied
+ * from the text component's getActions() method.
+ *
+ * @returna default ActionMap
+ */
+ private ActionMap createActionMap()
+ {
+ ActionMap am = new ActionMapUIResource();
+ Action[] actions = textComponent.getActions();
+ for (int i = actions.length - 1; i >= 0; i--)
+ {
+ Action action = actions[i];
+ am.put(action.getValue(Action.NAME), action);
+ }
+ // Add TransferHandler's actions here. They don't seem to be in the
+ // JTextComponent's default actions, and I can't make up a better place
+ // to add them.
+ Action copyAction = TransferHandler.getCopyAction();
+ am.put(copyAction.getValue(Action.NAME), copyAction);
+ Action cutAction = TransferHandler.getCutAction();
+ am.put(cutAction.getValue(Action.NAME), cutAction);
+ Action pasteAction = TransferHandler.getPasteAction();
+ am.put(pasteAction.getValue(Action.NAME), pasteAction);
+
+ return am;
+ }
+
+ /**
+ * Gets the input map for the specified <code>condition</code>.
+ *
+ * @return the InputMap for the specified condition
+ */
+ private InputMap getInputMap()
+ {
+ InputMap im = new InputMapUIResource();
+ String prefix = getPropertyPrefix();
+ InputMap shared =
+ (InputMap) SharedUIDefaults.get(prefix + ".focusInputMap");
+ if (shared != null)
+ im.setParent(shared);
+ return im;
+ }
+
+ /**
+ * Uninstalls this TextUI from the text component.
+ *
+ * @param component the text component to uninstall the UI from
+ */
+ public void uninstallUI(final JComponent component)
+ {
+ textComponent.removePropertyChangeListener(handler);
+ textComponent.getDocument().removeDocumentListener(handler);
+ rootView.setView(null);
+
+ uninstallDefaults();
+ uninstallFixedDefaults();
+ uninstallListeners();
+ uninstallKeyboardActions();
+
+ textComponent = null;
+ }
+
+ /**
+ * Uninstalls all default properties that have previously been installed by
+ * this UI.
+ */
+ protected void uninstallDefaults()
+ {
+ 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);
+ }
+
+ /**
+ * Uninstalls all listeners that have previously been installed by
+ * this UI.
+ */
+ protected void uninstallListeners()
+ {
+ // Don't nullify the focusListener field, as it is static and shared
+ // between components.
+ if (focusListener != null)
+ textComponent.removeFocusListener(focusListener);
+ }
+
+ /**
+ * Uninstalls all keyboard actions that have previously been installed by
+ * this UI.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED,
+ null);
+ SwingUtilities.replaceUIActionMap(textComponent, null);
+ }
+
+ /**
+ * Returns the property prefix by which the text component's UIDefaults
+ * are looked up.
+ *
+ * @return the property prefix by which the text component's UIDefaults
+ * are looked up
+ */
+ protected abstract String getPropertyPrefix();
+
+ /**
+ * Returns the preferred size of the text component.
+ *
+ * @param c not used here
+ *
+ * @return the preferred size of the text component
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ Dimension d = c.getSize();
+ Insets i = c.getInsets();
+ // 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);
+ }
+ else
+ {
+ // Not laid out yet. Force some pseudo size.
+ rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+ 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;
+ }
+
+ /**
+ * Returns the maximum size for text components that use this UI.
+ *
+ * This returns (Integer.MAX_VALUE, Integer.MAX_VALUE).
+ *
+ * @param c not used here
+ *
+ * @return the maximum size for text components that use this UI
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ 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;
+ }
+
+ /**
+ * Returns the minimum size for text components. This returns the size
+ * of the component's insets.
+ *
+ * @return the minimum size for text components
+ */
+ 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();
+ d.width += i.left + i.right;
+ d.height += i.top + i.bottom;
+ return d;
+ }
+
+ /**
+ * Paints the text component. This acquires a read lock on the model and then
+ * calls {@link #paintSafely(Graphics)} in order to actually perform the
+ * painting.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ * @param c not used here
+ */
+ public final void paint(Graphics g, JComponent c)
+ {
+ try
+ {
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument aDoc = (AbstractDocument) doc;
+ aDoc.readLock();
+ }
+ paintSafely(g);
+ }
+ finally
+ {
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument aDoc = (AbstractDocument) doc;
+ aDoc.readUnlock();
+ }
+ }
+ }
+
+ /**
+ * This paints the text component while beeing sure that the model is not
+ * modified while painting.
+ *
+ * The following is performed in this order:
+ * <ol>
+ * <li>If the text component is opaque, the background is painted by
+ * calling {@link #paintBackground(Graphics)}.</li>
+ * <li>If there is a highlighter, the highlighter is painted.</li>
+ * <li>The view hierarchy is painted.</li>
+ * <li>The Caret is painter.</li>
+ * </ol>
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ */
+ protected void paintSafely(Graphics g)
+ {
+ Caret caret = textComponent.getCaret();
+ Highlighter highlighter = textComponent.getHighlighter();
+
+ if (textComponent.isOpaque())
+ paintBackground(g);
+
+ // Try painting with the highlighter without checking whether there
+ // is a selection because a highlighter can be used to do more than
+ // marking selected text.
+ if (highlighter != null)
+ {
+ // Handle restoring of the color here to prevent
+ // drawing problems when the Highlighter implementor
+ // forgets to restore it.
+ Color oldColor = g.getColor();
+ highlighter.paint(g);
+ g.setColor(oldColor);
+ }
+
+ rootView.paint(g, getVisibleEditorRect());
+
+ if (caret != null && textComponent.hasFocus())
+ caret.paint(g);
+ }
+
+ /**
+ * Paints the background of the text component.
+ *
+ * @param g the <code>Graphics</code> context to paint to
+ */
+ protected void paintBackground(Graphics g)
+ {
+ Color old = g.getColor();
+ g.setColor(textComponent.getBackground());
+ g.fillRect(0, 0, textComponent.getWidth(), textComponent.getHeight());
+ g.setColor(old);
+ }
+
+ /**
+ * Overridden for better control over background painting. This now simply
+ * calls {@link #paint} and this delegates the background painting to
+ * {@link #paintBackground}.
+ *
+ * @param g the graphics to use
+ * @param c the component to be painted
+ */
+ public void update(Graphics g, JComponent c)
+ {
+ paint(g, c);
+ }
+
+ /**
+ * Marks the specified range inside the text component's model as
+ * damaged and queues a repaint request.
+ *
+ * @param t the text component
+ * @param p0 the start location inside the document model of the range that
+ * is damaged
+ * @param p1 the end location inside the document model of the range that
+ * is damaged
+ */
+ public void damageRange(JTextComponent t, int p0, int p1)
+ {
+ damageRange(t, p0, p1, Position.Bias.Forward, Position.Bias.Backward);
+ }
+
+ /**
+ * Marks the specified range inside the text component's model as
+ * damaged and queues a repaint request. This variant of this method
+ * allows a {@link Position.Bias} object to be specified for the start
+ * and end location of the range.
+ *
+ * @param t the text component
+ * @param p0 the start location inside the document model of the range that
+ * is damaged
+ * @param p1 the end location inside the document model of the range that
+ * is damaged
+ * @param firstBias the bias for the start location
+ * @param secondBias the bias for the end location
+ */
+ public void damageRange(JTextComponent t, int p0, int p1,
+ Position.Bias firstBias, Position.Bias secondBias)
+ {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc != null)
+ {
+ Document doc = t.getDocument();
+
+ // Acquire lock here to avoid structural changes in between.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ rootView.setSize(alloc.width, alloc.height);
+ Shape damage = rootView.modelToView(p0, firstBias, p1, secondBias,
+ alloc);
+ Rectangle r = damage instanceof Rectangle ? (Rectangle) damage
+ : damage.getBounds();
+ textComponent.repaint(r.x, r.y, r.width, r.height);
+ }
+ catch (BadLocationException ex)
+ {
+ // Lets ignore this as it causes no serious problems.
+ // For debugging, comment this out.
+ // ex.printStackTrace();
+ }
+ finally
+ {
+ // Release lock.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ }
+ }
+
+ /**
+ * Returns the {@link EditorKit} used for the text component that is managed
+ * by this UI.
+ *
+ * @param t the text component
+ *
+ * @return the {@link EditorKit} used for the text component that is managed
+ * by this UI
+ */
+ public EditorKit getEditorKit(JTextComponent t)
+ {
+ if (kit == null)
+ kit = new DefaultEditorKit();
+ return kit;
+ }
+
+ /**
+ * Gets the next position inside the document model that is visible on
+ * screen, starting from <code>pos</code>.
+ *
+ * @param t the text component
+ * @param pos the start positionn
+ * @param b the bias for pos
+ * @param direction the search direction
+ * @param biasRet filled by the method to indicate the bias of the return
+ * value
+ *
+ * @return the next position inside the document model that is visible on
+ * screen
+ */
+ public int getNextVisualPositionFrom(JTextComponent t, int pos,
+ Position.Bias b, int direction,
+ Position.Bias[] biasRet)
+ throws BadLocationException
+ {
+ int offset = -1;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc != null)
+ {
+ rootView.setSize(alloc.width, alloc.height);
+ offset = rootView.getNextVisualPositionFrom(pos, b, alloc,
+ direction, biasRet);
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return offset;
+ }
+
+ /**
+ * Returns the root {@link View} of a text component.
+ *
+ * @return the root {@link View} of a text component
+ */
+ public View getRootView(JTextComponent t)
+ {
+ return rootView;
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero. A bias of {@link Position.Bias#Forward} is used in this method.
+ *
+ * @param t the text component
+ * @param pos the position of the character in the model
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Rectangle modelToView(JTextComponent t, int pos)
+ throws BadLocationException
+ {
+ return modelToView(t, pos, Position.Bias.Forward);
+ }
+
+ /**
+ * Maps a position in the document into the coordinate space of the View.
+ * The output rectangle usually reflects the font height but has a width
+ * of zero.
+ *
+ * @param t the text component
+ * @param pos the position of the character in the model
+ * @param bias either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward} depending on the preferred
+ * direction bias. If <code>null</code> this defaults to
+ * <code>Position.Bias.Forward</code>
+ *
+ * @return a rectangle that gives the location of the document position
+ * inside the view coordinate space
+ *
+ * @throws BadLocationException if <code>pos</code> is invalid
+ * @throws IllegalArgumentException if b is not one of the above listed
+ * valid values
+ */
+ public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias)
+ throws BadLocationException
+ {
+ // We need to read-lock here because we depend on the document
+ // structure not beeing changed in between.
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ Rectangle rect = null;
+ try
+ {
+ Rectangle r = getVisibleEditorRect();
+ if (r != null)
+ {
+ rootView.setSize(r.width, r.height);
+ Shape s = rootView.modelToView(pos, r, bias);
+ if (s != null)
+ rect = s.getBounds();
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return rect;
+ }
+
+ /**
+ * Maps a point in the <code>View</code> coordinate space to a position
+ * inside a document model.
+ *
+ * @param t the text component
+ * @param pt the point to be mapped
+ *
+ * @return the position inside the document model that corresponds to
+ * <code>pt</code>
+ */
+ public int viewToModel(JTextComponent t, Point pt)
+ {
+ return viewToModel(t, pt, new Position.Bias[1]);
+ }
+
+ /**
+ * Maps a point in the <code>View</code> coordinate space to a position
+ * inside a document model.
+ *
+ * @param t the text component
+ * @param pt the point to be mapped
+ * @param biasReturn filled in by the method to indicate the bias of the
+ * return value
+ *
+ * @return the position inside the document model that corresponds to
+ * <code>pt</code>
+ */
+ public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn)
+ {
+ int offset = -1;
+ Document doc = textComponent.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readLock();
+ try
+ {
+ Rectangle alloc = getVisibleEditorRect();
+ if (alloc != null)
+ {
+ rootView.setSize(alloc.width, alloc.height);
+ offset = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
+ }
+ }
+ finally
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).readUnlock();
+ }
+ return offset;
+ }
+
+ /**
+ * Creates a {@link View} for the specified {@link Element}.
+ *
+ * @param elem the <code>Element</code> to create a <code>View</code> for
+ *
+ * @see ViewFactory
+ */
+ public View create(Element elem)
+ {
+ // Subclasses have to implement this to get this functionality.
+ return null;
+ }
+
+ /**
+ * Creates a {@link View} for the specified {@link Element}.
+ *
+ * @param elem the <code>Element</code> to create a <code>View</code> for
+ * @param p0 the start offset
+ * @param p1 the end offset
+ *
+ * @see ViewFactory
+ */
+ public View create(Element elem, int p0, int p1)
+ {
+ // Subclasses have to implement this to get this functionality.
+ return null;
+ }
+
+ /**
+ * A cached Insets instance to be reused below.
+ */
+ private Insets cachedInsets;
+
+ /**
+ * Returns the allocation to give the root view.
+ *
+ * @return the allocation to give the root view
+ *
+ * @specnote The allocation has nothing to do with visibility. According
+ * to the specs the naming of this method is unfortunate and
+ * has historical reasons
+ */
+ protected Rectangle getVisibleEditorRect()
+ {
+ int width = textComponent.getWidth();
+ int height = textComponent.getHeight();
+
+ // Return null if the component has no valid size.
+ if (width <= 0 || height <= 0)
+ return null;
+
+ Insets insets = textComponent.getInsets(cachedInsets);
+ return new Rectangle(insets.left, insets.top,
+ width - insets.left - insets.right,
+ height - insets.top - insets.bottom);
+ }
+
+ /**
+ * Sets the root view for the text component.
+ *
+ * @param view the <code>View</code> to be set as root view
+ */
+ protected final void setView(View view)
+ {
+ rootView.setView(view);
+ textComponent.revalidate();
+ textComponent.repaint();
+ }
+
+ /**
+ * Indicates that the model of a text component has changed. This
+ * triggers a rebuild of the view hierarchy.
+ */
+ protected void modelChanged()
+ {
+ if (textComponent == null || rootView == null)
+ return;
+ ViewFactory factory = rootView.getViewFactory();
+ if (factory == null)
+ return;
+ Document doc = textComponent.getDocument();
+ if (doc == null)
+ return;
+ Element elem = doc.getDefaultRootElement();
+ if (elem == null)
+ return;
+ View view = factory.create(elem);
+ setView(view);
+ }
+
+ /**
+ * Receives notification whenever one of the text component's bound
+ * properties changes. This default implementation does nothing.
+ * It is a hook that enables subclasses to react to property changes
+ * on the text component.
+ *
+ * @param ev the property change event
+ */
+ protected void propertyChange(PropertyChangeEvent ev)
+ {
+ // The default implementation does nothing.
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java
new file mode 100644
index 000000000..4550f08e6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToggleButtonUI.java
@@ -0,0 +1,134 @@
+/* BasicToggleButtonUI.java
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.swing.AbstractButton;
+import javax.swing.JComponent;
+import javax.swing.JToggleButton;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * A UI delegate for the {@link JToggleButton} component.
+ */
+public class BasicToggleButtonUI extends BasicButtonUI
+{
+
+ /**
+ * Returns a UI delegate for the specified component.
+ *
+ * @param component the component (should be an instance of
+ * {@link JToggleButton}).
+ *
+ * @return An instance of <code>BasicToggleButtonUI</code>.
+ */
+ public static ComponentUI createUI(JComponent component)
+ {
+ return new BasicToggleButtonUI();
+ }
+
+ /**
+ * Returns the prefix for entries in the {@link UIManager} defaults table
+ * (<code>"ToggleButton."</code> in this case).
+ *
+ * @return <code>"ToggleButton."</code>
+ */
+ protected String getPropertyPrefix()
+ {
+ return "ToggleButton.";
+ }
+
+ /**
+ * Paint the component, which is an {@link AbstractButton}, according to
+ * its current state.
+ *
+ * @param g The graphics context to paint with
+ * @param c The component to paint the state of
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ AbstractButton b = (AbstractButton) c;
+
+ Rectangle tr = new Rectangle();
+ Rectangle ir = new Rectangle();
+ Rectangle vr = new Rectangle();
+
+ Font f = c.getFont();
+
+ g.setFont(f);
+
+ 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())
+ paintButtonPressed(g, b);
+
+ paintIcon(g, b, ir);
+ if (text != null)
+ paintText(g, b, tr, text);
+ if (b.isFocusOwner() && b.isFocusPainted())
+ paintFocus(g, b, vr, tr, ir);
+ }
+
+ /**
+ * Paints the icon for the toggle button. This delegates to
+ * {@link BasicButtonUI#paintIcon(Graphics, JComponent, Rectangle)}.
+ *
+ * @param g the graphics context
+ * @param b the button to paint the icon for
+ * @param iconRect the area allocated for the icon
+ */
+ protected void paintIcon(Graphics g, AbstractButton b, Rectangle iconRect)
+ {
+ super.paintIcon(g, b, iconRect);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java
new file mode 100644
index 000000000..79cf0b0c2
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarSeparatorUI.java
@@ -0,0 +1,124 @@
+/* BasicToolBarSeparatorUI.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import java.awt.Dimension;
+import java.awt.Graphics;
+
+import javax.swing.JComponent;
+import javax.swing.JSeparator;
+import javax.swing.UIManager;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * The Basic Look and Feel UI delegate for Separator.
+ */
+public class BasicToolBarSeparatorUI extends BasicSeparatorUI
+{
+ private transient Dimension size;
+
+ /**
+ * Creates a new UI delegate for the given JComponent.
+ *
+ * @param c The JComponent to create a delegate for.
+ *
+ * @return A new BasicToolBarSeparatorUI.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicToolBarSeparatorUI();
+ }
+
+ /**
+ * This method installs the defaults that are given by the Basic L&F.
+ *
+ * @param s The Separator that is being installed.
+ */
+ protected void installDefaults(JSeparator s)
+ {
+ size = UIManager.getDimension("ToolBar.separatorSize");
+ }
+
+ /**
+ * This method does nothing as a Separator is just blank space.
+ *
+ * @param g The Graphics object to paint with
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method returns the preferred size of the JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return size;
+ }
+
+ /**
+ * This method returns the minimum size of the JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return size;
+ }
+
+ /**
+ * This method returns the maximum size of the JComponent.
+ *
+ * @param c The JComponent to measure.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return size;
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java
new file mode 100644
index 000000000..f5b2b2d1e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolBarUI.java
@@ -0,0 +1,1610 @@
+/* BasicToolBarUI.java --
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+import java.awt.BorderLayout;
+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.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Hashtable;
+
+import javax.swing.AbstractAction;
+import javax.swing.AbstractButton;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JButton;
+import javax.swing.JComponent;
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+import javax.swing.JToolBar;
+import javax.swing.KeyStroke;
+import javax.swing.LookAndFeel;
+import javax.swing.RootPaneContainer;
+import javax.swing.SwingConstants;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.event.MouseInputListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ToolBarUI;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.basic.BasicBorders.ButtonBorder;
+
+/**
+ * This is the Basic Look and Feel UI class for JToolBar.
+ */
+public class BasicToolBarUI extends ToolBarUI implements SwingConstants
+{
+
+ /**
+ * Implements the keyboard actions for JToolBar.
+ */
+ static class ToolBarAction
+ extends AbstractAction
+ {
+ /**
+ * Performs the action.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ Object cmd = getValue("__command__");
+ JToolBar toolBar = (JToolBar) event.getSource();
+ BasicToolBarUI ui = (BasicToolBarUI) toolBar.getUI();
+
+ if (cmd.equals("navigateRight"))
+ ui.navigateFocusedComp(EAST);
+ else if (cmd.equals("navigateLeft"))
+ ui.navigateFocusedComp(WEST);
+ else if (cmd.equals("navigateUp"))
+ ui.navigateFocusedComp(NORTH);
+ else if (cmd.equals("navigateDown"))
+ ui.navigateFocusedComp(SOUTH);
+ else
+ assert false : "Shouldn't reach here";
+ }
+ }
+
+ /** Static owner of all DragWindows.
+ * This is package-private to avoid an accessor method. */
+ static JFrame owner = new JFrame();
+
+ /** The border used when the JToolBar is in nonrollover mode. */
+ private static Border nonRolloverBorder;
+
+ /** The border used when the JToolBar is in rollover mode. */
+ private static Border rolloverBorder;
+
+ /** The last known BorderLayout constraint before floating. */
+ protected String constraintBeforeFloating;
+
+ /** The last known orientation of the JToolBar before floating.
+ * This is package-private to avoid an accessor method. */
+ int lastGoodOrientation;
+
+ /** The color of the border when it is dockable. */
+ protected Color dockingBorderColor;
+
+ /** The background color of the JToolBar when it is dockable. */
+ protected Color dockingColor;
+
+ /** The docking listener responsible for mouse events on the JToolBar. */
+ protected MouseInputListener dockingListener;
+
+ /** The window used for dragging the JToolBar. */
+ protected BasicToolBarUI.DragWindow dragWindow;
+
+ /** The color of the border when it is not dockable. */
+ protected Color floatingBorderColor;
+
+ /** The background color of the JToolBar when it is not dockable. */
+ protected Color floatingColor;
+
+ /** The index of the focused component. */
+ protected int focusedCompIndex;
+
+ /** The PropertyChangeListener for the JToolBar. */
+ protected PropertyChangeListener propertyListener;
+
+ /** The JToolBar this UI delegate is responsible for. */
+ protected JToolBar toolBar;
+
+ /** The Container listener for the JToolBar. */
+ protected ContainerListener toolBarContListener;
+
+ /** The Focus listener for the JToolBar. */
+ protected FocusListener toolBarFocusListener;
+
+ /**
+ * @deprecated since JDK1.3.
+ */
+ protected KeyStroke leftKey;
+
+ /**
+ * @deprecated since JDK1.3.
+ */
+ protected KeyStroke rightKey;
+
+ /**
+ * @deprecated since JDK1.3.
+ */
+ protected KeyStroke upKey;
+
+ /**
+ * @deprecated since JDK1.3.
+ */
+ protected KeyStroke downKey;
+
+ /**
+ * The floating window that is responsible for holding the JToolBar when it
+ * is dragged outside of its original parent.
+ */
+ private transient Window floatFrame;
+
+ /** The original parent of the JToolBar.
+ * This is package-private to avoid an accessor method. */
+ transient Container origParent;
+
+ /** A hashtable of components and their original borders.
+ * This is package-private to avoid an accessor method. */
+ transient Hashtable borders;
+
+ /** A window listener for the floatable frame. */
+ private transient WindowListener windowListener;
+
+ /** A set of cached bounds of the JToolBar.
+ * This is package-private to avoid an accessor method. */
+ transient Dimension cachedBounds;
+
+ /** The cached orientation of the JToolBar.
+ * This is package-private to avoid an accessor method. */
+ transient int cachedOrientation;
+
+ /**
+ * This method creates a new <code>BasicToolBarUI</code> object for the given JToolBar.
+ */
+ public BasicToolBarUI()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * This method returns whether the JToolBar can dock at the given position.
+ *
+ * @param c The component to try to dock in.
+ * @param p The position of the mouse cursor relative to the given
+ * component.
+ *
+ * @return Whether the JToolBar can dock.
+ */
+ public boolean canDock(Component c, Point p)
+ {
+ return areaOfClick(c, p) != -1;
+ }
+
+ /**
+ * This helper method returns the position of the JToolBar if it can dock.
+ *
+ * @param c The component to try to dock in.
+ * @param p The position of the mouse cursor relative to the given
+ * component.
+ *
+ * @return One of the SwingConstants directions or -1 if the JToolBar can't
+ * dock.
+ */
+ private int areaOfClick(Component c, Point p)
+ {
+ // Has to dock in immediate parent, not eventual root container.
+ Rectangle pBounds = c.getBounds();
+
+ // XXX: In Sun's implementation, the space the toolbar has to dock is dependent on the size it had last.
+ Dimension d = toolBar.getSize();
+ int limit = Math.min(d.width, d.height);
+
+ // The order of checking is 1. top 2. bottom 3. left 4. right
+ if (! pBounds.contains(p))
+ return -1;
+
+ if (p.y < limit)
+ return SwingConstants.NORTH;
+
+ if (p.y > (pBounds.height - limit))
+ return SwingConstants.SOUTH;
+
+ if (p.x < limit)
+ return SwingConstants.WEST;
+
+ if (p.x > (pBounds.width - limit))
+ return SwingConstants.EAST;
+
+ return -1;
+ }
+
+ /**
+ * This method creates a new DockingListener for the JToolBar.
+ *
+ * @return A new DockingListener for the JToolBar.
+ */
+ protected MouseInputListener createDockingListener()
+ {
+ return new DockingListener(toolBar);
+ }
+
+ /**
+ * This method creates a new DragWindow for the given JToolBar.
+ *
+ * @param toolbar The JToolBar to create a DragWindow for.
+ *
+ * @return A new DragWindow.
+ */
+ protected BasicToolBarUI.DragWindow createDragWindow(JToolBar toolbar)
+ {
+ return new DragWindow();
+ }
+
+ /**
+ * This method creates a new floating frame for the JToolBar. By default,
+ * this UI uses createFloatingWindow instead. This method of creating a
+ * floating frame is deprecated.
+ *
+ * @param toolbar The JToolBar to create a floating frame for.
+ *
+ * @return A new floating frame.
+ */
+ protected JFrame createFloatingFrame(JToolBar toolbar)
+ {
+ // FIXME: Though deprecated, this should still work.
+ return null;
+ }
+
+ /**
+ * This method creates a new floating window for the JToolBar. This is the
+ * method used by default to create a floating container for the JToolBar.
+ *
+ * @param toolbar The JToolBar to create a floating window for.
+ *
+ * @return A new floating window.
+ */
+ protected RootPaneContainer createFloatingWindow(JToolBar toolbar)
+ {
+ // This one is used by default though.
+ return new ToolBarDialog();
+ }
+
+ /**
+ * This method creates a new WindowListener for the JToolBar.
+ *
+ * @return A new WindowListener.
+ */
+ protected WindowListener createFrameListener()
+ {
+ return new FrameListener();
+ }
+
+ /**
+ * This method creates a new nonRolloverBorder for JButtons when the
+ * JToolBar's rollover property is set to false.
+ *
+ * @return A new NonRolloverBorder.
+ */
+ protected Border createNonRolloverBorder()
+ {
+ Border b = UIManager.getBorder("ToolBar.nonrolloverBorder");
+
+ if (b == null)
+ {
+ b = new CompoundBorder(
+ new ButtonBorder(UIManager.getColor("Button.shadow"),
+ UIManager.getColor("Button.darkShadow"),
+ UIManager.getColor("Button.light"),
+ UIManager.getColor("Button.highlight")),
+ BasicBorders.getMarginBorder());
+ }
+
+ return b; }
+
+ /**
+ * This method creates a new PropertyChangeListener for the JToolBar.
+ *
+ * @return A new PropertyChangeListener.
+ */
+ protected PropertyChangeListener createPropertyListener()
+ {
+ return new PropertyListener();
+ }
+
+ /**
+ * This method creates a new rollover border for JButtons when the
+ * JToolBar's rollover property is set to true.
+ *
+ * @return A new rollover border.
+ */
+ protected Border createRolloverBorder()
+ {
+ Border b = UIManager.getBorder("ToolBar.rolloverBorder");
+
+ if (b == null)
+ {
+ b = new CompoundBorder(
+ new ButtonBorder(UIManager.getColor("Button.shadow"),
+ UIManager.getColor("Button.darkShadow"),
+ UIManager.getColor("Button.light"),
+ UIManager.getColor("Button.highlight")),
+ BasicBorders.getMarginBorder());
+ }
+
+ return b;
+ }
+
+ /**
+ * This method creates a new Container listener for the JToolBar.
+ *
+ * @return A new Container listener.
+ */
+ protected ContainerListener createToolBarContListener()
+ {
+ return new ToolBarContListener();
+ }
+
+ /**
+ * This method creates a new FocusListener for the JToolBar.
+ *
+ * @return A new FocusListener for the JToolBar.
+ */
+ protected FocusListener createToolBarFocusListener()
+ {
+ return new ToolBarFocusListener();
+ }
+
+ /**
+ * This method creates a new UI delegate for the given JComponent.
+ *
+ * @param c The JComponent to create a UI delegate for.
+ *
+ * @return A new UI delegate.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicToolBarUI();
+ }
+
+ /**
+ * This method is called to drag the DragWindow around when the JToolBar is
+ * being dragged around.
+ *
+ * @param position The mouse cursor coordinates relative to the JToolBar.
+ * @param origin The screen position of the JToolBar.
+ */
+ protected void dragTo(Point position, Point origin)
+ {
+ int loc = areaOfClick(origParent,
+ SwingUtilities.convertPoint(toolBar, position,
+ origParent));
+
+ if (loc != -1)
+ {
+ dragWindow.setBorderColor(dockingBorderColor);
+ dragWindow.setBackground(dockingColor);
+ }
+ else
+ {
+ dragWindow.setBorderColor(floatingBorderColor);
+ dragWindow.setBackground(floatingColor);
+ }
+
+ int w = 0;
+ int h = 0;
+
+ boolean tmp = (loc == SwingConstants.NORTH)
+ || (loc == SwingConstants.SOUTH) || (loc == -1);
+
+ cachedOrientation = toolBar.getOrientation();
+ cachedBounds = toolBar.getSize();
+ if (((cachedOrientation == SwingConstants.HORIZONTAL) && tmp)
+ || ((cachedOrientation == VERTICAL) && ! tmp))
+ {
+ w = cachedBounds.width;
+ h = cachedBounds.height;
+ }
+ else
+ {
+ w = cachedBounds.height;
+ h = cachedBounds.width;
+ }
+
+ Point p = dragWindow.getOffset();
+ Insets insets = toolBar.getInsets();
+
+ dragWindow.setBounds((origin.x + position.x) - p.x
+ - ((insets.left + insets.right) / 2),
+ (origin.y + position.y) - p.y
+ - ((insets.top + insets.bottom) / 2), w, h);
+
+ if (! dragWindow.isVisible())
+ dragWindow.show();
+ }
+
+ /**
+ * This method is used at the end of a drag session to place the frame in
+ * either its original parent as a docked JToolBar or in its floating
+ * frame.
+ *
+ * @param position The position of the mouse cursor relative to the
+ * JToolBar.
+ * @param origin The screen position of the JToolBar before the drag session
+ * started.
+ */
+ protected void floatAt(Point position, Point origin)
+ {
+ Point p = new Point(position);
+ int aoc = areaOfClick(origParent,
+ SwingUtilities.convertPoint(toolBar, p, origParent));
+
+ Container oldParent = toolBar.getParent();
+
+ oldParent.remove(toolBar);
+ oldParent.doLayout();
+ oldParent.repaint();
+
+ Container newParent;
+
+ if (aoc == -1)
+ newParent = ((RootPaneContainer) floatFrame).getContentPane();
+ else
+ {
+ floatFrame.hide();
+ newParent = origParent;
+ }
+
+ String constraint;
+ switch (aoc)
+ {
+ case SwingConstants.EAST:
+ constraint = BorderLayout.EAST;
+ break;
+ case SwingConstants.NORTH:
+ constraint = BorderLayout.NORTH;
+ break;
+ case SwingConstants.SOUTH:
+ constraint = BorderLayout.SOUTH;
+ break;
+ case SwingConstants.WEST:
+ constraint = BorderLayout.WEST;
+ break;
+ default:
+ constraint = BorderLayout.CENTER;
+ break;
+ }
+
+ int newOrientation = SwingConstants.HORIZONTAL;
+ if ((aoc != -1)
+ && ((aoc == SwingConstants.EAST) || (aoc == SwingConstants.WEST)))
+ newOrientation = SwingConstants.VERTICAL;
+
+ if (aoc != -1)
+ {
+ constraintBeforeFloating = constraint;
+ lastGoodOrientation = newOrientation;
+ }
+
+ newParent.add(toolBar, constraint);
+
+ setFloating(aoc == -1, null);
+ toolBar.setOrientation(newOrientation);
+
+ Insets insets = floatFrame.getInsets();
+ Dimension dims = toolBar.getPreferredSize();
+ p = dragWindow.getOffset();
+ setFloatingLocation((position.x + origin.x) - p.x
+ - ((insets.left + insets.right) / 2),
+ (position.y + origin.y) - p.y
+ - ((insets.top + insets.bottom) / 2));
+
+ if (aoc == -1)
+ {
+ floatFrame.pack();
+ floatFrame.setSize(dims.width + insets.left + insets.right,
+ dims.height + insets.top + insets.bottom);
+ floatFrame.show();
+ }
+
+ newParent.invalidate();
+ newParent.validate();
+ newParent.repaint();
+ }
+
+ /**
+ * This method returns the docking color.
+ *
+ * @return The docking color.
+ */
+ public Color getDockingColor()
+ {
+ return dockingColor;
+ }
+
+ /**
+ * This method returns the Color which is displayed when over a floating
+ * area.
+ *
+ * @return The color which is displayed when over a floating area.
+ */
+ public Color getFloatingColor()
+ {
+ return floatingColor;
+ }
+
+ /**
+ * This method returns the maximum size of the given JComponent for this UI.
+ *
+ * @param c The JComponent to find the maximum size for.
+ *
+ * @return The maximum size for this UI.
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method returns the minimum size of the given JComponent for this UI.
+ *
+ * @param c The JComponent to find a minimum size for.
+ *
+ * @return The minimum size for this UI.
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * This method installs the needed components for the JToolBar.
+ */
+ protected void installComponents()
+ {
+ floatFrame = (Window) createFloatingWindow(toolBar);
+
+ dragWindow = createDragWindow(toolBar);
+
+ nonRolloverBorder = createNonRolloverBorder();
+ rolloverBorder = createRolloverBorder();
+
+ borders = new Hashtable();
+ setRolloverBorders(toolBar.isRollover());
+
+ fillHashtable();
+ }
+
+ /**
+ * This method installs the defaults as specified by the look and feel.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installBorder(toolBar, "ToolBar.border");
+ LookAndFeel.installColorsAndFont(toolBar, "ToolBar.background",
+ "ToolBar.foreground", "ToolBar.font");
+
+ dockingBorderColor = UIManager.getColor("ToolBar.dockingForeground");
+ dockingColor = UIManager.getColor("ToolBar.dockingBackground");
+
+ floatingBorderColor = UIManager.getColor("ToolBar.floatingForeground");
+ floatingColor = UIManager.getColor("ToolBar.floatingBackground");
+ }
+
+ /**
+ * This method installs the keyboard actions for the JToolBar as specified
+ * by the look and feel.
+ */
+ protected void installKeyboardActions()
+ {
+ // Install the input map.
+ InputMap inputMap =
+ (InputMap) SharedUIDefaults.get("ToolBar.ancestorInputMap");
+ SwingUtilities.replaceUIInputMap(toolBar,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ inputMap);
+
+ // FIXME: The JDK uses a LazyActionMap for parentActionMap
+ SwingUtilities.replaceUIActionMap(toolBar, getActionMap());
+ }
+
+ /**
+ * Fetches the action map from the UI defaults, or create a new one
+ * if the action map hasn't been initialized.
+ *
+ * @return the action map
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("ToolBar.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("ToolBar.actionMap", am);
+ }
+ return am;
+ }
+
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new ToolBarAction();
+
+ am.put("navigateLeft", action);
+ am.put("navigateRight", action);
+ am.put("navigateUp", action);
+ am.put("navigateDown", action);
+
+ return am;
+ }
+
+ /**
+ * This method installs listeners for the JToolBar.
+ */
+ protected void installListeners()
+ {
+ dockingListener = createDockingListener();
+ toolBar.addMouseListener(dockingListener);
+ toolBar.addMouseMotionListener(dockingListener);
+
+ propertyListener = createPropertyListener();
+ toolBar.addPropertyChangeListener(propertyListener);
+
+ toolBarContListener = createToolBarContListener();
+ toolBar.addContainerListener(toolBarContListener);
+
+ windowListener = createFrameListener();
+ floatFrame.addWindowListener(windowListener);
+
+ toolBarFocusListener = createToolBarFocusListener();
+ if (toolBarFocusListener != null)
+ {
+ int count = toolBar.getComponentCount();
+ for (int i = 0; i < count; i++)
+ toolBar.getComponent(i).addFocusListener(toolBarFocusListener);
+ }
+ }
+
+ /**
+ * This method installs non rollover borders for each component inside the
+ * given JComponent.
+ *
+ * @param c The JComponent whose children need to have non rollover borders
+ * installed.
+ */
+ protected void installNonRolloverBorders(JComponent c)
+ {
+ Component[] components = toolBar.getComponents();
+
+ for (int i = 0; i < components.length; i++)
+ setBorderToNonRollover(components[i]);
+ }
+
+ /**
+ * This method installs normal (or their original) borders for each
+ * component inside the given JComponent.
+ *
+ * @param c The JComponent whose children need to have their original
+ * borders installed.
+ */
+ protected void installNormalBorders(JComponent c)
+ {
+ Component[] components = toolBar.getComponents();
+
+ for (int i = 0; i < components.length; i++)
+ setBorderToNormal(components[i]);
+ }
+
+ /**
+ * This method install rollover borders for each component inside the given
+ * JComponent.
+ *
+ * @param c The JComponent whose children need to have rollover borders
+ * installed.
+ */
+ protected void installRolloverBorders(JComponent c)
+ {
+ Component[] components = toolBar.getComponents();
+
+ for (int i = 0; i < components.length; i++)
+ setBorderToRollover(components[i]);
+ }
+
+ /**
+ * This method fills the borders hashtable with a list of components that
+ * are JButtons and their borders.
+ */
+ private void fillHashtable()
+ {
+ Component[] c = toolBar.getComponents();
+
+ for (int i = 0; i < c.length; i++)
+ {
+ if (c[i] instanceof JButton)
+ {
+ // Don't really care about anything other than JButtons
+ JButton b = (JButton) c[i];
+
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
+ }
+ }
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install a UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+
+ if (c instanceof JToolBar)
+ {
+ toolBar = (JToolBar) c;
+ installDefaults();
+ installComponents();
+ installListeners();
+ installKeyboardActions();
+ }
+ }
+
+ /**
+ * This method returns whether the JToolBar is floating.
+ *
+ * @return Whether the JToolBar is floating.
+ */
+ public boolean isFloating()
+ {
+ return floatFrame.isVisible();
+ }
+
+ /**
+ * This method returns whether rollover borders have been set.
+ *
+ * @return Whether rollover borders have been set.
+ */
+ public boolean isRolloverBorders()
+ {
+ return toolBar.isRollover();
+ }
+
+ /**
+ * This method navigates in the given direction giving focus to the next
+ * component in the given direction.
+ *
+ * @param direction The direction to give focus to.
+ */
+ protected void navigateFocusedComp(int direction)
+ {
+ int count = toolBar.getComponentCount();
+ switch (direction)
+ {
+ case EAST:
+ case SOUTH:
+ if (focusedCompIndex >= 0 && focusedCompIndex < count)
+ {
+ int i = focusedCompIndex + 1;
+ boolean focusRequested = false;
+ // Find component to focus and request focus on it.
+ while (i != focusedCompIndex && ! focusRequested)
+ {
+ if (i >= count)
+ i = 0;
+ Component comp = toolBar.getComponentAtIndex(i++);
+ if (comp != null && comp.isFocusable()
+ && comp.isEnabled())
+ {
+ comp.requestFocus();
+ focusRequested = true;
+ }
+ }
+ }
+ break;
+ case WEST:
+ case NORTH:
+ if (focusedCompIndex >= 0 && focusedCompIndex < count)
+ {
+ int i = focusedCompIndex - 1;
+ boolean focusRequested = false;
+ // Find component to focus and request focus on it.
+ while (i != focusedCompIndex && ! focusRequested)
+ {
+ if (i < 0)
+ i = count - 1;
+ Component comp = toolBar.getComponentAtIndex(i--);
+ if (comp != null && comp.isFocusable()
+ && comp.isEnabled())
+ {
+ comp.requestFocus();
+ focusRequested = true;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * This method sets the border of the given component to a non rollover
+ * border.
+ *
+ * @param c The Component whose border needs to be set.
+ */
+ protected void setBorderToNonRollover(Component c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ b.setRolloverEnabled(false);
+
+ // Save old border in hashtable.
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
+
+ b.setBorder(nonRolloverBorder);
+ }
+ }
+
+ /**
+ * This method sets the border of the given component to its original value.
+ *
+ * @param c The Component whose border needs to be set.
+ */
+ protected void setBorderToNormal(Component c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ b.setRolloverEnabled(true);
+ b.setBorder((Border) borders.remove(b));
+ }
+ }
+
+ /**
+ * This method sets the border of the given component to a rollover border.
+ *
+ * @param c The Component whose border needs to be set.
+ */
+ protected void setBorderToRollover(Component c)
+ {
+ if (c instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) c;
+ b.setRolloverEnabled(false);
+
+ // Save old border in hashtable.
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
+
+ b.setBorder(rolloverBorder);
+ }
+ }
+
+ /**
+ * This method sets the docking color.
+ *
+ * @param c The docking color.
+ */
+ public void setDockingColor(Color c)
+ {
+ dockingColor = c;
+ }
+
+ /**
+ * This method sets the floating property for the JToolBar.
+ *
+ * @param b Whether the JToolBar is floating.
+ * @param p FIXME
+ */
+ public void setFloating(boolean b, Point p)
+ {
+ // FIXME: use p for something. It's not location
+ // since we already have setFloatingLocation.
+ floatFrame.setVisible(b);
+ }
+
+ /**
+ * This method sets the color displayed when the JToolBar is not in a
+ * dockable area.
+ *
+ * @param c The floating color.
+ */
+ public void setFloatingColor(Color c)
+ {
+ floatingColor = c;
+ }
+
+ /**
+ * This method sets the floating location of the JToolBar.
+ *
+ * @param x The x coordinate for the floating frame.
+ * @param y The y coordinate for the floating frame.
+ */
+ public void setFloatingLocation(int x, int y)
+ {
+ // x,y are the coordinates of the new JFrame created to store the toolbar
+ // XXX: The floating location is bogus is not floating.
+ floatFrame.setLocation(x, y);
+ floatFrame.invalidate();
+ floatFrame.validate();
+ floatFrame.repaint();
+ }
+
+ /**
+ * This is a convenience method for changing the orientation of the
+ * JToolBar.
+ *
+ * @param orientation The new orientation.
+ */
+ public void setOrientation(int orientation)
+ {
+ toolBar.setOrientation(orientation);
+ }
+
+ /**
+ * This method changes the child components to have rollover borders if the
+ * given parameter is true. Otherwise, the components are set to have non
+ * rollover borders.
+ *
+ * @param rollover Whether the children will have rollover borders.
+ */
+ public void setRolloverBorders(boolean rollover)
+ {
+ if (rollover)
+ installRolloverBorders(toolBar);
+ else
+ installNonRolloverBorders(toolBar);
+ }
+
+ /**
+ * This method uninstall UI installed components from the JToolBar.
+ */
+ protected void uninstallComponents()
+ {
+ installNormalBorders(toolBar);
+ borders = null;
+ cachedBounds = null;
+
+ floatFrame = null;
+ dragWindow = null;
+ }
+
+ /**
+ * This method removes the defaults installed by the Look and Feel.
+ */
+ protected void uninstallDefaults()
+ {
+ toolBar.setBackground(null);
+ toolBar.setForeground(null);
+ toolBar.setFont(null);
+
+ dockingBorderColor = null;
+ dockingColor = null;
+ floatingBorderColor = null;
+ floatingColor = null;
+ }
+
+ /**
+ * This method uninstalls keyboard actions installed by the UI.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ SwingUtilities.replaceUIInputMap(toolBar, JComponent.
+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ SwingUtilities.replaceUIActionMap(toolBar, null);
+ }
+
+ /**
+ * This method uninstalls listeners installed by the UI.
+ */
+ protected void uninstallListeners()
+ {
+ if (toolBarFocusListener != null)
+ {
+ int count = toolBar.getComponentCount();
+ for (int i = 0; i < count; i++)
+ toolBar.getComponent(i).removeFocusListener(toolBarFocusListener);
+ toolBarFocusListener = null;
+ }
+
+ floatFrame.removeWindowListener(windowListener);
+ windowListener = null;
+
+ toolBar.removeContainerListener(toolBarContListener);
+ toolBarContListener = null;
+
+ toolBar.removeMouseMotionListener(dockingListener);
+ toolBar.removeMouseListener(dockingListener);
+ dockingListener = null;
+ }
+
+ /**
+ * This method uninstalls the UI.
+ *
+ * @param c The JComponent that is having this UI removed.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallComponents();
+ uninstallDefaults();
+ toolBar = null;
+ }
+
+ /**
+ * This is the MouseHandler class that allows the user to drag the JToolBar
+ * in and out of the parent and dock it if it can.
+ */
+ public class DockingListener implements MouseInputListener
+ {
+ /** Whether the JToolBar is being dragged. */
+ protected boolean isDragging;
+
+ /**
+ * The origin point. This point is saved from the beginning press and is
+ * used until the end of the drag session.
+ */
+ protected Point origin;
+
+ /** The JToolBar being dragged. */
+ protected JToolBar toolBar;
+
+ /**
+ * Creates a new DockingListener object.
+ *
+ * @param t The JToolBar this DockingListener is being used for.
+ */
+ public DockingListener(JToolBar t)
+ {
+ toolBar = t;
+ }
+
+ /**
+ * This method is called when the mouse is clicked.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the mouse is dragged. It delegates the drag
+ * painting to the dragTo method.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ if (isDragging)
+ dragTo(e.getPoint(), origin);
+ }
+
+ /**
+ * This method is called when the mouse enters the JToolBar.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the mouse exits the JToolBar.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the mouse is moved in the JToolBar.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the mouse is pressed in the JToolBar. If the
+ * press doesn't occur in a place where it causes the JToolBar to be
+ * dragged, it returns. Otherwise, it starts a drag session.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (! toolBar.isFloatable())
+ return;
+
+ Point ssd = e.getPoint();
+ Insets insets = toolBar.getInsets();
+
+ // Verify that this click occurs in the top inset.
+ if (toolBar.getOrientation() == SwingConstants.HORIZONTAL)
+ {
+ if (e.getX() > insets.left)
+ return;
+ }
+ else
+ {
+ if (e.getY() > insets.top)
+ return;
+ }
+
+ origin = new Point(0, 0);
+ if (toolBar.isShowing())
+ SwingUtilities.convertPointToScreen(ssd, toolBar);
+
+ if (! (SwingUtilities.getAncestorOfClass(Window.class, toolBar) instanceof UIResource))
+ // Need to know who keeps the toolBar if it gets dragged back into it.
+ origParent = toolBar.getParent();
+
+ if (toolBar.isShowing())
+ SwingUtilities.convertPointToScreen(origin, toolBar);
+
+ isDragging = true;
+
+ if (dragWindow != null)
+ dragWindow.setOffset(new Point(cachedBounds.width / 2,
+ cachedBounds.height / 2));
+
+ dragTo(e.getPoint(), origin);
+ }
+
+ /**
+ * This method is called when the mouse is released from the JToolBar.
+ *
+ * @param e The MouseEvent.
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ if (! isDragging || ! toolBar.isFloatable())
+ return;
+
+ isDragging = false;
+ floatAt(e.getPoint(), origin);
+ dragWindow.hide();
+ }
+ }
+
+ /**
+ * This is the window that appears when the JToolBar is being dragged
+ * around.
+ */
+ protected class DragWindow extends Window
+ {
+ /**
+ * The current border color. It changes depending on whether the JToolBar
+ * is over a place that allows it to dock.
+ */
+ private Color borderColor;
+
+ /** The between the mouse and the top left corner of the window. */
+ private Point offset;
+
+ /**
+ * Creates a new DragWindow object.
+ * This is package-private to avoid an accessor method.
+ */
+ DragWindow()
+ {
+ super(owner);
+ }
+
+ /**
+ * The color that the border should be.
+ *
+ * @return The border color.
+ */
+ public Color getBorderColor()
+ {
+ if (borderColor == null)
+ return Color.BLACK;
+
+ return borderColor;
+ }
+
+ /**
+ * This method returns the insets for the DragWindow.
+ *
+ * @return The insets for the DragWindow.
+ */
+ public Insets getInsets()
+ {
+ // This window has no decorations, so insets are empty.
+ return new Insets(0, 0, 0, 0);
+ }
+
+ /**
+ * This method returns the mouse offset from the top left corner of the
+ * DragWindow.
+ *
+ * @return The mouse offset.
+ */
+ public Point getOffset()
+ {
+ return offset;
+ }
+
+ /**
+ * This method paints the DragWindow.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void paint(Graphics g)
+ {
+ // No visiting children necessary.
+ Color saved = g.getColor();
+ Rectangle b = getBounds();
+
+ g.setColor(getBorderColor());
+ g.drawRect(0, 0, b.width - 1, b.height - 1);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method changes the border color.
+ *
+ * @param c The new border color.
+ */
+ public void setBorderColor(Color c)
+ {
+ borderColor = c;
+ }
+
+ /**
+ * This method changes the mouse offset.
+ *
+ * @param p The new mouse offset.
+ */
+ public void setOffset(Point p)
+ {
+ offset = p;
+ }
+
+ /**
+ * Sets the orientation of the toolbar and the
+ * drag window.
+ *
+ * @param o - the new orientation of the toolbar and drag
+ * window.
+ */
+ public void setOrientation(int o)
+ {
+ toolBar.setOrientation(o);
+ if (dragWindow != null)
+ dragWindow.setOrientation(o);
+ }
+ }
+
+ /**
+ * This helper class listens for Window events from the floatable window and
+ * if it is closed, returns the JToolBar to the last known good location.
+ */
+ protected class FrameListener extends WindowAdapter
+ {
+ /**
+ * This method is called when the floating window is closed.
+ *
+ * @param e The WindowEvent.
+ */
+ public void windowClosing(WindowEvent e)
+ {
+ Container parent = toolBar.getParent();
+ parent.remove(toolBar);
+
+ if (origParent != null)
+ {
+ origParent.add(toolBar,
+ (constraintBeforeFloating != null)
+ ? constraintBeforeFloating : BorderLayout.NORTH);
+ toolBar.setOrientation(lastGoodOrientation);
+ }
+
+ origParent.invalidate();
+ origParent.validate();
+ origParent.repaint();
+ }
+ }
+
+ /**
+ * This helper class listens for PropertyChangeEvents from the JToolBar.
+ */
+ protected class PropertyListener implements PropertyChangeListener
+ {
+ /**
+ * This method is called when a property from the JToolBar is changed.
+ *
+ * @param e The PropertyChangeEvent.
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // FIXME: need name properties so can change floatFrame title.
+ if (e.getPropertyName().equals("rollover") && toolBar != null)
+ setRolloverBorders(toolBar.isRollover());
+ }
+ }
+
+ /**
+ * This helper class listens for components added to and removed from the
+ * JToolBar.
+ */
+ protected class ToolBarContListener implements ContainerListener
+ {
+ /**
+ * This method is responsible for setting rollover or non rollover for new
+ * buttons added to the JToolBar.
+ *
+ * @param e The ContainerEvent.
+ */
+ public void componentAdded(ContainerEvent e)
+ {
+ if (e.getChild() instanceof JButton)
+ {
+ JButton b = (JButton) e.getChild();
+
+ if (b.getBorder() != null)
+ borders.put(b, b.getBorder());
+ }
+
+ if (isRolloverBorders())
+ setBorderToRollover(e.getChild());
+ else
+ setBorderToNonRollover(e.getChild());
+
+ cachedBounds = toolBar.getPreferredSize();
+ cachedOrientation = toolBar.getOrientation();
+
+ Component c = e.getChild();
+ if (toolBarFocusListener != null)
+ c.addFocusListener(toolBarFocusListener);
+ }
+
+ /**
+ * This method is responsible for giving the child components their
+ * original borders when they are removed.
+ *
+ * @param e The ContainerEvent.
+ */
+ public void componentRemoved(ContainerEvent e)
+ {
+ setBorderToNormal(e.getChild());
+ cachedBounds = toolBar.getPreferredSize();
+ cachedOrientation = toolBar.getOrientation();
+
+ Component c = e.getChild();
+ if (toolBarFocusListener != null)
+ c.removeFocusListener(toolBarFocusListener);
+ }
+ }
+
+ /**
+ * This is the floating window that is returned when getFloatingWindow is
+ * called.
+ */
+ private class ToolBarDialog extends JDialog implements UIResource
+ {
+ /**
+ * Creates a new ToolBarDialog object with the name given by the JToolBar.
+ */
+ public ToolBarDialog()
+ {
+ super();
+ setName((toolBar.getName() != null) ? toolBar.getName() : "");
+ }
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ protected class ToolBarFocusListener implements FocusListener
+ {
+ /**
+ * Creates a new ToolBarFocusListener object.
+ */
+ protected ToolBarFocusListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification when the toolbar or one of it's component
+ * receives the keyboard input focus.
+ *
+ * @param e the focus event
+ */
+ public void focusGained(FocusEvent e)
+ {
+ Component c = e.getComponent();
+ focusedCompIndex = toolBar.getComponentIndex(c);
+ }
+
+ /**
+ * Receives notification when the toolbar or one of it's component
+ * looses the keyboard input focus.
+ *
+ * @param e the focus event
+ */
+ public void focusLost(FocusEvent e)
+ {
+ // Do nothing here.
+ }
+ }
+
+ /**
+ * This helper class acts as the border for the JToolBar.
+ */
+ private static class ToolBarBorder implements Border
+ {
+ /** The size of the larger, draggable side of the border. */
+ private static final int offset = 10;
+
+ /** The other sides. */
+ private static final int regular = 2;
+
+ /**
+ * This method returns the border insets for the JToolBar.
+ *
+ * @param c The Component to find insets for.
+ *
+ * @return The border insets.
+ */
+ public Insets getBorderInsets(Component c)
+ {
+ if (c instanceof JToolBar)
+ {
+ JToolBar tb = (JToolBar) c;
+ int orientation = tb.getOrientation();
+
+ if (! tb.isFloatable())
+ return new Insets(regular, regular, regular, regular);
+ else if (orientation == SwingConstants.HORIZONTAL)
+ return new Insets(regular, offset, regular, regular);
+ else
+ return new Insets(offset, regular, regular, regular);
+ }
+
+ return new Insets(0, 0, 0, 0);
+ }
+
+ /**
+ * This method returns whether the border is opaque.
+ *
+ * @return Whether the border is opaque.
+ */
+ public boolean isBorderOpaque()
+ {
+ return false;
+ }
+
+ /**
+ * This method paints the ribbed area of the border.
+ *
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate of the area.
+ * @param y The y coordinate of the area.
+ * @param w The width of the area.
+ * @param h The height of the area.
+ * @param size The size of the bump.
+ * @param c The color of the bumps.
+ */
+ private void paintBumps(Graphics g, int x, int y, int w, int h, int size,
+ Color c)
+ {
+ Color saved = g.getColor();
+ g.setColor(c);
+
+ int hgap = 2 * size;
+ int vgap = 4 * size;
+ int count = 0;
+
+ for (int i = x; i < (w + x); i += hgap)
+ for (int j = ((count++ % 2) == 0) ? y : (y + (2 * size)); j < (h + y);
+ j += vgap)
+ g.fillRect(i, j, size, size);
+
+ g.setColor(saved);
+ }
+
+ /**
+ * This method paints the border around the given Component.
+ *
+ * @param c The Component whose border is being painted.
+ * @param g The Graphics object to paint with.
+ * @param x The x coordinate of the component.
+ * @param y The y coordinate of the component.
+ * @param width The width of the component.
+ * @param height The height of the component.
+ */
+ public void paintBorder(Component c, Graphics g, int x, int y, int width,
+ int height)
+ {
+ if (c instanceof JToolBar)
+ {
+ JToolBar tb = (JToolBar) c;
+
+ int orientation = tb.getOrientation();
+
+ if (orientation == SwingConstants.HORIZONTAL)
+ {
+ paintBumps(g, x, y, offset, height, 1, Color.WHITE);
+ paintBumps(g, x + 1, y + 1, offset - 1, height - 1, 1, Color.GRAY);
+ }
+ else
+ {
+ paintBumps(g, x, y, width, offset, 1, Color.WHITE);
+ paintBumps(g, x + 1, y + 1, width - 1, offset - 1, 1, Color.GRAY);
+ }
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java
new file mode 100644
index 000000000..37c084bb8
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicToolTipUI.java
@@ -0,0 +1,292 @@
+/* BasicToolTipUI.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.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.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JComponent;
+import javax.swing.JToolTip;
+import javax.swing.LookAndFeel;
+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.
+ */
+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;
+
+ /** The tooltip's text */
+ private String text;
+
+ /**
+ * Handles property changes.
+ */
+ private PropertyChangeListener propertyChangeHandler;
+
+ /**
+ * Creates a new BasicToolTipUI object.
+ */
+ public BasicToolTipUI()
+ {
+ super();
+ }
+
+ /**
+ * This method creates a new BasicToolTip UI for the given
+ * JComponent.
+ *
+ * @param c The JComponent to create a UI for.
+ *
+ * @return A BasicToolTipUI that can be used by the given JComponent.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ if (shared == null)
+ shared = new BasicToolTipUI();
+ return shared;
+ }
+
+ /**
+ * This method returns the msximum size of the given JComponent.
+ *
+ * @param c The JComponent to find a maximum size for.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize(JComponent 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;
+ }
+
+ /**
+ * This method returns the minimum size of the given JComponent.
+ *
+ * @param c The JComponent to find a minimum size for.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize(JComponent 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;
+ }
+
+ /**
+ * This method returns the preferred size of the given JComponent.
+ *
+ * @param c The JComponent to find a preferred size for.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ JToolTip tip = (JToolTip) c;
+ 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;
+ }
+
+ /**
+ * This method installs the defaults for the given JComponent.
+ *
+ * @param c The JComponent to install defaults for.
+ */
+ protected void installDefaults(JComponent c)
+ {
+ LookAndFeel.installColorsAndFont(c, "ToolTip.background",
+ "ToolTip.foreground", "ToolTip.font");
+ LookAndFeel.installBorder(c, "ToolTip.border");
+ }
+
+ /**
+ * This method installs the listeners for the given JComponent.
+ *
+ * @param c The JComponent to install listeners for.
+ */
+ protected void installListeners(JComponent c)
+ {
+ propertyChangeHandler = new PropertyChangeHandler();
+ c.addPropertyChangeListener(propertyChangeHandler);
+ }
+
+ /**
+ * This method installs the UI for the given JComponent.
+ *
+ * @param c The JComponent to install the UI for.
+ */
+ public void installUI(JComponent c)
+ {
+ c.setOpaque(true);
+ installDefaults(c);
+ BasicHTML.updateRenderer(c, ((JToolTip) c).getTipText());
+ installListeners(c);
+ }
+
+ /**
+ * This method paints the given JComponent with the given Graphics object.
+ *
+ * @param g The Graphics object to paint with.
+ * @param c The JComponent to paint.
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ JToolTip tip = (JToolTip) c;
+
+ String text = tip.getTipText();
+ Font font = c.getFont();
+ FontMetrics fm = c.getFontMetrics(font);
+ int ascent = fm.getAscent();
+ 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);
+
+ 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);
+ }
+
+ /**
+ * This method uninstalls the defaults for the given JComponent.
+ *
+ * @param c The JComponent to uninstall defaults for.
+ */
+ protected void uninstallDefaults(JComponent c)
+ {
+ c.setForeground(null);
+ c.setBackground(null);
+ c.setFont(null);
+ c.setBorder(null);
+ }
+
+ /**
+ * This method uninstalls listeners for the given JComponent.
+ *
+ * @param c The JComponent to uninstall listeners for.
+ */
+ protected void uninstallListeners(JComponent c)
+ {
+ if (propertyChangeHandler != null)
+ {
+ c.removePropertyChangeListener(propertyChangeHandler);
+ propertyChangeHandler = null;
+ }
+ }
+
+ /**
+ * This method uninstalls the UI for the given JComponent.
+ *
+ * @param c The JComponent to uninstall.
+ */
+ public void uninstallUI(JComponent c)
+ {
+ uninstallDefaults(c);
+ BasicHTML.updateRenderer(c, "");
+ uninstallListeners(c);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java
new file mode 100644
index 000000000..af61a422a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicTreeUI.java
@@ -0,0 +1,3939 @@
+/* BasicTreeUI.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+ This file is part of GNU Classpath.
+
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Classpath is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library. Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ modules, and to copy and distribute the resulting executable under
+ terms of your choice, provided that you also meet, for each linked
+ independent module, the terms and conditions of the license of that
+ module. An independent module is a module which is not derived from
+ or based on this library. If you modify this library, you may extend
+ this exception to your version of the library, but you are not
+ obligated to do so. If you do not wish to do so, delete this
+ exception statement from your version. */
+
+
+package javax.swing.plaf.basic;
+
+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.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;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.event.ComponentListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.CellRendererPane;
+import javax.swing.Icon;
+import javax.swing.InputMap;
+import javax.swing.JComponent;
+import javax.swing.JScrollBar;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.LookAndFeel;
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+import javax.swing.UIManager;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.MouseInputListener;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeExpansionListener;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.TreeUI;
+import javax.swing.tree.AbstractLayoutCache;
+import javax.swing.tree.DefaultTreeCellEditor;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import javax.swing.tree.TreeCellEditor;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+import javax.swing.tree.VariableHeightLayoutCache;
+
+/**
+ * A delegate providing the user interface for <code>JTree</code> according to
+ * the Basic look and feel.
+ *
+ * @see javax.swing.JTree
+ * @author Lillian Angel (langel@redhat.com)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+public class BasicTreeUI
+ extends TreeUI
+{
+ /**
+ * The tree cell editing may be started by the single mouse click on the
+ * selected cell. To separate it from the double mouse click, the editing
+ * session starts after this time (in ms) after that single click, and only no
+ * other clicks were performed during that time.
+ */
+ static int WAIT_TILL_EDITING = 900;
+
+ /** Collapse Icon for the tree. */
+ protected transient Icon collapsedIcon;
+
+ /** Expanded Icon for the tree. */
+ protected transient Icon expandedIcon;
+
+ /** Distance between left margin and where vertical dashes will be drawn. */
+ protected int leftChildIndent;
+
+ /**
+ * Distance between leftChildIndent and where cell contents will be drawn.
+ */
+ protected int rightChildIndent;
+
+ /**
+ * Total fistance that will be indented. The sum of leftChildIndent and
+ * rightChildIndent .
+ */
+ protected int totalChildIndent;
+
+ /** Index of the row that was last selected. */
+ protected int lastSelectedRow;
+
+ /** Component that we're going to be drawing onto. */
+ protected JTree tree;
+
+ /** Renderer that is being used to do the actual cell drawing. */
+ protected transient TreeCellRenderer currentCellRenderer;
+
+ /**
+ * Set to true if the renderer that is currently in the tree was created by
+ * this instance.
+ */
+ protected boolean createdRenderer;
+
+ /** Editor for the tree. */
+ protected transient TreeCellEditor cellEditor;
+
+ /**
+ * Set to true if editor that is currently in the tree was created by this
+ * instance.
+ */
+ protected boolean createdCellEditor;
+
+ /**
+ * Set to false when editing and shouldSelectCall() returns true meaning the
+ * node should be selected before editing, used in completeEditing.
+ * GNU Classpath editing is implemented differently, so this value is not
+ * actually read anywhere. However it is always set correctly to maintain
+ * interoperability with the derived classes that read this field.
+ */
+ protected boolean stopEditingInCompleteEditing;
+
+ /** Used to paint the TreeCellRenderer. */
+ protected CellRendererPane rendererPane;
+
+ /** Size needed to completely display all the nodes. */
+ protected Dimension preferredSize;
+
+ /** Minimum size needed to completely display all the nodes. */
+ protected Dimension preferredMinSize;
+
+ /** Is the preferredSize valid? */
+ protected boolean validCachedPreferredSize;
+
+ /** Object responsible for handling sizing and expanded issues. */
+ protected AbstractLayoutCache treeState;
+
+ /** Used for minimizing the drawing of vertical lines. */
+ protected Hashtable<TreePath, Boolean> drawingCache;
+
+ /**
+ * True if doing optimizations for a largeModel. Subclasses that don't support
+ * this may wish to override createLayoutCache to not return a
+ * FixedHeightLayoutCache instance.
+ */
+ protected boolean largeModel;
+
+ /** Responsible for telling the TreeState the size needed for a node. */
+ protected AbstractLayoutCache.NodeDimensions nodeDimensions;
+
+ /** Used to determine what to display. */
+ protected TreeModel treeModel;
+
+ /** Model maintaining the selection. */
+ protected TreeSelectionModel treeSelectionModel;
+
+ /**
+ * How much the depth should be offset to properly calculate x locations. This
+ * is based on whether or not the root is visible, and if the root handles are
+ * visible.
+ */
+ protected int depthOffset;
+
+ /**
+ * When editing, this will be the Component that is doing the actual editing.
+ */
+ protected Component editingComponent;
+
+ /** Path that is being edited. */
+ protected TreePath editingPath;
+
+ /**
+ * Row that is being edited. Should only be referenced if editingComponent is
+ * null.
+ */
+ protected int editingRow;
+
+ /** Set to true if the editor has a different size than the renderer. */
+ protected boolean editorHasDifferentSize;
+
+ /** Boolean to keep track of editing. */
+ boolean isEditing;
+
+ /** The current path of the visible nodes in the tree. */
+ TreePath currentVisiblePath;
+
+ /** The gap between the icon and text. */
+ int gap = 4;
+
+ /** The max height of the nodes in the tree. */
+ int maxHeight;
+
+ /** The hash color. */
+ Color hashColor;
+
+ /** Listeners */
+ PropertyChangeListener propertyChangeListener;
+
+ FocusListener focusListener;
+
+ TreeSelectionListener treeSelectionListener;
+
+ MouseListener mouseListener;
+
+ KeyListener keyListener;
+
+ PropertyChangeListener selectionModelPropertyChangeListener;
+
+ ComponentListener componentListener;
+
+ CellEditorListener cellEditorListener;
+
+ TreeExpansionListener treeExpansionListener;
+
+ TreeModelListener treeModelListener;
+
+ /**
+ * The zero size icon, used for expand controls, if they are not visible.
+ */
+ static Icon nullIcon;
+
+ /**
+ * Creates a new BasicTreeUI object.
+ */
+ public BasicTreeUI()
+ {
+ validCachedPreferredSize = false;
+ drawingCache = new Hashtable();
+ nodeDimensions = createNodeDimensions();
+ configureLayoutCache();
+
+ editingRow = - 1;
+ lastSelectedRow = - 1;
+ }
+
+ /**
+ * Returns an instance of the UI delegate for the specified component.
+ *
+ * @param c the <code>JComponent</code> for which we need a UI delegate for.
+ * @return the <code>ComponentUI</code> for c.
+ */
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicTreeUI();
+ }
+
+ /**
+ * Returns the Hash color.
+ *
+ * @return the <code>Color</code> of the Hash.
+ */
+ protected Color getHashColor()
+ {
+ return hashColor;
+ }
+
+ /**
+ * Sets the Hash color.
+ *
+ * @param color the <code>Color</code> to set the Hash to.
+ */
+ protected void setHashColor(Color color)
+ {
+ hashColor = color;
+ }
+
+ /**
+ * Sets the left child's indent value.
+ *
+ * @param newAmount is the new indent value for the left child.
+ */
+ public void setLeftChildIndent(int newAmount)
+ {
+ leftChildIndent = newAmount;
+ }
+
+ /**
+ * Returns the indent value for the left child.
+ *
+ * @return the indent value for the left child.
+ */
+ public int getLeftChildIndent()
+ {
+ return leftChildIndent;
+ }
+
+ /**
+ * Sets the right child's indent value.
+ *
+ * @param newAmount is the new indent value for the right child.
+ */
+ public void setRightChildIndent(int newAmount)
+ {
+ rightChildIndent = newAmount;
+ }
+
+ /**
+ * Returns the indent value for the right child.
+ *
+ * @return the indent value for the right child.
+ */
+ public int getRightChildIndent()
+ {
+ return rightChildIndent;
+ }
+
+ /**
+ * Sets the expanded icon.
+ *
+ * @param newG is the new expanded icon.
+ */
+ public void setExpandedIcon(Icon newG)
+ {
+ expandedIcon = newG;
+ }
+
+ /**
+ * Returns the current expanded icon.
+ *
+ * @return the current expanded icon.
+ */
+ public Icon getExpandedIcon()
+ {
+ return expandedIcon;
+ }
+
+ /**
+ * Sets the collapsed icon.
+ *
+ * @param newG is the new collapsed icon.
+ */
+ public void setCollapsedIcon(Icon newG)
+ {
+ collapsedIcon = newG;
+ }
+
+ /**
+ * Returns the current collapsed icon.
+ *
+ * @return the current collapsed icon.
+ */
+ public Icon getCollapsedIcon()
+ {
+ return collapsedIcon;
+ }
+
+ /**
+ * Updates the componentListener, if necessary.
+ *
+ * @param largeModel sets this.largeModel to it.
+ */
+ protected void setLargeModel(boolean largeModel)
+ {
+ if (largeModel != this.largeModel)
+ {
+ completeEditing();
+ tree.removeComponentListener(componentListener);
+ this.largeModel = largeModel;
+ tree.addComponentListener(componentListener);
+ }
+ }
+
+ /**
+ * Returns true if largeModel is set
+ *
+ * @return true if largeModel is set, otherwise false.
+ */
+ protected boolean isLargeModel()
+ {
+ return largeModel;
+ }
+
+ /**
+ * Sets the row height.
+ *
+ * @param rowHeight is the height to set this.rowHeight to.
+ */
+ protected void setRowHeight(int rowHeight)
+ {
+ completeEditing();
+ if (rowHeight == 0)
+ rowHeight = getMaxHeight(tree);
+ treeState.setRowHeight(rowHeight);
+ }
+
+ /**
+ * Returns the current row height.
+ *
+ * @return current row height.
+ */
+ protected int getRowHeight()
+ {
+ return tree.getRowHeight();
+ }
+
+ /**
+ * Sets the TreeCellRenderer to <code>tcr</code>. This invokes
+ * <code>updateRenderer</code>.
+ *
+ * @param tcr is the new TreeCellRenderer.
+ */
+ protected void setCellRenderer(TreeCellRenderer tcr)
+ {
+ // Finish editing before changing the renderer.
+ completeEditing();
+
+ // The renderer is set in updateRenderer.
+ updateRenderer();
+
+ // Refresh the layout if necessary.
+ if (treeState != null)
+ {
+ treeState.invalidateSizes();
+ updateSize();
+ }
+ }
+
+ /**
+ * Return currentCellRenderer, which will either be the trees renderer, or
+ * defaultCellRenderer, which ever was not null.
+ *
+ * @return the current Cell Renderer
+ */
+ protected TreeCellRenderer getCellRenderer()
+ {
+ if (currentCellRenderer != null)
+ return currentCellRenderer;
+
+ return createDefaultCellRenderer();
+ }
+
+ /**
+ * Sets the tree's model.
+ *
+ * @param model to set the treeModel to.
+ */
+ protected void setModel(TreeModel model)
+ {
+ completeEditing();
+
+ if (treeModel != null && treeModelListener != null)
+ treeModel.removeTreeModelListener(treeModelListener);
+
+ treeModel = tree.getModel();
+
+ if (treeModel != null && treeModelListener != null)
+ treeModel.addTreeModelListener(treeModelListener);
+
+ if (treeState != null)
+ {
+ treeState.setModel(treeModel);
+ updateLayoutCacheExpandedNodes();
+ updateSize();
+ }
+ }
+
+ /**
+ * Returns the tree's model
+ *
+ * @return treeModel
+ */
+ protected TreeModel getModel()
+ {
+ return treeModel;
+ }
+
+ /**
+ * Sets the root to being visible.
+ *
+ * @param newValue sets the visibility of the root
+ */
+ protected void setRootVisible(boolean newValue)
+ {
+ completeEditing();
+ tree.setRootVisible(newValue);
+ }
+
+ /**
+ * Returns true if the root is visible.
+ *
+ * @return true if the root is visible.
+ */
+ protected boolean isRootVisible()
+ {
+ return tree.isRootVisible();
+ }
+
+ /**
+ * Determines whether the node handles are to be displayed.
+ *
+ * @param newValue sets whether or not node handles should be displayed.
+ */
+ protected void setShowsRootHandles(boolean newValue)
+ {
+ completeEditing();
+ updateDepthOffset();
+ if (treeState != null)
+ {
+ treeState.invalidateSizes();
+ updateSize();
+ }
+ }
+
+ /**
+ * Returns true if the node handles are to be displayed.
+ *
+ * @return true if the node handles are to be displayed.
+ */
+ protected boolean getShowsRootHandles()
+ {
+ return tree.getShowsRootHandles();
+ }
+
+ /**
+ * Sets the cell editor.
+ *
+ * @param editor to set the cellEditor to.
+ */
+ protected void setCellEditor(TreeCellEditor editor)
+ {
+ updateCellEditor();
+ }
+
+ /**
+ * Returns the <code>TreeCellEditor</code> for this tree.
+ *
+ * @return the cellEditor for this tree.
+ */
+ protected TreeCellEditor getCellEditor()
+ {
+ return cellEditor;
+ }
+
+ /**
+ * Configures the receiver to allow, or not allow, editing.
+ *
+ * @param newValue sets the receiver to allow editing if true.
+ */
+ protected void setEditable(boolean newValue)
+ {
+ updateCellEditor();
+ }
+
+ /**
+ * Returns true if the receiver allows editing.
+ *
+ * @return true if the receiver allows editing.
+ */
+ protected boolean isEditable()
+ {
+ return tree.isEditable();
+ }
+
+ /**
+ * Resets the selection model. The appropriate listeners are installed on the
+ * model.
+ *
+ * @param newLSM resets the selection model.
+ */
+ protected void setSelectionModel(TreeSelectionModel newLSM)
+ {
+ completeEditing();
+ if (newLSM != null)
+ {
+ treeSelectionModel = newLSM;
+ tree.setSelectionModel(treeSelectionModel);
+ }
+ }
+
+ /**
+ * Returns the current selection model.
+ *
+ * @return the current selection model.
+ */
+ protected TreeSelectionModel getSelectionModel()
+ {
+ return treeSelectionModel;
+ }
+
+ /**
+ * Returns the Rectangle enclosing the label portion that the last item in
+ * path will be drawn to. Will return null if any component in path is
+ * currently valid.
+ *
+ * @param tree is the current tree the path will be drawn to.
+ * @param path is the current path the tree to draw to.
+ * @return the Rectangle enclosing the label portion that the last item in the
+ * path will be drawn to.
+ */
+ public Rectangle getPathBounds(JTree tree, TreePath path)
+ {
+ Rectangle bounds = null;
+ if (tree != null && treeState != null)
+ {
+ bounds = treeState.getBounds(path, null);
+ Insets i = tree.getInsets();
+ if (bounds != null && i != null)
+ {
+ bounds.x += i.left;
+ bounds.y += i.top;
+ }
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the max height of all the nodes in the tree.
+ *
+ * @param tree - the current tree
+ * @return the max height.
+ */
+ int getMaxHeight(JTree tree)
+ {
+ if (maxHeight != 0)
+ return maxHeight;
+
+ Icon e = UIManager.getIcon("Tree.openIcon");
+ Icon c = UIManager.getIcon("Tree.closedIcon");
+ Icon l = UIManager.getIcon("Tree.leafIcon");
+ int rc = getRowCount(tree);
+ int iconHeight = 0;
+
+ for (int row = 0; row < rc; row++)
+ {
+ if (isLeaf(row))
+ iconHeight = l.getIconHeight();
+ else if (tree.isExpanded(row))
+ iconHeight = e.getIconHeight();
+ else
+ iconHeight = c.getIconHeight();
+
+ maxHeight = Math.max(maxHeight, iconHeight + gap);
+ }
+
+ treeState.setRowHeight(maxHeight);
+ return maxHeight;
+ }
+
+ /**
+ * Get the tree node icon.
+ */
+ Icon getNodeIcon(TreePath path)
+ {
+ Object node = path.getLastPathComponent();
+ if (treeModel.isLeaf(node))
+ return UIManager.getIcon("Tree.leafIcon");
+ else if (treeState.getExpandedState(path))
+ return UIManager.getIcon("Tree.openIcon");
+ else
+ return UIManager.getIcon("Tree.closedIcon");
+ }
+
+ /**
+ * Returns the path for passed in row. If row is not visible null is returned.
+ *
+ * @param tree is the current tree to return path for.
+ * @param row is the row number of the row to return.
+ * @return the path for passed in row. If row is not visible null is returned.
+ */
+ public TreePath getPathForRow(JTree tree, int row)
+ {
+ return treeState.getPathForRow(row);
+ }
+
+ /**
+ * Returns the row that the last item identified in path is visible at. Will
+ * return -1 if any of the elments in the path are not currently visible.
+ *
+ * @param tree is the current tree to return the row for.
+ * @param path is the path used to find the row.
+ * @return the row that the last item identified in path is visible at. Will
+ * return -1 if any of the elments in the path are not currently
+ * visible.
+ */
+ public int getRowForPath(JTree tree, TreePath path)
+ {
+ return treeState.getRowForPath(path);
+ }
+
+ /**
+ * Returns the number of rows that are being displayed.
+ *
+ * @param tree is the current tree to return the number of rows for.
+ * @return the number of rows being displayed.
+ */
+ public int getRowCount(JTree tree)
+ {
+ return treeState.getRowCount();
+ }
+
+ /**
+ * Returns the path to the node that is closest to x,y. If there is nothing
+ * currently visible this will return null, otherwise it'll always return a
+ * valid path. If you need to test if the returned object is exactly at x,y
+ * you should get the bounds for the returned path and test x,y against that.
+ *
+ * @param tree the tree to search for the closest path
+ * @param x is the x coordinate of the location to search
+ * @param y is the y coordinate of the location to search
+ * @return the tree path closes to x,y.
+ */
+ public TreePath getClosestPathForLocation(JTree tree, int x, int y)
+ {
+ return treeState.getPathClosestTo(x, y);
+ }
+
+ /**
+ * Returns true if the tree is being edited. The item that is being edited can
+ * be returned by getEditingPath().
+ *
+ * @param tree is the tree to check for editing.
+ * @return true if the tree is being edited.
+ */
+ public boolean isEditing(JTree tree)
+ {
+ return isEditing;
+ }
+
+ /**
+ * Stops the current editing session. This has no effect if the tree is not
+ * being edited. Returns true if the editor allows the editing session to
+ * stop.
+ *
+ * @param tree is the tree to stop the editing on
+ * @return true if the editor allows the editing session to stop.
+ */
+ public boolean stopEditing(JTree tree)
+ {
+ boolean ret = false;
+ if (editingComponent != null && cellEditor.stopCellEditing())
+ {
+ completeEditing(false, false, true);
+ ret = true;
+ }
+ return ret;
+ }
+
+ /**
+ * Cancels the current editing session.
+ *
+ * @param tree is the tree to cancel the editing session on.
+ */
+ public void cancelEditing(JTree tree)
+ {
+ // 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.
+ if (editingComponent != null)
+ completeEditing(false, true, false);
+ }
+
+ /**
+ * Selects the last item in path and tries to edit it. Editing will fail if
+ * the CellEditor won't allow it for the selected item.
+ *
+ * @param tree is the tree to edit on.
+ * @param path is the path in tree to edit on.
+ */
+ public void startEditingAtPath(JTree tree, TreePath path)
+ {
+ tree.scrollPathToVisible(path);
+ if (path != null && tree.isVisible(path))
+ startEditing(path, null);
+ }
+
+ /**
+ * Returns the path to the element that is being editted.
+ *
+ * @param tree is the tree to get the editing path from.
+ * @return the path that is being edited.
+ */
+ public TreePath getEditingPath(JTree tree)
+ {
+ return editingPath;
+ }
+
+ /**
+ * Invoked after the tree instance variable has been set, but before any
+ * default/listeners have been installed.
+ */
+ protected void prepareForUIInstall()
+ {
+ lastSelectedRow = -1;
+ preferredSize = new Dimension();
+ largeModel = tree.isLargeModel();
+ preferredSize = new Dimension();
+ stopEditingInCompleteEditing = true;
+ setModel(tree.getModel());
+ }
+
+ /**
+ * Invoked from installUI after all the defaults/listeners have been
+ * installed.
+ */
+ protected void completeUIInstall()
+ {
+ setShowsRootHandles(tree.getShowsRootHandles());
+ updateRenderer();
+ updateDepthOffset();
+ setSelectionModel(tree.getSelectionModel());
+ configureLayoutCache();
+ treeState.setRootVisible(tree.isRootVisible());
+ treeSelectionModel.setRowMapper(treeState);
+ updateSize();
+ }
+
+ /**
+ * Invoked from uninstallUI after all the defaults/listeners have been
+ * uninstalled.
+ */
+ protected void completeUIUninstall()
+ {
+ tree = null;
+ }
+
+ /**
+ * Installs the subcomponents of the tree, which is the renderer pane.
+ */
+ protected void installComponents()
+ {
+ currentCellRenderer = createDefaultCellRenderer();
+ rendererPane = createCellRendererPane();
+ createdRenderer = true;
+ setCellRenderer(currentCellRenderer);
+ }
+
+ /**
+ * Creates an instance of NodeDimensions that is able to determine the size of
+ * a given node in the tree. The node dimensions must be created before
+ * configuring the layout cache.
+ *
+ * @return the NodeDimensions of a given node in the tree
+ */
+ protected AbstractLayoutCache.NodeDimensions createNodeDimensions()
+ {
+ return new NodeDimensionsHandler();
+ }
+
+ /**
+ * Creates a listener that is reponsible for the updates the UI based on how
+ * the tree changes.
+ *
+ * @return the PropertyChangeListener that is reposnsible for the updates
+ */
+ protected PropertyChangeListener createPropertyChangeListener()
+ {
+ return new PropertyChangeHandler();
+ }
+
+ /**
+ * Creates the listener responsible for updating the selection based on mouse
+ * events.
+ *
+ * @return the MouseListener responsible for updating.
+ */
+ protected MouseListener createMouseListener()
+ {
+ return new MouseHandler();
+ }
+
+ /**
+ * Creates the listener that is responsible for updating the display when
+ * focus is lost/grained.
+ *
+ * @return the FocusListener responsible for updating.
+ */
+ protected FocusListener createFocusListener()
+ {
+ return new FocusHandler();
+ }
+
+ /**
+ * Creates the listener reponsible for getting key events from the tree.
+ *
+ * @return the KeyListener responsible for getting key events.
+ */
+ protected KeyListener createKeyListener()
+ {
+ return new KeyHandler();
+ }
+
+ /**
+ * Creates the listener responsible for getting property change events from
+ * the selection model.
+ *
+ * @returns the PropertyChangeListener reponsible for getting property change
+ * events from the selection model.
+ */
+ protected PropertyChangeListener createSelectionModelPropertyChangeListener()
+ {
+ return new SelectionModelPropertyChangeHandler();
+ }
+
+ /**
+ * Creates the listener that updates the display based on selection change
+ * methods.
+ *
+ * @return the TreeSelectionListener responsible for updating.
+ */
+ protected TreeSelectionListener createTreeSelectionListener()
+ {
+ return new TreeSelectionHandler();
+ }
+
+ /**
+ * Creates a listener to handle events from the current editor
+ *
+ * @return the CellEditorListener that handles events from the current editor
+ */
+ protected CellEditorListener createCellEditorListener()
+ {
+ return new CellEditorHandler();
+ }
+
+ /**
+ * Creates and returns a new ComponentHandler. This is used for the large
+ * model to mark the validCachedPreferredSize as invalid when the component
+ * moves.
+ *
+ * @return a new ComponentHandler.
+ */
+ protected ComponentListener createComponentListener()
+ {
+ return new ComponentHandler();
+ }
+
+ /**
+ * Creates and returns the object responsible for updating the treestate when
+ * a nodes expanded state changes.
+ *
+ * @return the TreeExpansionListener responsible for updating the treestate
+ */
+ protected TreeExpansionListener createTreeExpansionListener()
+ {
+ return new TreeExpansionHandler();
+ }
+
+ /**
+ * Creates the object responsible for managing what is expanded, as well as
+ * the size of nodes.
+ *
+ * @return the object responsible for managing what is expanded.
+ */
+ protected AbstractLayoutCache createLayoutCache()
+ {
+ return new VariableHeightLayoutCache();
+ }
+
+ /**
+ * Returns the renderer pane that renderer components are placed in.
+ *
+ * @return the rendererpane that render components are placed in.
+ */
+ protected CellRendererPane createCellRendererPane()
+ {
+ return new CellRendererPane();
+ }
+
+ /**
+ * Creates a default cell editor.
+ *
+ * @return the default cell editor.
+ */
+ protected TreeCellEditor createDefaultCellEditor()
+ {
+ DefaultTreeCellEditor ed;
+ if (currentCellRenderer != null
+ && currentCellRenderer instanceof DefaultTreeCellRenderer)
+ ed = new DefaultTreeCellEditor(tree,
+ (DefaultTreeCellRenderer) currentCellRenderer);
+ else
+ ed = new DefaultTreeCellEditor(tree, null);
+ return ed;
+ }
+
+ /**
+ * Returns the default cell renderer that is used to do the stamping of each
+ * node.
+ *
+ * @return the default cell renderer that is used to do the stamping of each
+ * node.
+ */
+ protected TreeCellRenderer createDefaultCellRenderer()
+ {
+ return new DefaultTreeCellRenderer();
+ }
+
+ /**
+ * Returns a listener that can update the tree when the model changes.
+ *
+ * @return a listener that can update the tree when the model changes.
+ */
+ protected TreeModelListener createTreeModelListener()
+ {
+ return new TreeModelHandler();
+ }
+
+ /**
+ * Uninstall all registered listeners
+ */
+ protected void uninstallListeners()
+ {
+ tree.removePropertyChangeListener(propertyChangeListener);
+ tree.removeFocusListener(focusListener);
+ tree.removeTreeSelectionListener(treeSelectionListener);
+ tree.removeMouseListener(mouseListener);
+ tree.removeKeyListener(keyListener);
+ tree.removePropertyChangeListener(selectionModelPropertyChangeListener);
+ tree.removeComponentListener(componentListener);
+ tree.removeTreeExpansionListener(treeExpansionListener);
+
+ TreeCellEditor tce = tree.getCellEditor();
+ if (tce != null)
+ tce.removeCellEditorListener(cellEditorListener);
+ if (treeModel != null)
+ treeModel.removeTreeModelListener(treeModelListener);
+ }
+
+ /**
+ * Uninstall all keyboard actions.
+ */
+ protected void uninstallKeyboardActions()
+ {
+ tree.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).setParent(
+ null);
+ tree.getActionMap().setParent(null);
+ }
+
+ /**
+ * Uninstall the rendererPane.
+ */
+ protected void uninstallComponents()
+ {
+ currentCellRenderer = null;
+ rendererPane = null;
+ createdRenderer = false;
+ setCellRenderer(currentCellRenderer);
+ }
+
+ /**
+ * The vertical element of legs between nodes starts at the bottom of the
+ * parent node by default. This method makes the leg start below that.
+ *
+ * @return the vertical leg buffer
+ */
+ protected int getVerticalLegBuffer()
+ {
+ return getRowHeight() / 2;
+ }
+
+ /**
+ * The horizontal element of legs between nodes starts at the right of the
+ * left-hand side of the child node by default. This method makes the leg end
+ * before that.
+ *
+ * @return the horizontal leg buffer
+ */
+ protected int getHorizontalLegBuffer()
+ {
+ return rightChildIndent / 2;
+ }
+
+ /**
+ * Make all the nodes that are expanded in JTree expanded in LayoutCache. This
+ * invokes updateExpandedDescendants with the root path.
+ */
+ protected void updateLayoutCacheExpandedNodes()
+ {
+ if (treeModel != null && treeModel.getRoot() != null)
+ updateExpandedDescendants(new TreePath(treeModel.getRoot()));
+ }
+
+ /**
+ * Updates the expanded state of all the descendants of the <code>path</code>
+ * by getting the expanded descendants from the tree and forwarding to the
+ * tree state.
+ *
+ * @param path the path used to update the expanded states
+ */
+ protected void updateExpandedDescendants(TreePath path)
+ {
+ completeEditing();
+ Enumeration expanded = tree.getExpandedDescendants(path);
+ while (expanded.hasMoreElements())
+ treeState.setExpandedState((TreePath) expanded.nextElement(), true);
+ }
+
+ /**
+ * Returns a path to the last child of <code>parent</code>
+ *
+ * @param parent is the topmost path to specified
+ * @return a path to the last child of parent
+ */
+ protected TreePath getLastChildPath(TreePath parent)
+ {
+ return (TreePath) parent.getLastPathComponent();
+ }
+
+ /**
+ * Updates how much each depth should be offset by.
+ */
+ protected void updateDepthOffset()
+ {
+ depthOffset += getVerticalLegBuffer();
+ }
+
+ /**
+ * Updates the cellEditor based on editability of the JTree that we're
+ * contained in. If the tree is editable but doesn't have a cellEditor, a
+ * basic one will be used.
+ */
+ protected void updateCellEditor()
+ {
+ 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;
+ }
+ }
+
+ /**
+ * Messaged from the tree we're in when the renderer has changed.
+ */
+ protected void updateRenderer()
+ {
+ if (tree != null)
+ {
+ TreeCellRenderer rend = tree.getCellRenderer();
+ if (rend != null)
+ {
+ createdRenderer = false;
+ currentCellRenderer = rend;
+ if (createdCellEditor)
+ tree.setCellEditor(null);
+ }
+ else
+ {
+ tree.setCellRenderer(createDefaultCellRenderer());
+ createdRenderer = true;
+ }
+ }
+ else
+ {
+ currentCellRenderer = null;
+ createdRenderer = false;
+ }
+
+ updateCellEditor();
+ }
+
+ /**
+ * Resets the treeState instance based on the tree we're providing the look
+ * and feel for. The node dimensions handler is required and must be created
+ * in advance.
+ */
+ protected void configureLayoutCache()
+ {
+ treeState = createLayoutCache();
+ treeState.setNodeDimensions(nodeDimensions);
+ }
+
+ /**
+ * Marks the cached size as being invalid, and messages the tree with
+ * <code>treeDidChange</code>.
+ */
+ protected void updateSize()
+ {
+ preferredSize = null;
+ updateCachedPreferredSize();
+ tree.treeDidChange();
+ }
+
+ /**
+ * Updates the <code>preferredSize</code> instance variable, which is
+ * returned from <code>getPreferredSize()</code>.
+ */
+ protected void updateCachedPreferredSize()
+ {
+ validCachedPreferredSize = false;
+ }
+
+ /**
+ * Messaged from the VisibleTreeNode after it has been expanded.
+ *
+ * @param path is the path that has been expanded.
+ */
+ protected void pathWasExpanded(TreePath path)
+ {
+ validCachedPreferredSize = false;
+ treeState.setExpandedState(path, true);
+ tree.repaint();
+ }
+
+ /**
+ * Messaged from the VisibleTreeNode after it has collapsed
+ */
+ protected void pathWasCollapsed(TreePath path)
+ {
+ validCachedPreferredSize = false;
+ treeState.setExpandedState(path, false);
+ tree.repaint();
+ }
+
+ /**
+ * Install all defaults for the tree.
+ */
+ protected void installDefaults()
+ {
+ LookAndFeel.installColorsAndFont(tree, "Tree.background",
+ "Tree.foreground", "Tree.font");
+
+ hashColor = UIManager.getColor("Tree.hash");
+ if (hashColor == null)
+ hashColor = Color.black;
+
+ tree.setOpaque(true);
+
+ rightChildIndent = UIManager.getInt("Tree.rightChildIndent");
+ leftChildIndent = UIManager.getInt("Tree.leftChildIndent");
+ totalChildIndent = rightChildIndent + leftChildIndent;
+ setRowHeight(UIManager.getInt("Tree.rowHeight"));
+ tree.setRowHeight(getRowHeight());
+ tree.setScrollsOnExpand(UIManager.getBoolean("Tree.scrollsOnExpand"));
+ setExpandedIcon(UIManager.getIcon("Tree.expandedIcon"));
+ setCollapsedIcon(UIManager.getIcon("Tree.collapsedIcon"));
+ }
+
+ /**
+ * Install all keyboard actions for this
+ */
+ protected void installKeyboardActions()
+ {
+ InputMap focusInputMap =
+ (InputMap) SharedUIDefaults.get("Tree.focusInputMap");
+ SwingUtilities.replaceUIInputMap(tree, JComponent.WHEN_FOCUSED,
+ focusInputMap);
+ InputMap ancestorInputMap =
+ (InputMap) SharedUIDefaults.get("Tree.ancestorInputMap");
+ SwingUtilities.replaceUIInputMap(tree,
+ JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
+ ancestorInputMap);
+
+ SwingUtilities.replaceUIActionMap(tree, getActionMap());
+ }
+
+ /**
+ * Creates and returns the shared action map for JTrees.
+ *
+ * @return the shared action map for JTrees
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("Tree.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("Tree.actionMap", am);
+ }
+ return am;
+ }
+
+ /**
+ * Creates the default actions when there are none specified by the L&F.
+ *
+ * @return the default actions
+ */
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action;
+
+ // TreeHomeAction.
+ action = new TreeHomeAction(-1, "selectFirst");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(-1, "selectFirstChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(-1, "selectFirstExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(1, "selectLast");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(1, "selectLastChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeHomeAction(1, "selectLastExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+
+ // TreeIncrementAction.
+ action = new TreeIncrementAction(-1, "selectPrevious");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(-1, "selectPreviousExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(-1, "selectPreviousChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(1, "selectNext");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(1, "selectNextExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeIncrementAction(1, "selectNextChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+
+ // TreeTraverseAction.
+ action = new TreeTraverseAction(-1, "selectParent");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeTraverseAction(1, "selectChild");
+ am.put(action.getValue(Action.NAME), action);
+
+ // TreeToggleAction.
+ action = new TreeToggleAction("toggleAndAnchor");
+ am.put(action.getValue(Action.NAME), action);
+
+ // TreePageAction.
+ action = new TreePageAction(-1, "scrollUpChangeSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(-1, "scrollUpExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(-1, "scrollUpChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(1, "scrollDownChangeSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(1, "scrollDownExtendSelection");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreePageAction(1, "scrollDownChangeLead");
+ am.put(action.getValue(Action.NAME), action);
+
+ // Tree editing actions
+ action = new TreeStartEditingAction("startEditing");
+ am.put(action.getValue(Action.NAME), action);
+ action = new TreeCancelEditingAction("cancel");
+ am.put(action.getValue(Action.NAME), action);
+
+
+ return am;
+ }
+
+ /**
+ * Converts the modifiers.
+ *
+ * @param mod - modifier to convert
+ * @returns the new modifier
+ */
+ private int convertModifiers(int mod)
+ {
+ if ((mod & KeyEvent.SHIFT_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.SHIFT_MASK;
+ mod &= ~ KeyEvent.SHIFT_DOWN_MASK;
+ }
+ if ((mod & KeyEvent.CTRL_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.CTRL_MASK;
+ mod &= ~ KeyEvent.CTRL_DOWN_MASK;
+ }
+ if ((mod & KeyEvent.META_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.META_MASK;
+ mod &= ~ KeyEvent.META_DOWN_MASK;
+ }
+ if ((mod & KeyEvent.ALT_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.ALT_MASK;
+ mod &= ~ KeyEvent.ALT_DOWN_MASK;
+ }
+ if ((mod & KeyEvent.ALT_GRAPH_DOWN_MASK) != 0)
+ {
+ mod |= KeyEvent.ALT_GRAPH_MASK;
+ mod &= ~ KeyEvent.ALT_GRAPH_DOWN_MASK;
+ }
+ return mod;
+ }
+
+ /**
+ * Install all listeners for this
+ */
+ protected void installListeners()
+ {
+ propertyChangeListener = createPropertyChangeListener();
+ tree.addPropertyChangeListener(propertyChangeListener);
+
+ focusListener = createFocusListener();
+ tree.addFocusListener(focusListener);
+
+ treeSelectionListener = createTreeSelectionListener();
+ tree.addTreeSelectionListener(treeSelectionListener);
+
+ mouseListener = createMouseListener();
+ tree.addMouseListener(mouseListener);
+
+ keyListener = createKeyListener();
+ tree.addKeyListener(keyListener);
+
+ selectionModelPropertyChangeListener =
+ createSelectionModelPropertyChangeListener();
+ if (treeSelectionModel != null
+ && selectionModelPropertyChangeListener != null)
+ {
+ treeSelectionModel.addPropertyChangeListener(
+ selectionModelPropertyChangeListener);
+ }
+
+ componentListener = createComponentListener();
+ tree.addComponentListener(componentListener);
+
+ treeExpansionListener = createTreeExpansionListener();
+ tree.addTreeExpansionListener(treeExpansionListener);
+
+ treeModelListener = createTreeModelListener();
+ if (treeModel != null)
+ treeModel.addTreeModelListener(treeModelListener);
+
+ cellEditorListener = createCellEditorListener();
+ }
+
+ /**
+ * Install the UI for the component
+ *
+ * @param c the component to install UI for
+ */
+ public void installUI(JComponent c)
+ {
+ tree = (JTree) c;
+
+ prepareForUIInstall();
+ installDefaults();
+ installComponents();
+ installKeyboardActions();
+ installListeners();
+ completeUIInstall();
+ }
+
+ /**
+ * Uninstall the defaults for the tree
+ */
+ protected void uninstallDefaults()
+ {
+ tree.setFont(null);
+ tree.setForeground(null);
+ tree.setBackground(null);
+ }
+
+ /**
+ * Uninstall the UI for the component
+ *
+ * @param c the component to uninstall UI for
+ */
+ public void uninstallUI(JComponent c)
+ {
+ completeEditing();
+
+ prepareForUIUninstall();
+ uninstallDefaults();
+ uninstallKeyboardActions();
+ uninstallListeners();
+ uninstallComponents();
+ completeUIUninstall();
+ }
+
+ /**
+ * Paints the specified component appropriate for the look and feel. This
+ * method is invoked from the ComponentUI.update method when the specified
+ * component is being painted. Subclasses should override this method and use
+ * the specified Graphics object to render the content of the component.
+ *
+ * @param g the Graphics context in which to paint
+ * @param c the component being painted; this argument is often ignored, but
+ * might be used if the UI object is stateless and shared by multiple
+ * components
+ */
+ public void paint(Graphics g, JComponent c)
+ {
+ JTree tree = (JTree) c;
+
+ int rows = treeState.getRowCount();
+
+ if (rows == 0)
+ // There is nothing to do if the tree is empty.
+ return;
+
+ Rectangle clip = g.getClipBounds();
+
+ Insets insets = tree.getInsets();
+
+ if (clip != null && treeModel != null)
+ {
+ int startIndex = tree.getClosestRowForLocation(clip.x, clip.y);
+ int endIndex = tree.getClosestRowForLocation(clip.x + clip.width,
+ clip.y + clip.height);
+ // Also paint dashes to the invisible nodes below.
+ // These should be painted first, otherwise they may cover
+ // the control icons.
+ if (endIndex < rows)
+ for (int i = endIndex + 1; i < rows; i++)
+ {
+ TreePath path = treeState.getPathForRow(i);
+ if (isLastChild(path))
+ paintVerticalPartOfLeg(g, clip, insets, path);
+ }
+
+ // The two loops are required to ensure that the lines are not
+ // painted over the other tree components.
+
+ int n = endIndex - startIndex + 1;
+ Rectangle[] bounds = new Rectangle[n];
+ boolean[] isLeaf = new boolean[n];
+ boolean[] isExpanded = new boolean[n];
+ TreePath[] path = new TreePath[n];
+ int k;
+
+ k = 0;
+ for (int i = startIndex; i <= endIndex; i++, k++)
+ {
+ path[k] = treeState.getPathForRow(i);
+ if (path[k] != null)
+ {
+ isLeaf[k] = treeModel.isLeaf(path[k].getLastPathComponent());
+ isExpanded[k] = tree.isExpanded(path[k]);
+ bounds[k] = getPathBounds(tree, path[k]);
+
+ paintHorizontalPartOfLeg(g, clip, insets, bounds[k], path[k],
+ i, isExpanded[k], false, isLeaf[k]);
+ }
+ if (isLastChild(path[k]))
+ paintVerticalPartOfLeg(g, clip, insets, path[k]);
+ }
+
+ k = 0;
+ for (int i = startIndex; i <= endIndex; i++, k++)
+ {
+ if (path[k] != null)
+ paintRow(g, clip, insets, bounds[k], path[k], i, isExpanded[k],
+ false, isLeaf[k]);
+ }
+ }
+ }
+
+ /**
+ * Check if the path is referring to the last child of some parent.
+ */
+ private boolean isLastChild(TreePath path)
+ {
+ if (path == null)
+ return false;
+ else if (path instanceof GnuPath)
+ {
+ // Except the seldom case when the layout cache is changed, this
+ // optimized code will be executed.
+ return ((GnuPath) path).isLastChild;
+ }
+ else
+ {
+ // Non optimized general case.
+ TreePath parent = path.getParentPath();
+ if (parent == null)
+ return false;
+ int childCount = treeState.getVisibleChildCount(parent);
+ int p = treeModel.getIndexOfChild(parent, path.getLastPathComponent());
+ return p == childCount - 1;
+ }
+ }
+
+ /**
+ * Ensures that the rows identified by beginRow through endRow are visible.
+ *
+ * @param beginRow is the first row
+ * @param endRow is the last row
+ */
+ protected void ensureRowsAreVisible(int beginRow, int endRow)
+ {
+ if (beginRow < endRow)
+ {
+ int temp = endRow;
+ endRow = beginRow;
+ beginRow = temp;
+ }
+
+ for (int i = beginRow; i < endRow; i++)
+ {
+ TreePath path = getPathForRow(tree, i);
+ if (! tree.isVisible(path))
+ tree.makeVisible(path);
+ }
+ }
+
+ /**
+ * Sets the preferred minimum size.
+ *
+ * @param newSize is the new preferred minimum size.
+ */
+ public void setPreferredMinSize(Dimension newSize)
+ {
+ preferredMinSize = newSize;
+ }
+
+ /**
+ * Gets the preferred minimum size.
+ *
+ * @returns the preferred minimum size.
+ */
+ public Dimension getPreferredMinSize()
+ {
+ if (preferredMinSize == null)
+ return getPreferredSize(tree);
+ else
+ return preferredMinSize;
+ }
+
+ /**
+ * Returns the preferred size to properly display the tree, this is a cover
+ * method for getPreferredSize(c, false).
+ *
+ * @param c the component whose preferred size is being queried; this argument
+ * is often ignored but might be used if the UI object is stateless
+ * and shared by multiple components
+ * @return the preferred size
+ */
+ public Dimension getPreferredSize(JComponent c)
+ {
+ return getPreferredSize(c, false);
+ }
+
+ /**
+ * Returns the preferred size to represent the tree in c. If checkConsistancy
+ * is true, checkConsistancy is messaged first.
+ *
+ * @param c the component whose preferred size is being queried.
+ * @param checkConsistancy if true must check consistancy
+ * @return the preferred size
+ */
+ public Dimension getPreferredSize(JComponent c, boolean checkConsistancy)
+ {
+ if (! validCachedPreferredSize)
+ {
+ Rectangle size = tree.getBounds();
+ // Add the scrollbar dimensions to the preferred size.
+ preferredSize = new Dimension(treeState.getPreferredWidth(size),
+ treeState.getPreferredHeight());
+ validCachedPreferredSize = true;
+ }
+ return preferredSize;
+ }
+
+ /**
+ * Returns the minimum size for this component. Which will be the min
+ * preferred size or (0,0).
+ *
+ * @param c the component whose min size is being queried.
+ * @returns the preferred size or null
+ */
+ public Dimension getMinimumSize(JComponent c)
+ {
+ return preferredMinSize = getPreferredSize(c);
+ }
+
+ /**
+ * Returns the maximum size for the component, which will be the preferred
+ * size if the instance is currently in JTree or (0,0).
+ *
+ * @param c the component whose preferred size is being queried
+ * @return the max size or null
+ */
+ public Dimension getMaximumSize(JComponent c)
+ {
+ return getPreferredSize(c);
+ }
+
+ /**
+ * Messages to stop the editing session. If the UI the receiver is providing
+ * the look and feel for returns true from
+ * <code>getInvokesStopCellEditing</code>, stopCellEditing will be invoked
+ * on the current editor. Then completeEditing will be messaged with false,
+ * true, false to cancel any lingering editing.
+ */
+ protected void completeEditing()
+ {
+ if (tree.getInvokesStopCellEditing() && stopEditingInCompleteEditing
+ && editingComponent != null)
+ cellEditor.stopCellEditing();
+
+ completeEditing(false, true, false);
+ }
+
+ /**
+ * Stops the editing session. If messageStop is true, the editor is messaged
+ * with stopEditing, if messageCancel is true the editor is messaged with
+ * cancelEditing. If messageTree is true, the treeModel is messaged with
+ * valueForPathChanged.
+ *
+ * @param messageStop message to stop editing
+ * @param messageCancel message to cancel editing
+ * @param messageTree message to treeModel
+ */
+ protected void completeEditing(boolean messageStop, boolean messageCancel,
+ boolean messageTree)
+ {
+ // Make no attempt to complete the non existing editing session.
+ if (stopEditingInCompleteEditing && editingComponent != null)
+ {
+ 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)
+ {
+ Object value = cellEditor.getCellEditorValue();
+ treeModel.valueForPathChanged(p, value);
+ }
+ }
+ }
+
+ /**
+ * Will start editing for node if there is a cellEditor and shouldSelectCall
+ * returns true. This assumes that path is valid and visible.
+ *
+ * @param path is the path to start editing
+ * @param event is the MouseEvent performed on the path
+ * @return true if successful
+ */
+ protected boolean startEditing(TreePath path, MouseEvent event)
+ {
+ // Maybe cancel editing.
+ if (isEditing(tree) && tree.getInvokesStopCellEditing()
+ && ! stopEditing(tree))
+ return false;
+
+ completeEditing();
+ TreeCellEditor ed = cellEditor;
+ if (ed != null && tree.isPathEditable(path))
+ {
+ 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;
+ }
+ 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.
+ *
+ * @param path the path we are concerned with
+ * @param mouseX is the cursor's x position
+ * @param mouseY is the cursor's y position
+ */
+ protected void checkForClickInExpandControl(TreePath path, int mouseX,
+ int mouseY)
+ {
+ if (isLocationInExpandControl(path, mouseX, mouseY))
+ handleExpandControlClick(path, mouseX, mouseY);
+ }
+
+ /**
+ * Returns true if the <code>mouseX</code> and <code>mouseY</code> fall in
+ * the area of row that is used to expand/collpse the node and the node at row
+ * does not represent a leaf.
+ *
+ * @param path the path we are concerned with
+ * @param mouseX is the cursor's x position
+ * @param mouseY is the cursor's y position
+ * @return true if the <code>mouseX</code> and <code>mouseY</code> fall in
+ * the area of row that is used to expand/collpse the node and the
+ * node at row does not represent a leaf.
+ */
+ protected boolean isLocationInExpandControl(TreePath path, int mouseX,
+ int mouseY)
+ {
+ boolean cntlClick = false;
+ if (! treeModel.isLeaf(path.getLastPathComponent()))
+ {
+ int width;
+ Icon expandedIcon = getExpandedIcon();
+ if (expandedIcon != null)
+ width = expandedIcon.getIconWidth();
+ else
+ // Only guessing. This is the width of
+ // the tree control icon in Metal L&F.
+ width = 18;
+
+ Insets i = tree.getInsets();
+
+ int depth;
+ if (isRootVisible())
+ depth = path.getPathCount()-1;
+ else
+ depth = path.getPathCount()-2;
+
+ int left = getRowX(tree.getRowForPath(path), depth)
+ - width + i.left;
+ cntlClick = mouseX >= left && mouseX <= left + width;
+ }
+ return cntlClick;
+ }
+
+ /**
+ * Messaged when the user clicks the particular row, this invokes
+ * toggleExpandState.
+ *
+ * @param path the path we are concerned with
+ * @param mouseX is the cursor's x position
+ * @param mouseY is the cursor's y position
+ */
+ protected void handleExpandControlClick(TreePath path, int mouseX, int mouseY)
+ {
+ toggleExpandState(path);
+ }
+
+ /**
+ * Expands path if it is not expanded, or collapses row if it is expanded. If
+ * expanding a path and JTree scroll on expand, ensureRowsAreVisible is
+ * invoked to scroll as many of the children to visible as possible (tries to
+ * scroll to last visible descendant of path).
+ *
+ * @param path the path we are concerned with
+ */
+ protected void toggleExpandState(TreePath path)
+ {
+ // tree.isExpanded(path) would do the same, but treeState knows faster.
+ if (treeState.isExpanded(path))
+ tree.collapsePath(path);
+ else
+ tree.expandPath(path);
+ }
+
+ /**
+ * Returning true signifies a mouse event on the node should toggle the
+ * selection of only the row under the mouse. The BasisTreeUI treats the
+ * event as "toggle selection event" if the CTRL button was pressed while
+ * clicking. The event is not counted as toggle event if the associated
+ * tree does not support the multiple selection.
+ *
+ * @param event is the MouseEvent performed on the row.
+ * @return true signifies a mouse event on the node should toggle the
+ * selection of only the row under the mouse.
+ */
+ protected boolean isToggleSelectionEvent(MouseEvent event)
+ {
+ return
+ (tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION) &&
+ ((event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0);
+ }
+
+ /**
+ * Returning true signifies a mouse event on the node should select from the
+ * anchor point. The BasisTreeUI treats the event as "multiple selection
+ * event" if the SHIFT button was pressed while clicking. The event is not
+ * counted as multiple selection event if the associated tree does not support
+ * the multiple selection.
+ *
+ * @param event is the MouseEvent performed on the node.
+ * @return true signifies a mouse event on the node should select from the
+ * anchor point.
+ */
+ protected boolean isMultiSelectEvent(MouseEvent event)
+ {
+ return
+ (tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION) &&
+ ((event.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0);
+ }
+
+ /**
+ * Returning true indicates the row under the mouse should be toggled based on
+ * the event. This is invoked after checkForClickInExpandControl, implying the
+ * location is not in the expand (toggle) control.
+ *
+ * @param event is the MouseEvent performed on the row.
+ * @return true indicates the row under the mouse should be toggled based on
+ * the event.
+ */
+ protected boolean isToggleEvent(MouseEvent event)
+ {
+ boolean toggle = false;
+ if (SwingUtilities.isLeftMouseButton(event))
+ {
+ int clickCount = tree.getToggleClickCount();
+ if (clickCount > 0 && event.getClickCount() == clickCount)
+ toggle = true;
+ }
+ return toggle;
+ }
+
+ /**
+ * Messaged to update the selection based on a MouseEvent over a particular
+ * row. If the even is a toggle selection event, the row is either selected,
+ * or deselected. If the event identifies a multi selection event, the
+ * selection is updated from the anchor point. Otherwise, the row is selected,
+ * and the previous selection is cleared.</p>
+ *
+ * @param path is the path selected for an event
+ * @param event is the MouseEvent performed on the path.
+ *
+ * @see #isToggleSelectionEvent(MouseEvent)
+ * @see #isMultiSelectEvent(MouseEvent)
+ */
+ protected void selectPathForEvent(TreePath path, MouseEvent event)
+ {
+ if (isToggleSelectionEvent(event))
+ {
+ // The event selects or unselects the clicked row.
+ if (tree.isPathSelected(path))
+ tree.removeSelectionPath(path);
+ else
+ {
+ tree.addSelectionPath(path);
+ tree.setAnchorSelectionPath(path);
+ }
+ }
+ else if (isMultiSelectEvent(event))
+ {
+ // The event extends selection form anchor till the clicked row.
+ TreePath anchor = tree.getAnchorSelectionPath();
+ if (anchor != null)
+ {
+ int aRow = getRowForPath(tree, anchor);
+ tree.addSelectionInterval(aRow, getRowForPath(tree, path));
+ }
+ else
+ tree.addSelectionPath(path);
+ }
+ else
+ {
+ // This is an ordinary event that just selects the clicked row.
+ tree.setSelectionPath(path);
+ if (isToggleEvent(event))
+ toggleExpandState(path);
+ }
+ }
+
+ /**
+ * Returns true if the node at <code>row</code> is a leaf.
+ *
+ * @param row is the row we are concerned with.
+ * @return true if the node at <code>row</code> is a leaf.
+ */
+ protected boolean isLeaf(int row)
+ {
+ TreePath pathForRow = getPathForRow(tree, row);
+ if (pathForRow == null)
+ return true;
+
+ Object node = pathForRow.getLastPathComponent();
+ return treeModel.isLeaf(node);
+ }
+
+ /**
+ * The action to start editing at the current lead selection path.
+ */
+ class TreeStartEditingAction
+ extends AbstractAction
+ {
+ /**
+ * Creates the new tree cancel editing action.
+ *
+ * @param name the name of the action (used in toString).
+ */
+ public TreeStartEditingAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Start editing at the current lead selection path.
+ *
+ * @param e the ActionEvent that caused this action.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ TreePath lead = tree.getLeadSelectionPath();
+ if (!tree.isEditing())
+ tree.startEditingAtPath(lead);
+ }
+ }
+
+ /**
+ * Updates the preferred size when scrolling, if necessary.
+ */
+ public class ComponentHandler
+ extends ComponentAdapter
+ implements ActionListener
+ {
+ /**
+ * Timer used when inside a scrollpane and the scrollbar is adjusting
+ */
+ protected Timer timer;
+
+ /** ScrollBar that is being adjusted */
+ protected JScrollBar scrollBar;
+
+ /**
+ * Constructor
+ */
+ public ComponentHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when the component's position changes.
+ *
+ * @param e the event that occurs when moving the component
+ */
+ public void componentMoved(ComponentEvent e)
+ {
+ if (timer == null)
+ {
+ JScrollPane scrollPane = getScrollPane();
+ if (scrollPane == null)
+ updateSize();
+ else
+ {
+ // Determine the scrollbar that is adjusting, if any, and
+ // start the timer for that. If no scrollbar is adjusting,
+ // we simply call updateSize().
+ scrollBar = scrollPane.getVerticalScrollBar();
+ if (scrollBar == null || !scrollBar.getValueIsAdjusting())
+ {
+ // It's not the vertical scrollbar, try the horizontal one.
+ scrollBar = scrollPane.getHorizontalScrollBar();
+ if (scrollBar != null && scrollBar.getValueIsAdjusting())
+ startTimer();
+ else
+ updateSize();
+ }
+ else
+ {
+ startTimer();
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates, if necessary, and starts a Timer to check if needed to resize
+ * the bounds
+ */
+ protected void startTimer()
+ {
+ if (timer == null)
+ {
+ timer = new Timer(200, this);
+ timer.setRepeats(true);
+ }
+ timer.start();
+ }
+
+ /**
+ * Returns the JScrollPane housing the JTree, or null if one isn't found.
+ *
+ * @return JScrollPane housing the JTree, or null if one isn't found.
+ */
+ protected JScrollPane getScrollPane()
+ {
+ JScrollPane found = null;
+ Component p = tree.getParent();
+ while (p != null && !(p instanceof JScrollPane))
+ p = p.getParent();
+ if (p instanceof JScrollPane)
+ found = (JScrollPane) p;
+ return found;
+ }
+
+ /**
+ * Public as a result of Timer. If the scrollBar is null, or not adjusting,
+ * this stops the timer and updates the sizing.
+ *
+ * @param ae is the action performed
+ */
+ public void actionPerformed(ActionEvent ae)
+ {
+ if (scrollBar == null || !scrollBar.getValueIsAdjusting())
+ {
+ if (timer != null)
+ timer.stop();
+ updateSize();
+ timer = null;
+ scrollBar = null;
+ }
+ }
+ }
+
+ /**
+ * Listener responsible for getting cell editing events and updating the tree
+ * accordingly.
+ */
+ public class CellEditorHandler
+ implements CellEditorListener
+ {
+ /**
+ * Constructor
+ */
+ public CellEditorHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Messaged when editing has stopped in the tree. Tells the listeners
+ * editing has stopped.
+ *
+ * @param e is the notification event
+ */
+ public void editingStopped(ChangeEvent e)
+ {
+ completeEditing(false, false, true);
+ }
+
+ /**
+ * Messaged when editing has been canceled in the tree. This tells the
+ * listeners the editor has canceled editing.
+ *
+ * @param e is the notification event
+ */
+ public void editingCanceled(ChangeEvent e)
+ {
+ completeEditing(false, false, false);
+ }
+ } // CellEditorHandler
+
+ /**
+ * Repaints the lead selection row when focus is lost/grained.
+ */
+ public class FocusHandler
+ implements FocusListener
+ {
+ /**
+ * Constructor
+ */
+ public FocusHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when focus is activated on the tree we're in, redraws the lead
+ * row. Invoked when a component gains the keyboard focus. The method
+ * repaints the lead row that is shown differently when the tree is in
+ * focus.
+ *
+ * @param e is the focus event that is activated
+ */
+ public void focusGained(FocusEvent e)
+ {
+ repaintLeadRow();
+ }
+
+ /**
+ * Invoked when focus is deactivated on the tree we're in, redraws the lead
+ * row. Invoked when a component loses the keyboard focus. The method
+ * repaints the lead row that is shown differently when the tree is in
+ * focus.
+ *
+ * @param e is the focus event that is deactivated
+ */
+ public void focusLost(FocusEvent e)
+ {
+ repaintLeadRow();
+ }
+
+ /**
+ * Repaint the lead row.
+ */
+ void repaintLeadRow()
+ {
+ TreePath lead = tree.getLeadSelectionPath();
+ if (lead != null)
+ tree.repaint(tree.getPathBounds(lead));
+ }
+ }
+
+ /**
+ * This is used to get multiple key down events to appropriately genereate
+ * events.
+ */
+ public class KeyHandler
+ extends KeyAdapter
+ {
+ /** Key code that is being generated for. */
+ protected Action repeatKeyAction;
+
+ /** Set to true while keyPressed is active */
+ protected boolean isKeyDown;
+
+ /**
+ * Constructor
+ */
+ public KeyHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a key has been typed. Moves the keyboard focus to the first
+ * element whose first letter matches the alphanumeric key pressed by the
+ * user. Subsequent same key presses move the keyboard focus to the next
+ * object that starts with the same letter.
+ *
+ * @param e the key typed
+ */
+ public void keyTyped(KeyEvent e)
+ {
+ char typed = Character.toLowerCase(e.getKeyChar());
+ for (int row = tree.getLeadSelectionRow() + 1;
+ row < tree.getRowCount(); row++)
+ {
+ if (checkMatch(row, typed))
+ {
+ tree.setSelectionRow(row);
+ tree.scrollRowToVisible(row);
+ return;
+ }
+ }
+
+ // Not found below, search above:
+ for (int row = 0; row < tree.getLeadSelectionRow(); row++)
+ {
+ if (checkMatch(row, typed))
+ {
+ tree.setSelectionRow(row);
+ tree.scrollRowToVisible(row);
+ return;
+ }
+ }
+ }
+
+ /**
+ * Check if the given tree row starts with this character
+ *
+ * @param row the tree row
+ * @param typed the typed char, must be converted to lowercase
+ * @return true if the given tree row starts with this character
+ */
+ boolean checkMatch(int row, char typed)
+ {
+ TreePath path = treeState.getPathForRow(row);
+ String node = path.getLastPathComponent().toString();
+ if (node.length() > 0)
+ {
+ char x = node.charAt(0);
+ if (typed == Character.toLowerCase(x))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Invoked when a key has been pressed.
+ *
+ * @param e the key pressed
+ */
+ public void keyPressed(KeyEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a key has been released
+ *
+ * @param e the key released
+ */
+ public void keyReleased(KeyEvent e)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * MouseListener is responsible for updating the selection based on mouse
+ * events.
+ */
+ public class MouseHandler
+ extends MouseAdapter
+ implements MouseMotionListener
+ {
+
+ /**
+ * If the cell has been selected on mouse press.
+ */
+ private boolean selectedOnPress;
+
+ /**
+ * Constructor
+ */
+ public MouseHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a mouse button has been pressed on a component.
+ *
+ * @param e is the mouse event that occured
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ if (! e.isConsumed())
+ {
+ handleEvent(e);
+ selectedOnPress = true;
+ }
+ else
+ {
+ selectedOnPress = false;
+ }
+ }
+
+ /**
+ * Invoked when a mouse button is pressed on a component and then dragged.
+ * MOUSE_DRAGGED events will continue to be delivered to the component where
+ * the drag originated until the mouse button is released (regardless of
+ * whether the mouse position is within the bounds of the component).
+ *
+ * @param e is the mouse event that occured
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when the mouse button has been moved on a component (with no
+ * buttons no down).
+ *
+ * @param e the mouse event that occured
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a mouse button has been released on a component.
+ *
+ * @param e is the mouse event that occured
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ 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);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * MouseInputHandler handles passing all mouse events, including mouse motion
+ * events, until the mouse is released to the destination it is constructed
+ * with.
+ */
+ public class MouseInputHandler
+ implements MouseInputListener
+ {
+ /** Source that events are coming from */
+ protected Component source;
+
+ /** Destination that receives all events. */
+ protected Component destination;
+
+ /**
+ * Constructor
+ *
+ * @param source that events are coming from
+ * @param destination that receives all events
+ * @param e is the event received
+ */
+ public MouseInputHandler(Component source, Component destination,
+ MouseEvent e)
+ {
+ this.source = source;
+ this.destination = destination;
+ source.addMouseListener(this);
+ source.addMouseMotionListener(this);
+ dispatch(e);
+ }
+
+ /**
+ * Invoked when the mouse button has been clicked (pressed and released) on
+ * a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseClicked(MouseEvent e)
+ {
+ dispatch(e);
+ }
+
+ /**
+ * Invoked when a mouse button has been pressed on a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mousePressed(MouseEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked when a mouse button has been released on a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseReleased(MouseEvent e)
+ {
+ dispatch(e);
+ removeFromSource();
+ }
+
+ /**
+ * Invoked when the mouse enters a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseEntered(MouseEvent e)
+ {
+ if (! SwingUtilities.isLeftMouseButton(e))
+ removeFromSource();
+ }
+
+ /**
+ * Invoked when the mouse exits a component.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseExited(MouseEvent e)
+ {
+ if (! SwingUtilities.isLeftMouseButton(e))
+ removeFromSource();
+ }
+
+ /**
+ * Invoked when a mouse button is pressed on a component and then dragged.
+ * MOUSE_DRAGGED events will continue to be delivered to the component where
+ * the drag originated until the mouse button is released (regardless of
+ * whether the mouse position is within the bounds of the component).
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseDragged(MouseEvent e)
+ {
+ dispatch(e);
+ }
+
+ /**
+ * Invoked when the mouse cursor has been moved onto a component but no
+ * buttons have been pushed.
+ *
+ * @param e mouse event that occured
+ */
+ public void mouseMoved(MouseEvent e)
+ {
+ removeFromSource();
+ }
+
+ /**
+ * Removes event from the source
+ */
+ protected void removeFromSource()
+ {
+ 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);
+ }
+ }
+ }
+
+ /**
+ * Class responsible for getting size of node, method is forwarded to
+ * BasicTreeUI method. X location does not include insets, that is handled in
+ * getPathBounds.
+ */
+ public class NodeDimensionsHandler
+ extends AbstractLayoutCache.NodeDimensions
+ {
+ /**
+ * Constructor
+ */
+ public NodeDimensionsHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns, by reference in bounds, the size and x origin to place value at.
+ * The calling method is responsible for determining the Y location. If
+ * bounds is null, a newly created Rectangle should be returned, otherwise
+ * the value should be placed in bounds and returned.
+ *
+ * @param cell the value to be represented
+ * @param row row being queried
+ * @param depth the depth of the row
+ * @param expanded true if row is expanded
+ * @param size a Rectangle containing the size needed to represent value
+ * @return containing the node dimensions, or null if node has no dimension
+ */
+ public Rectangle getNodeDimensions(Object cell, int row, int depth,
+ boolean expanded, Rectangle size)
+ {
+ 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)
+ {
+ size.x = getRowX(row, depth);
+ // 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;
+ }
+
+ /**
+ * Returns the amount to indent the given row
+ *
+ * @return amount to indent the given row.
+ */
+ protected int getRowX(int row, int depth)
+ {
+ return BasicTreeUI.this.getRowX(row, depth);
+ }
+ } // NodeDimensionsHandler
+
+ /**
+ * PropertyChangeListener for the tree. Updates the appropriate variable, or
+ * TreeState, based on what changes.
+ */
+ public class PropertyChangeHandler
+ implements PropertyChangeListener
+ {
+
+ /**
+ * Constructor
+ */
+ public PropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method gets called when a bound property is changed.
+ *
+ * @param event A PropertyChangeEvent object describing the event source and
+ * the property that has changed.
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ String property = event.getPropertyName();
+ if (property.equals(JTree.ROOT_VISIBLE_PROPERTY))
+ {
+ validCachedPreferredSize = false;
+ treeState.setRootVisible(tree.isRootVisible());
+ tree.repaint();
+ }
+ else if (property.equals(JTree.SELECTION_MODEL_PROPERTY))
+ {
+ treeSelectionModel = tree.getSelectionModel();
+ treeSelectionModel.setRowMapper(treeState);
+ }
+ else if (property.equals(JTree.TREE_MODEL_PROPERTY))
+ {
+ setModel(tree.getModel());
+ }
+ else if (property.equals(JTree.CELL_RENDERER_PROPERTY))
+ {
+ setCellRenderer(tree.getCellRenderer());
+ // Update layout.
+ if (treeState != null)
+ treeState.invalidateSizes();
+ }
+ else if (property.equals(JTree.EDITABLE_PROPERTY))
+ setEditable(((Boolean) event.getNewValue()).booleanValue());
+
+ }
+ }
+
+ /**
+ * Listener on the TreeSelectionModel, resets the row selection if any of the
+ * properties of the model change.
+ */
+ public class SelectionModelPropertyChangeHandler
+ implements PropertyChangeListener
+ {
+
+ /**
+ * Constructor
+ */
+ public SelectionModelPropertyChangeHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method gets called when a bound property is changed.
+ *
+ * @param event A PropertyChangeEvent object describing the event source and
+ * the property that has changed.
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ treeSelectionModel.resetRowSelection();
+ }
+ }
+
+ /**
+ * The action to cancel editing on this tree.
+ */
+ public class TreeCancelEditingAction
+ extends AbstractAction
+ {
+ /**
+ * Creates the new tree cancel editing action.
+ *
+ * @param name the name of the action (used in toString).
+ */
+ public TreeCancelEditingAction(String name)
+ {
+ super(name);
+ }
+
+ /**
+ * Invoked when an action occurs, cancels the cell editing (if the
+ * tree cell is being edited).
+ *
+ * @param e event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (isEnabled() && tree.isEditing())
+ tree.cancelEditing();
+ }
+ }
+
+ /**
+ * Updates the TreeState in response to nodes expanding/collapsing.
+ */
+ public class TreeExpansionHandler
+ implements TreeExpansionListener
+ {
+
+ /**
+ * Constructor
+ */
+ public TreeExpansionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Called whenever an item in the tree has been expanded.
+ *
+ * @param event is the event that occured
+ */
+ public void treeExpanded(TreeExpansionEvent event)
+ {
+ validCachedPreferredSize = false;
+ treeState.setExpandedState(event.getPath(), true);
+ // The maximal cell height may change
+ maxHeight = 0;
+ tree.revalidate();
+ tree.repaint();
+ }
+
+ /**
+ * Called whenever an item in the tree has been collapsed.
+ *
+ * @param event is the event that occured
+ */
+ public void treeCollapsed(TreeExpansionEvent event)
+ {
+ completeEditing();
+ validCachedPreferredSize = false;
+ treeState.setExpandedState(event.getPath(), false);
+ // The maximal cell height may change
+ maxHeight = 0;
+ tree.revalidate();
+ tree.repaint();
+ }
+ } // TreeExpansionHandler
+
+ /**
+ * TreeHomeAction is used to handle end/home actions. Scrolls either the first
+ * or last cell to be visible based on direction.
+ */
+ public class TreeHomeAction
+ extends AbstractAction
+ {
+
+ /** The direction, either home or end */
+ protected int direction;
+
+ /**
+ * Creates a new TreeHomeAction instance.
+ *
+ * @param dir the direction to go to, <code>-1</code> for home,
+ * <code>1</code> for end
+ * @param name the name of the action
+ */
+ public TreeHomeAction(int dir, String name)
+ {
+ direction = dir;
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e is the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ if (tree != null)
+ {
+ String command = (String) getValue(Action.NAME);
+ if (command.equals("selectFirst"))
+ {
+ ensureRowsAreVisible(0, 0);
+ tree.setSelectionInterval(0, 0);
+ }
+ if (command.equals("selectFirstChangeLead"))
+ {
+ ensureRowsAreVisible(0, 0);
+ tree.setLeadSelectionPath(getPathForRow(tree, 0));
+ }
+ if (command.equals("selectFirstExtendSelection"))
+ {
+ ensureRowsAreVisible(0, 0);
+ TreePath anchorPath = tree.getAnchorSelectionPath();
+ if (anchorPath == null)
+ tree.setSelectionInterval(0, 0);
+ else
+ {
+ int anchorRow = getRowForPath(tree, anchorPath);
+ tree.setSelectionInterval(0, anchorRow);
+ tree.setAnchorSelectionPath(anchorPath);
+ tree.setLeadSelectionPath(getPathForRow(tree, 0));
+ }
+ }
+ else if (command.equals("selectLast"))
+ {
+ int end = getRowCount(tree) - 1;
+ ensureRowsAreVisible(end, end);
+ tree.setSelectionInterval(end, end);
+ }
+ else if (command.equals("selectLastChangeLead"))
+ {
+ int end = getRowCount(tree) - 1;
+ ensureRowsAreVisible(end, end);
+ tree.setLeadSelectionPath(getPathForRow(tree, end));
+ }
+ else if (command.equals("selectLastExtendSelection"))
+ {
+ int end = getRowCount(tree) - 1;
+ ensureRowsAreVisible(end, end);
+ TreePath anchorPath = tree.getAnchorSelectionPath();
+ if (anchorPath == null)
+ tree.setSelectionInterval(end, end);
+ else
+ {
+ int anchorRow = getRowForPath(tree, anchorPath);
+ tree.setSelectionInterval(end, anchorRow);
+ tree.setAnchorSelectionPath(anchorPath);
+ tree.setLeadSelectionPath(getPathForRow(tree, end));
+ }
+ }
+ }
+
+ // Ensure that the lead path is visible after the increment action.
+ tree.scrollPathToVisible(tree.getLeadSelectionPath());
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ }
+
+ /**
+ * TreeIncrementAction is used to handle up/down actions. Selection is moved
+ * up or down based on direction.
+ */
+ public class TreeIncrementAction
+ extends AbstractAction
+ {
+
+ /**
+ * Specifies the direction to adjust the selection by.
+ */
+ protected int direction;
+
+ /**
+ * Creates a new TreeIncrementAction.
+ *
+ * @param dir up or down, <code>-1</code> for up, <code>1</code> for down
+ * @param name is the name of the direction
+ */
+ public TreeIncrementAction(int dir, String name)
+ {
+ direction = dir;
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e is the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ TreePath currentPath = tree.getLeadSelectionPath();
+ int currentRow;
+
+ if (currentPath != null)
+ currentRow = treeState.getRowForPath(currentPath);
+ else
+ currentRow = 0;
+
+ int rows = treeState.getRowCount();
+
+ int nextRow = currentRow + 1;
+ int prevRow = currentRow - 1;
+ boolean hasNext = nextRow < rows;
+ boolean hasPrev = prevRow >= 0 && rows > 0;
+ TreePath newPath;
+ String command = (String) getValue(Action.NAME);
+
+ if (command.equals("selectPreviousChangeLead") && hasPrev)
+ {
+ newPath = treeState.getPathForRow(prevRow);
+ tree.setSelectionPath(newPath);
+ tree.setAnchorSelectionPath(newPath);
+ tree.setLeadSelectionPath(newPath);
+ }
+ else if (command.equals("selectPreviousExtendSelection") && hasPrev)
+ {
+ newPath = treeState.getPathForRow(prevRow);
+
+ // If the new path is already selected, the selection shrinks,
+ // unselecting the previously current path.
+ if (tree.isPathSelected(newPath))
+ tree.getSelectionModel().removeSelectionPath(currentPath);
+
+ // This must be called in any case because it updates the model
+ // lead selection index.
+ tree.addSelectionPath(newPath);
+ tree.setLeadSelectionPath(newPath);
+ }
+ else if (command.equals("selectPrevious") && hasPrev)
+ {
+ newPath = treeState.getPathForRow(prevRow);
+ tree.setSelectionPath(newPath);
+ }
+ else if (command.equals("selectNext") && hasNext)
+ {
+ newPath = treeState.getPathForRow(nextRow);
+ tree.setSelectionPath(newPath);
+ }
+ else if (command.equals("selectNextExtendSelection") && hasNext)
+ {
+ newPath = treeState.getPathForRow(nextRow);
+
+ // If the new path is already selected, the selection shrinks,
+ // unselecting the previously current path.
+ if (tree.isPathSelected(newPath))
+ tree.getSelectionModel().removeSelectionPath(currentPath);
+
+ // This must be called in any case because it updates the model
+ // lead selection index.
+ tree.addSelectionPath(newPath);
+
+ tree.setLeadSelectionPath(newPath);
+ }
+ else if (command.equals("selectNextChangeLead") && hasNext)
+ {
+ newPath = treeState.getPathForRow(nextRow);
+ tree.setSelectionPath(newPath);
+ tree.setAnchorSelectionPath(newPath);
+ tree.setLeadSelectionPath(newPath);
+ }
+
+ // Ensure that the lead path is visible after the increment action.
+ tree.scrollPathToVisible(tree.getLeadSelectionPath());
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ }
+
+ /**
+ * Forwards all TreeModel events to the TreeState.
+ */
+ public class TreeModelHandler
+ implements TreeModelListener
+ {
+ /**
+ * Constructor
+ */
+ public TreeModelHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Invoked after a node (or a set of siblings) has changed in some way. The
+ * node(s) have not changed locations in the tree or altered their children
+ * arrays, but other attributes have changed and may affect presentation.
+ * Example: the name of a file has changed, but it is in the same location
+ * in the file system. To indicate the root has changed, childIndices and
+ * children will be null. Use e.getPath() to get the parent of the changed
+ * node(s). e.getChildIndices() returns the index(es) of the changed
+ * node(s).
+ *
+ * @param e is the event that occured
+ */
+ public void treeNodesChanged(TreeModelEvent e)
+ {
+ validCachedPreferredSize = false;
+ treeState.treeNodesChanged(e);
+ tree.repaint();
+ }
+
+ /**
+ * Invoked after nodes have been inserted into the tree. Use e.getPath() to
+ * get the parent of the new node(s). e.getChildIndices() returns the
+ * index(es) of the new node(s) in ascending order.
+ *
+ * @param e is the event that occured
+ */
+ public void treeNodesInserted(TreeModelEvent e)
+ {
+ validCachedPreferredSize = false;
+ treeState.treeNodesInserted(e);
+ tree.repaint();
+ }
+
+ /**
+ * Invoked after nodes have been removed from the tree. Note that if a
+ * subtree is removed from the tree, this method may only be invoked once
+ * for the root of the removed subtree, not once for each individual set of
+ * siblings removed. Use e.getPath() to get the former parent of the deleted
+ * node(s). e.getChildIndices() returns, in ascending order, the index(es)
+ * the node(s) had before being deleted.
+ *
+ * @param e is the event that occured
+ */
+ public void treeNodesRemoved(TreeModelEvent e)
+ {
+ validCachedPreferredSize = false;
+ treeState.treeNodesRemoved(e);
+ tree.repaint();
+ }
+
+ /**
+ * Invoked after the tree has drastically changed structure from a given
+ * node down. If the path returned by e.getPath() is of length one and the
+ * first element does not identify the current root node the first element
+ * should become the new root of the tree. Use e.getPath() to get the path
+ * to the node. e.getChildIndices() returns null.
+ *
+ * @param e is the event that occured
+ */
+ public void treeStructureChanged(TreeModelEvent e)
+ {
+ if (e.getPath().length == 1
+ && ! e.getPath()[0].equals(treeModel.getRoot()))
+ tree.expandPath(new TreePath(treeModel.getRoot()));
+ validCachedPreferredSize = false;
+ treeState.treeStructureChanged(e);
+ tree.repaint();
+ }
+ } // TreeModelHandler
+
+ /**
+ * TreePageAction handles page up and page down events.
+ */
+ public class TreePageAction
+ extends AbstractAction
+ {
+ /** Specifies the direction to adjust the selection by. */
+ protected int direction;
+
+ /**
+ * Constructor
+ *
+ * @param direction up or down
+ * @param name is the name of the direction
+ */
+ public TreePageAction(int direction, String name)
+ {
+ this.direction = direction;
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e is the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ String command = (String) getValue(Action.NAME);
+ boolean extendSelection = command.equals("scrollUpExtendSelection")
+ || command.equals("scrollDownExtendSelection");
+ boolean changeSelection = command.equals("scrollUpChangeSelection")
+ || command.equals("scrollDownChangeSelection");
+
+ // Disable change lead, unless we are in discontinuous mode.
+ if (!extendSelection && !changeSelection
+ && tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION)
+ {
+ changeSelection = true;
+ }
+
+ int rowCount = getRowCount(tree);
+ if (rowCount > 0 && treeSelectionModel != null)
+ {
+ Dimension maxSize = tree.getSize();
+ TreePath lead = tree.getLeadSelectionPath();
+ TreePath newPath = null;
+ Rectangle visible = tree.getVisibleRect();
+ if (direction == -1) // The RI handles -1 as up.
+ {
+ newPath = getClosestPathForLocation(tree, visible.x, visible.y);
+ if (newPath.equals(lead)) // Corner case, adjust one page up.
+ {
+ visible.y = Math.max(0, visible.y - visible.height);
+ newPath = getClosestPathForLocation(tree, visible.x,
+ visible.y);
+ }
+ }
+ else // +1 is down.
+ {
+ visible.y = Math.min(maxSize.height,
+ visible.y + visible.height - 1);
+ newPath = getClosestPathForLocation(tree, visible.x, visible.y);
+ if (newPath.equals(lead)) // Corner case, adjust one page down.
+ {
+ visible.y = Math.min(maxSize.height,
+ visible.y + visible.height - 1);
+ newPath = getClosestPathForLocation(tree, visible.x,
+ visible.y);
+ }
+ }
+
+ // Determine new visible rect.
+ Rectangle newVisible = getPathBounds(tree, newPath);
+ newVisible.x = visible.x;
+ newVisible.width = visible.width;
+ if (direction == -1)
+ {
+ newVisible.height = visible.height;
+ }
+ else
+ {
+ newVisible.y -= visible.height - newVisible.height;
+ newVisible.height = visible.height;
+ }
+
+ if (extendSelection)
+ {
+ // Extend selection.
+ TreePath anchorPath = tree.getAnchorSelectionPath();
+ if (anchorPath == null)
+ {
+ tree.setSelectionPath(newPath);
+ }
+ else
+ {
+ int newIndex = getRowForPath(tree, newPath);
+ int anchorIndex = getRowForPath(tree, anchorPath);
+ tree.setSelectionInterval(Math.min(anchorIndex, newIndex),
+ Math.max(anchorIndex, newIndex));
+ tree.setAnchorSelectionPath(anchorPath);
+ tree.setLeadSelectionPath(newPath);
+ }
+ }
+ else if (changeSelection)
+ {
+ tree.setSelectionPath(newPath);
+ }
+ else // Change lead.
+ {
+ tree.setLeadSelectionPath(newPath);
+ }
+
+ tree.scrollRectToVisible(newVisible);
+ }
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled.
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ } // TreePageAction
+
+ /**
+ * Listens for changes in the selection model and updates the display
+ * accordingly.
+ */
+ public class TreeSelectionHandler
+ implements TreeSelectionListener
+ {
+ /**
+ * Constructor
+ */
+ public TreeSelectionHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Messaged when the selection changes in the tree we're displaying for.
+ * Stops editing, messages super and displays the changed paths.
+ *
+ * @param event the event that characterizes the change.
+ */
+ public void valueChanged(TreeSelectionEvent event)
+ {
+ completeEditing();
+
+ TreePath op = event.getOldLeadSelectionPath();
+ TreePath np = event.getNewLeadSelectionPath();
+
+ // Repaint of the changed lead selection path.
+ if (op != np)
+ {
+ Rectangle o = treeState.getBounds(event.getOldLeadSelectionPath(),
+ new Rectangle());
+ Rectangle n = treeState.getBounds(event.getNewLeadSelectionPath(),
+ new Rectangle());
+
+ if (o != null)
+ tree.repaint(o);
+ if (n != null)
+ tree.repaint(n);
+ }
+ }
+ } // TreeSelectionHandler
+
+ /**
+ * For the first selected row expandedness will be toggled.
+ */
+ public class TreeToggleAction
+ extends AbstractAction
+ {
+ /**
+ * Creates a new TreeToggleAction.
+ *
+ * @param name is the name of <code>Action</code> field
+ */
+ public TreeToggleAction(String name)
+ {
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ int selected = tree.getLeadSelectionRow();
+ if (selected != -1 && isLeaf(selected))
+ {
+ TreePath anchorPath = tree.getAnchorSelectionPath();
+ TreePath leadPath = tree.getLeadSelectionPath();
+ toggleExpandState(getPathForRow(tree, selected));
+ // Need to do this, so that the toggling doesn't mess up the lead
+ // and anchor.
+ tree.setLeadSelectionPath(leadPath);
+ tree.setAnchorSelectionPath(anchorPath);
+
+ // Ensure that the lead path is visible after the increment action.
+ tree.scrollPathToVisible(tree.getLeadSelectionPath());
+ }
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled, false otherwise
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ } // TreeToggleAction
+
+ /**
+ * TreeTraverseAction is the action used for left/right keys. Will toggle the
+ * expandedness of a node, as well as potentially incrementing the selection.
+ */
+ public class TreeTraverseAction
+ extends AbstractAction
+ {
+ /**
+ * Determines direction to traverse, 1 means expand, -1 means collapse.
+ */
+ protected int direction;
+
+ /**
+ * Constructor
+ *
+ * @param direction to traverse
+ * @param name is the name of the direction
+ */
+ public TreeTraverseAction(int direction, String name)
+ {
+ this.direction = direction;
+ putValue(Action.NAME, name);
+ }
+
+ /**
+ * Invoked when an action occurs.
+ *
+ * @param e the event that occured
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ TreePath current = tree.getLeadSelectionPath();
+ if (current == null)
+ return;
+
+ String command = (String) getValue(Action.NAME);
+ if (command.equals("selectParent"))
+ {
+ if (current == null)
+ return;
+
+ if (tree.isExpanded(current))
+ {
+ tree.collapsePath(current);
+ }
+ else
+ {
+ // If the node is not expanded (also, if it is a leaf node),
+ // we just select the parent. We do not select the root if it
+ // is not visible.
+ TreePath parent = current.getParentPath();
+ if (parent != null &&
+ ! (parent.getPathCount() == 1 && ! tree.isRootVisible()))
+ tree.setSelectionPath(parent);
+ }
+ }
+ else if (command.equals("selectChild"))
+ {
+ Object node = current.getLastPathComponent();
+ int nc = treeModel.getChildCount(node);
+ if (nc == 0 || treeState.isExpanded(current))
+ {
+ // If the node is leaf or it is already expanded,
+ // we just select the next row.
+ int nextRow = tree.getLeadSelectionRow() + 1;
+ if (nextRow <= tree.getRowCount())
+ tree.setSelectionRow(nextRow);
+ }
+ else
+ {
+ tree.expandPath(current);
+ }
+ }
+
+ // Ensure that the lead path is visible after the increment action.
+ tree.scrollPathToVisible(tree.getLeadSelectionPath());
+ }
+
+ /**
+ * Returns true if the action is enabled.
+ *
+ * @return true if the action is enabled, false otherwise
+ */
+ public boolean isEnabled()
+ {
+ return (tree != null) && tree.isEnabled();
+ }
+ }
+
+ /**
+ * Returns true if the LookAndFeel implements the control icons. Package
+ * private for use in inner classes.
+ *
+ * @returns true if there are control icons
+ */
+ boolean hasControlIcons()
+ {
+ if (expandedIcon != null || collapsedIcon != null)
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns control icon. It is null if the LookAndFeel does not implements the
+ * control icons. Package private for use in inner classes.
+ *
+ * @return control icon if it exists.
+ */
+ Icon getCurrentControlIcon(TreePath path)
+ {
+ if (hasControlIcons())
+ {
+ if (tree.isExpanded(path))
+ return expandedIcon;
+ else
+ return collapsedIcon;
+ }
+ else
+ {
+ if (nullIcon == null)
+ nullIcon = new Icon()
+ {
+ public int getIconHeight()
+ {
+ return 0;
+ }
+
+ public int getIconWidth()
+ {
+ return 0;
+ }
+
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ // No action here.
+ }
+ };
+ return nullIcon;
+ }
+ }
+
+ /**
+ * Returns the parent of the current node
+ *
+ * @param root is the root of the tree
+ * @param node is the current node
+ * @return is the parent of the current node
+ */
+ Object getParent(Object root, Object node)
+ {
+ if (root == null || node == null || root.equals(node))
+ return null;
+
+ if (node instanceof TreeNode)
+ return ((TreeNode) node).getParent();
+ return findNode(root, node);
+ }
+
+ /**
+ * Recursively checks the tree for the specified node, starting at the root.
+ *
+ * @param root is starting node to start searching at.
+ * @param node is the node to search for
+ * @return the parent node of node
+ */
+ private Object findNode(Object root, Object node)
+ {
+ if (! treeModel.isLeaf(root) && ! root.equals(node))
+ {
+ int size = treeModel.getChildCount(root);
+ for (int j = 0; j < size; j++)
+ {
+ Object child = treeModel.getChild(root, j);
+ if (node.equals(child))
+ return root;
+
+ Object n = findNode(child, node);
+ if (n != null)
+ return n;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Selects the specified path in the tree depending on modes. Package private
+ * for use in inner classes.
+ *
+ * @param tree is the tree we are selecting the path in
+ * @param path is the path we are selecting
+ */
+ void selectPath(JTree tree, TreePath path)
+ {
+ if (path != null)
+ {
+ tree.setSelectionPath(path);
+ tree.setLeadSelectionPath(path);
+ tree.makeVisible(path);
+ tree.scrollPathToVisible(path);
+ }
+ }
+
+ /**
+ * Returns the path from node to the root. Package private for use in inner
+ * classes.
+ *
+ * @param node the node to get the path to
+ * @param depth the depth of the tree to return a path for
+ * @return an array of tree nodes that represent the path to node.
+ */
+ Object[] getPathToRoot(Object node, int depth)
+ {
+ if (node == null)
+ {
+ if (depth == 0)
+ return null;
+
+ return new Object[depth];
+ }
+
+ Object[] path = getPathToRoot(getParent(treeModel.getRoot(), node),
+ depth + 1);
+ path[path.length - depth - 1] = node;
+ return path;
+ }
+
+ /**
+ * Draws a vertical line using the given graphic context
+ *
+ * @param g is the graphic context
+ * @param c is the component the new line will belong to
+ * @param x is the horizonal position
+ * @param top specifies the top of the line
+ * @param bottom specifies the bottom of the line
+ */
+ protected void paintVerticalLine(Graphics g, JComponent c, int x, int top,
+ int bottom)
+ {
+ // FIXME: Check if drawing a dashed line or not.
+ g.setColor(getHashColor());
+ g.drawLine(x, top, x, bottom);
+ }
+
+ /**
+ * Draws a horizontal line using the given graphic context
+ *
+ * @param g is the graphic context
+ * @param c is the component the new line will belong to
+ * @param y is the vertical position
+ * @param left specifies the left point of the line
+ * @param right specifies the right point of the line
+ */
+ protected void paintHorizontalLine(Graphics g, JComponent c, int y, int left,
+ int right)
+ {
+ // FIXME: Check if drawing a dashed line or not.
+ g.setColor(getHashColor());
+ g.drawLine(left, y, right, y);
+ }
+
+ /**
+ * Draws an icon at around a specific position
+ *
+ * @param c is the component the new line will belong to
+ * @param g is the graphic context
+ * @param icon is the icon which will be drawn
+ * @param x is the center position in x-direction
+ * @param y is the center position in y-direction
+ */
+ protected void drawCentered(Component c, Graphics g, Icon icon, int x, int y)
+ {
+ x -= icon.getIconWidth() / 2;
+ y -= icon.getIconHeight() / 2;
+
+ if (x < 0)
+ x = 0;
+ if (y < 0)
+ y = 0;
+
+ icon.paintIcon(c, g, x, y);
+ }
+
+ /**
+ * Draws a dashed horizontal line.
+ *
+ * @param g - the graphics configuration.
+ * @param y - the y location to start drawing at
+ * @param x1 - the x location to start drawing at
+ * @param x2 - the x location to finish drawing at
+ */
+ protected void drawDashedHorizontalLine(Graphics g, int y, int x1, int x2)
+ {
+ g.setColor(getHashColor());
+ for (int i = x1; i < x2; i += 2)
+ g.drawLine(i, y, i + 1, y);
+ }
+
+ /**
+ * Draws a dashed vertical line.
+ *
+ * @param g - the graphics configuration.
+ * @param x - the x location to start drawing at
+ * @param y1 - the y location to start drawing at
+ * @param y2 - the y location to finish drawing at
+ */
+ protected void drawDashedVerticalLine(Graphics g, int x, int y1, int y2)
+ {
+ g.setColor(getHashColor());
+ for (int i = y1; i < y2; i += 2)
+ g.drawLine(x, i, x, i + 1);
+ }
+
+ /**
+ * Paints the expand (toggle) part of a row. The receiver should NOT modify
+ * clipBounds, or insets.
+ *
+ * @param g - the graphics configuration
+ * @param clipBounds -
+ * @param insets -
+ * @param bounds - bounds of expand control
+ * @param path - path to draw control for
+ * @param row - row to draw control for
+ * @param isExpanded - is the row expanded
+ * @param hasBeenExpanded - has the row already been expanded
+ * @param isLeaf - is the path a leaf
+ */
+ protected void paintExpandControl(Graphics g, Rectangle clipBounds,
+ Insets insets, Rectangle bounds,
+ TreePath path, int row, boolean isExpanded,
+ boolean hasBeenExpanded, boolean isLeaf)
+ {
+ if (shouldPaintExpandControl(path, row, isExpanded, hasBeenExpanded, isLeaf))
+ {
+ Icon icon = getCurrentControlIcon(path);
+ int iconW = icon.getIconWidth();
+ int x = bounds.x - iconW - gap;
+ icon.paintIcon(tree, g, x, bounds.y + bounds.height / 2
+ - icon.getIconHeight() / 2);
+ }
+ }
+
+ /**
+ * Paints the horizontal part of the leg. The receiver should NOT modify
+ * clipBounds, or insets. NOTE: parentRow can be -1 if the root is not
+ * visible.
+ *
+ * @param g - the graphics configuration
+ * @param clipBounds -
+ * @param insets -
+ * @param bounds - bounds of the cell
+ * @param path - path to draw leg for
+ * @param row - row to start drawing at
+ * @param isExpanded - is the row expanded
+ * @param hasBeenExpanded - has the row already been expanded
+ * @param isLeaf - is the path a leaf
+ */
+ protected void paintHorizontalPartOfLeg(Graphics g, Rectangle clipBounds,
+ Insets insets, Rectangle bounds,
+ TreePath path, int row,
+ boolean isExpanded,
+ boolean hasBeenExpanded,
+ boolean isLeaf)
+ {
+ if (row != 0)
+ {
+ paintHorizontalLine(g, tree, bounds.y + bounds.height / 2,
+ bounds.x - leftChildIndent - gap, bounds.x - gap);
+ }
+ }
+
+ /**
+ * Paints the vertical part of the leg. The receiver should NOT modify
+ * clipBounds, insets.
+ *
+ * @param g - the graphics configuration.
+ * @param clipBounds -
+ * @param insets -
+ * @param path - the path to draw the vertical part for.
+ */
+ protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds,
+ Insets insets, TreePath path)
+ {
+ Rectangle bounds = getPathBounds(tree, path);
+ TreePath parent = path.getParentPath();
+
+ boolean paintLine;
+ if (isRootVisible())
+ paintLine = parent != null;
+ else
+ paintLine = parent != null && parent.getPathCount() > 1;
+ if (paintLine)
+ {
+ Rectangle parentBounds = getPathBounds(tree, parent);
+ paintVerticalLine(g, tree, parentBounds.x + 2 * gap,
+ parentBounds.y + parentBounds.height / 2,
+ bounds.y + bounds.height / 2);
+ }
+ }
+
+ /**
+ * Paints the renderer part of a row. The receiver should NOT modify
+ * clipBounds, or insets.
+ *
+ * @param g - the graphics configuration
+ * @param clipBounds -
+ * @param insets -
+ * @param bounds - bounds of expand control
+ * @param path - path to draw control for
+ * @param row - row to draw control for
+ * @param isExpanded - is the row expanded
+ * @param hasBeenExpanded - has the row already been expanded
+ * @param isLeaf - is the path a leaf
+ */
+ protected void paintRow(Graphics g, Rectangle clipBounds, Insets insets,
+ Rectangle bounds, TreePath path, int row,
+ boolean isExpanded, boolean hasBeenExpanded,
+ boolean isLeaf)
+ {
+ boolean selected = tree.isPathSelected(path);
+ boolean hasIcons = false;
+ Object node = path.getLastPathComponent();
+
+ paintExpandControl(g, clipBounds, insets, bounds, path, row, isExpanded,
+ hasBeenExpanded, isLeaf);
+
+ TreeCellRenderer dtcr = currentCellRenderer;
+
+ boolean focused = false;
+ if (treeSelectionModel != null)
+ focused = treeSelectionModel.getLeadSelectionRow() == row
+ && tree.isFocusOwner();
+
+ Component c = dtcr.getTreeCellRendererComponent(tree, node, selected,
+ isExpanded, isLeaf, row,
+ focused);
+
+ rendererPane.paintComponent(g, c, c.getParent(), bounds);
+ }
+
+ /**
+ * Prepares for the UI to uninstall.
+ */
+ protected void prepareForUIUninstall()
+ {
+ // Nothing to do here yet.
+ }
+
+ /**
+ * Returns true if the expand (toggle) control should be drawn for the
+ * specified row.
+ *
+ * @param path - current path to check for.
+ * @param row - current row to check for.
+ * @param isExpanded - true if the path is expanded
+ * @param hasBeenExpanded - true if the path has been expanded already
+ * @param isLeaf - true if the row is a lead
+ */
+ protected boolean shouldPaintExpandControl(TreePath path, int row,
+ boolean isExpanded,
+ boolean hasBeenExpanded,
+ boolean isLeaf)
+ {
+ Object node = path.getLastPathComponent();
+ return ! isLeaf && hasControlIcons();
+ }
+
+ /**
+ * Returns the amount to indent the given row
+ *
+ * @return amount to indent the given row.
+ */
+ protected int getRowX(int row, int depth)
+ {
+ return depth * totalChildIndent;
+ }
+} // BasicTreeUI
diff --git a/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java b/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java
new file mode 100644
index 000000000..11c7d6390
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/BasicViewportUI.java
@@ -0,0 +1,75 @@
+/* BasicViewportUI.java --
+ Copyright (C) 2002, 2004 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.plaf.basic;
+
+import javax.swing.JComponent;
+import javax.swing.LookAndFeel;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.ViewportUI;
+
+public class BasicViewportUI extends ViewportUI
+{
+ protected void installDefaults(JComponent c)
+ {
+ c.setOpaque(true);
+ LookAndFeel.installColorsAndFont(c, "Viewport.background",
+ "Viewport.foreground", "Viewport.font");
+ }
+ protected void uninstallDefaults(JComponent c)
+ {
+ // TODO: Implement this properly.
+ }
+
+ public static ComponentUI createUI(JComponent c)
+ {
+ return new BasicViewportUI();
+ }
+
+ public void installUI(JComponent c)
+ {
+ super.installUI(c);
+ installDefaults(c);
+ }
+
+ public void uninstallUI(JComponent c)
+ {
+ super.uninstallUI(c);
+ uninstallDefaults(c);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/ComboPopup.java b/libjava/classpath/javax/swing/plaf/basic/ComboPopup.java
new file mode 100644
index 000000000..8bdcc51b0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/ComboPopup.java
@@ -0,0 +1,103 @@
+/* ComboPopup.java --
+ Copyright (C) 2004 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.plaf.basic;
+
+import java.awt.event.KeyListener;
+import java.awt.event.MouseListener;
+import java.awt.event.MouseMotionListener;
+
+import javax.swing.JList;
+
+public interface ComboPopup
+{
+ /**
+ * This method display popup menu containing list of JComboBox's items to
+ * the screen
+ */
+ void show();
+
+ /**
+ * This method hides popup menu with list of JComboBox's item from the
+ * screen
+ */
+ void hide();
+
+ /**
+ * Retursn true if popup menu with JComboBOx's item is currently visible on
+ * the screen and false otherwise
+ *
+ * @return true if JComboBox's popup menu with list of items is currently
+ * visible on the screen and false otherwise.
+ */
+ boolean isVisible();
+
+ /**
+ * Return JList that is used to draw cells of the JComboBox.
+ *
+ * @return JList that is used to draw cells of the JcomboBox
+ */
+ JList getList();
+
+ /**
+ * This method returns MouseListener that listen's to mouse events occuring
+ * in the combo box
+ *
+ * @return MouseListenere
+ */
+ MouseListener getMouseListener();
+
+ /**
+ * This method returns MouseListener that listen's to mouse events occuring
+ * in the combo box.
+ *
+ * @return MouseMotionListener
+ */
+ MouseMotionListener getMouseMotionListener();
+
+ /**
+ * This method returns KeyListener that listen's to key events occuring in
+ * the combo box.
+ *
+ * @return KeyListener
+ */
+ KeyListener getKeyListener();
+
+ /* This method removes any listeners that were installed */
+ void uninstallingUI();
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java b/libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java
new file mode 100644
index 000000000..9760e82a6
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/DefaultMenuLayout.java
@@ -0,0 +1,78 @@
+/* DefaultMenuLayout.java
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing.plaf.basic;
+
+import java.awt.Container;
+import java.awt.Dimension;
+
+import javax.swing.BoxLayout;
+import javax.swing.plaf.UIResource;
+
+/**
+ * The LayoutManager that is used in PopupMenus. This is a derived from
+ * {@link BoxLayout}.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class DefaultMenuLayout
+ extends BoxLayout
+ implements UIResource
+{
+
+ /**
+ * Creates a new instance of DefaultMenuLayout.
+ *
+ * @param target the component that is laid out
+ * @param axis the axis along which the component is laid out
+ */
+ public DefaultMenuLayout(Container target, int axis)
+ {
+ super(target, axis);
+ }
+
+ /**
+ * Returns the preferred size for the layout of the menu.
+ *
+ * @param target the Container which's preferred size we calculate
+ */
+ public Dimension preferredLayoutSize(Container target)
+ {
+ return super.preferredLayoutSize(target);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java b/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java
new file mode 100644
index 000000000..34278052b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/SharedUIDefaults.java
@@ -0,0 +1,91 @@
+/* SharedUIDefaults.java -- Manages shared instances for UIDefaults
+ 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.plaf.basic;
+
+import java.awt.Color;
+import java.util.HashMap;
+
+import javax.swing.UIManager;
+
+/**
+ * Manages shared instances for UI defaults. For example, all Swing components
+ * of one type usually share one InputMap/ActionMap pair. In order to avoid
+ * duplication of such objects we store them in a Map here.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class SharedUIDefaults
+{
+
+ /**
+ * Stores the shared instances, indexed by their UI names
+ * (i.e. "TextField.InputMap").
+ */
+ private static HashMap sharedDefaults = new HashMap();
+
+ /**
+ * Returns a shared UI defaults object.
+ *
+ * @param key the key for the shared object
+ *
+ * @return a shared UI defaults object for the specified key
+ */
+ static Object get(String key)
+ {
+ Object o = sharedDefaults.get(key);
+ if (o == null)
+ {
+ o = UIManager.get(key);
+ sharedDefaults.put(key, o);
+ }
+ return o;
+ }
+
+ /**
+ * Returns a shared UI color.
+ *
+ * @param key the key
+ *
+ * @return the shared color instance
+ */
+ static Color getColor(String key)
+ {
+ return (Color) get(key);
+ }
+}
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.png
new file mode 100644
index 000000000..daeb16c78
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.png
new file mode 100644
index 000000000..60f6a8abe
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.png
new file mode 100644
index 000000000..54047dcc1
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ButtonBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.png
new file mode 100644
index 000000000..7c8991106
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.FieldBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.png
new file mode 100644
index 000000000..a3841baac
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MarginBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.png
new file mode 100644
index 000000000..13a9fa4e9
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.MenuBarBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.png
new file mode 100644
index 000000000..a6408ec7e
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.RadioButtonBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.png
new file mode 100644
index 000000000..db283c29a
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.png
new file mode 100644
index 000000000..65381bd8b
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneBorder-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.png
new file mode 100644
index 000000000..c22763a97
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.SplitPaneDividerBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.png
new file mode 100644
index 000000000..f898bee8f
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicBorders.ToggleButtonBorder-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.png
new file mode 100644
index 000000000..99f8c6ec4
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-1.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.png
new file mode 100644
index 000000000..59d9a6192
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-2.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.png
new file mode 100644
index 000000000..5b0971c16
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-3.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.png
new file mode 100644
index 000000000..ceba0b6e0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-4.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.png
new file mode 100644
index 000000000..fa3055f8c
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-5.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.png
new file mode 100644
index 000000000..c760313e0
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-6.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.png b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.png
new file mode 100644
index 000000000..6a557a044
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/doc-files/BasicGraphicsUtils-7.png
Binary files differ
diff --git a/libjava/classpath/javax/swing/plaf/basic/package.html b/libjava/classpath/javax/swing/plaf/basic/package.html
new file mode 100644
index 000000000..700c8cdbc
--- /dev/null
+++ b/libjava/classpath/javax/swing/plaf/basic/package.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.plaf.basic package.
+ Copyright (C) 2002, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.plaf.basic</title></head>
+
+<body>
+<p>Provides a "basic" look and feel implementation.</p>
+
+</body>
+</html>