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