Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/imagero/gui/swing/TLToolTipManager.java


1   /*
2    * Copyright (c) imagero Andrey Kuznetsov. All Rights Reserved.
3    * http://jgui.imagero.com
4    *
5    * This program is free software; you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation; either version 2, or (at your option)
8    * any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with this program; if not, write to the Free Software
17   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18   */
19  
20  package com.imagero.gui.swing;
21  
22  import javax.swing.*;
23  import javax.swing.border.*;
24  import javax.swing.event.*;
25  import javax.swing.tree.*;
26  import java.awt.*;
27  import java.awt.event.*;
28  import java.awt.image.*;
29  
30  /**
31   * <pre>
32   * TLToolTipManager.java
33   * ToolTipManager for both J<b>T</b>ree and J<b>L</b>ist.
34   * Usage:
35   *  new TLToolTipManager(JTree) or new TLToolTipManager(JList);
36   *  Note - there is no need to keep reference to TLToolTipManager objects.
37   * </pre>
38   * @author Andrei Kouznetsov
39   * @version 1.3
40   */
41  public class TLToolTipManager {
42  
43      public static final int UNDEFINED_ROW = -1;
44  
45      int lastRow = -1;
46      TipWindow window;
47      JComponent owner;
48      boolean showFullTip;
49  
50      JToolTip tip = new JToolTip();
51  
52      Rectangle visibleRect = new Rectangle();
53  
54      Rectangle rowBounds = new Rectangle();
55      Border lb = new LineBorder(Color.gray);
56  
57      /**
58       * create new TLToolTipManager for supplied JTree
59       * @param tree JTree
60       */
61      public TLToolTipManager(JTree tree) {
62          this(tree, false);
63      }
64  
65      /**
66       * create new TLToolTipManager for supplied JTree
67       * @param tree JTree
68       * @param showFullTip if true then full tooltip shown,
69       * otherwise only missed part shown (this is preferred way)
70       */
71      public TLToolTipManager(JTree tree, boolean showFullTip) {
72          this.owner = tree;
73          this.showFullTip = showFullTip;
74  
75          MouseHandler tmh = new MouseHandler();
76          this.owner.addMouseListener(tmh);
77          this.owner.addMouseMotionListener(tmh);
78  
79          TipMouseHandler tmh2 = new TipMouseHandler();
80          this.tip.addMouseListener(tmh2);
81          this.tip.addMouseMotionListener(tmh2);
82          owner.addComponentListener(new OwnerListener());
83      }
84  
85      /**
86       * create new TLToolTipManager for supplied JList
87       * @param list JList
88       */
89      public TLToolTipManager(JList list) {
90          this(list, false);
91      }
92  
93      /**
94       * create new TLToolTipManager for supplied JList
95       * @param list JList
96       * @param showFullTip if true then full tooltip shown,
97       * otherwise only missed part shown (this is preferred way)
98       */
99      public TLToolTipManager(JList list, boolean showFullTip) {
100         this.owner = list;
101         this.showFullTip = showFullTip;
102 
103         MouseHandler tmh = new MouseHandler();
104         this.owner.addMouseListener(tmh);
105         this.owner.addMouseMotionListener(tmh);
106 
107         TipMouseHandler tmh2 = new TipMouseHandler();
108         this.tip.addMouseListener(tmh2);
109         this.tip.addMouseMotionListener(tmh2);
110         owner.addComponentListener(new OwnerListener());
111     }
112 
113     /**
114      * set lastRow to UNDEFINED_ROW
115      * @see #UNDEFINED_ROW
116      *
117      */
118     private void resetRow() {
119         lastRow = UNDEFINED_ROW;
120     }
121 
122     /**
123      * Get TipWindow. If TipWindow is null then new one is created.
124      * @return JWindow
125      */
126     protected JWindow getTipWindow() {
127         if (this.window == null) {
128             Window w = SwingUtilities.getWindowAncestor(owner);
129             this.window = new TipWindow(w);
130         }
131         return this.window;
132     }
133 
134     /**
135      * TipWindow. Used for showing tooltip.
136      */
137     static class TipWindow extends JWindow {
138         BufferedImage bi;
139         CellRendererPane pane;
140 
141         int xOffset;
142 
143         public TipWindow(Window owner) {
144             super(owner);
145             pane = new CellRendererPane();
146 //            getContentPane().add(pane);
147         }
148 
149         public void setXOffset(int offset) {
150             this.xOffset = offset;
151             if (bi != null) {
152                 setSize(bi.getWidth(), bi.getHeight());
153             }
154         }
155 
156         void setImage(BufferedImage bi) {
157             this.bi = bi;
158             if (bi != null) {
159                 setSize(bi.getWidth(), bi.getHeight());
160             }
161             repaint();
162         }
163 
164         public void paint(Graphics g) {
165             g.setColor(Color.white);
166             g.fillRect(0, 0, getWidth(), getHeight());
167             if (bi != null) {
168                 g.drawImage(bi, 0 - (bi.getWidth() - xOffset), 0, null);
169             }
170         }
171     }
172 
173     /**
174      * Get row for given location.
175      * For JTree it calls JTree#getRowForLocation, for JList - JList#locationToIndex
176      * @param comp JTree or JList
177      * @param p Point
178      * @return row for given point
179      * @see JTree#getRowForLocation
180      * @see JList#locationToIndex
181      */
182     private int getRow(JComponent comp, Point p) {
183         if (comp instanceof JTree) {
184             return ((JTree) comp).getRowForLocation(p.x, p.y);
185         }
186         else if (comp instanceof JList) {
187             return ((JList) comp).locationToIndex(p);
188         }
189         throw new RuntimeException("JComponent shouls be JList or JTree");
190     }
191 
192     /**
193      * Get bounds for given row.<br>
194      * If comp is instance of JTree then bounds are determined by call to JTree#getRowBounds.<br>
195      * If comp is instance of JList then bounds are determined by call to JList#getCellBounds<br>
196      * @param comp JTree or JList
197      * @param row row number
198      * @return  bounds of given row (Rectangle)
199      * @see JTree#getRowBounds
200      * @see JList#getCellBounds
201      */
202     private Rectangle getRowBounds(JComponent comp, int row) {
203         if (comp instanceof JTree) {
204             return ((JTree) comp).getRowBounds(row);
205         }
206         else if (comp instanceof JList) {
207             return ((JList) comp).getCellBounds(row, row);
208         }
209         throw new RuntimeException("JComponent shouls be JList or JTree");
210     }
211 
212     /**
213      * get start of label's text. <br>
214      * Should the ComponentOrientation be checked here?
215      */
216     static int getLabelStart(JLabel label) {
217         Icon currentI = label.getIcon();
218         if (currentI != null && label.getText() != null) {
219             return currentI.getIconWidth() + Math.max(0, label.getIconTextGap() - 4);
220         }
221         return 0;
222     }
223 
224     /**
225      * MouseHandler.<br>
226      * MouseListener for TLToolTipManager.
227      */
228     class MouseHandler extends MouseInputAdapter {
229 
230         public void mousePressed(MouseEvent e) {
231             resetRow();
232             mouseMoved(e);
233         }
234 
235         public void mouseMoved(MouseEvent e) {
236             JComponent component = (JComponent) e.getSource();
237 
238             Point p = e.getPoint();
239 
240             int row = getRow(component, p);
241 
242             if (row == -1) {
243                 resetRow();
244                 hideTipWindow();
245                 return;
246             }
247 
248             Rectangle rowBounds = getRowBounds(component, row);
249 
250             if (!rowBounds.contains(p)) {
251                 hideTipWindow();
252                 resetRow();
253                 return;
254             }
255 
256             Rectangle vr = computeVisibleRect();
257 
258             if (vr.contains(rowBounds)) {
259                 lastRow = row;
260                 hideTipWindow();
261                 return;
262             }
263 
264             // row does not changed
265             if (row == lastRow) {
266                 return;
267             }
268 
269             lastRow = row;
270             hideTipWindow();
271 
272             Component label = getRenderer(component, row, rowBounds);
273 
274             if (vr.contains(rowBounds)) {
275                 hideTipWindow();
276                 return;
277             }
278 
279             createImage(rowBounds, label, component);
280 
281             Point screenLocation = new Point(rowBounds.x, rowBounds.y);
282             SwingUtilities.convertPointToScreen(screenLocation, component);
283 
284             getTipWindow().getContentPane().add(tip);
285 
286             if (showFullTip) {
287                 window.setXOffset(rowBounds.width);
288                 window.setLocation(screenLocation.x, screenLocation.y);
289             }
290             else {
291                 final int offset = (rowBounds.width + rowBounds.x) - (vr.width + vr.x);
292                 window.setXOffset(offset);
293                 window.setBounds(screenLocation.x + rowBounds.width - offset, screenLocation.y, offset, rowBounds.height);
294             }
295             window.show();
296         }
297 
298         private Component getRenderer(JComponent comp, int row, Rectangle rowBounds) {
299             if (comp instanceof JTree) {
300                 JTree tree = (JTree) comp;
301                 TreePath tp = tree.getPathForRow(row);
302                 DefaultMutableTreeNode value = (DefaultMutableTreeNode) tp.getLastPathComponent();
303 
304                 boolean isSelected = tree.isRowSelected(row);
305                 TreeCellRenderer renderer = tree.getCellRenderer();
306 
307                 Component label = renderer.getTreeCellRendererComponent(
308                         tree, value, isSelected, !tree.isCollapsed(row), value.isLeaf(), row, false);
309                 return label;
310             }
311             else if (comp instanceof JList) {
312                 JList list = (JList) comp;
313                 boolean isSelected = list.isSelectedIndex(row);
314                 ListCellRenderer renderer = list.getCellRenderer();
315 
316                 Object value = list.getModel().getElementAt(row);
317                 String tipText = String.valueOf(value);
318 
319                 tip.setTipText(tipText);
320 
321 //                FontMetrics fm = tip.getFontMetrics(tip.getFont());
322 //                int strlen = fm.stringWidth(tipText);
323 
324                 Component label = renderer.getListCellRendererComponent(
325                         list, value, row, isSelected, false);
326 
327 //                int labelStart = getLabelStart(label);
328 
329 //                rowBounds.x += labelStart;
330                 rowBounds.width = label.getPreferredSize().width;//strlen + 20;
331                 return label;
332             }
333             throw new RuntimeException("JComponent shouls be JList or JTree");
334         }
335 
336         /**
337          * Create buffer image.<br>
338          *
339          * @param rowBounds row bounds
340          * @param label renderer component
341          * @param cont Container (JTree or JList)
342          * @see #getRowBounds
343          * @see #getRenderer
344          */
345         private void createImage(Rectangle rowBounds, Component label, Container cont) {
346             int w = rowBounds.width;
347             int h = rowBounds.height;
348             BufferedImage bi = window.bi;
349             if (w > 0 && h > 0) {
350                 window.getContentPane().add(label);
351                 window.validate();
352                 if (bi == null || bi.getWidth() < w || bi.getHeight() != h) {
353                     /*BufferedImage */bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
354                 }
355             }
356             Graphics2D g = bi.createGraphics();
357             g.setColor(label.getBackground());
358             g.fillRect(0, 0, bi.getWidth(), bi.getHeight());
359             lb.paintBorder(label, g, bi.getWidth() - w, 0, w, h);
360             g.setClip(1, 1, bi.getWidth() - 2, bi.getHeight() - 2);
361             window.pane.paintComponent(g, label, cont, bi.getWidth() - w, 0, w, h, true);
362             g.dispose();
363 
364             window.setImage(bi);
365         }
366 
367         public void mouseExited(MouseEvent e) {
368             Rectangle vr = computeVisibleRect();
369             if (vr.contains(e.getPoint())) {
370                 return;
371             }
372             hideTipWindow();
373         }
374     }
375 
376     /**
377      * Optimized computing of visible rectangle (without creating new Rectangle Object every time)
378      * @return visible rectangle
379      */
380     protected Rectangle computeVisibleRect() {
381         owner.computeVisibleRect(visibleRect);
382         return visibleRect;
383     }
384 
385     /**
386      * hide TipWindow
387      */
388     void hideTipWindow() {
389         getTipWindow().setVisible(false);
390     }
391 
392     /**
393      * MouseListener for TLToolTipManager
394      */
395     protected class TipMouseHandler extends MouseInputAdapter {
396 
397         public void mouseExited(MouseEvent e) {
398             hideTipWindow();
399             resetRow();
400         }
401 
402         public void mousePressed(MouseEvent e) {
403             resetRow();
404             Point p = e.getPoint();
405             Point nps = new Point(p);
406 
407             SwingUtilities.convertPointToScreen(nps, tip);
408             Point np = new Point(nps);
409 
410             SwingUtilities.convertPointFromScreen(np, owner);
411 
412             hideTipWindow();
413 
414             MouseEvent ne = new MouseEvent(owner, e.getID(), e.getWhen(), e.getModifiers(),
415                     np.x, np.y, e.getClickCount(), e.isPopupTrigger()
416             );
417             computeVisibleRect();
418             if (visibleRect.contains(np)) {
419                 owner.dispatchEvent(ne);
420             }
421         }
422 
423         public void mouseMoved(MouseEvent e) {
424             Point p = e.getPoint();
425             Rectangle vr = computeVisibleRect();
426             Point rp = new Point(vr.x, vr.y);
427             SwingUtilities.convertPointToScreen(rp, owner);
428             SwingUtilities.convertPointToScreen(p, tip);
429             vr = new Rectangle(rp.x, rp.y, vr.width, vr.height);
430             if (!vr.contains(p)) {
431                 hideTipWindow();
432                 resetRow();
433             }
434         }
435     }
436 
437     /**
438      * OwnerListener hides TipWindow if Component is resized, moved or made invisible.
439      */
440     private class OwnerListener extends ComponentAdapter {
441         public void componentResized(ComponentEvent e) {
442             hideTipWindow();
443         }
444 
445         public void componentMoved(ComponentEvent e) {
446             hideTipWindow();
447         }
448 
449         public void componentHidden(ComponentEvent e) {
450             hideTipWindow();
451         }
452     }
453 }