/*
* 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>"TitledBorder.border"
* </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();
}
}
}
}