src/jdk.jconsole/share/classes/sun/tools/jconsole/BorderedComponent.java
changeset 47216 71c04702a3d5
parent 25859 3317bb8137f4
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     1 /*
       
     2  * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.tools.jconsole;
       
    27 
       
    28 import java.awt.*;
       
    29 import java.awt.event.*;
       
    30 
       
    31 import javax.swing.*;
       
    32 import javax.swing.border.*;
       
    33 import javax.swing.plaf.*;
       
    34 import javax.swing.plaf.basic.BasicGraphicsUtils;
       
    35 
       
    36 
       
    37 import static javax.swing.SwingConstants.*;
       
    38 
       
    39 import static sun.tools.jconsole.JConsole.*;
       
    40 
       
    41 @SuppressWarnings("serial")
       
    42 public class BorderedComponent extends JPanel implements ActionListener {
       
    43     JButton moreOrLessButton;
       
    44     String valueLabelStr;
       
    45     JLabel label;
       
    46     JComponent comp;
       
    47     boolean collapsed = false;
       
    48 
       
    49     private Icon collapseIcon;
       
    50     private Icon expandIcon;
       
    51 
       
    52     private static Image getImage(String name) {
       
    53         Toolkit tk = Toolkit.getDefaultToolkit();
       
    54         name = "resources/" + name + ".png";
       
    55         return tk.getImage(BorderedComponent.class.getResource(name));
       
    56     }
       
    57 
       
    58     public BorderedComponent(String text) {
       
    59         this(text, null, false);
       
    60     }
       
    61 
       
    62     public BorderedComponent(String text, JComponent comp) {
       
    63         this(text, comp, false);
       
    64     }
       
    65 
       
    66     public BorderedComponent(String text, JComponent comp, boolean collapsible) {
       
    67         super(null);
       
    68 
       
    69         this.comp = comp;
       
    70 
       
    71         // Only add border if text is not null
       
    72         if (text != null) {
       
    73             TitledBorder border;
       
    74             if (collapsible) {
       
    75                 final JLabel textLabel = new JLabel(text);
       
    76                 JPanel borderLabel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)) {
       
    77                     public int getBaseline(int w, int h) {
       
    78                         Dimension dim = textLabel.getPreferredSize();
       
    79                         return textLabel.getBaseline(dim.width, dim.height) + textLabel.getY();
       
    80                     }
       
    81                 };
       
    82                 borderLabel.add(textLabel);
       
    83                 border = new LabeledBorder(borderLabel);
       
    84                 textLabel.setForeground(border.getTitleColor());
       
    85 
       
    86                 if (IS_WIN) {
       
    87                     collapseIcon = new ImageIcon(getImage("collapse-winlf"));
       
    88                     expandIcon = new ImageIcon(getImage("expand-winlf"));
       
    89                 } else {
       
    90                     collapseIcon = new ArrowIcon(SOUTH, textLabel);
       
    91                     expandIcon = new ArrowIcon(EAST, textLabel);
       
    92                 }
       
    93 
       
    94                 moreOrLessButton = new JButton(collapseIcon);
       
    95                 moreOrLessButton.setContentAreaFilled(false);
       
    96                 moreOrLessButton.setBorderPainted(false);
       
    97                 moreOrLessButton.setMargin(new Insets(0, 0, 0, 0));
       
    98                 moreOrLessButton.addActionListener(this);
       
    99                 String toolTip =
       
   100                     Messages.BORDERED_COMPONENT_MORE_OR_LESS_BUTTON_TOOLTIP;
       
   101                 moreOrLessButton.setToolTipText(toolTip);
       
   102                 borderLabel.add(moreOrLessButton);
       
   103                 borderLabel.setSize(borderLabel.getPreferredSize());
       
   104                 add(borderLabel);
       
   105             } else {
       
   106                 border = new TitledBorder(text);
       
   107             }
       
   108             setBorder(new CompoundBorder(new FocusBorder(this), border));
       
   109         } else {
       
   110             setBorder(new FocusBorder(this));
       
   111         }
       
   112         if (comp != null) {
       
   113             add(comp);
       
   114         }
       
   115     }
       
   116 
       
   117     public void setComponent(JComponent comp) {
       
   118         if (this.comp != null) {
       
   119             remove(this.comp);
       
   120         }
       
   121         this.comp = comp;
       
   122         if (!collapsed) {
       
   123             LayoutManager lm = getLayout();
       
   124             if (lm instanceof BorderLayout) {
       
   125                 add(comp, BorderLayout.CENTER);
       
   126             } else {
       
   127                 add(comp);
       
   128             }
       
   129         }
       
   130         revalidate();
       
   131     }
       
   132 
       
   133     public void setValueLabel(String str) {
       
   134         this.valueLabelStr = str;
       
   135         if (label != null) {
       
   136             label.setText(Resources.format(Messages.CURRENT_VALUE,
       
   137                                            valueLabelStr));
       
   138         }
       
   139     }
       
   140 
       
   141     public void actionPerformed(ActionEvent ev) {
       
   142         if (collapsed) {
       
   143             if (label != null) {
       
   144                 remove(label);
       
   145             }
       
   146             add(comp);
       
   147             moreOrLessButton.setIcon(collapseIcon);
       
   148         } else {
       
   149             remove(comp);
       
   150             if (valueLabelStr != null) {
       
   151                 if (label == null) {
       
   152                     label = new JLabel(Resources.format(Messages.CURRENT_VALUE,
       
   153                                                         valueLabelStr));
       
   154                 }
       
   155                 add(label);
       
   156             }
       
   157             moreOrLessButton.setIcon(expandIcon);
       
   158         }
       
   159         collapsed = !collapsed;
       
   160 
       
   161         JComponent container = (JComponent)getParent();
       
   162         if (container != null &&
       
   163             container.getLayout() instanceof VariableGridLayout) {
       
   164 
       
   165             ((VariableGridLayout)container.getLayout()).setFillRow(this, !collapsed);
       
   166             container.revalidate();
       
   167         }
       
   168     }
       
   169 
       
   170     public Dimension getMinimumSize() {
       
   171         if (getLayout() != null) {
       
   172             // A layout manager has been set, so delegate to it
       
   173             return super.getMinimumSize();
       
   174         }
       
   175 
       
   176         if (moreOrLessButton != null) {
       
   177             Dimension d = moreOrLessButton.getMinimumSize();
       
   178             Insets i = getInsets();
       
   179             d.width  += i.left + i.right;
       
   180             d.height += i.top + i.bottom;
       
   181             return d;
       
   182         } else {
       
   183             return super.getMinimumSize();
       
   184         }
       
   185     }
       
   186 
       
   187     public void doLayout() {
       
   188         if (getLayout() != null) {
       
   189             // A layout manager has been set, so delegate to it
       
   190             super.doLayout();
       
   191             return;
       
   192         }
       
   193 
       
   194         Dimension d = getSize();
       
   195         Insets i = getInsets();
       
   196 
       
   197         if (collapsed) {
       
   198             if (label != null) {
       
   199                 Dimension p = label.getPreferredSize();
       
   200                 label.setBounds(i.left,
       
   201                                 i.top + (d.height - i.top - i.bottom - p.height) / 2,
       
   202                                 p.width,
       
   203                                 p.height);
       
   204             }
       
   205         } else {
       
   206             if (comp != null) {
       
   207                 comp.setBounds(i.left,
       
   208                                i.top,
       
   209                                d.width - i.left - i.right,
       
   210                                d.height - i.top - i.bottom);
       
   211             }
       
   212         }
       
   213     }
       
   214 
       
   215     private static class ArrowIcon implements Icon {
       
   216         private int direction;
       
   217         private JLabel textLabel;
       
   218 
       
   219         public ArrowIcon(int direction, JLabel textLabel) {
       
   220             this.direction = direction;
       
   221             this.textLabel = textLabel;
       
   222         }
       
   223 
       
   224         public void paintIcon(Component c, Graphics g, int x, int y) {
       
   225             int w = getIconWidth();
       
   226             int h = w;
       
   227             Polygon p = new Polygon();
       
   228             switch (direction) {
       
   229               case EAST:
       
   230                 p.addPoint(x + 2,     y);
       
   231                 p.addPoint(x + w - 2, y + h / 2);
       
   232                 p.addPoint(x + 2,     y + h - 1);
       
   233                 break;
       
   234 
       
   235               case SOUTH:
       
   236                 p.addPoint(x,         y + 2);
       
   237                 p.addPoint(x + w / 2, y + h - 2);
       
   238                 p.addPoint(x + w - 1, y + 2);
       
   239                 break;
       
   240             }
       
   241             g.fillPolygon(p);
       
   242         }
       
   243 
       
   244         public int getIconWidth() {
       
   245             return getIconHeight();
       
   246         }
       
   247 
       
   248         public int getIconHeight() {
       
   249             Graphics g = textLabel.getGraphics();
       
   250             if (g != null) {
       
   251                 int h = g.getFontMetrics(textLabel.getFont()).getAscent() * 6/10;
       
   252                 if (h % 2 == 0) {
       
   253                     h += 1;     // Make it odd
       
   254                 }
       
   255                 return h;
       
   256             } else {
       
   257                 return 7;
       
   258             }
       
   259         }
       
   260     }
       
   261 
       
   262 
       
   263     /**
       
   264      * A subclass of <code>TitledBorder</code> which implements an arbitrary border
       
   265      * with the addition of a JComponent (JLabel, JPanel, etc) in the
       
   266      * default position.
       
   267      * <p>
       
   268      * If the border property value is not
       
   269      * specified in the constructor or by invoking the appropriate
       
   270      * set method, the property value will be defined by the current
       
   271      * look and feel, using the following property name in the
       
   272      * Defaults Table:
       
   273      * <ul>
       
   274      * <li>&quot;TitledBorder.border&quot;
       
   275      * </ul>
       
   276      */
       
   277     protected static class LabeledBorder extends TitledBorder {
       
   278         protected JComponent label;
       
   279 
       
   280         private Point compLoc = new Point();
       
   281 
       
   282         /**
       
   283          * Creates a LabeledBorder instance.
       
   284          *
       
   285          * @param label  the label the border should display
       
   286          */
       
   287         public LabeledBorder(JComponent label)     {
       
   288             this(null, label);
       
   289         }
       
   290 
       
   291         /**
       
   292          * Creates a LabeledBorder instance with the specified border
       
   293          * and an empty label.
       
   294          *
       
   295          * @param border  the border
       
   296          */
       
   297         public LabeledBorder(Border border)       {
       
   298             this(border, null);
       
   299         }
       
   300 
       
   301         /**
       
   302          * Creates a LabeledBorder instance with the specified border and
       
   303          * label.
       
   304          *
       
   305          * @param border  the border
       
   306          * @param label  the label the border should display
       
   307          */
       
   308         public LabeledBorder(Border border, JComponent label) {
       
   309             super(border);
       
   310 
       
   311             this.label = label;
       
   312 
       
   313             if (label instanceof JLabel &&
       
   314                 label.getForeground() instanceof ColorUIResource) {
       
   315 
       
   316                 label.setForeground(getTitleColor());
       
   317             }
       
   318 
       
   319         }
       
   320 
       
   321         /**
       
   322          * Paints the border for the specified component with the
       
   323          * specified position and size.
       
   324          * @param c the component for which this border is being painted
       
   325          * @param g the paint graphics
       
   326          * @param x the x position of the painted border
       
   327          * @param y the y position of the painted border
       
   328          * @param width the width of the painted border
       
   329          * @param height the height of the painted border
       
   330          */
       
   331         public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
       
   332 
       
   333             Border border = getBorder();
       
   334 
       
   335             if (label == null) {
       
   336                 if (border != null) {
       
   337                     border.paintBorder(c, g, x, y, width, height);
       
   338                 }
       
   339                 return;
       
   340             }
       
   341 
       
   342             Rectangle grooveRect = new Rectangle(x + EDGE_SPACING, y + EDGE_SPACING,
       
   343                                                  width - (EDGE_SPACING * 2),
       
   344                                                  height - (EDGE_SPACING * 2));
       
   345 
       
   346             Dimension   labelDim = label.getPreferredSize();
       
   347             int baseline = label.getBaseline(labelDim.width, labelDim.height);
       
   348             int         ascent = Math.max(0, baseline);
       
   349             int         descent = labelDim.height - ascent;
       
   350             int         diff;
       
   351             Insets      insets;
       
   352 
       
   353             if (border != null) {
       
   354                 insets = border.getBorderInsets(c);
       
   355             } else {
       
   356                 insets = new Insets(0, 0, 0, 0);
       
   357             }
       
   358 
       
   359             diff = Math.max(0, ascent/2 + TEXT_SPACING - EDGE_SPACING);
       
   360             grooveRect.y += diff;
       
   361             grooveRect.height -= diff;
       
   362             compLoc.y = grooveRect.y + insets.top/2 - (ascent + descent) / 2 - 1;
       
   363 
       
   364             int justification;
       
   365             if (c.getComponentOrientation().isLeftToRight()) {
       
   366                 justification = LEFT;
       
   367             } else {
       
   368                 justification = RIGHT;
       
   369             }
       
   370 
       
   371             switch (justification) {
       
   372                 case LEFT:
       
   373                     compLoc.x = grooveRect.x + TEXT_INSET_H + insets.left;
       
   374                     break;
       
   375                 case RIGHT:
       
   376                     compLoc.x = (grooveRect.x + grooveRect.width
       
   377                                  - (labelDim.width + TEXT_INSET_H + insets.right));
       
   378                     break;
       
   379             }
       
   380 
       
   381             // If title is positioned in middle of border AND its fontsize
       
   382             // is greater than the border's thickness, we'll need to paint
       
   383             // the border in sections to leave space for the component's background
       
   384             // to show through the title.
       
   385             //
       
   386             if (border != null) {
       
   387                 if (grooveRect.y > compLoc.y - ascent) {
       
   388                     Rectangle clipRect = new Rectangle();
       
   389 
       
   390                     // save original clip
       
   391                     Rectangle saveClip = g.getClipBounds();
       
   392 
       
   393                     // paint strip left of text
       
   394                     clipRect.setBounds(saveClip);
       
   395                     if (computeIntersection(clipRect, x, y, compLoc.x-1-x, height)) {
       
   396                         g.setClip(clipRect);
       
   397                         border.paintBorder(c, g, grooveRect.x, grooveRect.y,
       
   398                                       grooveRect.width, grooveRect.height);
       
   399                     }
       
   400 
       
   401                     // paint strip right of text
       
   402                     clipRect.setBounds(saveClip);
       
   403                     if (computeIntersection(clipRect, compLoc.x+ labelDim.width +1, y,
       
   404                                    x+width-(compLoc.x+ labelDim.width +1), height)) {
       
   405                         g.setClip(clipRect);
       
   406                         border.paintBorder(c, g, grooveRect.x, grooveRect.y,
       
   407                                       grooveRect.width, grooveRect.height);
       
   408                     }
       
   409 
       
   410                     // paint strip below text
       
   411                     clipRect.setBounds(saveClip);
       
   412                     if (computeIntersection(clipRect,
       
   413                                             compLoc.x - 1, compLoc.y + ascent + descent,
       
   414                                             labelDim.width + 2,
       
   415                                             y + height - compLoc.y - ascent - descent)) {
       
   416                         g.setClip(clipRect);
       
   417                         border.paintBorder(c, g, grooveRect.x, grooveRect.y,
       
   418                                   grooveRect.width, grooveRect.height);
       
   419                     }
       
   420 
       
   421                     // restore clip
       
   422                     g.setClip(saveClip);
       
   423 
       
   424                 } else {
       
   425                     border.paintBorder(c, g, grooveRect.x, grooveRect.y,
       
   426                                       grooveRect.width, grooveRect.height);
       
   427                 }
       
   428 
       
   429                 label.setLocation(compLoc);
       
   430                 label.setSize(labelDim);
       
   431             }
       
   432         }
       
   433 
       
   434         /**
       
   435          * Reinitialize the insets parameter with this Border's current Insets.
       
   436          * @param c the component for which this border insets value applies
       
   437          * @param insets the object to be reinitialized
       
   438          */
       
   439         public Insets getBorderInsets(Component c, Insets insets) {
       
   440             Border border = getBorder();
       
   441             if (border != null) {
       
   442                 if (border instanceof AbstractBorder) {
       
   443                     ((AbstractBorder)border).getBorderInsets(c, insets);
       
   444                 } else {
       
   445                     // Can't reuse border insets because the Border interface
       
   446                     // can't be enhanced.
       
   447                     Insets i = border.getBorderInsets(c);
       
   448                     insets.top = i.top;
       
   449                     insets.right = i.right;
       
   450                     insets.bottom = i.bottom;
       
   451                     insets.left = i.left;
       
   452                 }
       
   453             } else {
       
   454                 insets.left = insets.top = insets.right = insets.bottom = 0;
       
   455             }
       
   456 
       
   457             insets.left += EDGE_SPACING + TEXT_SPACING;
       
   458             insets.right += EDGE_SPACING + TEXT_SPACING;
       
   459             insets.top += EDGE_SPACING + TEXT_SPACING;
       
   460             insets.bottom += EDGE_SPACING + TEXT_SPACING;
       
   461 
       
   462             if (c == null || label == null) {
       
   463                 return insets;
       
   464             }
       
   465 
       
   466             insets.top += label.getHeight();
       
   467 
       
   468             return insets;
       
   469         }
       
   470 
       
   471         /**
       
   472          * Returns the label of the labeled border.
       
   473          */
       
   474         public JComponent getLabel() {
       
   475             return label;
       
   476         }
       
   477 
       
   478 
       
   479         /**
       
   480          * Sets the title of the titled border.
       
   481          * param title the title for the border
       
   482          */
       
   483         public void setLabel(JComponent label) {
       
   484             this.label = label;
       
   485         }
       
   486 
       
   487 
       
   488 
       
   489         /**
       
   490          * Returns the minimum dimensions this border requires
       
   491          * in order to fully display the border and title.
       
   492          * @param c the component where this border will be drawn
       
   493          */
       
   494         public Dimension getMinimumSize(Component c) {
       
   495             Insets insets = getBorderInsets(c);
       
   496             Dimension minSize = new Dimension(insets.right + insets.left,
       
   497                                               insets.top + insets.bottom);
       
   498             minSize.width += label.getWidth();
       
   499 
       
   500             return minSize;
       
   501         }
       
   502 
       
   503 
       
   504         private static boolean computeIntersection(Rectangle dest,
       
   505                                                    int rx, int ry, int rw, int rh) {
       
   506             int x1 = Math.max(rx, dest.x);
       
   507             int x2 = Math.min(rx + rw, dest.x + dest.width);
       
   508             int y1 = Math.max(ry, dest.y);
       
   509             int y2 = Math.min(ry + rh, dest.y + dest.height);
       
   510             dest.x = x1;
       
   511             dest.y = y1;
       
   512             dest.width = x2 - x1;
       
   513             dest.height = y2 - y1;
       
   514 
       
   515             if (dest.width <= 0 || dest.height <= 0) {
       
   516                 return false;
       
   517             }
       
   518             return true;
       
   519         }
       
   520     }
       
   521 
       
   522 
       
   523     protected static class FocusBorder extends AbstractBorder implements FocusListener {
       
   524         private Component comp;
       
   525         private Color focusColor;
       
   526         private boolean focusLostTemporarily = false;
       
   527 
       
   528         public FocusBorder(Component comp) {
       
   529             this.comp = comp;
       
   530 
       
   531             comp.addFocusListener(this);
       
   532 
       
   533             // This is the best guess for a L&F specific color
       
   534             focusColor = UIManager.getColor("TabbedPane.focus");
       
   535         }
       
   536 
       
   537         public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
       
   538             if (comp.hasFocus() || focusLostTemporarily) {
       
   539                 Color color = g.getColor();
       
   540                 g.setColor(focusColor);
       
   541                 BasicGraphicsUtils.drawDashedRect(g, x, y, width, height);
       
   542                 g.setColor(color);
       
   543             }
       
   544         }
       
   545 
       
   546         public Insets getBorderInsets(Component c, Insets insets) {
       
   547             insets.set(2, 2, 2, 2);
       
   548             return insets;
       
   549         }
       
   550 
       
   551 
       
   552         public void focusGained(FocusEvent e) {
       
   553             comp.repaint();
       
   554         }
       
   555 
       
   556         public void focusLost(FocusEvent e) {
       
   557             // We will still paint focus even if lost temporarily
       
   558             focusLostTemporarily = e.isTemporary();
       
   559             if (!focusLostTemporarily) {
       
   560                 comp.repaint();
       
   561             }
       
   562         }
       
   563     }
       
   564 }