jdk/src/share/classes/sun/tools/jconsole/BorderedComponent.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/tools/jconsole/BorderedComponent.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,568 @@
+/*
+ * Copyright 2004-2006 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.tools.jconsole;
+
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.plaf.*;
+import javax.swing.plaf.basic.BasicGraphicsUtils;
+
+import static javax.swing.SwingConstants.*;
+
+import static sun.tools.jconsole.JConsole.*;
+import static sun.tools.jconsole.Resources.*;
+import static sun.tools.jconsole.Utilities.*;
+
+@SuppressWarnings("serial")
+public class BorderedComponent extends JPanel implements ActionListener {
+    JButton moreOrLessButton;
+    String valueLabelStr;
+    JLabel label;
+    JComponent comp;
+    boolean collapsed = false;
+
+    private JPopupMenu popupMenu;
+
+    private Icon collapseIcon;
+    private Icon expandIcon;
+
+    private static Image getImage(String name) {
+        Toolkit tk = Toolkit.getDefaultToolkit();
+        name = "resources/" + name + ".png";
+        return tk.getImage(BorderedComponent.class.getResource(name));
+    }
+
+    public BorderedComponent(String text) {
+        this(text, null, false);
+    }
+
+    public BorderedComponent(String text, JComponent comp) {
+        this(text, comp, false);
+    }
+
+    public BorderedComponent(String text, JComponent comp, boolean collapsible) {
+        super(null);
+
+        this.comp = comp;
+
+        // Only add border if text is not null
+        if (text != null) {
+            TitledBorder border;
+            if (collapsible) {
+                final JLabel textLabel = new JLabel(text);
+                JPanel borderLabel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)) {
+                    public int getBaseline(int w, int h) {
+                        Dimension dim = textLabel.getPreferredSize();
+                        return textLabel.getBaseline(dim.width, dim.height) + textLabel.getY();
+                    }
+                };
+                borderLabel.add(textLabel);
+                border = new LabeledBorder(borderLabel);
+                textLabel.setForeground(border.getTitleColor());
+
+                if (IS_WIN) {
+                    collapseIcon = new ImageIcon(getImage("collapse-winlf"));
+                    expandIcon = new ImageIcon(getImage("expand-winlf"));
+                } else {
+                    collapseIcon = new ArrowIcon(SOUTH, textLabel);
+                    expandIcon = new ArrowIcon(EAST, textLabel);
+                }
+
+                moreOrLessButton = new JButton(collapseIcon);
+                moreOrLessButton.setContentAreaFilled(false);
+                moreOrLessButton.setBorderPainted(false);
+                moreOrLessButton.setMargin(new Insets(0, 0, 0, 0));
+                moreOrLessButton.addActionListener(this);
+                String toolTip =
+                    getText("BorderedComponent.moreOrLessButton.toolTip");
+                moreOrLessButton.setToolTipText(toolTip);
+                borderLabel.add(moreOrLessButton);
+                borderLabel.setSize(borderLabel.getPreferredSize());
+                add(borderLabel);
+            } else {
+                border = new TitledBorder(text);
+            }
+            setBorder(new CompoundBorder(new FocusBorder(this), border));
+        } else {
+            setBorder(new FocusBorder(this));
+        }
+        if (comp != null) {
+            add(comp);
+        }
+    }
+
+    public void setComponent(JComponent comp) {
+        if (this.comp != null) {
+            remove(this.comp);
+        }
+        this.comp = comp;
+        if (!collapsed) {
+            LayoutManager lm = getLayout();
+            if (lm instanceof BorderLayout) {
+                add(comp, BorderLayout.CENTER);
+            } else {
+                add(comp);
+            }
+        }
+        revalidate();
+    }
+
+    public void setValueLabel(String str) {
+        this.valueLabelStr = str;
+        if (label != null) {
+            label.setText(Resources.getText("Current value",valueLabelStr));
+        }
+    }
+
+    public void actionPerformed(ActionEvent ev) {
+        if (collapsed) {
+            if (label != null) {
+                remove(label);
+            }
+            add(comp);
+            moreOrLessButton.setIcon(collapseIcon);
+        } else {
+            remove(comp);
+            if (valueLabelStr != null) {
+                if (label == null) {
+                    label = new JLabel(Resources.getText("Current value",
+                                                         valueLabelStr));
+                }
+                add(label);
+            }
+            moreOrLessButton.setIcon(expandIcon);
+        }
+        collapsed = !collapsed;
+
+        JComponent container = (JComponent)getParent();
+        if (container != null &&
+            container.getLayout() instanceof VariableGridLayout) {
+
+            ((VariableGridLayout)container.getLayout()).setFillRow(this, !collapsed);
+            container.revalidate();
+        }
+    }
+
+    public Dimension getMinimumSize() {
+        if (getLayout() != null) {
+            // A layout manager has been set, so delegate to it
+            return super.getMinimumSize();
+        }
+
+        if (moreOrLessButton != null) {
+            Dimension d = moreOrLessButton.getMinimumSize();
+            Insets i = getInsets();
+            d.width  += i.left + i.right;
+            d.height += i.top + i.bottom;
+            return d;
+        } else {
+            return super.getMinimumSize();
+        }
+    }
+
+    public void doLayout() {
+        if (getLayout() != null) {
+            // A layout manager has been set, so delegate to it
+            super.doLayout();
+            return;
+        }
+
+        Dimension d = getSize();
+        Insets i = getInsets();
+
+        if (collapsed) {
+            if (label != null) {
+                Dimension p = label.getPreferredSize();
+                label.setBounds(i.left,
+                                i.top + (d.height - i.top - i.bottom - p.height) / 2,
+                                p.width,
+                                p.height);
+            }
+        } else {
+            if (comp != null) {
+                comp.setBounds(i.left,
+                               i.top,
+                               d.width - i.left - i.right,
+                               d.height - i.top - i.bottom);
+            }
+        }
+    }
+
+    private static class ArrowIcon implements Icon {
+        private int direction;
+        private JLabel textLabel;
+
+        public ArrowIcon(int direction, JLabel textLabel) {
+            this.direction = direction;
+            this.textLabel = textLabel;
+        }
+
+        public void paintIcon(Component c, Graphics g, int x, int y) {
+            int w = getIconWidth();
+            int h = w;
+            Polygon p = new Polygon();
+            switch (direction) {
+              case EAST:
+                p.addPoint(x + 2,     y);
+                p.addPoint(x + w - 2, y + h / 2);
+                p.addPoint(x + 2,     y + h - 1);
+                break;
+
+              case SOUTH:
+                p.addPoint(x,         y + 2);
+                p.addPoint(x + w / 2, y + h - 2);
+                p.addPoint(x + w - 1, y + 2);
+                break;
+            }
+            g.fillPolygon(p);
+        }
+
+        public int getIconWidth() {
+            return getIconHeight();
+        }
+
+        public int getIconHeight() {
+            Graphics g = textLabel.getGraphics();
+            if (g != null) {
+                int h = g.getFontMetrics(textLabel.getFont()).getAscent() * 6/10;
+                if (h % 2 == 0) {
+                    h += 1;     // Make it odd
+                }
+                return h;
+            } else {
+                return 7;
+            }
+        }
+    }
+
+
+    /**
+     * A subclass of <code>TitledBorder</code> which implements an arbitrary border
+     * with the addition of a JComponent (JLabel, JPanel, etc) in the
+     * default position.
+     * <p>
+     * If the border property value is not
+     * specified in the constuctor or by invoking the appropriate
+     * set method, the property value will be defined by the current
+     * look and feel, using the following property name in the
+     * Defaults Table:
+     * <ul>
+     * <li>&quot;TitledBorder.border&quot;
+     * </ul>
+     */
+    protected static class LabeledBorder extends TitledBorder {
+        protected JComponent label;
+
+        private Point compLoc = new Point();
+
+        /**
+         * Creates a LabeledBorder instance.
+         *
+         * @param label  the label the border should display
+         */
+        public LabeledBorder(JComponent label)     {
+            this(null, label);
+        }
+
+        /**
+         * Creates a LabeledBorder instance with the specified border
+         * and an empty label.
+         *
+         * @param border  the border
+         */
+        public LabeledBorder(Border border)       {
+            this(border, null);
+        }
+
+        /**
+         * Creates a LabeledBorder instance with the specified border and
+         * label.
+         *
+         * @param border  the border
+         * @param label  the label the border should display
+         */
+        public LabeledBorder(Border border, JComponent label) {
+            super(border);
+
+            this.label = label;
+
+            if (label instanceof JLabel &&
+                label.getForeground() instanceof ColorUIResource) {
+
+                label.setForeground(getTitleColor());
+            }
+
+        }
+
+        /**
+         * Paints the border for the specified component with the
+         * specified position and size.
+         * @param c the component for which this border is being painted
+         * @param g the paint graphics
+         * @param x the x position of the painted border
+         * @param y the y position of the painted border
+         * @param width the width of the painted border
+         * @param height the height of the painted border
+         */
+        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
+
+            Border border = getBorder();
+
+            if (label == null) {
+                if (border != null) {
+                    border.paintBorder(c, g, x, y, width, height);
+                }
+                return;
+            }
+
+            Rectangle grooveRect = new Rectangle(x + EDGE_SPACING, y + EDGE_SPACING,
+                                                 width - (EDGE_SPACING * 2),
+                                                 height - (EDGE_SPACING * 2));
+
+            Dimension   labelDim = label.getPreferredSize();
+            int baseline = label.getBaseline(labelDim.width, labelDim.height);
+            int         ascent = Math.max(0, baseline);
+            int         descent = labelDim.height - ascent;
+            int         diff;
+            Insets      insets;
+
+            if (border != null) {
+                insets = border.getBorderInsets(c);
+            } else {
+                insets = new Insets(0, 0, 0, 0);
+            }
+
+            diff = Math.max(0, ascent/2 + TEXT_SPACING - EDGE_SPACING);
+            grooveRect.y += diff;
+            grooveRect.height -= diff;
+            compLoc.y = grooveRect.y + insets.top/2 - (ascent + descent) / 2 - 1;
+
+            int justification;
+            if (c.getComponentOrientation().isLeftToRight()) {
+                justification = LEFT;
+            } else {
+                justification = RIGHT;
+            }
+
+            switch (justification) {
+                case LEFT:
+                    compLoc.x = grooveRect.x + TEXT_INSET_H + insets.left;
+                    break;
+                case RIGHT:
+                    compLoc.x = (grooveRect.x + grooveRect.width
+                                 - (labelDim.width + TEXT_INSET_H + insets.right));
+                    break;
+            }
+
+            // If title is positioned in middle of border AND its fontsize
+            // is greater than the border's thickness, we'll need to paint
+            // the border in sections to leave space for the component's background
+            // to show through the title.
+            //
+            if (border != null) {
+                if (grooveRect.y > compLoc.y - ascent) {
+                    Rectangle clipRect = new Rectangle();
+
+                    // save original clip
+                    Rectangle saveClip = g.getClipBounds();
+
+                    // paint strip left of text
+                    clipRect.setBounds(saveClip);
+                    if (computeIntersection(clipRect, x, y, compLoc.x-1-x, height)) {
+                        g.setClip(clipRect);
+                        border.paintBorder(c, g, grooveRect.x, grooveRect.y,
+                                      grooveRect.width, grooveRect.height);
+                    }
+
+                    // paint strip right of text
+                    clipRect.setBounds(saveClip);
+                    if (computeIntersection(clipRect, compLoc.x+ labelDim.width +1, y,
+                                   x+width-(compLoc.x+ labelDim.width +1), height)) {
+                        g.setClip(clipRect);
+                        border.paintBorder(c, g, grooveRect.x, grooveRect.y,
+                                      grooveRect.width, grooveRect.height);
+                    }
+
+                    // paint strip below text
+                    clipRect.setBounds(saveClip);
+                    if (computeIntersection(clipRect,
+                                            compLoc.x - 1, compLoc.y + ascent + descent,
+                                            labelDim.width + 2,
+                                            y + height - compLoc.y - ascent - descent)) {
+                        g.setClip(clipRect);
+                        border.paintBorder(c, g, grooveRect.x, grooveRect.y,
+                                  grooveRect.width, grooveRect.height);
+                    }
+
+                    // restore clip
+                    g.setClip(saveClip);
+
+                } else {
+                    border.paintBorder(c, g, grooveRect.x, grooveRect.y,
+                                      grooveRect.width, grooveRect.height);
+                }
+
+                label.setLocation(compLoc);
+                label.setSize(labelDim);
+            }
+        }
+
+        /**
+         * Reinitialize the insets parameter with this Border's current Insets.
+         * @param c the component for which this border insets value applies
+         * @param insets the object to be reinitialized
+         */
+        public Insets getBorderInsets(Component c, Insets insets) {
+            int height = 16;
+
+            Border border = getBorder();
+            if (border != null) {
+                if (border instanceof AbstractBorder) {
+                    ((AbstractBorder)border).getBorderInsets(c, insets);
+                } else {
+                    // Can't reuse border insets because the Border interface
+                    // can't be enhanced.
+                    Insets i = border.getBorderInsets(c);
+                    insets.top = i.top;
+                    insets.right = i.right;
+                    insets.bottom = i.bottom;
+                    insets.left = i.left;
+                }
+            } else {
+                insets.left = insets.top = insets.right = insets.bottom = 0;
+            }
+
+            insets.left += EDGE_SPACING + TEXT_SPACING;
+            insets.right += EDGE_SPACING + TEXT_SPACING;
+            insets.top += EDGE_SPACING + TEXT_SPACING;
+            insets.bottom += EDGE_SPACING + TEXT_SPACING;
+
+            if (c == null || label == null) {
+                return insets;
+            }
+
+            insets.top += label.getHeight();
+
+            return insets;
+        }
+
+        /**
+         * Returns the label of the labeled border.
+         */
+        public JComponent getLabel() {
+            return label;
+        }
+
+
+        /**
+         * Sets the title of the titled border.
+         * param title the title for the border
+         */
+        public void setLabel(JComponent label) {
+            this.label = label;
+        }
+
+
+
+        /**
+         * Returns the minimum dimensions this border requires
+         * in order to fully display the border and title.
+         * @param c the component where this border will be drawn
+         */
+        public Dimension getMinimumSize(Component c) {
+            Insets insets = getBorderInsets(c);
+            Dimension minSize = new Dimension(insets.right + insets.left,
+                                              insets.top + insets.bottom);
+            minSize.width += label.getWidth();
+
+            return minSize;
+        }
+
+
+        private static boolean computeIntersection(Rectangle dest,
+                                                   int rx, int ry, int rw, int rh) {
+            int x1 = Math.max(rx, dest.x);
+            int x2 = Math.min(rx + rw, dest.x + dest.width);
+            int y1 = Math.max(ry, dest.y);
+            int y2 = Math.min(ry + rh, dest.y + dest.height);
+            dest.x = x1;
+            dest.y = y1;
+            dest.width = x2 - x1;
+            dest.height = y2 - y1;
+
+            if (dest.width <= 0 || dest.height <= 0) {
+                return false;
+            }
+            return true;
+        }
+    }
+
+
+    protected static class FocusBorder extends AbstractBorder implements FocusListener {
+        private Component comp;
+        private Color focusColor;
+        private boolean focusLostTemporarily = false;
+
+        public FocusBorder(Component comp) {
+            this.comp = comp;
+
+            comp.addFocusListener(this);
+
+            // This is the best guess for a L&F specific color
+            focusColor = UIManager.getColor("TabbedPane.focus");
+        }
+
+        public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
+            if (comp.hasFocus() || focusLostTemporarily) {
+                Color color = g.getColor();
+                g.setColor(focusColor);
+                BasicGraphicsUtils.drawDashedRect(g, x, y, width, height);
+                g.setColor(color);
+            }
+        }
+
+        public Insets getBorderInsets(Component c, Insets insets) {
+            insets.set(2, 2, 2, 2);
+            return insets;
+        }
+
+
+        public void focusGained(FocusEvent e) {
+            comp.repaint();
+        }
+
+        public void focusLost(FocusEvent e) {
+            // We will still paint focus even if lost temporarily
+            focusLostTemporarily = e.isTemporary();
+            if (!focusLostTemporarily) {
+                comp.repaint();
+            }
+        }
+    }
+}