/* * 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.*; 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) { return getBorderInsets(c, new Insets(0, 0, 0, 0)); } public Insets getBorderInsets(Component c, Insets insets) { insets.left = insets.top = insets.right = insets.bottom = 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(); } } } }