View Javadoc

1   /*
2   Wotonomy: OpenStep design patterns for pure Java applications.
3   Copyright (C) 2000 Intersect Software Corporation
4   
5   This library is free software; you can redistribute it and/or
6   modify it under the terms of the GNU Lesser General Public
7   License as published by the Free Software Foundation; either
8   version 2.1 of the License, or (at your option) any later version.
9   
10  This library 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 GNU
13  Lesser General Public License for more details.
14  
15  You should have received a copy of the GNU Lesser General Public
16  License along with this library; if not, see http://www.gnu.org
17  */
18  
19  package net.wotonomy.ui.swing.components;
20  
21  import java.awt.Color;
22  import java.awt.Component;
23  import java.awt.GridBagConstraints;
24  import java.awt.GridBagLayout;
25  import java.awt.Insets;
26  import java.awt.Point;
27  import java.awt.event.ActionEvent;
28  import java.awt.event.ActionListener;
29  import java.awt.event.MouseEvent;
30  import java.awt.event.MouseListener;
31  import java.util.Enumeration;
32  import java.util.EventObject;
33  import java.util.Vector;
34  
35  import javax.swing.Icon;
36  import javax.swing.JButton;
37  import javax.swing.JComponent;
38  import javax.swing.JLabel;
39  import javax.swing.JList;
40  import javax.swing.JPanel;
41  import javax.swing.JTable;
42  import javax.swing.JTree;
43  import javax.swing.ListCellRenderer;
44  import javax.swing.SwingUtilities;
45  import javax.swing.UIManager;
46  import javax.swing.border.Border;
47  import javax.swing.border.EmptyBorder;
48  import javax.swing.border.LineBorder;
49  import javax.swing.event.CellEditorListener;
50  import javax.swing.event.ChangeEvent;
51  import javax.swing.table.TableCellEditor;
52  import javax.swing.table.TableCellRenderer;
53  import javax.swing.tree.TreeCellEditor;
54  import javax.swing.tree.TreeCellRenderer;
55  
56  /***
57  * A cell renderer that displays icons in addition to text,
58  * and additionally is an editor in case you want to click
59  * the icon to trigger some kind of action.  
60  * You probably should override both getStringForContext and
61  * getIconForContext to achieve your desired results. 
62  * To receive mouse clicks, set the same instance of the
63  * renderer as the editor for the same component.<br><br>
64  *
65  * One notable addition is that this class is an action event
66  * broadcaster.  ActionEvents are broadcast when the mouse is
67  * clicked on the button with an action event containing a 
68  * user-configurable string that defaults to CLICKED. <br><br>
69  *
70  * The renderer itself can be used as a JComponent if
71  * you need something like a JLabel that allows you to click
72  * on the icon.  You will want to call setIcon and setText
73  * to configure the component since the renderer method would
74  * not be called.  (If you add an instance of the renderer
75  * to a container, you cannnot use the same instance as an
76  * editor in a table, tree, or list.)
77  *
78  * @author michael@mpowers.net
79  * @version $Revision: 904 $
80  * $Date: 2006-02-18 23:19:05 +0000 (Sat, 18 Feb 2006) $
81  */
82  public class IconCellRenderer extends JPanel
83  	implements TableCellRenderer, TableCellEditor, 
84          TreeCellRenderer, TreeCellEditor, ListCellRenderer,
85          Runnable, ActionListener, MouseListener 
86  {	
87  	public static final String CLICKED = "CLICKED";
88  	
89  	/***
90  	* The panel that is re-used to render everything.
91  	* This is returned by getRendererForContext.
92  	*/
93      protected JPanel rendererPanel;
94  	protected JLabel rendererLabel;
95      protected JButton rendererButton;
96  
97  	/***
98  	* The panel that is used to receive mouse clicks.
99  	* It must be a different component from rendererPanel.
100 	* This is returned by getEditorForContext.
101 	*/
102     protected JPanel editorPanel;
103 	protected JLabel editorLabel;
104     protected JButton editorButton;
105 	
106 	private Object lastKnownValue;
107     private JComponent lastKnownComponent;
108 		
109 	// do as DefaultTableCellRenderer does
110 	private Border noFocusBorder;
111 	private Border treeFocusBorder;
112 	private Color unselectedForeground;
113 	private Color unselectedBackground;
114 	
115 	private Vector actionListeners;
116 	private String actionCommand;
117 	private Vector cellEditorListeners;
118     
119     private boolean editable;
120     private boolean clickable;
121     				
122     /***
123     * Default constructor.
124     */
125     public IconCellRenderer()
126     {                                                
127         editable = true;
128         clickable = true;
129         
130         noFocusBorder = new EmptyBorder(1, 1, 1, 1);
131         treeFocusBorder = new LineBorder( 
132             UIManager.getColor("Tree.selectionBorderColor") );
133 		setActionCommand( CLICKED );
134 
135         rendererPanel = new JPanel();
136         rendererPanel.setLayout( new GridBagLayout() );
137 
138         editorPanel = this;
139         editorPanel.setLayout( new GridBagLayout() );
140                         
141         // set up constraints
142         GridBagConstraints imageConstraints = new GridBagConstraints();
143         imageConstraints.gridx = 0;
144         GridBagConstraints labelConstraints = new GridBagConstraints();
145         labelConstraints.fill = GridBagConstraints.HORIZONTAL;
146         labelConstraints.gridx = 1;
147         labelConstraints.weightx = 1.0;
148         labelConstraints.ipadx = 1;
149         labelConstraints.insets = new Insets( 0, 1, 0, 0 ); // sweat the pixel
150 
151         // make the editor panel go away when not in use
152         // and pass through all mouse events to container        
153         
154         //this is not very useful since editorLabel and editorButton
155         //get all of the events        
156         editorPanel.addMouseListener( this );
157         
158 		rendererLabel = new JLabel();
159 		rendererLabel.setOpaque( false );
160         rendererPanel.add( rendererLabel, labelConstraints );
161 
162 		editorLabel = new JLabel();
163         editorLabel.setText( "" ); // default state
164 		editorLabel.setOpaque( false );
165         editorPanel.add( editorLabel, labelConstraints );
166 
167 		unselectedForeground = rendererLabel.getForeground();
168 		unselectedBackground = rendererLabel.getBackground();
169         
170         rendererButton = new JButton();
171         rendererButton.setBorder( null );
172         rendererButton.setBorderPainted( false );
173         rendererButton.setContentAreaFilled( false );
174         rendererButton.setFocusPainted( false );
175         rendererButton.setMargin( new Insets( 0, 0, 0, 0 ) );
176         rendererPanel.add( rendererButton, imageConstraints );
177         
178         editorButton = new JButton();
179         editorButton.setEnabled( clickable ); // default state
180         editorButton.setIcon( null ); // default state
181         editorButton.setBorder( null );
182         editorButton.setBorderPainted( false );                                            
183         editorButton.setContentAreaFilled( false );
184         editorButton.setFocusPainted( false );
185         editorButton.setMargin( new Insets( 0, 0, 0, 0 ) );        
186         editorPanel.add( editorButton, imageConstraints );
187 
188         editorButton.addActionListener( this );      
189 
190         //add these in order to dispatch the MouseEvents
191         //to the lastKnownComponent, and proper management of
192         //DnD operations
193         editorLabel.addMouseListener( this );    
194         editorButton.addMouseListener( this );                    
195     }
196     
197 /***
198 * Returns the text string currently displayed in the editor component.
199 */    
200     public String getText() 
201     {
202         return editorLabel.getText();
203     }
204     
205 /***
206 * Sets the text string displayed in the editor component.
207 * Default is an empty string.
208 */
209     public void setText( String aString )
210     {
211         editorLabel.setText( aString );
212     }
213 
214 /***
215 * Returns the icon currently displayed in the editor component.
216 */    
217     public Icon getIcon() 
218     {
219         return editorButton.getIcon();
220     }
221     
222 /***
223 * Sets the icon currently displayed in the editor component.
224 * Default is null.
225 */
226     public void setIcon( Icon anIcon )
227     {
228         editorButton.setIcon( anIcon );
229         if ( !isClickable() )
230         {
231             editorButton.setDisabledIcon( anIcon );
232         }
233     }
234 
235 /***
236 * Returns whether the editor component's label text is editable.
237 */    
238     public boolean isEditable() 
239     {
240         return editable;
241     }
242     
243 /***
244 * Sets whether the editor component's label text is editable.
245 * Default is true.  Editable text is not yet implemented.
246 */
247     public void setEditable( boolean isEditable )
248     {
249         editable = isEditable;
250     }
251 
252 /***
253 * Returns whether the editor component's icon is clickable.
254 */    
255     public boolean isClickable() 
256     {
257         return clickable;
258     }
259     
260 /***
261 * Sets whether the editor component's icon is clickable.
262 * Default is true.
263 */
264     public void setClickable( boolean isClickable )
265     {
266         clickable = isClickable;
267         editorButton.setEnabled( clickable );
268     }
269 
270 /***
271 * Returns the component from getRendererForContext.
272 */    
273     public Component getListCellRendererComponent(JList list,
274                                               Object value,
275                                               int index,
276                                               boolean isSelected,
277                                               boolean cellHasFocus)
278     {
279         lastKnownComponent = list;
280         return getRendererForContext(
281             list, value, index, 0, isSelected, cellHasFocus, false, true );
282     }
283 
284 /***
285 * Returns the component from getRendererForContext.
286 */
287     public Component getTableCellRendererComponent(JTable table, Object value,
288                      boolean isSelected, boolean hasFocus, int row, int column)
289     {
290         lastKnownComponent = table;
291 		return getRendererForContext( 
292 			table, value, row, column, isSelected, hasFocus, false, true );
293     }
294 
295 /***
296 * Returns the component from getRendererForContext.
297 */
298     public Component getTreeCellRendererComponent(JTree tree,
299                                               Object value,
300                                               boolean selected,
301                                               boolean expanded,
302                                               boolean leaf,
303                                               int row,
304                                               boolean hasFocus)
305     {                
306         lastKnownComponent = tree;
307 		return getRendererForContext( 
308 			tree, value, row, 0, selected, hasFocus, expanded, leaf );
309     }
310 	
311 /***
312 * Returns getEditorForContext with the same parameters with hasFocus true.
313 */
314 	public Component getTableCellEditorComponent(JTable table,
315 		Object value, boolean isSelected, int row, int column)
316 	{
317 		lastKnownValue = value;
318         lastKnownComponent = table;
319 		return getEditorForContext( 
320 			table, value, row, column, isSelected, true, false, true );
321 	}
322 
323 /***
324 * Returns the component from getEditorForContext with hasFocus true.
325 */
326     public Component getTreeCellEditorComponent(JTree tree,
327                                                 Object value,
328                                                 boolean isSelected,
329                                                 boolean expanded,
330                                                 boolean leaf,
331                                                 int row)
332     {
333 
334         
335         lastKnownValue = value;
336         lastKnownComponent = tree;
337 		
338 		return getEditorForContext( 
339 			tree, value, row, 0, isSelected, true, expanded, leaf );
340     }                                                
341                                                 
342 /***
343 * This default implementation returns a JPanel that is configured by
344 * calling configureComponentForContext.  
345 * @return An component that is used to render content.
346 */
347     public Component getRendererForContext(
348         JComponent container, Object value,
349         int row, int column,
350         boolean isSelected, boolean hasFocus, 
351         boolean isExpanded, boolean isLeaf )
352     {
353 
354 		
355 		configureComponentForContext( rendererPanel, rendererButton, rendererLabel, 
356             container, value, row, column,
357 			isSelected, hasFocus, isExpanded, isLeaf );
358 		return rendererPanel;
359     }
360 
361 /***
362 * This method returns a separate component that should be visually
363 * identical to the renderer component.  We can't simply reuse the
364 * renderer component because the renderer is still used to paint
365 * the table while the editor component is displayed. Clicks are 
366 * received on this component.
367 * This default implementation returns a JPanel that is configured by
368 * calling configureComponentForContext.  
369 * @return A component used to receive clicks on the cell.
370 */
371     public Component getEditorForContext(
372         JComponent container, Object value,
373         int row, int column,
374         boolean isSelected, boolean hasFocus, 
375         boolean isExpanded, boolean isLeaf )
376     {
377 		configureComponentForContext( editorPanel, editorButton, editorLabel, 
378             container, value, row, column,
379 			true, hasFocus, isExpanded, isLeaf ); // editor should always be selected
380 
381 		return editorPanel;
382     }
383 
384 /***
385 * Called to configure components
386 */ 
387 	protected void configureComponentForContext(
388         JPanel component, JButton iconButton, JLabel label, 
389         JComponent container, Object value, 
390         int row, int column,
391         boolean isSelected, boolean hasFocus, 
392         boolean isExpanded, boolean isLeaf )
393 	{
394         if (hasFocus) 
395         {
396             if ( container instanceof JTable )
397             {
398                 component.setBorder( 
399                     UIManager.getBorder("Table.focusCellHighlightBorder") );
400             }
401             else
402             {
403                 component.setBorder( noFocusBorder );   
404             }
405 
406             if ( container instanceof JTree ) // was: (false)
407             {   
408                 label.setBorder( treeFocusBorder );
409             }
410             else
411             {
412                 label.setBorder( noFocusBorder );
413             }
414         } 
415         else 
416         {
417             label.setBorder(noFocusBorder);
418             component.setBorder(noFocusBorder);
419         }
420         
421         if (isSelected) 
422         {
423             if ( container instanceof JTree )
424             {
425                 label.setOpaque( true );
426                 label.setForeground(UIManager.getColor("Tree.selectionForeground"));
427                 label.setBackground(UIManager.getColor("Tree.selectionBackground"));
428                 component.setBackground(container.getBackground());
429             }
430             else if ( container instanceof JTable )
431             {
432                 label.setOpaque( false );
433                 label.setForeground( ((JTable)container).getSelectionForeground() );
434                 component.setBackground(((JTable)container).getSelectionBackground());
435             }
436             else
437             {
438                 label.setOpaque( false );
439                 label.setForeground(UIManager.getColor("Table.selectionForeground"));
440                 component.setBackground(UIManager.getColor("Table.selectionBackground"));
441             }
442         }
443         else 
444         {
445             label.setOpaque( false );
446             label.setForeground(container.getForeground());
447             component.setBackground(container.getBackground());
448         }
449 
450         label.setFont(container.getFont());
451 
452         Icon icon = getIconForContext( 
453 			container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf );
454         iconButton.setIcon( icon );
455         if ( !isClickable() )
456         {
457             iconButton.setDisabledIcon( icon );
458         }
459 
460         String text = getStringForContext( 
461 			container, value, row, column, isSelected, hasFocus, isExpanded, isLeaf );
462             
463 		if ( ( text == null ) || ( "".equals( text ) ) )
464 		{
465             if ( ! label.getText().equals( "" ) )
466     			label.setText( "" );
467 		}
468 		else
469 		{
470             if ( ! label.getText().equals( text ) )
471     			label.setText( text );
472 		}
473 	}
474 	
475 /***
476 * Override this method to provide an icon for the renderer.
477 * This default implementation returns null.
478 * @return An icon to be displayed in the cell, or null to omit the
479 * icon from the cell.
480 */
481     public Icon getIconForContext(
482         JComponent container, Object value,
483         int row, int column,
484         boolean isSelected, boolean hasFocus, 
485         boolean isExpanded, boolean isLeaf )
486     {
487         return null;
488     }
489 
490 /***
491 * Override this method to provide a string for the renderer.
492 * This default implementation returns toString on the value parameter,
493 * or null if the value is null.
494 * @return A string to be displayed in the cell.
495 */
496     public String getStringForContext(
497         JComponent container, Object value,
498         int row, int column,
499         boolean isSelected, boolean hasFocus, 
500         boolean isExpanded, boolean isLeaf )
501     {
502 		if ( value == null ) return null;
503         return value.toString();
504     }
505 	
506 	/***
507 	* Adds the specified listener to the list of listeners
508 	* to be notified when the button receives a click.
509 	*/
510 	public void addActionListener( ActionListener aListener )
511 	{
512 		if ( actionListeners == null ) 
513 		{
514 			actionListeners = new Vector( 2 );	
515 		}
516 		actionListeners.add( aListener );
517 	}
518 	
519 	/***
520 	* Removes the specified listener from the list of listeners
521 	* to be notified when the button receives a click.
522 	*/
523 	public void removeActionListener( ActionListener aListener )
524 	{
525 		actionListeners.remove( aListener );
526 	}
527 	
528 	/***
529 	* Broadcasts the specified action event to all listeners.
530 	*/
531 	protected void fireActionEvent( ActionEvent anActionEvent )
532 	{
533 		if ( actionListeners == null ) return;
534 		// vector's enumeration is not fail-fast
535 		Enumeration e = actionListeners.elements();
536 		while ( e.hasMoreElements() )
537 		{
538 			((ActionListener)e.nextElement()).actionPerformed( anActionEvent );
539 		}
540 	}
541 	
542 	/***
543 	* Returns the action command broadcast when this icon
544     * receives a click.  Defaults to CLICKED.
545 	*/
546 	public String getActionCommand()
547 	{
548 		return actionCommand;
549 	}
550 
551 	/***
552 	* Sets the action command broadcast when this table
553 	* receives a double click.
554 	*/	
555 	public void setActionCommand( String anActionCommand )
556 	{
557 		actionCommand = anActionCommand;	
558 	}
559 	
560 // interface CellEditor
561 
562 	/***
563 	* Returns lastKnownValue, although this should not be called.
564 	*/
565 	public Object getCellEditorValue()
566 	{
567 		return lastKnownValue;
568 	}
569 	
570 	/***
571 	* Returns true.
572 	*/
573 	public boolean isCellEditable(EventObject anEvent)
574 	{
575         return true;
576 	}
577 	
578 	/***
579 	* Returns true.
580 	*/
581 	public boolean shouldSelectCell(EventObject anEvent)
582 	{
583 		return true;
584 	}
585 	
586 	/***
587 	* Fires an editing stopped event and returns true.
588 	*/
589 	public boolean stopCellEditing()
590 	{
591 		ChangeEvent event = new ChangeEvent( this );
592 		if ( cellEditorListeners != null )
593 		{
594 			// vector's enumeration is not fail-fast
595 			Enumeration e = cellEditorListeners.elements();
596 			while ( e.hasMoreElements() )
597 			{
598                 // broadcast editing cancelled since no value is edited
599 				((CellEditorListener)e.nextElement()).editingCanceled( event );
600 			}
601 		}
602         lastKnownComponent = null;		
603 		return true;
604 	}
605 	
606 	/***
607 	* Fires an editing cancelled event and returns true.
608 	*/
609 	public void cancelCellEditing()
610 	{                
611 		//HACK: cancelCellEditing() causes for the dragGesture
612 		//to be NOT recognized AT ALL since on the next MOUSE_PRESSED
613 		//the cell editor first needs to startEditing() [if in the tree
614 		//the CellEditorListener is a BasicTreeUI class] 
615 		//(before the drag gesture event can be recognized).        
616         //Also the lastKnownComponent should not be set to null, 
617         //none of the mouse events won't dispathced to the lastKnownComponent
618         //in that case.		
619 		
620 		//Not calling it at all does seem to fix it, but what are the
621 		//consequences???
622 		//Trying to workaround this might solve it, but it introduces having
623 		//an extra listener (a MouseMotionListnener), which might be wasteful
624 		//(i.e. only if a Mouse_dragged event has been initiated, but DragGesture
625 		//hasn't been recognized, postpone calling this till finish the DnD event)
626 		//But what if do DnD and not exited ??? The mouseExited() is not called
627 		//anyway until the DnD event is done.				
628 				        
629 		ChangeEvent event = new ChangeEvent( this );
630 		if ( cellEditorListeners == null ) return;
631 		// vector's enumeration is not fail-fast
632 		Enumeration e = cellEditorListeners.elements();
633 		
634 		while ( e.hasMoreElements() )
635 		{            
636 			((CellEditorListener)e.nextElement()).editingCanceled( event );
637 		}                
638         
639         //DO not nullify this 
640         lastKnownComponent = null;                       
641 	}
642 	
643 	/***
644 	* Adds the specified listener to the list of listeners
645 	* to be notified when the table receives a double click.
646 	*/
647 	public void addCellEditorListener( CellEditorListener aListener )
648 	{		
649 		if ( cellEditorListeners == null ) 
650 		{
651 			cellEditorListeners = new Vector( 2 );	
652 		}
653 		cellEditorListeners.add( aListener );	
654 	}
655 	
656 	/***
657 	* Removes the specified listener from the list of listeners
658 	* to be notified when the table receives a double click.
659 	*/
660 	public void removeCellEditorListener( CellEditorListener aListener )
661 	{
662 		cellEditorListeners.remove( aListener );
663 	}
664     
665 // interface ActionListener
666     
667     /***
668     * Puts ourself on the end of the event queue for 
669     * firing our action event to all listeners.
670     */
671     public void actionPerformed( ActionEvent evt )
672     {        
673         //commented out in order NOT to set lastKnownComponent to null, since
674         //if this object is inside a table or tree, relying on getCellEditorValue() 
675         //to return the currently edited object
676         //cancelCellEditing();        
677         
678         SwingUtilities.invokeLater( this );
679     }
680 	
681 // interface Runnable
682 	
683 	/***
684 	* Fires the action event to all listeners.  
685 	* This is triggered by a click on the icon.
686 	*/
687 	public void run()
688 	{
689 		fireActionEvent( new ActionEvent( this, 0, getActionCommand() ) );	
690 	}
691 
692 // interface MouseListener 
693     
694     /***
695     * Passes through editor mouse clicks to last known component.
696     * (left click only)
697     */
698 	public void mouseClicked(MouseEvent e)
699     {                        
700         if(lastKnownComponent != null){
701             Object source = e.getSource();            
702             if(source != null)
703             {
704                 if(source == editorPanel)
705                 {
706                     lastKnownComponent.dispatchEvent(                 
707                             SwingUtilities.convertMouseEvent(                    
708                                 editorPanel, e, lastKnownComponent ) );                     
709 
710                 }                
711                 else if(source == editorLabel)
712                 {
713                     lastKnownComponent.dispatchEvent(                 
714                         SwingUtilities.convertMouseEvent(                    
715                             editorLabel, e, lastKnownComponent ) );         
716                 }           
717 
718                 else if(source == editorButton)
719                 {
720                     lastKnownComponent.dispatchEvent(                 
721                         SwingUtilities.convertMouseEvent(                    
722                             editorButton, e, lastKnownComponent ) );         
723                 }           
724             }                
725         }                
726     }
727 
728     /***
729     * Passes through editor right-mouse (popup trigger) mouse events to last known component.
730     * Needed for possible displaying of popup menus on right click 
731     */
732     public void mousePressed(MouseEvent e)
733     {                
734         if ( e.isPopupTrigger() )
735         {
736             if(lastKnownComponent != null)
737             {
738                 Object source = e.getSource();            
739                 if(source != null)
740                 {
741                     if(source == editorPanel)
742                     {                     
743                         lastKnownComponent.dispatchEvent(                 
744                                 SwingUtilities.convertMouseEvent(                    
745                                     editorPanel, e, lastKnownComponent ) );                     
746                     }
747                     else if(source == editorLabel)
748                     {
749                         lastKnownComponent.dispatchEvent(                 
750                             SwingUtilities.convertMouseEvent(                    
751                                 editorLabel, e, lastKnownComponent ) );         
752                     }           
753     
754                     else if(source == editorButton)
755                     {   
756                         lastKnownComponent.dispatchEvent(                 
757                             SwingUtilities.convertMouseEvent(                    
758                                 editorButton, e, lastKnownComponent ) );         
759                     }           
760                 }                
761             }
762         }                
763     }
764 
765     /***
766     * Does nothing.
767     */
768     public void mouseReleased(MouseEvent e)
769     {                      
770         if ( e.isPopupTrigger() )
771         {
772             if(lastKnownComponent != null){        
773     
774                 Object source = e.getSource();            
775                 if(source != null)
776                 {
777                     if(source == editorPanel) 
778                     {                    
779                         lastKnownComponent.dispatchEvent(                 
780                                 SwingUtilities.convertMouseEvent(                    
781                                     editorPanel, e, lastKnownComponent ) );                     
782                     }            
783         
784                     else if(source == editorLabel) 
785                     {
786                         lastKnownComponent.dispatchEvent(                 
787                             SwingUtilities.convertMouseEvent(                    
788                                 editorLabel, e, lastKnownComponent ) );         
789                     }           
790     
791                     else if(source == editorButton) 
792                     {
793                         lastKnownComponent.dispatchEvent(                 
794                             SwingUtilities.convertMouseEvent(                    
795                                 editorButton, e, lastKnownComponent ) );         
796                     }           
797                 }                
798             }
799         }                
800     }
801 
802     /***
803     * Does nothing.
804     */
805     public void mouseEntered(MouseEvent e)
806     {
807     }
808     
809     /***
810     * Cancels cell editing.
811     */
812     public void mouseExited(MouseEvent e)
813     {        
814         Object source = e.getSource();
815         if(source != null && source instanceof JComponent){
816             //need to convert the Point from the source's coordinate system to editorPanel's coordinate system.
817             //(note that simple editorPanel.contains(e.getPoint()) fails if source is editorButton)
818             
819             Point convertedPoint = SwingUtilities.convertPoint((JComponent) source, e.getPoint(), editorPanel); 
820             
821             //check if exited from editorButton, but still inside the editorPanel (works for editorLabel as well)            
822             if(!editorPanel.contains(convertedPoint)){
823 
824                 //This was getting called before, but it interfers with the DnD operation
825                 cancelCellEditing();
826             }
827         }      
828     }
829     
830     /* This might be redundant    
831     public void cleanUp(){
832         
833         //since cancelCellEditing() was never called call it now                
834         cancelCellEditing();
835         stopCellEditing();
836         
837         editorButton.removeActionListener( this );      
838         editorPanel.removeMouseListener( this ); 
839         editorLabel.removeMouseListener( this );    
840         editorButton.removeMouseListener( this );                            
841         lastKnownComponent = null;
842         lastKnownValue = null;
843     }        
844     */
845 }