001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.openide.explorer.view;
043:
044: import java.awt.Color;
045: import java.awt.Component;
046: import java.awt.Dimension;
047: import java.awt.Graphics;
048: import java.awt.Graphics2D;
049: import java.awt.Insets;
050: import java.awt.KeyboardFocusManager;
051: import java.awt.Point;
052: import java.awt.Rectangle;
053: import java.awt.event.ComponentEvent;
054: import java.awt.event.ComponentListener;
055: import java.awt.event.HierarchyBoundsListener;
056: import java.awt.event.HierarchyEvent;
057: import java.awt.event.HierarchyListener;
058: import java.awt.event.MouseAdapter;
059: import java.awt.event.MouseEvent;
060: import java.awt.event.MouseMotionListener;
061: import java.awt.geom.AffineTransform;
062: import java.awt.image.BufferedImage;
063: import java.beans.PropertyChangeEvent;
064: import java.beans.PropertyChangeListener;
065: import javax.swing.JComponent;
066: import javax.swing.JList;
067: import javax.swing.JScrollPane;
068: import javax.swing.JTree;
069: import javax.swing.ListCellRenderer;
070: import javax.swing.Popup;
071: import javax.swing.PopupFactory;
072: import javax.swing.SwingUtilities;
073: import javax.swing.border.Border;
074: import javax.swing.event.ChangeEvent;
075: import javax.swing.event.ChangeListener;
076: import javax.swing.event.ListDataEvent;
077: import javax.swing.event.ListDataListener;
078: import javax.swing.event.ListSelectionEvent;
079: import javax.swing.event.ListSelectionListener;
080: import javax.swing.event.TreeModelEvent;
081: import javax.swing.event.TreeModelListener;
082: import javax.swing.event.TreeSelectionEvent;
083: import javax.swing.event.TreeSelectionListener;
084: import javax.swing.tree.TreePath;
085: import org.openide.util.Lookup;
086: import org.openide.util.Utilities;
087:
088: /**
089: * Displays pseudo-tooltips for tree and list views which don't have enough
090: * space. This class is not NB specific, and can be used with any
091: * JTree or JList.
092: *
093: * @author Tim Boudreau
094: */
095: final class ViewTooltips extends MouseAdapter implements
096: MouseMotionListener {
097: /** The default instance, reference counted */
098: private static ViewTooltips INSTANCE = null;
099: /** A reference count for number of comps listened to */
100: private int refcount = 0;
101: /** The last known component we were invoked against, nulled on hide() */
102: private JComponent inner = null;
103: /** The last row we were invoked against */
104: private int row = -1;
105: /** An array of currently visible popups */
106: private Popup[] popups = new Popup[2];
107: /** A component we'll reuse to paint into the popups */
108: private ImgComp painter = new ImgComp();
109:
110: /** Nobody should instantiate this */
111: private ViewTooltips() {
112: }
113:
114: /**
115: * Register a child of a JScrollPane (only JList or JTree supported
116: * for now) which should show helper tooltips. Should be called
117: * from the component's addNotify() method.
118: */
119: static void register(JComponent comp) {
120: if (INSTANCE == null) {
121: INSTANCE = new ViewTooltips();
122: }
123: INSTANCE.attachTo(comp);
124: }
125:
126: /**
127: * Unregister a child of a JScrollPane (only JList or JTree supported
128: * for now) which should show helper tooltips. Should be called
129: * from the component's removeNotify() method.
130: */
131: static void unregister(JComponent comp) {
132: assert INSTANCE != null : "Unregister asymmetrically called";
133: if (INSTANCE.detachFrom(comp) == 0) {
134: INSTANCE.hide();
135: INSTANCE = null;
136: }
137: }
138:
139: /** Start listening to mouse motion on the passed component */
140: private void attachTo(JComponent comp) {
141: assert comp instanceof JTree || comp instanceof JList;
142: comp.addMouseListener(this );
143: comp.addMouseMotionListener(this );
144: refcount++;
145: }
146:
147: /** Stop listening to mouse motion on the passed component */
148: private int detachFrom(JComponent comp) {
149: assert comp instanceof JTree || comp instanceof JList;
150: comp.removeMouseMotionListener(this );
151: comp.removeMouseListener(this );
152: return refcount--;
153: }
154:
155: public void mouseMoved(MouseEvent e) {
156: Point p = e.getPoint();
157: JComponent comp = (JComponent) e.getSource();
158: JScrollPane jsp = (JScrollPane) SwingUtilities
159: .getAncestorOfClass(JScrollPane.class, comp);
160: if (jsp != null) {
161: p = SwingUtilities.convertPoint(comp, p, jsp);
162: show(jsp, p);
163: }
164: }
165:
166: public void mouseDragged(MouseEvent e) {
167: hide();
168: }
169:
170: public void mouseEntered(MouseEvent e) {
171: hide();
172: }
173:
174: public void mouseExited(MouseEvent e) {
175: hide();
176: }
177:
178: /** Shows the appropriate popups given the state of the scroll pane and
179: * its view.
180: * @param view The scroll pane owning the component the event happened on
181: * @param pt The point at which the mouse event happened, in the coordinate
182: * space of the scroll pane.
183: */
184: void show(JScrollPane view, Point pt) {
185: if (view.getViewport().getView() instanceof JTree) {
186: showJTree(view, pt);
187: } else if (view.getViewport().getView() instanceof JList) {
188: showJList(view, pt);
189: } else {
190: assert false : "Bad component type registered: "
191: + view.getViewport().getView();
192: }
193: }
194:
195: private void showJList(JScrollPane view, Point pt) {
196: JList list = (JList) view.getViewport().getView();
197: Point p = SwingUtilities.convertPoint(view, pt.x, pt.y, list);
198: int row = list.locationToIndex(p);
199: if (row == -1) {
200: hide();
201: return;
202: }
203: Rectangle bds = list.getCellBounds(row, row);
204: //GetCellBounds returns a width that is the
205: //full component width; we want only what
206: //the renderer really needs.
207: ListCellRenderer ren = list.getCellRenderer();
208: Dimension rendererSize = ren.getListCellRendererComponent(list,
209: list.getModel().getElementAt(row), row, false, false)
210: .getPreferredSize();
211:
212: bds.width = rendererSize.width;
213: if (bds == null || !bds.contains(p)) {
214: hide();
215: return;
216: }
217: if (setCompAndRow(list, row)) {
218: Rectangle visible = getShowingRect(view);
219: Rectangle[] rects = getRects(bds, visible);
220: if (rects.length > 0) {
221: ensureOldPopupsHidden();
222: painter.configure(list.getModel().getElementAt(row),
223: view, list, row);
224: showPopups(rects, bds, visible, list, view);
225: } else {
226: hide();
227: }
228: }
229: }
230:
231: private void showJTree(JScrollPane view, Point pt) {
232: JTree tree = (JTree) view.getViewport().getView();
233: Point p = SwingUtilities.convertPoint(view, pt.x, pt.y, tree);
234:
235: int row = tree.getClosestRowForLocation(p.x, p.y);
236:
237: TreePath path = tree.getClosestPathForLocation(p.x, p.y);
238:
239: Rectangle bds = tree.getPathBounds(path);
240: if (bds == null || !bds.contains(p)) {
241: hide();
242: return;
243: }
244: if (setCompAndRow(tree, row)) {
245: Rectangle visible = getShowingRect(view);
246: Rectangle[] rects = getRects(bds, visible);
247: if (rects.length > 0) {
248: ensureOldPopupsHidden();
249: painter.configure(path.getLastPathComponent(), view,
250: tree, path, row);
251: showPopups(rects, bds, visible, tree, view);
252: } else {
253: hide();
254: }
255: }
256: }
257:
258: /**
259: * Set the currently shown component and row, returning true if they are
260: * not the same as the last known values.
261: */
262: private boolean setCompAndRow(JComponent inner, int row) {
263: boolean rowChanged = row != this .row;
264: boolean compChanged = inner != this .inner;
265: this .inner = inner;
266: this .row = row;
267: return (rowChanged || compChanged);
268: }
269:
270: /**
271: * Hide all popups and discard any references to the components the
272: * popups were showing for.
273: */
274: void hide() {
275: ensureOldPopupsHidden();
276: if (painter != null) {
277: painter.clear();
278: }
279: setHideComponent(null, null);
280: inner = null;
281: row = -1;
282: }
283:
284: private void ensureOldPopupsHidden() {
285: for (int i = 0; i < popups.length; i++) {
286: if (popups[i] != null) {
287: popups[i].hide();
288: popups[i] = null;
289: }
290: }
291: }
292:
293: /**
294: * Gets the sub-rectangle of a JScrollPane's area that
295: * is actually showing the view
296: */
297: private Rectangle getShowingRect(JScrollPane pane) {
298: Insets ins1 = pane.getViewport().getInsets();
299: Border inner = pane.getViewportBorder();
300: Insets ins2;
301: if (inner != null) {
302: ins2 = inner.getBorderInsets(pane);
303: } else {
304: ins2 = new Insets(0, 0, 0, 0);
305: }
306: Insets ins3 = new Insets(0, 0, 0, 0);
307: if (pane.getBorder() != null) {
308: ins3 = pane.getBorder().getBorderInsets(pane);
309: }
310:
311: Rectangle r = pane.getViewportBorderBounds();
312: r.translate(-r.x, -r.y);
313: r.width -= ins1.left + ins1.right;
314: r.width -= ins2.left + ins2.right;
315: r.height -= ins1.top + ins1.bottom;
316: r.height -= ins2.top + ins2.bottom;
317: r.x -= ins2.left;
318: r.x -= ins3.left;
319: Point p = pane.getViewport().getViewPosition();
320: r.translate(p.x, p.y);
321: r = SwingUtilities
322: .convertRectangle(pane.getViewport(), r, pane);
323: return r;
324: }
325:
326: /**
327: * Fetches an array or rectangles representing the non-overlapping
328: * portions of a cell rect against the visible portion of the component.
329: * @bds The cell's bounds, in the coordinate space of the tree or list
330: * @vis The visible area of the tree or list, in the tree or list's coordinate space
331: */
332: private static final Rectangle[] getRects(final Rectangle bds,
333: final Rectangle vis) {
334: Rectangle[] result;
335: if (vis.contains(bds)) {
336: result = new Rectangle[0];
337: } else {
338: if (bds.x < vis.x && bds.x + bds.width > vis.x + vis.width) {
339: Rectangle a = new Rectangle(bds.x, bds.y,
340: vis.x - bds.x, bds.height);
341: Rectangle b = new Rectangle(vis.x + vis.width, bds.y,
342: (bds.x + bds.width) - (vis.x + vis.width),
343: bds.height);
344: result = new Rectangle[] { a, b };
345: } else if (bds.x < vis.x) {
346: result = new Rectangle[] { new Rectangle(bds.x, bds.y,
347: vis.x - bds.x, bds.height) };
348: } else if (bds.x + bds.width > vis.x + vis.width) {
349: result = new Rectangle[] { new Rectangle(vis.x
350: + vis.width, bds.y, (bds.x + bds.width)
351: - (vis.x + vis.width), bds.height) };
352: } else {
353: result = new Rectangle[0];
354: }
355: }
356: return result;
357: }
358:
359: /**
360: * Show popups for each rectangle, using the now configured painter.
361: */
362: private void showPopups(Rectangle[] rects, Rectangle bds,
363: Rectangle visible, JComponent comp, JScrollPane view) {
364: boolean shown = false;
365: for (int i = 0; i < rects.length; i++) {
366: Rectangle sect = rects[i];
367: sect.translate(-bds.x, -bds.y);
368: ImgComp part = painter.getPartial(sect,
369: bds.x + rects[i].x < visible.x);
370: Point pos = new Point(bds.x + rects[i].x, bds.y
371: + rects[i].y);
372: SwingUtilities.convertPointToScreen(pos, comp);
373: if (comp instanceof JList) {
374: //XXX off by one somewhere, only with JLists - where?
375: pos.y--;
376: }
377: if (pos.x > 0) { //Mac OS will reposition off-screen popups to x=0,
378: //so don't try to show them
379: popups[i] = getPopupFactory().getPopup(view, part,
380: pos.x, pos.y);
381: popups[i].show();
382: shown = true;
383: }
384: }
385: if (shown) {
386: setHideComponent(comp, view);
387: } else {
388: setHideComponent(null, null); //clear references
389: }
390: }
391:
392: private static PopupFactory getPopupFactory() {
393: if (Utilities.isMac()) {
394:
395: // See ide/applemenu/src/org/netbeans/modules/applemenu/ApplePopupFactory
396: // We have a custom PopupFactory that will consistently use
397: // lightweight popups on Mac OS, since HW popups get a drop
398: // shadow. By default, popups returned when a heavyweight popup
399: // is needed (SDI mode) are no-op popups, since some hacks
400: // are necessary to make it really work.
401:
402: // To enable heavyweight popups which have no drop shadow
403: // *most* of the time on mac os, run with
404: // -J-Dnb.explorer.hw.completions=true
405:
406: // To enable heavyweight popups which have no drop shadow
407: // *ever* on mac os, you need to put the cocoa classes on the
408: // classpath - modify netbeans.conf to add
409: // System/Library/Java on the bootclasspath. *Then*
410: // run with the above line switch and
411: // -J-Dnb.explorer.hw.cocoahack=true
412:
413: PopupFactory result = (PopupFactory) Lookup.getDefault()
414: .lookup(PopupFactory.class);
415: return result == null ? PopupFactory.getSharedInstance()
416: : result;
417: } else {
418: return PopupFactory.getSharedInstance();
419: }
420: }
421:
422: private Hider hider = null;
423:
424: /**
425: * Set a component (JList or JTree) which should be listened to, such that if
426: * a model, selection or scroll event occurs, all currently open popups
427: * should be hidden.
428: */
429: private void setHideComponent(JComponent comp, JScrollPane parent) {
430: if (hider != null) {
431: if (hider.isListeningTo(comp)) {
432: return;
433: }
434: }
435: if (hider != null) {
436: hider.detach();
437: }
438: if (comp != null) {
439: hider = new Hider(comp, parent);
440: } else {
441: hider = null;
442: }
443: }
444:
445: /**
446: * A JComponent which creates a BufferedImage of a cell renderer and can
447: * produce clones of itself that display subrectangles of that cell
448: * renderer.
449: */
450: private static final class ImgComp extends JComponent {
451: private BufferedImage img;
452: private Dimension d = null;
453:
454: private Color bg = Color.WHITE;
455: private JScrollPane comp = null;
456:
457: private Object node = null;
458:
459: private AffineTransform at = AffineTransform
460: .getTranslateInstance(0d, 0d);
461: boolean isRight = false;
462:
463: ImgComp() {
464: }
465:
466: /**
467: * Create a clone with a specified backing image
468: */
469: ImgComp(BufferedImage img, Rectangle off, boolean right) {
470: this .img = img;
471: at = AffineTransform.getTranslateInstance(-off.x, 0);
472: d = new Dimension(off.width, off.height);
473: isRight = right;
474: }
475:
476: public ImgComp getPartial(Rectangle bds, boolean right) {
477: assert img != null;
478: return new ImgComp(img, bds, right);
479: }
480:
481: /** Configures a tree cell renderer and sets up sizing and the
482: * backing image from it */
483: public boolean configure(Object nd, JScrollPane tv, JTree tree,
484: TreePath path, int row) {
485: boolean sameVn = setLastRendereredObject(nd);
486: boolean sameComp = setLastRenderedScrollPane(tv);
487: Component renderer = null;
488: bg = tree.getBackground();
489: boolean sel = tree.isSelectionEmpty() ? false : tree
490: .getSelectionModel().isPathSelected(path);
491: boolean exp = tree.isExpanded(path);
492: boolean leaf = !exp && tree.getModel().isLeaf(nd);
493: boolean lead = path.equals(tree.getSelectionModel()
494: .getLeadSelectionPath());
495: renderer = tree.getCellRenderer()
496: .getTreeCellRendererComponent(tree, nd, sel, exp,
497: leaf, row, lead);
498: if (renderer != null) {
499: setComponent(renderer);
500: }
501: return true;
502: }
503:
504: /** Configures a list cell renderer and sets up sizing and the
505: * backing image from it */
506: public boolean configure(Object nd, JScrollPane tv, JList list,
507: int row) {
508: boolean sameVn = setLastRendereredObject(nd);
509: boolean sameComp = setLastRenderedScrollPane(tv);
510: Component renderer = null;
511: bg = list.getBackground();
512: boolean sel = list.isSelectionEmpty() ? false : list
513: .getSelectionModel().isSelectedIndex(row);
514: renderer = list.getCellRenderer()
515: .getListCellRendererComponent(list, nd, row, sel,
516: false);
517: if (renderer != null) {
518: setComponent(renderer);
519: }
520: return true;
521: }
522:
523: private boolean setLastRenderedScrollPane(JScrollPane comp) {
524: boolean result = comp != this .comp;
525: this .comp = comp;
526: return result;
527: }
528:
529: private boolean setLastRendereredObject(Object nd) {
530: boolean result = node != nd;
531: if (result) {
532: node = nd;
533: }
534: return result;
535: }
536:
537: void clear() {
538: comp = null;
539: node = null;
540: }
541:
542: /**
543: * Set the cell renderer we will proxy.
544: */
545: public void setComponent(Component jc) {
546: Dimension d = jc.getPreferredSize();
547: BufferedImage nue = new BufferedImage(d.width,
548: d.height + 2, BufferedImage.TYPE_INT_ARGB_PRE);
549: SwingUtilities.paintComponent(nue.getGraphics(), jc, this ,
550: 0, 0, d.width, d.height + 2);
551: setImage(nue);
552: }
553:
554: public Rectangle getBounds() {
555: Dimension dd = getPreferredSize();
556: return new Rectangle(0, 0, dd.width, dd.height);
557: }
558:
559: private void setImage(BufferedImage img) {
560: this .img = img;
561: d = null;
562: }
563:
564: public Dimension getPreferredSize() {
565: if (d == null) {
566: d = new Dimension(img.getWidth(), img.getHeight());
567: }
568: return d;
569: }
570:
571: public Dimension getSize() {
572: return getPreferredSize();
573: }
574:
575: public void paint(Graphics g) {
576: g.setColor(bg);
577: g.fillRect(0, 0, d.width, d.height);
578: Graphics2D g2d = (Graphics2D) g;
579: g2d.drawRenderedImage(img, at);
580: g.setColor(Color.GRAY);
581: g.drawLine(0, 0, d.width, 0);
582: g.drawLine(0, d.height - 1, d.width, d.height - 1);
583: if (isRight) {
584: g.drawLine(0, 0, 0, d.height - 1);
585: } else {
586: g.drawLine(d.width - 1, 0, d.width - 1, d.height - 1);
587: }
588: }
589:
590: public void firePropertyChange(String s, Object a, Object b) {
591: }
592:
593: public void invalidate() {
594: }
595:
596: public void validate() {
597: }
598:
599: public void revalidate() {
600: }
601: }
602:
603: /**
604: * A listener that listens to just about everything in the known universe
605: * and hides all currently displayed popups if anything happens.
606: */
607: private static final class Hider implements ChangeListener,
608: PropertyChangeListener, TreeModelListener,
609: TreeSelectionListener, HierarchyListener,
610: HierarchyBoundsListener, ListSelectionListener,
611: ListDataListener, ComponentListener {
612: private final JTree tree;
613:
614: private JScrollPane pane;
615: private final JList list;
616:
617: public Hider(JComponent comp, JScrollPane pane) {
618: if (comp instanceof JTree) {
619: this .tree = (JTree) comp;
620: this .list = null;
621: } else {
622: this .list = (JList) comp;
623: this .tree = null;
624: }
625: assert tree != null || list != null;
626: this .pane = pane;
627: attach();
628: }
629:
630: private boolean isListeningTo(JComponent comp) {
631: return !detached && (comp == list || comp == tree);
632: }
633:
634: private void attach() {
635: if (tree != null) {
636: tree.getModel().addTreeModelListener(this );
637: tree.getSelectionModel().addTreeSelectionListener(this );
638: tree.addHierarchyBoundsListener(this );
639: tree.addHierarchyListener(this );
640: tree.addComponentListener(this );
641: } else {
642: list.getSelectionModel().addListSelectionListener(this );
643: list.getModel().addListDataListener(this );
644: list.addHierarchyBoundsListener(this );
645: list.addHierarchyListener(this );
646: list.addComponentListener(this );
647: }
648: pane.getHorizontalScrollBar().getModel().addChangeListener(
649: this );
650: pane.getVerticalScrollBar().getModel().addChangeListener(
651: this );
652: KeyboardFocusManager.getCurrentKeyboardFocusManager()
653: .addPropertyChangeListener(this );
654: }
655:
656: private boolean detached = false;
657:
658: private void detach() {
659: KeyboardFocusManager.getCurrentKeyboardFocusManager()
660: .removePropertyChangeListener(this );
661: if (tree != null) {
662: tree.getSelectionModel().removeTreeSelectionListener(
663: this );
664: tree.getModel().removeTreeModelListener(this );
665: tree.removeHierarchyBoundsListener(this );
666: tree.removeHierarchyListener(this );
667: tree.removeComponentListener(this );
668: } else {
669: list.getSelectionModel().removeListSelectionListener(
670: this );
671: list.getModel().removeListDataListener(this );
672: list.removeHierarchyBoundsListener(this );
673: list.removeHierarchyListener(this );
674: list.removeComponentListener(this );
675: }
676: pane.getHorizontalScrollBar().getModel()
677: .removeChangeListener(this );
678: pane.getVerticalScrollBar().getModel()
679: .removeChangeListener(this );
680: detached = true;
681: }
682:
683: private void change() {
684: if (ViewTooltips.INSTANCE != null) {
685: ViewTooltips.INSTANCE.hide();
686: }
687: detach();
688: }
689:
690: public void propertyChange(PropertyChangeEvent evt) {
691: change();
692: }
693:
694: public void treeNodesChanged(TreeModelEvent e) {
695: change();
696: }
697:
698: public void treeNodesInserted(TreeModelEvent e) {
699: change();
700: }
701:
702: public void treeNodesRemoved(TreeModelEvent e) {
703: change();
704: }
705:
706: public void treeStructureChanged(TreeModelEvent e) {
707: change();
708: }
709:
710: public void hierarchyChanged(HierarchyEvent e) {
711: change();
712: }
713:
714: public void valueChanged(TreeSelectionEvent e) {
715: change();
716: }
717:
718: public void ancestorMoved(HierarchyEvent e) {
719: change();
720: }
721:
722: public void ancestorResized(HierarchyEvent e) {
723: change();
724: }
725:
726: public void stateChanged(ChangeEvent e) {
727: change();
728: }
729:
730: public void valueChanged(ListSelectionEvent e) {
731: change();
732: }
733:
734: public void intervalAdded(ListDataEvent e) {
735: change();
736: }
737:
738: public void intervalRemoved(ListDataEvent e) {
739: change();
740: }
741:
742: public void contentsChanged(ListDataEvent e) {
743: change();
744: }
745:
746: public void componentResized(ComponentEvent e) {
747: change();
748: }
749:
750: public void componentMoved(ComponentEvent e) {
751: change();
752: }
753:
754: public void componentShown(ComponentEvent e) {
755: change();
756: }
757:
758: public void componentHidden(ComponentEvent e) {
759: change();
760: }
761: }
762: }
|