| /* |
| * Copyright (c) 2004, 2006, 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 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) { |
| 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(); |
| } |
| } |
| } |
| } |