Path: blob/master/src/jdk.jconsole/share/classes/sun/tools/jconsole/BorderedComponent.java
40948 views
/*1* Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.tools.jconsole;2627import java.awt.*;28import java.awt.event.*;2930import javax.swing.*;31import javax.swing.border.*;32import javax.swing.plaf.*;33import javax.swing.plaf.basic.BasicGraphicsUtils;343536import static javax.swing.SwingConstants.*;3738import static sun.tools.jconsole.JConsole.*;3940@SuppressWarnings("serial")41public class BorderedComponent extends JPanel implements ActionListener {42JButton moreOrLessButton;43String valueLabelStr;44JLabel label;45JComponent comp;46boolean collapsed = false;4748private Icon collapseIcon;49private Icon expandIcon;5051private static Image getImage(String name) {52Toolkit tk = Toolkit.getDefaultToolkit();53name = "resources/" + name + ".png";54return tk.getImage(BorderedComponent.class.getResource(name));55}5657public BorderedComponent(String text) {58this(text, null, false);59}6061public BorderedComponent(String text, JComponent comp) {62this(text, comp, false);63}6465public BorderedComponent(String text, JComponent comp, boolean collapsible) {66super(null);6768this.comp = comp;6970// Only add border if text is not null71if (text != null) {72TitledBorder border;73if (collapsible) {74final JLabel textLabel = new JLabel(text);75JPanel borderLabel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)) {76public int getBaseline(int w, int h) {77Dimension dim = textLabel.getPreferredSize();78return textLabel.getBaseline(dim.width, dim.height) + textLabel.getY();79}80};81borderLabel.add(textLabel);82border = new LabeledBorder(borderLabel);83textLabel.setForeground(border.getTitleColor());8485if (IS_WIN) {86collapseIcon = new ImageIcon(getImage("collapse-winlf"));87expandIcon = new ImageIcon(getImage("expand-winlf"));88} else {89collapseIcon = new ArrowIcon(SOUTH, textLabel);90expandIcon = new ArrowIcon(EAST, textLabel);91}9293moreOrLessButton = new JButton(collapseIcon);94moreOrLessButton.setContentAreaFilled(false);95moreOrLessButton.setBorderPainted(false);96moreOrLessButton.setMargin(new Insets(0, 0, 0, 0));97moreOrLessButton.addActionListener(this);98String toolTip =99Messages.BORDERED_COMPONENT_MORE_OR_LESS_BUTTON_TOOLTIP;100moreOrLessButton.setToolTipText(toolTip);101borderLabel.add(moreOrLessButton);102borderLabel.setSize(borderLabel.getPreferredSize());103add(borderLabel);104} else {105border = new TitledBorder(text);106}107setBorder(new CompoundBorder(new FocusBorder(this), border));108} else {109setBorder(new FocusBorder(this));110}111if (comp != null) {112add(comp);113}114}115116public void setComponent(JComponent comp) {117if (this.comp != null) {118remove(this.comp);119}120this.comp = comp;121if (!collapsed) {122LayoutManager lm = getLayout();123if (lm instanceof BorderLayout) {124add(comp, BorderLayout.CENTER);125} else {126add(comp);127}128}129revalidate();130}131132public void setValueLabel(String str) {133this.valueLabelStr = str;134if (label != null) {135label.setText(Resources.format(Messages.CURRENT_VALUE,136valueLabelStr));137}138}139140public void actionPerformed(ActionEvent ev) {141if (collapsed) {142if (label != null) {143remove(label);144}145add(comp);146moreOrLessButton.setIcon(collapseIcon);147} else {148remove(comp);149if (valueLabelStr != null) {150if (label == null) {151label = new JLabel(Resources.format(Messages.CURRENT_VALUE,152valueLabelStr));153}154add(label);155}156moreOrLessButton.setIcon(expandIcon);157}158collapsed = !collapsed;159160JComponent container = (JComponent)getParent();161if (container != null &&162container.getLayout() instanceof VariableGridLayout) {163164((VariableGridLayout)container.getLayout()).setFillRow(this, !collapsed);165container.revalidate();166}167}168169public Dimension getMinimumSize() {170if (getLayout() != null) {171// A layout manager has been set, so delegate to it172return super.getMinimumSize();173}174175if (moreOrLessButton != null) {176Dimension d = moreOrLessButton.getMinimumSize();177Insets i = getInsets();178d.width += i.left + i.right;179d.height += i.top + i.bottom;180return d;181} else {182return super.getMinimumSize();183}184}185186public void doLayout() {187if (getLayout() != null) {188// A layout manager has been set, so delegate to it189super.doLayout();190return;191}192193Dimension d = getSize();194Insets i = getInsets();195196if (collapsed) {197if (label != null) {198Dimension p = label.getPreferredSize();199label.setBounds(i.left,200i.top + (d.height - i.top - i.bottom - p.height) / 2,201p.width,202p.height);203}204} else {205if (comp != null) {206comp.setBounds(i.left,207i.top,208d.width - i.left - i.right,209d.height - i.top - i.bottom);210}211}212}213214private static class ArrowIcon implements Icon {215private int direction;216private JLabel textLabel;217218public ArrowIcon(int direction, JLabel textLabel) {219this.direction = direction;220this.textLabel = textLabel;221}222223public void paintIcon(Component c, Graphics g, int x, int y) {224int w = getIconWidth();225int h = w;226Polygon p = new Polygon();227switch (direction) {228case EAST:229p.addPoint(x + 2, y);230p.addPoint(x + w - 2, y + h / 2);231p.addPoint(x + 2, y + h - 1);232break;233234case SOUTH:235p.addPoint(x, y + 2);236p.addPoint(x + w / 2, y + h - 2);237p.addPoint(x + w - 1, y + 2);238break;239}240g.fillPolygon(p);241}242243public int getIconWidth() {244return getIconHeight();245}246247public int getIconHeight() {248Graphics g = textLabel.getGraphics();249if (g != null) {250int h = g.getFontMetrics(textLabel.getFont()).getAscent() * 6/10;251if (h % 2 == 0) {252h += 1; // Make it odd253}254return h;255} else {256return 7;257}258}259}260261262/**263* A subclass of <code>TitledBorder</code> which implements an arbitrary border264* with the addition of a JComponent (JLabel, JPanel, etc) in the265* default position.266* <p>267* If the border property value is not268* specified in the constructor or by invoking the appropriate269* set method, the property value will be defined by the current270* look and feel, using the following property name in the271* Defaults Table:272* <ul>273* <li>"TitledBorder.border"274* </ul>275*/276protected static class LabeledBorder extends TitledBorder {277protected JComponent label;278279private Point compLoc = new Point();280281/**282* Creates a LabeledBorder instance.283*284* @param label the label the border should display285*/286public LabeledBorder(JComponent label) {287this(null, label);288}289290/**291* Creates a LabeledBorder instance with the specified border292* and an empty label.293*294* @param border the border295*/296public LabeledBorder(Border border) {297this(border, null);298}299300/**301* Creates a LabeledBorder instance with the specified border and302* label.303*304* @param border the border305* @param label the label the border should display306*/307public LabeledBorder(Border border, JComponent label) {308super(border);309310this.label = label;311312if (label instanceof JLabel &&313label.getForeground() instanceof ColorUIResource) {314315label.setForeground(getTitleColor());316}317318}319320/**321* Paints the border for the specified component with the322* specified position and size.323* @param c the component for which this border is being painted324* @param g the paint graphics325* @param x the x position of the painted border326* @param y the y position of the painted border327* @param width the width of the painted border328* @param height the height of the painted border329*/330public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {331332Border border = getBorder();333334if (label == null) {335if (border != null) {336border.paintBorder(c, g, x, y, width, height);337}338return;339}340341Rectangle grooveRect = new Rectangle(x + EDGE_SPACING, y + EDGE_SPACING,342width - (EDGE_SPACING * 2),343height - (EDGE_SPACING * 2));344345Dimension labelDim = label.getPreferredSize();346int baseline = label.getBaseline(labelDim.width, labelDim.height);347int ascent = Math.max(0, baseline);348int descent = labelDim.height - ascent;349int diff;350Insets insets;351352if (border != null) {353insets = border.getBorderInsets(c);354} else {355insets = new Insets(0, 0, 0, 0);356}357358diff = Math.max(0, ascent/2 + TEXT_SPACING - EDGE_SPACING);359grooveRect.y += diff;360grooveRect.height -= diff;361compLoc.y = grooveRect.y + insets.top/2 - (ascent + descent) / 2 - 1;362363int justification;364if (c.getComponentOrientation().isLeftToRight()) {365justification = LEFT;366} else {367justification = RIGHT;368}369370switch (justification) {371case LEFT:372compLoc.x = grooveRect.x + TEXT_INSET_H + insets.left;373break;374case RIGHT:375compLoc.x = (grooveRect.x + grooveRect.width376- (labelDim.width + TEXT_INSET_H + insets.right));377break;378}379380// If title is positioned in middle of border AND its fontsize381// is greater than the border's thickness, we'll need to paint382// the border in sections to leave space for the component's background383// to show through the title.384//385if (border != null) {386if (grooveRect.y > compLoc.y - ascent) {387Rectangle clipRect = new Rectangle();388389// save original clip390Rectangle saveClip = g.getClipBounds();391392// paint strip left of text393clipRect.setBounds(saveClip);394if (computeIntersection(clipRect, x, y, compLoc.x-1-x, height)) {395g.setClip(clipRect);396border.paintBorder(c, g, grooveRect.x, grooveRect.y,397grooveRect.width, grooveRect.height);398}399400// paint strip right of text401clipRect.setBounds(saveClip);402if (computeIntersection(clipRect, compLoc.x+ labelDim.width +1, y,403x+width-(compLoc.x+ labelDim.width +1), height)) {404g.setClip(clipRect);405border.paintBorder(c, g, grooveRect.x, grooveRect.y,406grooveRect.width, grooveRect.height);407}408409// paint strip below text410clipRect.setBounds(saveClip);411if (computeIntersection(clipRect,412compLoc.x - 1, compLoc.y + ascent + descent,413labelDim.width + 2,414y + height - compLoc.y - ascent - descent)) {415g.setClip(clipRect);416border.paintBorder(c, g, grooveRect.x, grooveRect.y,417grooveRect.width, grooveRect.height);418}419420// restore clip421g.setClip(saveClip);422423} else {424border.paintBorder(c, g, grooveRect.x, grooveRect.y,425grooveRect.width, grooveRect.height);426}427428label.setLocation(compLoc);429label.setSize(labelDim);430}431}432433/**434* Reinitialize the insets parameter with this Border's current Insets.435* @param c the component for which this border insets value applies436* @param insets the object to be reinitialized437*/438public Insets getBorderInsets(Component c, Insets insets) {439Border border = getBorder();440if (border != null) {441if (border instanceof AbstractBorder) {442((AbstractBorder)border).getBorderInsets(c, insets);443} else {444// Can't reuse border insets because the Border interface445// can't be enhanced.446Insets i = border.getBorderInsets(c);447insets.top = i.top;448insets.right = i.right;449insets.bottom = i.bottom;450insets.left = i.left;451}452} else {453insets.left = insets.top = insets.right = insets.bottom = 0;454}455456insets.left += EDGE_SPACING + TEXT_SPACING;457insets.right += EDGE_SPACING + TEXT_SPACING;458insets.top += EDGE_SPACING + TEXT_SPACING;459insets.bottom += EDGE_SPACING + TEXT_SPACING;460461if (c == null || label == null) {462return insets;463}464465insets.top += label.getHeight();466467return insets;468}469470/**471* Returns the label of the labeled border.472*/473public JComponent getLabel() {474return label;475}476477478/**479* Sets the title of the titled border.480* param title the title for the border481*/482public void setLabel(JComponent label) {483this.label = label;484}485486487488/**489* Returns the minimum dimensions this border requires490* in order to fully display the border and title.491* @param c the component where this border will be drawn492*/493public Dimension getMinimumSize(Component c) {494Insets insets = getBorderInsets(c);495Dimension minSize = new Dimension(insets.right + insets.left,496insets.top + insets.bottom);497minSize.width += label.getWidth();498499return minSize;500}501502503private static boolean computeIntersection(Rectangle dest,504int rx, int ry, int rw, int rh) {505int x1 = Math.max(rx, dest.x);506int x2 = Math.min(rx + rw, dest.x + dest.width);507int y1 = Math.max(ry, dest.y);508int y2 = Math.min(ry + rh, dest.y + dest.height);509dest.x = x1;510dest.y = y1;511dest.width = x2 - x1;512dest.height = y2 - y1;513514if (dest.width <= 0 || dest.height <= 0) {515return false;516}517return true;518}519}520521522protected static class FocusBorder extends AbstractBorder implements FocusListener {523private Component comp;524private Color focusColor;525private boolean focusLostTemporarily = false;526527public FocusBorder(Component comp) {528this.comp = comp;529530comp.addFocusListener(this);531532// This is the best guess for a L&F specific color533focusColor = UIManager.getColor("TabbedPane.focus");534}535536public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {537if (comp.hasFocus() || focusLostTemporarily) {538Color color = g.getColor();539g.setColor(focusColor);540BasicGraphicsUtils.drawDashedRect(g, x, y, width, height);541g.setColor(color);542}543}544545public Insets getBorderInsets(Component c, Insets insets) {546insets.set(2, 2, 2, 2);547return insets;548}549550551public void focusGained(FocusEvent e) {552comp.repaint();553}554555public void focusLost(FocusEvent e) {556// We will still paint focus even if lost temporarily557focusLostTemporarily = e.isTemporary();558if (!focusLostTemporarily) {559comp.repaint();560}561}562}563}564565566