001: /*
002: * Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.tools.jconsole;
027:
028: import java.awt.*;
029: import java.awt.event.*;
030:
031: import javax.swing.*;
032: import javax.swing.border.*;
033: import javax.swing.plaf.*;
034: import javax.swing.plaf.basic.BasicGraphicsUtils;
035:
036: import static javax.swing.SwingConstants.*;
037:
038: import static sun.tools.jconsole.JConsole.*;
039: import static sun.tools.jconsole.Resources.*;
040: import static sun.tools.jconsole.Utilities.*;
041:
042: @SuppressWarnings("serial")
043: public class BorderedComponent extends JPanel implements ActionListener {
044: JButton moreOrLessButton;
045: String valueLabelStr;
046: JLabel label;
047: JComponent comp;
048: boolean collapsed = false;
049:
050: private JPopupMenu popupMenu;
051:
052: private Icon collapseIcon;
053: private Icon expandIcon;
054:
055: private static Image getImage(String name) {
056: Toolkit tk = Toolkit.getDefaultToolkit();
057: name = "resources/" + name + ".png";
058: return tk.getImage(BorderedComponent.class.getResource(name));
059: }
060:
061: public BorderedComponent(String text) {
062: this (text, null, false);
063: }
064:
065: public BorderedComponent(String text, JComponent comp) {
066: this (text, comp, false);
067: }
068:
069: public BorderedComponent(String text, JComponent comp,
070: boolean collapsible) {
071: super (null);
072:
073: this .comp = comp;
074:
075: // Only add border if text is not null
076: if (text != null) {
077: TitledBorder border;
078: if (collapsible) {
079: final JLabel textLabel = new JLabel(text);
080: JPanel borderLabel = new JPanel(new FlowLayout(
081: FlowLayout.LEFT, 2, 0)) {
082: public int getBaseline(int w, int h) {
083: Dimension dim = textLabel.getPreferredSize();
084: return textLabel.getBaseline(dim.width,
085: dim.height)
086: + textLabel.getY();
087: }
088: };
089: borderLabel.add(textLabel);
090: border = new LabeledBorder(borderLabel);
091: textLabel.setForeground(border.getTitleColor());
092:
093: if (IS_WIN) {
094: collapseIcon = new ImageIcon(
095: getImage("collapse-winlf"));
096: expandIcon = new ImageIcon(getImage("expand-winlf"));
097: } else {
098: collapseIcon = new ArrowIcon(SOUTH, textLabel);
099: expandIcon = new ArrowIcon(EAST, textLabel);
100: }
101:
102: moreOrLessButton = new JButton(collapseIcon);
103: moreOrLessButton.setContentAreaFilled(false);
104: moreOrLessButton.setBorderPainted(false);
105: moreOrLessButton.setMargin(new Insets(0, 0, 0, 0));
106: moreOrLessButton.addActionListener(this );
107: String toolTip = getText("BorderedComponent.moreOrLessButton.toolTip");
108: moreOrLessButton.setToolTipText(toolTip);
109: borderLabel.add(moreOrLessButton);
110: borderLabel.setSize(borderLabel.getPreferredSize());
111: add(borderLabel);
112: } else {
113: border = new TitledBorder(text);
114: }
115: setBorder(new CompoundBorder(new FocusBorder(this ), border));
116: } else {
117: setBorder(new FocusBorder(this ));
118: }
119: if (comp != null) {
120: add(comp);
121: }
122: }
123:
124: public void setComponent(JComponent comp) {
125: if (this .comp != null) {
126: remove(this .comp);
127: }
128: this .comp = comp;
129: if (!collapsed) {
130: LayoutManager lm = getLayout();
131: if (lm instanceof BorderLayout) {
132: add(comp, BorderLayout.CENTER);
133: } else {
134: add(comp);
135: }
136: }
137: revalidate();
138: }
139:
140: public void setValueLabel(String str) {
141: this .valueLabelStr = str;
142: if (label != null) {
143: label.setText(Resources.getText("Current value",
144: valueLabelStr));
145: }
146: }
147:
148: public void actionPerformed(ActionEvent ev) {
149: if (collapsed) {
150: if (label != null) {
151: remove(label);
152: }
153: add(comp);
154: moreOrLessButton.setIcon(collapseIcon);
155: } else {
156: remove(comp);
157: if (valueLabelStr != null) {
158: if (label == null) {
159: label = new JLabel(Resources.getText(
160: "Current value", valueLabelStr));
161: }
162: add(label);
163: }
164: moreOrLessButton.setIcon(expandIcon);
165: }
166: collapsed = !collapsed;
167:
168: JComponent container = (JComponent) getParent();
169: if (container != null
170: && container.getLayout() instanceof VariableGridLayout) {
171:
172: ((VariableGridLayout) container.getLayout()).setFillRow(
173: this , !collapsed);
174: container.revalidate();
175: }
176: }
177:
178: public Dimension getMinimumSize() {
179: if (getLayout() != null) {
180: // A layout manager has been set, so delegate to it
181: return super .getMinimumSize();
182: }
183:
184: if (moreOrLessButton != null) {
185: Dimension d = moreOrLessButton.getMinimumSize();
186: Insets i = getInsets();
187: d.width += i.left + i.right;
188: d.height += i.top + i.bottom;
189: return d;
190: } else {
191: return super .getMinimumSize();
192: }
193: }
194:
195: public void doLayout() {
196: if (getLayout() != null) {
197: // A layout manager has been set, so delegate to it
198: super .doLayout();
199: return;
200: }
201:
202: Dimension d = getSize();
203: Insets i = getInsets();
204:
205: if (collapsed) {
206: if (label != null) {
207: Dimension p = label.getPreferredSize();
208: label.setBounds(i.left, i.top
209: + (d.height - i.top - i.bottom - p.height) / 2,
210: p.width, p.height);
211: }
212: } else {
213: if (comp != null) {
214: comp.setBounds(i.left, i.top, d.width - i.left
215: - i.right, d.height - i.top - i.bottom);
216: }
217: }
218: }
219:
220: private static class ArrowIcon implements Icon {
221: private int direction;
222: private JLabel textLabel;
223:
224: public ArrowIcon(int direction, JLabel textLabel) {
225: this .direction = direction;
226: this .textLabel = textLabel;
227: }
228:
229: public void paintIcon(Component c, Graphics g, int x, int y) {
230: int w = getIconWidth();
231: int h = w;
232: Polygon p = new Polygon();
233: switch (direction) {
234: case EAST:
235: p.addPoint(x + 2, y);
236: p.addPoint(x + w - 2, y + h / 2);
237: p.addPoint(x + 2, y + h - 1);
238: break;
239:
240: case SOUTH:
241: p.addPoint(x, y + 2);
242: p.addPoint(x + w / 2, y + h - 2);
243: p.addPoint(x + w - 1, y + 2);
244: break;
245: }
246: g.fillPolygon(p);
247: }
248:
249: public int getIconWidth() {
250: return getIconHeight();
251: }
252:
253: public int getIconHeight() {
254: Graphics g = textLabel.getGraphics();
255: if (g != null) {
256: int h = g.getFontMetrics(textLabel.getFont())
257: .getAscent() * 6 / 10;
258: if (h % 2 == 0) {
259: h += 1; // Make it odd
260: }
261: return h;
262: } else {
263: return 7;
264: }
265: }
266: }
267:
268: /**
269: * A subclass of <code>TitledBorder</code> which implements an arbitrary border
270: * with the addition of a JComponent (JLabel, JPanel, etc) in the
271: * default position.
272: * <p>
273: * If the border property value is not
274: * specified in the constuctor or by invoking the appropriate
275: * set method, the property value will be defined by the current
276: * look and feel, using the following property name in the
277: * Defaults Table:
278: * <ul>
279: * <li>"TitledBorder.border"
280: * </ul>
281: */
282: protected static class LabeledBorder extends TitledBorder {
283: protected JComponent label;
284:
285: private Point compLoc = new Point();
286:
287: /**
288: * Creates a LabeledBorder instance.
289: *
290: * @param label the label the border should display
291: */
292: public LabeledBorder(JComponent label) {
293: this (null, label);
294: }
295:
296: /**
297: * Creates a LabeledBorder instance with the specified border
298: * and an empty label.
299: *
300: * @param border the border
301: */
302: public LabeledBorder(Border border) {
303: this (border, null);
304: }
305:
306: /**
307: * Creates a LabeledBorder instance with the specified border and
308: * label.
309: *
310: * @param border the border
311: * @param label the label the border should display
312: */
313: public LabeledBorder(Border border, JComponent label) {
314: super (border);
315:
316: this .label = label;
317:
318: if (label instanceof JLabel
319: && label.getForeground() instanceof ColorUIResource) {
320:
321: label.setForeground(getTitleColor());
322: }
323:
324: }
325:
326: /**
327: * Paints the border for the specified component with the
328: * specified position and size.
329: * @param c the component for which this border is being painted
330: * @param g the paint graphics
331: * @param x the x position of the painted border
332: * @param y the y position of the painted border
333: * @param width the width of the painted border
334: * @param height the height of the painted border
335: */
336: public void paintBorder(Component c, Graphics g, int x, int y,
337: int width, int height) {
338:
339: Border border = getBorder();
340:
341: if (label == null) {
342: if (border != null) {
343: border.paintBorder(c, g, x, y, width, height);
344: }
345: return;
346: }
347:
348: Rectangle grooveRect = new Rectangle(x + EDGE_SPACING, y
349: + EDGE_SPACING, width - (EDGE_SPACING * 2), height
350: - (EDGE_SPACING * 2));
351:
352: Dimension labelDim = label.getPreferredSize();
353: int baseline = label.getBaseline(labelDim.width,
354: labelDim.height);
355: int ascent = Math.max(0, baseline);
356: int descent = labelDim.height - ascent;
357: int diff;
358: Insets insets;
359:
360: if (border != null) {
361: insets = border.getBorderInsets(c);
362: } else {
363: insets = new Insets(0, 0, 0, 0);
364: }
365:
366: diff = Math
367: .max(0, ascent / 2 + TEXT_SPACING - EDGE_SPACING);
368: grooveRect.y += diff;
369: grooveRect.height -= diff;
370: compLoc.y = grooveRect.y + insets.top / 2
371: - (ascent + descent) / 2 - 1;
372:
373: int justification;
374: if (c.getComponentOrientation().isLeftToRight()) {
375: justification = LEFT;
376: } else {
377: justification = RIGHT;
378: }
379:
380: switch (justification) {
381: case LEFT:
382: compLoc.x = grooveRect.x + TEXT_INSET_H + insets.left;
383: break;
384: case RIGHT:
385: compLoc.x = (grooveRect.x + grooveRect.width - (labelDim.width
386: + TEXT_INSET_H + insets.right));
387: break;
388: }
389:
390: // If title is positioned in middle of border AND its fontsize
391: // is greater than the border's thickness, we'll need to paint
392: // the border in sections to leave space for the component's background
393: // to show through the title.
394: //
395: if (border != null) {
396: if (grooveRect.y > compLoc.y - ascent) {
397: Rectangle clipRect = new Rectangle();
398:
399: // save original clip
400: Rectangle saveClip = g.getClipBounds();
401:
402: // paint strip left of text
403: clipRect.setBounds(saveClip);
404: if (computeIntersection(clipRect, x, y, compLoc.x
405: - 1 - x, height)) {
406: g.setClip(clipRect);
407: border.paintBorder(c, g, grooveRect.x,
408: grooveRect.y, grooveRect.width,
409: grooveRect.height);
410: }
411:
412: // paint strip right of text
413: clipRect.setBounds(saveClip);
414: if (computeIntersection(clipRect, compLoc.x
415: + labelDim.width + 1, y, x + width
416: - (compLoc.x + labelDim.width + 1), height)) {
417: g.setClip(clipRect);
418: border.paintBorder(c, g, grooveRect.x,
419: grooveRect.y, grooveRect.width,
420: grooveRect.height);
421: }
422:
423: // paint strip below text
424: clipRect.setBounds(saveClip);
425: if (computeIntersection(clipRect, compLoc.x - 1,
426: compLoc.y + ascent + descent,
427: labelDim.width + 2, y + height - compLoc.y
428: - ascent - descent)) {
429: g.setClip(clipRect);
430: border.paintBorder(c, g, grooveRect.x,
431: grooveRect.y, grooveRect.width,
432: grooveRect.height);
433: }
434:
435: // restore clip
436: g.setClip(saveClip);
437:
438: } else {
439: border.paintBorder(c, g, grooveRect.x,
440: grooveRect.y, grooveRect.width,
441: grooveRect.height);
442: }
443:
444: label.setLocation(compLoc);
445: label.setSize(labelDim);
446: }
447: }
448:
449: /**
450: * Reinitialize the insets parameter with this Border's current Insets.
451: * @param c the component for which this border insets value applies
452: * @param insets the object to be reinitialized
453: */
454: public Insets getBorderInsets(Component c, Insets insets) {
455: int height = 16;
456:
457: Border border = getBorder();
458: if (border != null) {
459: if (border instanceof AbstractBorder) {
460: ((AbstractBorder) border)
461: .getBorderInsets(c, insets);
462: } else {
463: // Can't reuse border insets because the Border interface
464: // can't be enhanced.
465: Insets i = border.getBorderInsets(c);
466: insets.top = i.top;
467: insets.right = i.right;
468: insets.bottom = i.bottom;
469: insets.left = i.left;
470: }
471: } else {
472: insets.left = insets.top = insets.right = insets.bottom = 0;
473: }
474:
475: insets.left += EDGE_SPACING + TEXT_SPACING;
476: insets.right += EDGE_SPACING + TEXT_SPACING;
477: insets.top += EDGE_SPACING + TEXT_SPACING;
478: insets.bottom += EDGE_SPACING + TEXT_SPACING;
479:
480: if (c == null || label == null) {
481: return insets;
482: }
483:
484: insets.top += label.getHeight();
485:
486: return insets;
487: }
488:
489: /**
490: * Returns the label of the labeled border.
491: */
492: public JComponent getLabel() {
493: return label;
494: }
495:
496: /**
497: * Sets the title of the titled border.
498: * param title the title for the border
499: */
500: public void setLabel(JComponent label) {
501: this .label = label;
502: }
503:
504: /**
505: * Returns the minimum dimensions this border requires
506: * in order to fully display the border and title.
507: * @param c the component where this border will be drawn
508: */
509: public Dimension getMinimumSize(Component c) {
510: Insets insets = getBorderInsets(c);
511: Dimension minSize = new Dimension(insets.right
512: + insets.left, insets.top + insets.bottom);
513: minSize.width += label.getWidth();
514:
515: return minSize;
516: }
517:
518: private static boolean computeIntersection(Rectangle dest,
519: int rx, int ry, int rw, int rh) {
520: int x1 = Math.max(rx, dest.x);
521: int x2 = Math.min(rx + rw, dest.x + dest.width);
522: int y1 = Math.max(ry, dest.y);
523: int y2 = Math.min(ry + rh, dest.y + dest.height);
524: dest.x = x1;
525: dest.y = y1;
526: dest.width = x2 - x1;
527: dest.height = y2 - y1;
528:
529: if (dest.width <= 0 || dest.height <= 0) {
530: return false;
531: }
532: return true;
533: }
534: }
535:
536: protected static class FocusBorder extends AbstractBorder implements
537: FocusListener {
538: private Component comp;
539: private Color focusColor;
540: private boolean focusLostTemporarily = false;
541:
542: public FocusBorder(Component comp) {
543: this .comp = comp;
544:
545: comp.addFocusListener(this );
546:
547: // This is the best guess for a L&F specific color
548: focusColor = UIManager.getColor("TabbedPane.focus");
549: }
550:
551: public void paintBorder(Component c, Graphics g, int x, int y,
552: int width, int height) {
553: if (comp.hasFocus() || focusLostTemporarily) {
554: Color color = g.getColor();
555: g.setColor(focusColor);
556: BasicGraphicsUtils.drawDashedRect(g, x, y, width,
557: height);
558: g.setColor(color);
559: }
560: }
561:
562: public Insets getBorderInsets(Component c) {
563: return getBorderInsets(c, new Insets(0, 0, 0, 0));
564: }
565:
566: public Insets getBorderInsets(Component c, Insets insets) {
567: insets.left = insets.top = insets.right = insets.bottom = 2;
568: return insets;
569: }
570:
571: public void focusGained(FocusEvent e) {
572: comp.repaint();
573: }
574:
575: public void focusLost(FocusEvent e) {
576: // We will still paint focus even if lost temporarily
577: focusLostTemporarily = e.isTemporary();
578: if (!focusLostTemporarily) {
579: comp.repaint();
580: }
581: }
582: }
583: }
|