diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/sun/tools/jconsole/BorderedComponent.java --- /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 TitledBorder which implements an arbitrary border + * with the addition of a JComponent (JLabel, JPanel, etc) in the + * default position. + *

+ * 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: + *

+ */ + 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(); + } + } + } +}