565 lines
19 KiB
Java
565 lines
19 KiB
Java
/*
|
|
* Copyright (c) 2004, 2013, Oracle and/or its affiliates. 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. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.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.*;
|
|
|
|
@SuppressWarnings("serial")
|
|
public class BorderedComponent extends JPanel implements ActionListener {
|
|
JButton moreOrLessButton;
|
|
String valueLabelStr;
|
|
JLabel label;
|
|
JComponent comp;
|
|
boolean collapsed = false;
|
|
|
|
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 =
|
|
Messages.BORDERED_COMPONENT_MORE_OR_LESS_BUTTON_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.format(Messages.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.format(Messages.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 constructor 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) {
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|